diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 3d6d8f2938d..060b5358a8c 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -170,7 +170,8 @@ pub(crate) struct MediaFrameRenderer {
     old_frame: Option<ImageKey>,
     very_old_frame: Option<ImageKey>,
     current_frame_holder: Option<FrameHolder>,
-    show_poster: bool,
+    /// <https://html.spec.whatwg.org/multipage/#poster-frame>
+    poster_frame: Option<MediaFrame>,
 }
 
 impl MediaFrameRenderer {
@@ -182,29 +183,23 @@ impl MediaFrameRenderer {
             old_frame: None,
             very_old_frame: None,
             current_frame_holder: None,
-            show_poster: false,
+            poster_frame: None,
         }
     }
 
-    fn render_poster_frame(&mut self, image: Arc<RasterImage>) {
-        if let Some(image_key) = image.id {
-            self.current_frame = Some(MediaFrame {
+    fn set_poster_frame(&mut self, image: Option<Arc<RasterImage>>) {
+        self.poster_frame = image.and_then(|image| {
+            image.id.map(|image_key| MediaFrame {
                 image_key,
                 width: image.metadata.width as i32,
                 height: image.metadata.height as i32,
-            });
-            self.show_poster = true;
-        }
+            })
+        });
     }
 }
 
 impl VideoFrameRenderer for MediaFrameRenderer {
     fn render(&mut self, frame: VideoFrame) {
-        // Don't render new frames if the poster should be shown
-        if self.show_poster {
-            return;
-        }
-
         let mut updates = vec![];
 
         if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) {
@@ -730,7 +725,7 @@ impl HTMLMediaElement {
                 self.paused.set(false);
                 // Step 2
                 if self.show_poster.get() {
-                    self.set_show_poster(false);
+                    self.show_poster.set(false);
                     self.time_marches_on();
                 }
                 // Step 3
@@ -753,7 +748,7 @@ impl HTMLMediaElement {
         self.network_state.set(NetworkState::NoSource);
 
         // Step 2.
-        self.set_show_poster(true);
+        self.show_poster.set(true);
 
         // Step 3.
         self.delay_load_event(true, can_gc);
@@ -1081,7 +1076,7 @@ impl HTMLMediaElement {
                     this.network_state.set(NetworkState::NoSource);
 
                     // Step 4.
-                    this.set_show_poster(true);
+                    this.show_poster.set(true);
 
                     // Step 5.
                     this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
@@ -1198,8 +1193,8 @@ impl HTMLMediaElement {
                 self.fulfill_in_flight_play_promises(|| ());
             }
 
-            // Step 6.7.
-            if !self.seeking.get() {
+            // Step 6.7. If seeking is true, set it to false.
+            if self.seeking.get() {
                 self.seeking.set(false);
             }
 
@@ -1298,7 +1293,7 @@ impl HTMLMediaElement {
     // https://html.spec.whatwg.org/multipage/#dom-media-seek
     fn seek(&self, time: f64, _approximate_for_speed: bool) {
         // Step 1.
-        self.set_show_poster(false);
+        self.show_poster.set(false);
 
         // Step 2.
         if self.ready_state.get() == ReadyState::HaveNothing {
@@ -1403,19 +1398,14 @@ impl HTMLMediaElement {
     }
 
     /// <https://html.spec.whatwg.org/multipage/#poster-frame>
-    pub(crate) fn process_poster_image_loaded(&self, image: Arc<RasterImage>) {
-        if !self.show_poster.get() {
-            return;
-        }
+    pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
+        let queue_postershown_event = pref!(media_testing_enabled) && image.is_some();
 
-        // Step 6.
-        self.handle_resize(Some(image.metadata.width), Some(image.metadata.height));
-        self.video_renderer
-            .lock()
-            .unwrap()
-            .render_poster_frame(image);
+        self.video_renderer.lock().unwrap().set_poster_frame(image);
 
-        if pref!(media_testing_enabled) {
+        self.upcast::<Node>().dirty(NodeDamage::Other);
+
+        if queue_postershown_event {
             self.owner_global()
                 .task_manager()
                 .media_element_task_source()
@@ -2097,6 +2087,7 @@ impl HTMLMediaElement {
         }
     }
 
+    /// Gets the video frame at the current playback position.
     pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
         self.video_renderer
             .lock()
@@ -2106,8 +2097,21 @@ impl HTMLMediaElement {
             .map(|holder| holder.get_frame())
     }
 
-    pub(crate) fn get_current_frame_data(&self) -> Option<MediaFrame> {
-        self.video_renderer.lock().unwrap().current_frame
+    /// Gets the current frame of the video element to present, if any.
+    /// <https://html.spec.whatwg.org/multipage/#the-video-element:the-video-element-7>
+    pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
+        let (current_frame, poster_frame) = {
+            let renderer = self.video_renderer.lock().unwrap();
+            (renderer.current_frame, renderer.poster_frame)
+        };
+
+        // If the show poster flag is set (or there is no current video frame to
+        // present) AND there is a poster frame, present that.
+        if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
+            return poster_frame;
+        }
+
+        current_frame
     }
 
     pub(crate) fn clear_current_frame_data(&self) {
@@ -2153,13 +2157,6 @@ impl HTMLMediaElement {
         self.duration.set(duration);
     }
 
-    /// Sets a new value for the show_poster propperty. Updates video_rederer
-    /// with the new value.
-    pub(crate) fn set_show_poster(&self, show_poster: bool) {
-        self.show_poster.set(show_poster);
-        self.video_renderer.lock().unwrap().show_poster = show_poster;
-    }
-
     pub(crate) fn reset(&self) {
         if let Some(ref player) = *self.player.borrow() {
             if let Err(e) = player.lock().unwrap().stop() {
@@ -2378,7 +2375,7 @@ impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
 
             // Step 6.2.
             if self.show_poster.get() {
-                self.set_show_poster(false);
+                self.show_poster.set(false);
                 self.time_marches_on();
             }
 
diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs
index 622e5079ee9..8c03b5f8c91 100644
--- a/components/script/dom/htmlvideoelement.rs
+++ b/components/script/dom/htmlvideoelement.rs
@@ -133,6 +133,9 @@ impl HTMLVideoElement {
         sent_resize
     }
 
+    /// Gets the copy of the video frame at the current playback position,
+    /// if that is available, or else (e.g. when the video is seeking or buffering)
+    /// its previous appearance, if any.
     pub(crate) fn get_current_frame_data(&self) -> Option<Snapshot> {
         let frame = self.htmlmediaelement.get_current_frame();
         if frame.is_some() {
@@ -163,22 +166,31 @@ impl HTMLVideoElement {
     }
 
     /// <https://html.spec.whatwg.org/multipage/#poster-frame>
-    fn fetch_poster_frame(&self, poster_url: &str, can_gc: CanGc) {
-        // Step 1.
+    fn update_poster_frame(&self, poster_url: Option<&str>, can_gc: CanGc) {
+        // Step 1. If there is an existing instance of this algorithm running
+        // for this video element, abort that instance of this algorithm without
+        // changing the poster frame.
         self.generation_id.set(self.generation_id.get() + 1);
 
-        // Step 2.
-        if poster_url.is_empty() {
+        // Step 2. If the poster attribute's value is the empty string or
+        // if the attribute is absent, then there is no poster frame; return.
+        if poster_url.is_none_or(|url| url.is_empty()) {
+            self.htmlmediaelement.set_poster_frame(None);
             return;
         }
 
-        // Step 3.
-        let poster_url = match self.owner_document().url().join(poster_url) {
+        // Step 3. Let url be the result of encoding-parsing a URL given
+        // the poster attribute's value, relative to the element's node
+        // document.
+        // Step 4. If url is failure, then return. There is no poster frame.
+        let poster_url = match self.owner_document().url().join(poster_url.unwrap()) {
             Ok(url) => url,
-            Err(_) => return,
+            Err(_) => {
+                self.htmlmediaelement.set_poster_frame(None);
+                return;
+            },
         };
 
-        // Step 4.
         // We use the image cache for poster frames so we save as much
         // network activity as possible.
         let window = self.owner_window();
@@ -228,7 +240,9 @@ impl HTMLVideoElement {
 
     /// <https://html.spec.whatwg.org/multipage/#poster-frame>
     fn do_fetch_poster_frame(&self, poster_url: ServoUrl, id: PendingImageId, can_gc: CanGc) {
-        // Continuation of step 4.
+        // Step 5. Let request be a new request whose URL is url, client is the element's node
+        // document's relevant settings object, destination is "image", initiator type is "video",
+        // credentials mode is "include", and whose use-URL-credentials flag is set.
         let document = self.owner_document();
         let request = RequestBuilder::new(
             Some(document.webview_id()),
@@ -243,7 +257,8 @@ impl HTMLVideoElement {
         .insecure_requests_policy(document.insecure_requests_policy())
         .has_trustworthy_ancestor_origin(document.has_trustworthy_ancestor_origin())
         .policy_container(document.policy_container().to_owned());
-        // Step 5.
+
+        // Step 6. Fetch request. This must delay the load event of the element's node document.
         // This delay must be independent from the ones created by HTMLMediaElement during
         // its media load algorithm, otherwise a code like
         // <video poster="poster.png"></video>
@@ -264,12 +279,15 @@ impl HTMLVideoElement {
         self.generation_id.get()
     }
 
+    /// <https://html.spec.whatwg.org/multipage/#poster-frame>
     fn process_image_response(&self, response: ImageResponse, can_gc: CanGc) {
+        // Step 7. If an image is thus obtained, the poster frame is that image.
+        // Otherwise, there is no poster frame.
         match response {
             ImageResponse::Loaded(image, url) => {
                 debug!("Loaded poster image for video element: {:?}", url);
                 match image.as_raster_image() {
-                    Some(image) => self.htmlmediaelement.process_poster_image_loaded(image),
+                    Some(image) => self.htmlmediaelement.set_poster_frame(Some(image)),
                     None => warn!("Vector images are not yet supported in video poster"),
                 }
                 LoadBlocker::terminate(&self.load_blocker, can_gc);
@@ -277,6 +295,7 @@ impl HTMLVideoElement {
             ImageResponse::MetadataLoaded(..) => {},
             // The image cache may have loaded a placeholder for an invalid poster url
             ImageResponse::PlaceholderLoaded(..) | ImageResponse::None => {
+                self.htmlmediaelement.set_poster_frame(None);
                 // A failed load should unblock the document load.
                 LoadBlocker::terminate(&self.load_blocker, can_gc);
             },
@@ -340,10 +359,9 @@ impl VirtualMethods for HTMLVideoElement {
 
         if attr.local_name() == &local_name!("poster") {
             if let Some(new_value) = mutation.new_value(attr) {
-                self.fetch_poster_frame(&new_value, CanGc::note())
+                self.update_poster_frame(Some(&new_value), CanGc::note())
             } else {
-                self.htmlmediaelement.clear_current_frame_data();
-                self.htmlmediaelement.set_show_poster(false);
+                self.update_poster_frame(None, CanGc::note())
             }
         };
     }
@@ -521,7 +539,7 @@ impl LayoutHTMLVideoElementHelpers for LayoutDom<'_, HTMLVideoElement> {
         let video = self.unsafe_get();
 
         // Get the current frame being rendered.
-        let current_frame = video.htmlmediaelement.get_current_frame_data();
+        let current_frame = video.htmlmediaelement.get_current_frame_to_present();
 
         // This value represents the natural width and height of the video.
         // It may exist even if there is no current frame (for example, after the
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html.ini
index 3456a9eac35..ab8397b7990 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html.ini
@@ -1,6 +1,3 @@
 [createImageBitmap-colorSpaceConversion.html]
   [createImageBitmap from a Blob, and drawImage on the created ImageBitmap with colorSpaceConversion: none]
     expected: FAIL
-
-  [createImageBitmap from a Video, and drawImage on the created ImageBitmap with colorSpaceConversion: none]
-    expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini
index 0b2c6bcd0ff..22ec677f4ea 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini
@@ -1,45 +1,27 @@
 [createImageBitmap-origin.sub.html]
-  [redirected to cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage]
-    expected: FAIL
-
   [redirected to cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
   [unclean HTMLCanvasElement: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
-  [cross-origin HTMLVideoElement: origin unclear getImageData]
-    expected: FAIL
-
   [cross-origin SVGImageElement: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
   [cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
-  [redirected to same-origin HTMLVideoElement: origin unclear getImageData]
-    expected: FAIL
-
   [cross-origin SVGImageElement: origin unclear 2dContext.drawImage]
     expected: FAIL
 
   [cross-origin HTMLImageElement: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
-  [redirected to same-origin HTMLVideoElement: origin unclear 2dContext.drawImage]
-    expected: FAIL
-
   [unclean ImageBitmap: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
   [redirected to same-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap]
     expected: FAIL
 
-  [redirected to cross-origin HTMLVideoElement: origin unclear getImageData]
-    expected: FAIL
-
-  [cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage]
-    expected: FAIL
-
   [cross-origin SVGImageElement: origin unclear getImageData]
     expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-resolves-in-task.any.js.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-resolves-in-task.any.js.ini
index d414b0aa93f..f94f75164bf 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-resolves-in-task.any.js.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-resolves-in-task.any.js.ini
@@ -1,10 +1,4 @@
 [createImageBitmap-resolves-in-task.any.html]
-  [createImageBitmap with an HTMLVideoElement source should resolve async]
-    expected: FAIL
-
-  [createImageBitmap with an HTMLVideoElement from a data URL source should resolve async]
-    expected: FAIL
-
   [createImageBitmap with a vector HTMLImageElement source should resolve async]
     expected: FAIL
 
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini
index fd32af77cd5..e710fa8be3e 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini
@@ -2,12 +2,6 @@
   [Serialize ImageBitmap created from a vector SVGImageElement]
     expected: FAIL
 
-  [Serialize ImageBitmap created from an HTMLVideoElement]
-    expected: FAIL
-
-  [Serialize ImageBitmap created from an HTMLVideoElement from a data URL]
-    expected: FAIL
-
   [Serialize ImageBitmap created from a vector HTMLImageElement]
     expected: FAIL
 
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini
index 1c8de6ec5a9..a0c2c0b3a83 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini
@@ -8,11 +8,5 @@
   [Transfer ImageBitmap created from a Blob]
     expected: FAIL
 
-  [Transfer ImageBitmap created from an HTMLVideoElement from a data URL]
-    expected: FAIL
-
   [Transfer ImageBitmap created from a bitmap SVGImageElement]
     expected: FAIL
-
-  [Transfer ImageBitmap created from an HTMLVideoElement]
-    expected: FAIL
diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini
index ac0696f9b90..3a3b73a56cb 100644
--- a/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini
+++ b/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini
@@ -2,23 +2,5 @@
   [cross-origin SVGImageElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
     expected: FAIL
 
-  [cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
-    expected: FAIL
-
-  [redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
-    expected: FAIL
-
-  [redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
-    expected: FAIL
-
   [cross-origin SVGImageElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
     expected: FAIL
-
-  [cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
-    expected: FAIL
-
-  [redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
-    expected: FAIL
-
-  [redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
-    expected: FAIL