mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Refactored image cache task - details below.
* Simpler image cache API for clients to use. * Significantly fewer threads. * One thread for image cache task (multiplexes commands, decoder threads and async resource requests). * 4 threads for decoder worker tasks. * Removed ReflowEvent hacks in script and layout tasks. * Image elements pass a Trusted<T> to image cache, which is used to dirty nodes via script task. Previous use of Untrusted addresses was unsafe. * Image requests such as background-image on layout / paint threads trigger repaint only rather than full reflow. * Add reflow batching for when multiple images load quickly. * Reduces the number of paints loading wikipedia from ~95 to ~35. * Reasonably simple to add proper prefetch support in a follow up PR. * Async loaded images always construct Image fragments now, instead of generic. * Image fragments support the image not being present. * Simpler implementation of synchronous image loading for reftests. * Removed image holder. * image.onload support. * image NaturalWidth and NaturalHeight support. * Updated WPT expectations.
This commit is contained in:
parent
e278e5b9a2
commit
d8aef7208e
33 changed files with 2785 additions and 1679 deletions
|
@ -7,30 +7,36 @@ use dom::attr::{AttrHelpers, AttrValue};
|
|||
use dom::bindings::cell::DOMRefCell;
|
||||
use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
|
||||
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
|
||||
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, HTMLElementCast, HTMLImageElementDerived};
|
||||
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, EventTargetCast, HTMLElementCast, HTMLImageElementDerived};
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::{JSRef, LayoutJS, Temporary};
|
||||
use dom::bindings::refcounted::Trusted;
|
||||
use dom::document::{Document, DocumentHelpers};
|
||||
use dom::element::Element;
|
||||
use dom::element::AttributeHandlers;
|
||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||
use dom::element::ElementTypeId;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
|
||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||
use dom::node::{Node, NodeTypeId, NodeHelpers, NodeDamage, window_from_node};
|
||||
use dom::node::{document_from_node, Node, NodeTypeId, NodeHelpers, NodeDamage, window_from_node};
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use dom::window::WindowHelpers;
|
||||
use net_traits::image_cache_task;
|
||||
use util::geometry::to_px;
|
||||
use util::str::DOMString;
|
||||
use string_cache::Atom;
|
||||
|
||||
use net_traits::image::base::Image;
|
||||
use net_traits::image_cache_task::ImageResponder;
|
||||
use url::{Url, UrlParser};
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct HTMLImageElement {
|
||||
htmlelement: HTMLElement,
|
||||
image: DOMRefCell<Option<Url>>,
|
||||
url: DOMRefCell<Option<Url>>,
|
||||
image: DOMRefCell<Option<Arc<Image>>>,
|
||||
}
|
||||
|
||||
impl HTMLImageElementDerived for EventTarget {
|
||||
|
@ -45,7 +51,7 @@ pub trait HTMLImageElementHelpers {
|
|||
|
||||
impl<'a> HTMLImageElementHelpers for JSRef<'a, HTMLImageElement> {
|
||||
fn get_url(&self) -> Option<Url>{
|
||||
self.image.borrow().clone()
|
||||
self.url.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +59,48 @@ trait PrivateHTMLImageElementHelpers {
|
|||
fn update_image(self, value: Option<(DOMString, &Url)>);
|
||||
}
|
||||
|
||||
/// This is passed to the image cache when the src attribute
|
||||
/// changes. It is returned via a message to the script task,
|
||||
/// which marks the element as dirty and triggers a reflow.
|
||||
struct Responder {
|
||||
element: Trusted<HTMLImageElement>,
|
||||
}
|
||||
|
||||
impl Responder {
|
||||
fn new(element: Trusted<HTMLImageElement>) -> Responder {
|
||||
Responder {
|
||||
element: element
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageResponder for Responder {
|
||||
fn respond(&self, image: Option<Arc<Image>>) {
|
||||
// Update the image field
|
||||
let element = self.element.to_temporary().root();
|
||||
let element_ref = element.r();
|
||||
*element_ref.image.borrow_mut() = image;
|
||||
|
||||
// Mark the node dirty
|
||||
let node = NodeCast::from_ref(element.r());
|
||||
let document = document_from_node(node).root();
|
||||
document.r().content_changed(node, NodeDamage::OtherNodeDamage);
|
||||
|
||||
// Fire image.onload
|
||||
let window = window_from_node(document.r()).root();
|
||||
let event = Event::new(GlobalRef::Window(window.r()),
|
||||
"load".to_owned(),
|
||||
EventBubbles::DoesNotBubble,
|
||||
EventCancelable::NotCancelable).root();
|
||||
let event = event.r();
|
||||
let target: JSRef<EventTarget> = EventTargetCast::from_ref(node);
|
||||
event.fire(target);
|
||||
|
||||
// Trigger reflow
|
||||
window.r().add_pending_reflow();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PrivateHTMLImageElementHelpers for JSRef<'a, HTMLImageElement> {
|
||||
/// 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.
|
||||
|
@ -64,17 +112,18 @@ impl<'a> PrivateHTMLImageElementHelpers for JSRef<'a, HTMLImageElement> {
|
|||
let image_cache = window.image_cache_task();
|
||||
match value {
|
||||
None => {
|
||||
*self.url.borrow_mut() = None;
|
||||
*self.image.borrow_mut() = None;
|
||||
}
|
||||
Some((src, base_url)) => {
|
||||
let img_url = UrlParser::new().base_url(base_url).parse(src.as_slice());
|
||||
// FIXME: handle URL parse errors more gracefully.
|
||||
let img_url = img_url.unwrap();
|
||||
*self.image.borrow_mut() = Some(img_url.clone());
|
||||
*self.url.borrow_mut() = Some(img_url.clone());
|
||||
|
||||
// inform the image cache to load this, but don't store a
|
||||
// handle.
|
||||
image_cache.send(image_cache_task::Msg::Prefetch(img_url));
|
||||
let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan());
|
||||
let responder = box Responder::new(trusted_node);
|
||||
image_cache.request_image(img_url, window.image_cache_chan(), Some(responder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +133,7 @@ impl HTMLImageElement {
|
|||
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLImageElement {
|
||||
HTMLImageElement {
|
||||
htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLImageElement, localName, prefix, document),
|
||||
url: DOMRefCell::new(None),
|
||||
image: DOMRefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
@ -97,14 +147,22 @@ impl HTMLImageElement {
|
|||
|
||||
pub trait LayoutHTMLImageElementHelpers {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn image(&self) -> Option<Url>;
|
||||
unsafe fn image(&self) -> Option<Arc<Image>>;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn image_url(&self) -> Option<Url>;
|
||||
}
|
||||
|
||||
impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn image(&self) -> Option<Url> {
|
||||
unsafe fn image(&self) -> Option<Arc<Image>> {
|
||||
(*self.unsafe_get()).image.borrow_for_layout().clone()
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn image_url(&self) -> Option<Url> {
|
||||
(*self.unsafe_get()).url.borrow_for_layout().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> {
|
||||
|
@ -163,6 +221,29 @@ impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> {
|
|||
elem.set_uint_attribute(&atom!("height"), height)
|
||||
}
|
||||
|
||||
fn NaturalWidth(self) -> u32 {
|
||||
let image = self.image.borrow();
|
||||
|
||||
match *image {
|
||||
Some(ref image) => image.width,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn NaturalHeight(self) -> u32 {
|
||||
let image = self.image.borrow();
|
||||
|
||||
match *image {
|
||||
Some(ref image) => image.height,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn Complete(self) -> bool {
|
||||
let image = self.image.borrow();
|
||||
image.is_some()
|
||||
}
|
||||
|
||||
make_getter!(Name);
|
||||
|
||||
make_setter!(SetName, "name");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue