summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/embedded-content
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/semantics/embedded-content')
-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
955 files changed, 30101 insertions, 0 deletions
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="passfield-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アパートfi"><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="passfield-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アパートfi"><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>