Don't crash when <img> fails to parse its src

This commit is contained in:
Manish Goregaokar 2016-07-05 12:30:42 +05:30
parent 5dde01ab5a
commit fe5ff70284
No known key found for this signature in database
GPG key ID: 3BBF4D3E2EF79F98

View file

@ -31,6 +31,8 @@ use script_thread::Runnable;
use std::sync::Arc; use std::sync::Arc;
use string_cache::Atom; use string_cache::Atom;
use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use task_source::TaskSource;
use task_source::dom_manipulation::DOMManipulationTask;
use url::Url; use url::Url;
#[derive(JSTraceable, HeapSizeOf)] #[derive(JSTraceable, HeapSizeOf)]
@ -44,7 +46,8 @@ enum State {
#[derive(JSTraceable, HeapSizeOf)] #[derive(JSTraceable, HeapSizeOf)]
struct ImageRequest { struct ImageRequest {
state: State, state: State,
url: Option<Url>, parsed_url: Option<Url>,
source_url: Option<DOMString>,
image: Option<Arc<Image>>, image: Option<Arc<Image>>,
metadata: Option<ImageMetadata>, metadata: Option<ImageMetadata>,
} }
@ -56,8 +59,8 @@ pub struct HTMLImageElement {
} }
impl HTMLImageElement { impl HTMLImageElement {
pub fn get_url(&self) -> Option<Url>{ pub fn get_url(&self) -> Option<Url> {
self.current_request.borrow().url.clone() self.current_request.borrow().parsed_url.clone()
} }
} }
@ -118,33 +121,70 @@ impl HTMLImageElement {
let image_cache = window.image_cache_thread(); let image_cache = window.image_cache_thread();
match value { match value {
None => { None => {
self.current_request.borrow_mut().url = None; self.current_request.borrow_mut().parsed_url = None;
self.current_request.borrow_mut().source_url = None;
self.current_request.borrow_mut().image = None; self.current_request.borrow_mut().image = None;
} }
Some((src, base_url)) => { Some((src, base_url)) => {
let img_url = base_url.join(&src); let img_url = base_url.join(&src);
// FIXME: handle URL parse errors more gracefully. if let Ok(img_url) = img_url {
let img_url = img_url.unwrap(); self.current_request.borrow_mut().parsed_url = Some(img_url.clone());
self.current_request.borrow_mut().url = Some(img_url.clone()); self.current_request.borrow_mut().source_url = Some(src);
let trusted_node = Trusted::new(self); let trusted_node = Trusted::new(self);
let (responder_sender, responder_receiver) = ipc::channel().unwrap(); let (responder_sender, responder_receiver) = ipc::channel().unwrap();
let script_chan = window.networking_task_source(); let script_chan = window.networking_task_source();
let wrapper = window.get_runnable_wrapper(); let wrapper = window.get_runnable_wrapper();
ROUTER.add_route(responder_receiver.to_opaque(), box move |message| { ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
// Return the image via a message to the script thread, which marks the element // Return the image via a message to the script thread, which marks the element
// as dirty and triggers a reflow. // as dirty and triggers a reflow.
let image_response = message.to().unwrap(); let image_response = message.to().unwrap();
let runnable = ImageResponseHandlerRunnable::new( let runnable = ImageResponseHandlerRunnable::new(
trusted_node.clone(), image_response); trusted_node.clone(), image_response);
let runnable = wrapper.wrap_runnable(runnable); let runnable = wrapper.wrap_runnable(runnable);
let _ = script_chan.send(CommonScriptMsg::RunnableMsg( let _ = script_chan.send(CommonScriptMsg::RunnableMsg(
UpdateReplacedElement, runnable)); UpdateReplacedElement, runnable));
}); });
image_cache.request_image_and_metadata(img_url, image_cache.request_image_and_metadata(img_url,
window.image_cache_chan(), window.image_cache_chan(),
Some(ImageResponder::new(responder_sender))); Some(ImageResponder::new(responder_sender)));
} 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();
// 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)
struct ImgParseErrorRunnable {
img: Trusted<HTMLImageElement>,
src: String,
}
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_simple_event("error");
img.upcast::<EventTarget>().fire_simple_event("loadend");
}
}
let runnable = Box::new(ImgParseErrorRunnable {
img: Trusted::new(self),
src: src.into(),
});
let task = window.dom_manipulation_task_source();
let _ = task.queue(DOMManipulationTask::Miscellaneous(runnable));
}
} }
} }
} }
@ -153,13 +193,15 @@ impl HTMLImageElement {
htmlelement: HTMLElement::new_inherited(localName, prefix, document), htmlelement: HTMLElement::new_inherited(localName, prefix, document),
current_request: DOMRefCell::new(ImageRequest { current_request: DOMRefCell::new(ImageRequest {
state: State::Unavailable, state: State::Unavailable,
url: None, parsed_url: None,
source_url: None,
image: None, image: None,
metadata: None metadata: None
}), }),
pending_request: DOMRefCell::new(ImageRequest { pending_request: DOMRefCell::new(ImageRequest {
state: State::Unavailable, state: State::Unavailable,
url: None, parsed_url: None,
source_url: None,
image: None, image: None,
metadata: None metadata: None
}), }),
@ -209,7 +251,7 @@ impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn image_url(&self) -> Option<Url> { unsafe fn image_url(&self) -> Option<Url> {
(*self.unsafe_get()).current_request.borrow_for_layout().url.clone() (*self.unsafe_get()).current_request.borrow_for_layout().parsed_url.clone()
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -313,9 +355,9 @@ impl HTMLImageElementMethods for HTMLImageElement {
// https://html.spec.whatwg.org/multipage/#dom-img-currentsrc // https://html.spec.whatwg.org/multipage/#dom-img-currentsrc
fn CurrentSrc(&self) -> DOMString { fn CurrentSrc(&self) -> DOMString {
let ref url = self.current_request.borrow().url; let ref url = self.current_request.borrow().source_url;
match *url { match *url {
Some(ref url) => DOMString::from(url.as_str()), Some(ref url) => url.clone(),
None => DOMString::from(""), None => DOMString::from(""),
} }
} }