Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from Jul 5, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -31,6 +31,8 @@ use script_thread::Runnable;
use std::sync::Arc;
use string_cache::Atom;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use task_source::TaskSource;
use task_source::dom_manipulation::DOMManipulationTask;
use url::Url;

#[derive(JSTraceable, HeapSizeOf)]
@@ -44,7 +46,8 @@ enum State {
#[derive(JSTraceable, HeapSizeOf)]
struct ImageRequest {
state: State,
url: Option<Url>,
parsed_url: Option<Url>,
source_url: Option<DOMString>,
image: Option<Arc<Image>>,
metadata: Option<ImageMetadata>,
}
@@ -56,8 +59,8 @@ pub struct HTMLImageElement {
}

impl HTMLImageElement {
pub fn get_url(&self) -> Option<Url>{
self.current_request.borrow().url.clone()
pub fn get_url(&self) -> Option<Url> {
self.current_request.borrow().parsed_url.clone()
}
}

@@ -118,33 +121,70 @@ impl HTMLImageElement {
let image_cache = window.image_cache_thread();
match value {
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;
}
Some((src, base_url)) => {
let img_url = base_url.join(&src);
// FIXME: handle URL parse errors more gracefully.
let img_url = img_url.unwrap();
self.current_request.borrow_mut().url = Some(img_url.clone());

let trusted_node = Trusted::new(self);
let (responder_sender, responder_receiver) = ipc::channel().unwrap();
let script_chan = window.networking_task_source();
let wrapper = window.get_runnable_wrapper();
ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
// Return the image via a message to the script thread, which marks the element
// as dirty and triggers a reflow.
let image_response = message.to().unwrap();
let runnable = ImageResponseHandlerRunnable::new(
trusted_node.clone(), image_response);
let runnable = wrapper.wrap_runnable(runnable);
let _ = script_chan.send(CommonScriptMsg::RunnableMsg(
UpdateReplacedElement, runnable));
});

image_cache.request_image_and_metadata(img_url,
window.image_cache_chan(),
Some(ImageResponder::new(responder_sender)));
if let Ok(img_url) = img_url {
self.current_request.borrow_mut().parsed_url = Some(img_url.clone());
self.current_request.borrow_mut().source_url = Some(src);

let trusted_node = Trusted::new(self);
let (responder_sender, responder_receiver) = ipc::channel().unwrap();
let script_chan = window.networking_task_source();
let wrapper = window.get_runnable_wrapper();
ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
// Return the image via a message to the script thread, which marks the element
// as dirty and triggers a reflow.
let image_response = message.to().unwrap();
let runnable = ImageResponseHandlerRunnable::new(
trusted_node.clone(), image_response);
let runnable = wrapper.wrap_runnable(runnable);
let _ = script_chan.send(CommonScriptMsg::RunnableMsg(
UpdateReplacedElement, runnable));
});

image_cache.request_image_and_metadata(img_url,
window.image_cache_chan(),
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),
current_request: DOMRefCell::new(ImageRequest {
state: State::Unavailable,
url: None,
parsed_url: None,
source_url: None,
image: None,
metadata: None
}),
pending_request: DOMRefCell::new(ImageRequest {
state: State::Unavailable,
url: None,
parsed_url: None,
source_url: None,
image: None,
metadata: None
}),
@@ -209,7 +251,7 @@ impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {

#[allow(unsafe_code)]
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)]
@@ -313,9 +355,9 @@ impl HTMLImageElementMethods for HTMLImageElement {

// https://html.spec.whatwg.org/multipage/#dom-img-currentsrc
fn CurrentSrc(&self) -> DOMString {
let ref url = self.current_request.borrow().url;
let ref url = self.current_request.borrow().source_url;
match *url {
Some(ref url) => DOMString::from(url.as_str()),
Some(ref url) => url.clone(),
None => DOMString::from(""),
}
}
@@ -52,7 +52,8 @@ pub enum DOMManipulationTask {
// https://html.spec.whatwg.org/multipage/#planned-navigation
PlannedNavigation(Box<Runnable + Send>),
// https://html.spec.whatwg.org/multipage/#send-a-storage-notification
SendStorageNotification(Box<MainThreadRunnable + Send>)
SendStorageNotification(Box<MainThreadRunnable + Send>),
Miscellaneous(Box<Runnable + Send>),
}

impl DOMManipulationTask {
@@ -72,7 +73,8 @@ impl DOMManipulationTask {
FireToggleEvent(runnable) => runnable.handler(),
MediaTask(runnable) => runnable.handler(),
PlannedNavigation(runnable) => runnable.handler(),
SendStorageNotification(runnable) => runnable.handler(script_thread)
SendStorageNotification(runnable) => runnable.handler(script_thread),
Miscellaneous(runnable) => runnable.handler(),
}
}
}
"path": "html/semantics/document-metadata/the-link-element/document-without-browsing-context.html",
"url": "/html/semantics/document-metadata/the-link-element/document-without-browsing-context.html"
}
],
"html/semantics/embedded-content/the-img-element/invalid-src.html": [
{
"path": "html/semantics/embedded-content/the-img-element/invalid-src.html",
"url": "/html/semantics/embedded-content/the-img-element/invalid-src.html"
}
]
}
},
@@ -1,3 +1,11 @@
[fail-to-resolve.html]
type: testharness
expected: CRASH
[<img srcset="//[">]
expected: FAIL

[<img srcset="//[" src="/images/red.png">]
expected: FAIL

[<img srcset="//[, /images/red.png">]
expected: FAIL

@@ -1,8 +1,5 @@
[update-the-source-set.html]
type: testharness
[<img src="" data-expect="">]
expected: FAIL

[<img srcset="data:,b" src="data:,a" data-expect="data:,b">]
expected: FAIL

@@ -0,0 +1,21 @@
<!doctype html>
<meta charset="utf-8">
<title>Loading a non-parsing URL as an image should silently fail; triggering appropriate events</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<img id=myimg />
<script>
async_test(function(t) {
var img = document.getElementById("myimg");
img.src = "http://also a broken url";
var errorevent = false;

// The errors should be queued in the event loop, so they should only trigger
// after this block of code finishes, not during the img.src setter itself
img.addEventListener('error', t.step_func(function(){errorevent = true;}));
img.addEventListener('loadend', t.step_func_done(function() {
assert_true(errorevent, "error event fired");
}));
});

</script>
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.