mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
improve spec compliance of update the image data
This commit is contained in:
parent
f05491166f
commit
71b0c10164
6 changed files with 401 additions and 118 deletions
|
@ -22,7 +22,7 @@ use dom::bindings::str::DOMString;
|
|||
use dom::document::Document;
|
||||
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
|
||||
use dom::element::{reflect_cross_origin_attribute, set_cross_origin_attribute};
|
||||
use dom::event::Event;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use dom::eventtarget::EventTarget;
|
||||
use dom::htmlareaelement::HTMLAreaElement;
|
||||
use dom::htmlelement::HTMLElement;
|
||||
|
@ -30,6 +30,7 @@ use dom::htmlformelement::{FormControl, HTMLFormElement};
|
|||
use dom::htmlmapelement::HTMLMapElement;
|
||||
use dom::mouseevent::MouseEvent;
|
||||
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
|
||||
use dom::progressevent::ProgressEvent;
|
||||
use dom::values::UNSIGNED_LONG_MAX;
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use dom::window::Window;
|
||||
|
@ -38,6 +39,7 @@ use euclid::point::Point2D;
|
|||
use html5ever::{LocalName, Prefix};
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use microtask::{Microtask, MicrotaskRunnable};
|
||||
use net_traits::{FetchResponseListener, FetchMetadata, NetworkError, FetchResponseMsg};
|
||||
use net_traits::image::base::{Image, ImageMetadata};
|
||||
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable};
|
||||
|
@ -46,17 +48,17 @@ use net_traits::image_cache::UsePlaceholder;
|
|||
use net_traits::request::{RequestInit, Type as RequestType};
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use num_traits::ToPrimitive;
|
||||
use script_thread::Runnable;
|
||||
use script_thread::{Runnable, ScriptThread};
|
||||
use servo_url::ServoUrl;
|
||||
use servo_url::origin::ImmutableOrigin;
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefMut};
|
||||
use std::default::Default;
|
||||
use std::i32;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
|
||||
use task_source::TaskSource;
|
||||
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
#[derive(Clone, Copy, JSTraceable, HeapSizeOf)]
|
||||
#[allow(dead_code)]
|
||||
enum State {
|
||||
Unavailable,
|
||||
|
@ -64,6 +66,11 @@ enum State {
|
|||
CompletelyAvailable,
|
||||
Broken,
|
||||
}
|
||||
#[derive(Copy, Clone, JSTraceable, HeapSizeOf)]
|
||||
enum ImageRequestPhase {
|
||||
Pending,
|
||||
Current
|
||||
}
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
#[must_root]
|
||||
struct ImageRequest {
|
||||
|
@ -79,6 +86,7 @@ struct ImageRequest {
|
|||
#[dom_struct]
|
||||
pub struct HTMLImageElement {
|
||||
htmlelement: HTMLElement,
|
||||
image_request: Cell<ImageRequestPhase>,
|
||||
current_request: DOMRefCell<ImageRequest>,
|
||||
pending_request: DOMRefCell<ImageRequest>,
|
||||
form_owner: MutNullableJS<HTMLFormElement>,
|
||||
|
@ -176,18 +184,7 @@ impl PreInvoke for ImageContext {}
|
|||
|
||||
impl HTMLImageElement {
|
||||
/// Update the current image with a valid URL.
|
||||
fn update_image_with_url(&self, img_url: ServoUrl, src: DOMString) {
|
||||
{
|
||||
let mut current_request = self.current_request.borrow_mut();
|
||||
current_request.parsed_url = Some(img_url.clone());
|
||||
current_request.source_url = Some(src);
|
||||
|
||||
LoadBlocker::terminate(&mut current_request.blocker);
|
||||
let document = document_from_node(self);
|
||||
current_request.blocker =
|
||||
Some(LoadBlocker::new(&*document, LoadType::Image(img_url.clone())));
|
||||
}
|
||||
|
||||
fn fetch_image(&self, img_url: &ServoUrl) {
|
||||
fn add_cache_listener_for_element(image_cache: Arc<ImageCache>,
|
||||
id: PendingImageId,
|
||||
elem: &HTMLImageElement) {
|
||||
|
@ -235,12 +232,12 @@ impl HTMLImageElement {
|
|||
|
||||
Err(ImageState::NotRequested(id)) => {
|
||||
add_cache_listener_for_element(image_cache, id, self);
|
||||
self.request_image(img_url, id);
|
||||
self.fetch_request(img_url, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn request_image(&self, img_url: ServoUrl, id: PendingImageId) {
|
||||
fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
|
||||
let document = document_from_node(self);
|
||||
let window = window_from_node(self);
|
||||
|
||||
|
@ -273,38 +270,71 @@ impl HTMLImageElement {
|
|||
document.loader().fetch_async_background(request, action_sender);
|
||||
}
|
||||
|
||||
/// Step 14 of https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn process_image_response(&self, image: ImageResponse) {
|
||||
let (image, metadata, trigger_image_load, trigger_image_error) = match image {
|
||||
ImageResponse::Loaded(image, url) | ImageResponse::PlaceholderLoaded(image, url) => {
|
||||
// TODO: Handle multipart/x-mixed-replace
|
||||
let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
|
||||
(ImageResponse::Loaded(image, url), ImageRequestPhase::Current) |
|
||||
(ImageResponse::PlaceholderLoaded(image, url), ImageRequestPhase::Current) => {
|
||||
self.current_request.borrow_mut().metadata = Some(ImageMetadata {
|
||||
height: image.height,
|
||||
width: image.width
|
||||
});
|
||||
self.current_request.borrow_mut().final_url = Some(url);
|
||||
(Some(image.clone()),
|
||||
Some(ImageMetadata { height: image.height, width: image.width }),
|
||||
true,
|
||||
false)
|
||||
}
|
||||
ImageResponse::MetadataLoaded(meta) => {
|
||||
(None, Some(meta), false, false)
|
||||
}
|
||||
ImageResponse::None => (None, None, false, true)
|
||||
self.current_request.borrow_mut().image = Some(image);
|
||||
self.current_request.borrow_mut().state = State::CompletelyAvailable;
|
||||
LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
|
||||
// Mark the node dirty
|
||||
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
(true, false)
|
||||
},
|
||||
(ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) |
|
||||
(ImageResponse::PlaceholderLoaded(image, url), ImageRequestPhase::Pending) => {
|
||||
self.abort_request(State::Unavailable, ImageRequestPhase::Pending);
|
||||
self.image_request.set(ImageRequestPhase::Current);
|
||||
self.current_request.borrow_mut().metadata = Some(ImageMetadata {
|
||||
height: image.height,
|
||||
width: image.width
|
||||
});
|
||||
self.current_request.borrow_mut().final_url = Some(url);
|
||||
self.current_request.borrow_mut().image = Some(image);
|
||||
self.current_request.borrow_mut().state = State::CompletelyAvailable;
|
||||
LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
|
||||
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
(true, false)
|
||||
},
|
||||
(ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
|
||||
self.current_request.borrow_mut().state = State::PartiallyAvailable;
|
||||
self.current_request.borrow_mut().metadata = Some(meta);
|
||||
(false, false)
|
||||
},
|
||||
(ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
|
||||
self.pending_request.borrow_mut().state = State::PartiallyAvailable;
|
||||
(false, false)
|
||||
},
|
||||
(ImageResponse::None, ImageRequestPhase::Current) => {
|
||||
self.abort_request(State::Broken, ImageRequestPhase::Current);
|
||||
(false, true)
|
||||
},
|
||||
(ImageResponse::None, ImageRequestPhase::Pending) => {
|
||||
self.abort_request(State::Broken, ImageRequestPhase::Current);
|
||||
self.abort_request(State::Broken, ImageRequestPhase::Pending);
|
||||
self.image_request.set(ImageRequestPhase::Current);
|
||||
(false, true)
|
||||
},
|
||||
};
|
||||
self.current_request.borrow_mut().image = image;
|
||||
self.current_request.borrow_mut().metadata = metadata;
|
||||
|
||||
// Mark the node dirty
|
||||
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
|
||||
// Fire image.onload
|
||||
// Fire image.onload and loadend
|
||||
if trigger_image_load {
|
||||
// TODO: https://html.spec.whatwg.org/multipage/#fire-a-progress-event-or-event
|
||||
self.upcast::<EventTarget>().fire_event(atom!("load"));
|
||||
self.upcast::<EventTarget>().fire_event(atom!("loadend"));
|
||||
}
|
||||
|
||||
// Fire image.onerror
|
||||
if trigger_image_error {
|
||||
self.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||
}
|
||||
|
||||
if trigger_image_load || trigger_image_error {
|
||||
LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
|
||||
self.upcast::<EventTarget>().fire_event(atom!("loadend"));
|
||||
}
|
||||
|
||||
// Trigger reflow
|
||||
|
@ -312,68 +342,298 @@ impl HTMLImageElement {
|
|||
window.add_pending_reflow();
|
||||
}
|
||||
|
||||
/// Makes the local `image` member match the status of the `src` attribute and starts
|
||||
/// prefetching the image. This method must be called after `src` is changed.
|
||||
fn update_image(&self, value: Option<(DOMString, ServoUrl)>) {
|
||||
// Force any in-progress request to be ignored.
|
||||
self.generation.set(self.generation.get() + 1);
|
||||
/// https://html.spec.whatwg.org/multipage/#abort-the-image-request
|
||||
fn abort_request(&self, state: State, request: ImageRequestPhase) {
|
||||
let mut request = match request {
|
||||
ImageRequestPhase::Current => self.current_request.borrow_mut(),
|
||||
ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
|
||||
};
|
||||
LoadBlocker::terminate(&mut request.blocker);
|
||||
request.state = state;
|
||||
request.image = None;
|
||||
request.metadata = None;
|
||||
}
|
||||
|
||||
/// Step 11.4 of https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn set_current_request_url_to_selected_fire_error_and_loadend(&self, src: DOMString) {
|
||||
struct Task {
|
||||
img: Trusted<HTMLImageElement>,
|
||||
src: String,
|
||||
}
|
||||
impl Runnable for Task {
|
||||
fn handler(self: Box<Self>) {
|
||||
let img = self.img.root();
|
||||
{
|
||||
let mut current_request = img.current_request.borrow_mut();
|
||||
current_request.source_url = Some(DOMString::from_string(self.src));
|
||||
}
|
||||
img.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||
img.upcast::<EventTarget>().fire_event(atom!("loadend"));
|
||||
img.abort_request(State::Broken, ImageRequestPhase::Current);
|
||||
img.abort_request(State::Broken, ImageRequestPhase::Pending);
|
||||
}
|
||||
}
|
||||
|
||||
let task = box Task {
|
||||
img: Trusted::new(self),
|
||||
src: src.into()
|
||||
};
|
||||
let document = document_from_node(self);
|
||||
let window = document.window();
|
||||
match value {
|
||||
None => {
|
||||
self.current_request.borrow_mut().parsed_url = None;
|
||||
self.current_request.borrow_mut().source_url = None;
|
||||
LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
|
||||
self.current_request.borrow_mut().image = None;
|
||||
let task_source = window.dom_manipulation_task_source();
|
||||
let _ = task_source.queue(task, window.upcast());
|
||||
}
|
||||
|
||||
/// Step 10 of html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn dispatch_loadstart_progress_event(&self) {
|
||||
struct FireprogressEventTask {
|
||||
img: Trusted<HTMLImageElement>,
|
||||
}
|
||||
impl Runnable for FireprogressEventTask {
|
||||
fn handler(self: Box<Self>) {
|
||||
let progressevent = ProgressEvent::new(&self.img.root().global(),
|
||||
atom!("loadstart"), EventBubbles::DoesNotBubble, EventCancelable::NotCancelable,
|
||||
false, 0, 0);
|
||||
progressevent.upcast::<Event>().fire(self.img.root().upcast());
|
||||
}
|
||||
Some((src, base_url)) => {
|
||||
let img_url = base_url.join(&src);
|
||||
if let Ok(img_url) = img_url {
|
||||
self.update_image_with_url(img_url, src);
|
||||
} else {
|
||||
// https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
// Step 11 (error substeps)
|
||||
debug!("Failed to parse URL {} with base {}", src, base_url);
|
||||
let mut req = self.current_request.borrow_mut();
|
||||
}
|
||||
let runnable = box FireprogressEventTask {
|
||||
img: Trusted::new(self),
|
||||
};
|
||||
let document = document_from_node(self);
|
||||
let window = document.window();
|
||||
let task = window.dom_manipulation_task_source();
|
||||
let _ = task.queue(runnable, window.upcast());
|
||||
}
|
||||
|
||||
// Substeps 1,2
|
||||
req.image = None;
|
||||
req.parsed_url = None;
|
||||
req.state = State::Broken;
|
||||
// todo: set pending request to null
|
||||
// (pending requests aren't being used yet)
|
||||
/// https://html.spec.whatwg.org/multipage/#update-the-source-set
|
||||
fn update_source_set(&self) -> Vec<DOMString> {
|
||||
let elem = self.upcast::<Element>();
|
||||
// TODO: follow the algorithm
|
||||
let src = elem.get_string_attribute(&local_name!("src"));
|
||||
if src.is_empty() {
|
||||
return vec![]
|
||||
}
|
||||
vec![src]
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#select-an-image-source
|
||||
fn select_image_source(&self) -> Option<DOMString> {
|
||||
// TODO: select an image source from source set
|
||||
self.update_source_set().first().cloned()
|
||||
}
|
||||
|
||||
struct ImgParseErrorRunnable {
|
||||
img: Trusted<HTMLImageElement>,
|
||||
src: String,
|
||||
/// Step 9.2 of https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn set_current_request_url_to_none_fire_error(&self) {
|
||||
struct SetUrlToNoneTask {
|
||||
img: Trusted<HTMLImageElement>,
|
||||
}
|
||||
impl Runnable for SetUrlToNoneTask {
|
||||
fn handler(self: Box<Self>) {
|
||||
let img = self.img.root();
|
||||
{
|
||||
let mut current_request = img.current_request.borrow_mut();
|
||||
current_request.source_url = None;
|
||||
current_request.parsed_url = None;
|
||||
}
|
||||
let elem = img.upcast::<Element>();
|
||||
if elem.has_attribute(&local_name!("src")) {
|
||||
img.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||
}
|
||||
img.abort_request(State::Broken, ImageRequestPhase::Current);
|
||||
img.abort_request(State::Broken, ImageRequestPhase::Pending);
|
||||
}
|
||||
}
|
||||
|
||||
let task = box SetUrlToNoneTask {
|
||||
img: Trusted::new(self),
|
||||
};
|
||||
let document = document_from_node(self);
|
||||
let window = document.window();
|
||||
let task_source = window.dom_manipulation_task_source();
|
||||
let _ = task_source.queue(task, window.upcast());
|
||||
}
|
||||
|
||||
/// Step 5.3.7 of https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn set_current_request_url_to_string_and_fire_load(&self, src: DOMString, url: ServoUrl) {
|
||||
struct SetUrlToStringTask {
|
||||
img: Trusted<HTMLImageElement>,
|
||||
src: String,
|
||||
url: ServoUrl
|
||||
}
|
||||
impl Runnable for SetUrlToStringTask {
|
||||
fn handler(self: Box<Self>) {
|
||||
let img = self.img.root();
|
||||
{
|
||||
let mut current_request = img.current_request.borrow_mut();
|
||||
current_request.parsed_url = Some(self.url.clone());
|
||||
current_request.source_url = Some(self.src.into());
|
||||
}
|
||||
// TODO: restart animation, if set
|
||||
img.upcast::<EventTarget>().fire_event(atom!("load"));
|
||||
}
|
||||
}
|
||||
let runnable = box SetUrlToStringTask {
|
||||
img: Trusted::new(self),
|
||||
src: src.into(),
|
||||
url: url
|
||||
};
|
||||
let document = document_from_node(self);
|
||||
let window = document.window();
|
||||
let task = window.dom_manipulation_task_source();
|
||||
let _ = task.queue(runnable, window.upcast());
|
||||
}
|
||||
|
||||
fn init_image_request(&self,
|
||||
request: &mut RefMut<ImageRequest>,
|
||||
url: &ServoUrl,
|
||||
src: &DOMString) {
|
||||
request.parsed_url = Some(url.clone());
|
||||
request.source_url = Some(src.clone());
|
||||
request.image = None;
|
||||
request.metadata = None;
|
||||
let document = document_from_node(self);
|
||||
request.blocker = Some(LoadBlocker::new(&*document, LoadType::Image(url.clone())));
|
||||
}
|
||||
|
||||
/// Step 12 of html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn prepare_image_request(&self, url: &ServoUrl, src: &DOMString) {
|
||||
match self.image_request.get() {
|
||||
ImageRequestPhase::Pending => {
|
||||
if let Some(pending_url) = self.pending_request.borrow().parsed_url.clone() {
|
||||
// Step 12.1
|
||||
if pending_url == *url {
|
||||
return
|
||||
}
|
||||
impl Runnable for ImgParseErrorRunnable {
|
||||
fn handler(self: Box<Self>) {
|
||||
// https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
// Step 11, substep 5
|
||||
let img = self.img.root();
|
||||
img.current_request.borrow_mut().source_url = Some(self.src.into());
|
||||
img.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||
img.upcast::<EventTarget>().fire_event(atom!("loadend"));
|
||||
}
|
||||
},
|
||||
ImageRequestPhase::Current => {
|
||||
let mut current_request = self.current_request.borrow_mut();
|
||||
let mut pending_request = self.pending_request.borrow_mut();
|
||||
// step 12.4, create a new "image_request"
|
||||
match (current_request.parsed_url.clone(), current_request.state) {
|
||||
(Some(parsed_url), State::PartiallyAvailable) => {
|
||||
// Step 12.2
|
||||
if parsed_url == *url {
|
||||
// 12.3 abort pending request
|
||||
pending_request.image = None;
|
||||
pending_request.parsed_url = None;
|
||||
LoadBlocker::terminate(&mut pending_request.blocker);
|
||||
// TODO: queue a task to restart animation, if restart-animation is set
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let runnable = box ImgParseErrorRunnable {
|
||||
img: Trusted::new(self),
|
||||
src: src.into(),
|
||||
};
|
||||
let task = window.dom_manipulation_task_source();
|
||||
let _ = task.queue(runnable, window.upcast());
|
||||
self.image_request.set(ImageRequestPhase::Pending);
|
||||
self.init_image_request(&mut pending_request, &url, &src);
|
||||
self.fetch_image(&url);
|
||||
},
|
||||
(_, State::Broken) | (_, State::Unavailable) => {
|
||||
// Step 12.5
|
||||
self.init_image_request(&mut current_request, &url, &src);
|
||||
self.fetch_image(&url);
|
||||
},
|
||||
(_, _) => {
|
||||
// step 12.6
|
||||
self.image_request.set(ImageRequestPhase::Pending);
|
||||
self.init_image_request(&mut pending_request, &url, &src);
|
||||
self.fetch_image(&url);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Step 8-12 of html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn update_the_image_data_sync_steps(&self) {
|
||||
let document = document_from_node(self);
|
||||
// Step 8
|
||||
// TODO: take pixel density into account
|
||||
match self.select_image_source() {
|
||||
Some(src) => {
|
||||
// Step 10
|
||||
self.dispatch_loadstart_progress_event();
|
||||
// Step 11
|
||||
let base_url = document.base_url();
|
||||
let parsed_url = base_url.join(&src);
|
||||
match parsed_url {
|
||||
Ok(url) => {
|
||||
// Step 12
|
||||
self.prepare_image_request(&url, &src);
|
||||
},
|
||||
Err(_) => {
|
||||
// Step 11.1-11.5
|
||||
self.set_current_request_url_to_selected_fire_error_and_loadend(src);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
// Step 9
|
||||
self.set_current_request_url_to_none_fire_error();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn update_the_image_data(&self) {
|
||||
let document = document_from_node(self);
|
||||
let window = document.window();
|
||||
let elem = self.upcast::<Element>();
|
||||
let src = elem.get_string_attribute(&local_name!("src"));
|
||||
let base_url = document.base_url();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#reacting-to-dom-mutations
|
||||
// Always first set the current request to unavailable,
|
||||
// ensuring img.complete is false.
|
||||
{
|
||||
let mut current_request = self.current_request.borrow_mut();
|
||||
current_request.state = State::Unavailable;
|
||||
}
|
||||
|
||||
if !document.is_active() {
|
||||
// Step 1 (if the document is inactive)
|
||||
// TODO: use GlobalScope::enqueue_microtask,
|
||||
// to queue micro task to come back to this algorithm
|
||||
}
|
||||
// Step 2 abort if user-agent does not supports images
|
||||
// NOTE: Servo only supports images, skipping this step
|
||||
|
||||
// step 3, 4
|
||||
// TODO: take srcset and parent images into account
|
||||
if !src.is_empty() {
|
||||
// TODO: take pixel density into account
|
||||
if let Ok(img_url) = base_url.join(&src) {
|
||||
// step 5, check the list of available images
|
||||
let image_cache = window.image_cache();
|
||||
let response = image_cache.find_image_or_metadata(img_url.clone().into(),
|
||||
UsePlaceholder::No,
|
||||
CanRequestImages::No);
|
||||
if let Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) = response {
|
||||
// Step 5.3
|
||||
let metadata = ImageMetadata { height: image.height, width: image.width };
|
||||
// Step 5.3.2 abort requests
|
||||
self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current);
|
||||
self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Pending);
|
||||
let mut current_request = self.current_request.borrow_mut();
|
||||
current_request.final_url = Some(url);
|
||||
current_request.image = Some(image.clone());
|
||||
current_request.metadata = Some(metadata);
|
||||
self.set_current_request_url_to_string_and_fire_load(src, img_url);
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// step 6, await a stable state.
|
||||
self.generation.set(self.generation.get() + 1);
|
||||
let task = ImageElementMicrotask::StableStateUpdateImageDataTask {
|
||||
elem: Root::from_ref(self),
|
||||
generation: self.generation.get(),
|
||||
};
|
||||
ScriptThread::await_stable_state(Microtask::ImageElement(task));
|
||||
}
|
||||
|
||||
fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLImageElement {
|
||||
HTMLImageElement {
|
||||
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
|
||||
image_request: Cell::new(ImageRequestPhase::Current),
|
||||
current_request: DOMRefCell::new(ImageRequest {
|
||||
state: State::Unavailable,
|
||||
parsed_url: None,
|
||||
|
@ -456,6 +716,28 @@ impl HTMLImageElement {
|
|||
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
pub enum ImageElementMicrotask {
|
||||
StableStateUpdateImageDataTask {
|
||||
elem: Root<HTMLImageElement>,
|
||||
generation: u32,
|
||||
}
|
||||
}
|
||||
|
||||
impl MicrotaskRunnable for ImageElementMicrotask {
|
||||
fn handler(&self) {
|
||||
match self {
|
||||
&ImageElementMicrotask::StableStateUpdateImageDataTask { ref elem, ref generation } => {
|
||||
// Step 7 of https://html.spec.whatwg.org/multipage/#update-the-image-data,
|
||||
// stop here if other instances of this algorithm have been scheduled
|
||||
if elem.generation.get() == *generation {
|
||||
elem.update_the_image_data_sync_steps();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LayoutHTMLImageElementHelpers {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn image(&self) -> Option<Arc<Image>>;
|
||||
|
@ -582,8 +864,21 @@ impl HTMLImageElementMethods for HTMLImageElement {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-img-complete
|
||||
fn Complete(&self) -> bool {
|
||||
let ref image = self.current_request.borrow().image;
|
||||
image.is_some()
|
||||
let elem = self.upcast::<Element>();
|
||||
// TODO: take srcset into account
|
||||
if !elem.has_attribute(&local_name!("src")) {
|
||||
return true
|
||||
}
|
||||
let src = elem.get_string_attribute(&local_name!("src"));
|
||||
if src.is_empty() {
|
||||
return true
|
||||
}
|
||||
let request = self.current_request.borrow();
|
||||
let request_state = request.state;
|
||||
match request_state {
|
||||
State::CompletelyAvailable | State::Broken => return true,
|
||||
State::PartiallyAvailable | State::Unavailable => return false,
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-img-currentsrc
|
||||
|
@ -639,22 +934,13 @@ impl VirtualMethods for HTMLImageElement {
|
|||
|
||||
fn adopting_steps(&self, old_doc: &Document) {
|
||||
self.super_type().unwrap().adopting_steps(old_doc);
|
||||
|
||||
let elem = self.upcast::<Element>();
|
||||
let document = document_from_node(self);
|
||||
self.update_image(Some((elem.get_string_attribute(&local_name!("src")),
|
||||
document.base_url())));
|
||||
self.update_the_image_data();
|
||||
}
|
||||
|
||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||
match attr.local_name() {
|
||||
&local_name!("src") => {
|
||||
self.update_image(mutation.new_value(attr).map(|value| {
|
||||
// FIXME(ajeffrey): convert directly from AttrValue to DOMString
|
||||
(DOMString::from(&**value), document_from_node(self).base_url())
|
||||
}));
|
||||
},
|
||||
&local_name!("src") => self.update_the_image_data(),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use dom::bindings::cell::DOMRefCell;
|
|||
use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom::htmlimageelement::ImageElementMicrotask;
|
||||
use dom::htmlmediaelement::MediaElementMicrotask;
|
||||
use dom::mutationobserver::MutationObserver;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
|
@ -31,6 +32,7 @@ pub struct MicrotaskQueue {
|
|||
pub enum Microtask {
|
||||
Promise(EnqueuedPromiseCallback),
|
||||
MediaElement(MediaElementMicrotask),
|
||||
ImageElement(ImageElementMicrotask),
|
||||
NotifyMutationObservers,
|
||||
}
|
||||
|
||||
|
@ -81,7 +83,10 @@ impl MicrotaskQueue {
|
|||
},
|
||||
Microtask::MediaElement(ref task) => {
|
||||
task.handler();
|
||||
}
|
||||
},
|
||||
Microtask::ImageElement(ref task) => {
|
||||
task.handler();
|
||||
},
|
||||
Microtask::NotifyMutationObservers => {
|
||||
MutationObserver::notify_mutation_observers();
|
||||
}
|
||||
|
|
|
@ -571006,7 +571006,7 @@
|
|||
"support"
|
||||
],
|
||||
"html/semantics/embedded-content/the-img-element/img.complete.html": [
|
||||
"723ded8aca956dc1f3cbc60feb0e502c2b943d1d",
|
||||
"be6ea10ac070420948bcef0372c283f275a6b00f",
|
||||
"testharness"
|
||||
],
|
||||
"html/semantics/embedded-content/the-img-element/invalid-src.html": [
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
[img.complete.html]
|
||||
type: testharness
|
||||
[img src omitted]
|
||||
expected: FAIL
|
||||
|
||||
[img src empty]
|
||||
expected: FAIL
|
||||
|
||||
[IDL attribute complete returns true when image resource has been fetched but not run yet & image is not in broken state]
|
||||
expected: FAIL
|
||||
|
||||
[async src broken test]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[update-src-complete.html]
|
||||
type: testharness
|
||||
[Changing the img src should retain the 'complete' property]
|
||||
expected: FAIL
|
||||
|
|
@ -33,16 +33,22 @@
|
|||
var t = async_test("async src complete test");
|
||||
|
||||
t.step(function(){
|
||||
document.getElementById("imgTestTag3").src = '3.jpg?nocache=' + Math.random();
|
||||
var loaded = false;
|
||||
document.getElementById("imgTestTag3").onload = t.step_func_done(function(){
|
||||
assert_false(loaded);
|
||||
loaded = true;
|
||||
assert_true(document.getElementById("imgTestTag3").complete);
|
||||
var currentSrc = document.getElementById("imgTestTag3").currentSrc
|
||||
assert_equals(new URL(window.location.origin + "/" + currentSrc).pathname, "/3.jpg");
|
||||
}, "Only one onload, despite setting the src twice");
|
||||
|
||||
document.getElementById("imgTestTag3").src = 'test' + Math.random();
|
||||
//test if img.complete is set to false if src is changed
|
||||
assert_false(document.getElementById("imgTestTag3").complete, "src changed, should be set to false")
|
||||
//change src again, should make only one request as per 'await stable state'
|
||||
document.getElementById("imgTestTag3").src = '3.jpg?nocache=' + Math.random();
|
||||
});
|
||||
|
||||
document.getElementById("imgTestTag3").onload = t.step_func(function(){
|
||||
assert_true(document.getElementById("imgTestTag3").complete);
|
||||
t.done();
|
||||
});
|
||||
|
||||
// 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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue