Handle multiple image decode promises

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>
This commit is contained in:
Bentaimia Haddadi 2024-06-06 18:31:54 +02:00
parent 1782dea6ce
commit 46b054a744
3 changed files with 68 additions and 79 deletions

View file

@ -570,25 +570,6 @@ impl ImageCache for ImageCacheImpl {
cache_result cache_result
} }
fn decode(
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
) -> Option<Image> {
let mut store = self.store.lock().unwrap();
let result = store
.pending_loads
.get_cached(url.clone(), origin.clone(), cors_setting);
match result {
CacheResult::Hit(key, pl) => {
return decode_bytes_sync(key, &pl.bytes.as_slice(), pl.cors_status).image
},
CacheResult::Miss(_) => return None,
}
}
/// Add a new listener for the given pending image id. If the image is already present, /// Add a new listener for the given pending image id. If the image is already present,
/// the responder will still receive the expected response. /// the responder will still receive the expected response.
fn add_listener(&self, id: PendingImageId, listener: ImageResponder) { fn add_listener(&self, id: PendingImageId, listener: ImageResponder) {

View file

@ -174,7 +174,7 @@ pub struct HTMLImageElement {
source_set: DomRefCell<SourceSet>, source_set: DomRefCell<SourceSet>,
last_selected_source: DomRefCell<Option<USVString>>, last_selected_source: DomRefCell<Option<USVString>>,
#[ignore_malloc_size_of = "promises are hard"] #[ignore_malloc_size_of = "promises are hard"]
image_decode_promise: DomRefCell<Rc<Promise>>, image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
} }
impl HTMLImageElement { impl HTMLImageElement {
@ -192,8 +192,14 @@ impl HTMLImageElement {
match self.current_request.borrow().state { match self.current_request.borrow().state {
// If image's current request's state is broken, then throw an "InvalidStateError" DOMException. // If image's current request's state is broken, then throw an "InvalidStateError" DOMException.
State::Broken => Err(Error::InvalidState), State::Broken => {
State::CompletelyAvailable => Ok(true), self.reject_image_decode_promises();
Err(Error::InvalidState)
},
State::CompletelyAvailable => {
self.resolve_image_decode_promises();
Ok(true)
},
// If image is not fully decodable, then return bad. // If image is not fully decodable, then return bad.
State::PartiallyAvailable | State::Unavailable => Ok(false), State::PartiallyAvailable | State::Unavailable => Ok(false),
} }
@ -420,6 +426,7 @@ impl HTMLImageElement {
// Steps common to when an image has been loaded. // Steps common to when an image has been loaded.
fn handle_loaded_image(&self, image: Arc<Image>, url: ServoUrl) { fn handle_loaded_image(&self, image: Arc<Image>, url: ServoUrl) {
self.resolve_image_decode_promises();
self.current_request.borrow_mut().metadata = Some(ImageMetadata { self.current_request.borrow_mut().metadata = Some(ImageMetadata {
height: image.height, height: image.height,
width: image.width, width: image.width,
@ -504,29 +511,6 @@ impl HTMLImageElement {
) { ) {
match image { match image {
ImageResponse::Loaded(image, url) | ImageResponse::PlaceholderLoaded(image, url) => { ImageResponse::Loaded(image, url) | ImageResponse::PlaceholderLoaded(image, url) => {
// Step 2.2 of <https://html.spec.whatwg.org/multipage/#dom-img-decode>
let window = window_from_node(self);
let image_cache = window.image_cache();
let decode_result = image_cache.decode(
url.clone(),
window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()),
);
match decode_result {
Some(_) => {
self.image_decode_promise.borrow().resolve_native(&());
},
None => {
self.image_decode_promise
.borrow()
.reject_native(&DOMException::new(
&self.global(),
DOMErrorName::EncodingError,
));
},
}
self.pending_request.borrow_mut().metadata = Some(ImageMetadata { self.pending_request.borrow_mut().metadata = Some(ImageMetadata {
height: image.height, height: image.height,
width: image.width, width: image.width,
@ -546,6 +530,10 @@ impl HTMLImageElement {
/// <https://html.spec.whatwg.org/multipage/#abort-the-image-request> /// <https://html.spec.whatwg.org/multipage/#abort-the-image-request>
fn abort_request(&self, state: State, request: ImageRequestPhase) { fn abort_request(&self, state: State, request: ImageRequestPhase) {
if matches!(state, State::Broken | State::Unavailable) {
self.reject_image_decode_promises();
}
let mut request = match request { let mut request = match request {
ImageRequestPhase::Current => self.current_request.borrow_mut(), ImageRequestPhase::Current => self.current_request.borrow_mut(),
ImageRequestPhase::Pending => self.pending_request.borrow_mut(), ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
@ -861,7 +849,14 @@ impl HTMLImageElement {
self.image_request.set(ImageRequestPhase::Pending); self.image_request.set(ImageRequestPhase::Pending);
self.init_image_request(&mut pending_request, url, src); self.init_image_request(&mut pending_request, url, src);
}, },
(_, State::Broken) | (_, State::Unavailable) => { (_, State::Broken) => {
self.reject_image_decode_promises();
// Step 17
current_request.current_pixel_density = Some(selected_pixel_density);
self.init_image_request(&mut current_request, url, src);
},
(_, State::Unavailable) => {
// Step 17 // Step 17
current_request.current_pixel_density = Some(selected_pixel_density); current_request.current_pixel_density = Some(selected_pixel_density);
self.init_image_request(&mut current_request, url, src); self.init_image_request(&mut current_request, url, src);
@ -1188,6 +1183,38 @@ impl HTMLImageElement {
} }
} }
// Step 2 for <https://html.spec.whatwg.org/multipage/#dom-img-decode>
fn react_to_decode_image_sync_steps(&self) {
let document = document_from_node(self);
// Step 2.1 of <https://html.spec.whatwg.org/multipage/#dom-img-decode>
if !document.is_fully_active() ||
matches!(self.current_request.borrow().state, State::Broken)
{
self.reject_image_decode_promises();
} else if matches!(
self.current_request.borrow().state,
State::CompletelyAvailable
) {
self.resolve_image_decode_promises();
}
}
fn resolve_image_decode_promises(&self) {
for promise in self.image_decode_promises.borrow().iter() {
promise.resolve_native(&());
}
}
fn reject_image_decode_promises(&self) {
let document = document_from_node(self);
for promise in self.image_decode_promises.borrow().iter() {
promise.reject_native(&DOMException::new(
&document.global(),
DOMErrorName::EncodingError,
));
}
}
/// Step 15 for <https://html.spec.whatwg.org/multipage/#img-environment-changes> /// Step 15 for <https://html.spec.whatwg.org/multipage/#img-environment-changes>
fn finish_reacting_to_environment_change( fn finish_reacting_to_environment_change(
&self, &self,
@ -1276,7 +1303,7 @@ impl HTMLImageElement {
generation: Default::default(), generation: Default::default(),
source_set: DomRefCell::new(SourceSet::new()), source_set: DomRefCell::new(SourceSet::new()),
last_selected_source: DomRefCell::new(None), last_selected_source: DomRefCell::new(None),
image_decode_promise: DomRefCell::new(Promise::new(&document.global())), image_decode_promises: DomRefCell::new(vec![]),
} }
} }
@ -1377,7 +1404,6 @@ pub enum ImageElementMicrotask {
}, },
DecodeTask { DecodeTask {
elem: DomRoot<HTMLImageElement>, elem: DomRoot<HTMLImageElement>,
document: DomRoot<Document>,
}, },
} }
@ -1400,26 +1426,8 @@ impl MicrotaskRunnable for ImageElementMicrotask {
} => { } => {
elem.react_to_environment_changes_sync_steps(*generation); elem.react_to_environment_changes_sync_steps(*generation);
}, },
ImageElementMicrotask::DecodeTask { ImageElementMicrotask::DecodeTask { ref elem } => {
ref elem, elem.react_to_decode_image_sync_steps();
ref document,
} => {
// Step 2.1 of <https://html.spec.whatwg.org/multipage/#dom-img-decode>
let available = match elem.current_request.borrow().state {
State::Unavailable => false,
State::PartiallyAvailable => false,
State::CompletelyAvailable => true,
State::Broken => false,
};
if document.is_fully_active() || !available {
elem.image_decode_promise
.borrow()
.reject_native(&DOMException::new(
&document.global(),
DOMErrorName::EncodingError,
));
}
}, },
} }
} }
@ -1618,7 +1626,14 @@ impl HTMLImageElementMethods for HTMLImageElement {
let request = self.current_request.borrow(); let request = self.current_request.borrow();
let request_state = request.state; let request_state = request.state;
match request_state { match request_state {
State::CompletelyAvailable | State::Broken => true, State::CompletelyAvailable => {
self.resolve_image_decode_promises();
true
},
State::Broken => {
self.reject_image_decode_promises();
true
},
State::PartiallyAvailable | State::Unavailable => false, State::PartiallyAvailable | State::Unavailable => false,
} }
} }
@ -1663,16 +1678,16 @@ impl HTMLImageElementMethods for HTMLImageElement {
/// <https://html.spec.whatwg.org/multipage/#dom-img-decode> /// <https://html.spec.whatwg.org/multipage/#dom-img-decode>
fn Decode(&self) -> Rc<Promise> { fn Decode(&self) -> Rc<Promise> {
let document = document_from_node(self);
// Step 1 // Step 1
let promise = Promise::new(&self.global()); let promise = Promise::new(&self.global());
*self.image_decode_promise.borrow_mut() = promise.clone(); self.image_decode_promises
.borrow_mut()
.push(promise.clone());
// Step 2 // Step 2
let task = ImageElementMicrotask::DecodeTask { let task = ImageElementMicrotask::DecodeTask {
elem: DomRoot::from_ref(self), elem: DomRoot::from_ref(self),
document,
}; };
ScriptThread::await_stable_state(Microtask::ImageElement(task)); ScriptThread::await_stable_state(Microtask::ImageElement(task));

View file

@ -119,13 +119,6 @@ pub trait ImageCache: Sync + Send {
use_placeholder: UsePlaceholder, use_placeholder: UsePlaceholder,
) -> ImageCacheResult; ) -> ImageCacheResult;
fn decode(
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
) -> Option<Image>;
/// Add a listener for the provided pending image id, eventually called by /// Add a listener for the provided pending image id, eventually called by
/// ImageCacheStore::complete_load. /// ImageCacheStore::complete_load.
/// If only metadata is available, Available(ImageOrMetadataAvailable) will /// If only metadata is available, Available(ImageOrMetadataAvailable) will