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,
very_old_frame: Option,
current_frame_holder: Option,
- show_poster: bool,
+ ///
+ poster_frame: Option,
}
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) {
- if let Some(image_key) = image.id {
- self.current_frame = Some(MediaFrame {
+ fn set_poster_frame(&mut self, image: Option>) {
+ 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::().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 {
}
///
- pub(crate) fn process_poster_image_loaded(&self, image: Arc) {
- if !self.show_poster.get() {
- return;
- }
+ pub(crate) fn set_poster_frame(&self, image: Option>) {
+ 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::().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 {
self.video_renderer
.lock()
@@ -2106,8 +2097,21 @@ impl HTMLMediaElement {
.map(|holder| holder.get_frame())
}
- pub(crate) fn get_current_frame_data(&self) -> Option {
- self.video_renderer.lock().unwrap().current_frame
+ /// Gets the current frame of the video element to present, if any.
+ ///
+ pub(crate) fn get_current_frame_to_present(&self) -> Option {
+ 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 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 {
let frame = self.htmlmediaelement.get_current_frame();
if frame.is_some() {
@@ -163,22 +166,31 @@ impl HTMLVideoElement {
}
///
- 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 {
///
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
//
@@ -264,12 +279,15 @@ impl HTMLVideoElement {
self.generation_id.get()
}
+ ///
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