mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +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
|
@ -250,24 +250,6 @@ impl<'a> FlowConstructor<'a> {
|
|||
node.set_flow_construction_result(result);
|
||||
}
|
||||
|
||||
/// Builds the `ImageFragmentInfo` for the given image. This is out of line to guide inlining.
|
||||
fn build_fragment_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option<Url>)
|
||||
-> SpecificFragmentInfo {
|
||||
match url {
|
||||
None => SpecificFragmentInfo::Generic,
|
||||
Some(url) => {
|
||||
// FIXME(pcwalton): The fact that image fragments store the cache within them makes
|
||||
// little sense to me.
|
||||
SpecificFragmentInfo::Image(box ImageFragmentInfo::new(node,
|
||||
url,
|
||||
self.layout_context
|
||||
.shared
|
||||
.image_cache
|
||||
.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the fragment for the given block or subclass thereof.
|
||||
fn build_fragment_for_block(&mut self, node: &ThreadSafeLayoutNode) -> Fragment {
|
||||
let specific_fragment_info = match node.type_id() {
|
||||
|
@ -277,12 +259,17 @@ impl<'a> FlowConstructor<'a> {
|
|||
}
|
||||
Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||
HTMLElementTypeId::HTMLImageElement))) => {
|
||||
self.build_fragment_info_for_image(node, node.image_url())
|
||||
let image_info = box ImageFragmentInfo::new(node,
|
||||
node.image_url(),
|
||||
&self.layout_context);
|
||||
SpecificFragmentInfo::Image(image_info)
|
||||
}
|
||||
Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||
HTMLElementTypeId::HTMLObjectElement))) => {
|
||||
let data = node.get_object_data();
|
||||
self.build_fragment_info_for_image(node, data)
|
||||
let image_info = box ImageFragmentInfo::new(node,
|
||||
node.get_object_data(),
|
||||
&self.layout_context);
|
||||
SpecificFragmentInfo::Image(image_info)
|
||||
}
|
||||
Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||
HTMLElementTypeId::HTMLTableElement))) => {
|
||||
|
@ -1031,8 +1018,10 @@ impl<'a> FlowConstructor<'a> {
|
|||
};
|
||||
let marker_fragment = match node.style().get_list().list_style_image {
|
||||
Some(ref url) => {
|
||||
Some(Fragment::new(node,
|
||||
self.build_fragment_info_for_image(node, Some((*url).clone()))))
|
||||
let image_info = box ImageFragmentInfo::new(node,
|
||||
Some((*url).clone()),
|
||||
&self.layout_context);
|
||||
Some(Fragment::new(node, SpecificFragmentInfo::Image(image_info)))
|
||||
}
|
||||
None => {
|
||||
match ListStyleTypeContent::from_list_style_type(node.style()
|
||||
|
|
|
@ -13,17 +13,18 @@ use gfx::display_list::OpaqueNode;
|
|||
use gfx::font_cache_task::FontCacheTask;
|
||||
use gfx::font_context::FontContext;
|
||||
use msg::constellation_msg::ConstellationChan;
|
||||
use net_traits::image::base::Image;
|
||||
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageState};
|
||||
use script::layout_interface::{Animation, LayoutChan};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use net_traits::local_image_cache::LocalImageCache;
|
||||
use std::boxed;
|
||||
use std::cell::Cell;
|
||||
use std::ptr;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use style::selector_matching::Stylist;
|
||||
use url::Url;
|
||||
use util::geometry::Au;
|
||||
use util::opts;
|
||||
|
||||
struct LocalLayoutContext {
|
||||
font_context: FontContext,
|
||||
|
@ -55,8 +56,11 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext)
|
|||
|
||||
/// Layout information shared among all workers. This must be thread-safe.
|
||||
pub struct SharedLayoutContext {
|
||||
/// The local image cache.
|
||||
pub image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
|
||||
/// The shared image cache task.
|
||||
pub image_cache_task: ImageCacheTask,
|
||||
|
||||
/// A channel for the image cache to send responses to.
|
||||
pub image_cache_sender: ImageCacheChan,
|
||||
|
||||
/// The current screen size.
|
||||
pub screen_size: Size2D<Au>,
|
||||
|
@ -139,4 +143,42 @@ impl<'a> LayoutContext<'a> {
|
|||
&mut cached_context.style_sharing_candidate_cache
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_request_image(&self, url: Url) -> Option<Arc<Image>> {
|
||||
// See if the image is already available
|
||||
let result = self.shared.image_cache_task.get_image_if_available(url.clone());
|
||||
|
||||
match result {
|
||||
Ok(image) => Some(image),
|
||||
Err(state) => {
|
||||
// If we are emitting an output file, then we need to block on
|
||||
// image load or we risk emitting an output file missing the image.
|
||||
let is_sync = opts::get().output_file.is_some();
|
||||
|
||||
match (state, is_sync) {
|
||||
// Image failed to load, so just return nothing
|
||||
(ImageState::LoadError, _) => None,
|
||||
// Not loaded, test mode - load the image synchronously
|
||||
(_, true) => {
|
||||
let (sync_tx, sync_rx) = channel();
|
||||
self.shared.image_cache_task.request_image(url,
|
||||
ImageCacheChan(sync_tx),
|
||||
None);
|
||||
sync_rx.recv().unwrap().image
|
||||
}
|
||||
// Not yet requested, async mode - request image from the cache
|
||||
(ImageState::NotRequested, false) => {
|
||||
self.shared.image_cache_task.request_image(url,
|
||||
self.shared.image_cache_sender.clone(),
|
||||
None);
|
||||
None
|
||||
}
|
||||
// Image has been requested, is still pending. Return no image
|
||||
// for this paint loop. When the image loads it will trigger
|
||||
// a reflow and/or repaint.
|
||||
(ImageState::Pending, false) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo};
|
|||
use inline::InlineFlow;
|
||||
use list_item::ListItemFlow;
|
||||
use model::{self, MaybeAuto, ToGfxMatrix};
|
||||
use opaque_node::OpaqueNodeMethods;
|
||||
|
||||
use geom::{Matrix2D, Point2D, Rect, Size2D, SideOffsets2D};
|
||||
use gfx::color;
|
||||
|
@ -36,7 +35,6 @@ use png::{self, PixelsByColorType};
|
|||
use msg::compositor_msg::ScrollPolicy;
|
||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||
use msg::constellation_msg::ConstellationChan;
|
||||
use net_traits::image::holder::ImageHolder;
|
||||
use util::cursor::Cursor;
|
||||
use util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px};
|
||||
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
|
||||
|
@ -398,95 +396,86 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
clip: &ClippingRegion,
|
||||
image_url: &Url) {
|
||||
let background = style.get_background();
|
||||
let mut holder = ImageHolder::new(image_url.clone(),
|
||||
layout_context.shared.image_cache.clone());
|
||||
let image = match holder.get_image(self.node.to_untrusted_node_address()) {
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
// TODO: Add some kind of placeholder background image.
|
||||
debug!("(building display list) no background image :(");
|
||||
return
|
||||
}
|
||||
Some(image) => image,
|
||||
};
|
||||
debug!("(building display list) building background image");
|
||||
let image = layout_context.get_or_request_image(image_url.clone());
|
||||
if let Some(image) = image {
|
||||
debug!("(building display list) building background image");
|
||||
|
||||
// Use `background-size` to get the size.
|
||||
let mut bounds = *absolute_bounds;
|
||||
let image_size = self.compute_background_image_size(style, &bounds, &*image);
|
||||
// Use `background-size` to get the size.
|
||||
let mut bounds = *absolute_bounds;
|
||||
let image_size = self.compute_background_image_size(style, &bounds, &*image);
|
||||
|
||||
// Clip.
|
||||
//
|
||||
// TODO: Check the bounds to see if a clip item is actually required.
|
||||
let clip = clip.clone().intersect_rect(&bounds);
|
||||
// Clip.
|
||||
//
|
||||
// TODO: Check the bounds to see if a clip item is actually required.
|
||||
let clip = clip.clone().intersect_rect(&bounds);
|
||||
|
||||
// Use `background-attachment` to get the initial virtual origin
|
||||
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
|
||||
background_attachment::T::scroll => {
|
||||
(absolute_bounds.origin.x, absolute_bounds.origin.y)
|
||||
}
|
||||
background_attachment::T::fixed => {
|
||||
(Au(0), Au(0))
|
||||
}
|
||||
};
|
||||
// Use `background-attachment` to get the initial virtual origin
|
||||
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
|
||||
background_attachment::T::scroll => {
|
||||
(absolute_bounds.origin.x, absolute_bounds.origin.y)
|
||||
}
|
||||
background_attachment::T::fixed => {
|
||||
(Au(0), Au(0))
|
||||
}
|
||||
};
|
||||
|
||||
// Use `background-position` to get the offset.
|
||||
let horizontal_position = model::specified(background.background_position.horizontal,
|
||||
bounds.size.width - image_size.width);
|
||||
let vertical_position = model::specified(background.background_position.vertical,
|
||||
bounds.size.height - image_size.height);
|
||||
// Use `background-position` to get the offset.
|
||||
let horizontal_position = model::specified(background.background_position.horizontal,
|
||||
bounds.size.width - image_size.width);
|
||||
let vertical_position = model::specified(background.background_position.vertical,
|
||||
bounds.size.height - image_size.height);
|
||||
|
||||
let abs_x = virtual_origin_x + horizontal_position;
|
||||
let abs_y = virtual_origin_y + vertical_position;
|
||||
let abs_x = virtual_origin_x + horizontal_position;
|
||||
let abs_y = virtual_origin_y + vertical_position;
|
||||
|
||||
// Adjust origin and size based on background-repeat
|
||||
match background.background_repeat {
|
||||
background_repeat::T::no_repeat => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.width = image_size.width;
|
||||
bounds.size.height = image_size.height;
|
||||
}
|
||||
background_repeat::T::repeat_x => {
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.height = image_size.height;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
|
||||
&mut bounds.size.width,
|
||||
abs_x,
|
||||
image_size.width.to_nearest_px() as u32);
|
||||
}
|
||||
background_repeat::T::repeat_y => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.size.width = image_size.width;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
|
||||
&mut bounds.size.height,
|
||||
abs_y,
|
||||
image_size.height.to_nearest_px() as u32);
|
||||
}
|
||||
background_repeat::T::repeat => {
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
|
||||
&mut bounds.size.width,
|
||||
abs_x,
|
||||
image_size.width.to_nearest_px() as u32);
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
|
||||
&mut bounds.size.height,
|
||||
abs_y,
|
||||
image_size.height.to_nearest_px() as u32);
|
||||
}
|
||||
};
|
||||
// Adjust origin and size based on background-repeat
|
||||
match background.background_repeat {
|
||||
background_repeat::T::no_repeat => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.width = image_size.width;
|
||||
bounds.size.height = image_size.height;
|
||||
}
|
||||
background_repeat::T::repeat_x => {
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.height = image_size.height;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
|
||||
&mut bounds.size.width,
|
||||
abs_x,
|
||||
image_size.width.to_nearest_px() as u32);
|
||||
}
|
||||
background_repeat::T::repeat_y => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.size.width = image_size.width;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
|
||||
&mut bounds.size.height,
|
||||
abs_y,
|
||||
image_size.height.to_nearest_px() as u32);
|
||||
}
|
||||
background_repeat::T::repeat => {
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
|
||||
&mut bounds.size.width,
|
||||
abs_x,
|
||||
image_size.width.to_nearest_px() as u32);
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
|
||||
&mut bounds.size.height,
|
||||
abs_y,
|
||||
image_size.height.to_nearest_px() as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the image display item.
|
||||
display_list.push(DisplayItem::ImageClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(bounds,
|
||||
DisplayItemMetadata::new(self.node,
|
||||
style,
|
||||
Cursor::DefaultCursor),
|
||||
clip),
|
||||
image: image.clone(),
|
||||
stretch_size: Size2D(image_size.width, image_size.height),
|
||||
image_rendering: style.get_effects().image_rendering.clone(),
|
||||
}), level);
|
||||
// Create the image display item.
|
||||
display_list.push(DisplayItem::ImageClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(bounds,
|
||||
DisplayItemMetadata::new(self.node,
|
||||
style,
|
||||
Cursor::DefaultCursor),
|
||||
clip),
|
||||
image: image.clone(),
|
||||
stretch_size: Size2D(image_size.width, image_size.height),
|
||||
image_rendering: style.get_effects().image_rendering.clone(),
|
||||
}), level);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_display_list_for_background_linear_gradient(&self,
|
||||
|
@ -973,11 +962,8 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
}
|
||||
}
|
||||
SpecificFragmentInfo::Image(ref mut image_fragment) => {
|
||||
let image_ref = &mut image_fragment.image;
|
||||
if let Some(image) = image_ref.get_image(self.node.to_untrusted_node_address()) {
|
||||
debug!("(building display list) building image fragment");
|
||||
|
||||
// Place the image into the display list.
|
||||
// Place the image into the display list.
|
||||
if let Some(ref image) = image_fragment.image {
|
||||
display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(stacking_relative_content_box,
|
||||
DisplayItemMetadata::new(self.node,
|
||||
|
@ -988,11 +974,6 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
stretch_size: stacking_relative_content_box.size,
|
||||
image_rendering: self.style.get_effects().image_rendering.clone(),
|
||||
}));
|
||||
} else {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
// TODO: Add some kind of placeholder image.
|
||||
debug!("(building display list) no image :(");
|
||||
}
|
||||
}
|
||||
SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
|
||||
|
|
|
@ -28,8 +28,7 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
|
|||
use gfx::text::glyph::CharIndex;
|
||||
use gfx::text::text_run::{TextRun, TextRunSlice};
|
||||
use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId};
|
||||
use net_traits::image::holder::ImageHolder;
|
||||
use net_traits::local_image_cache::LocalImageCache;
|
||||
use net_traits::image::base::Image;
|
||||
use rustc_serialize::{Encodable, Encoder};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use std::borrow::ToOwned;
|
||||
|
@ -297,7 +296,7 @@ impl CanvasFragmentInfo {
|
|||
pub struct ImageFragmentInfo {
|
||||
/// The image held within this fragment.
|
||||
pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
|
||||
pub image: ImageHolder<UntrustedNodeAddress>,
|
||||
pub image: Option<Arc<Image>>,
|
||||
}
|
||||
|
||||
impl ImageFragmentInfo {
|
||||
|
@ -306,8 +305,8 @@ impl ImageFragmentInfo {
|
|||
/// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little
|
||||
/// sense to me.
|
||||
pub fn new(node: &ThreadSafeLayoutNode,
|
||||
image_url: Url,
|
||||
local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>)
|
||||
url: Option<Url>,
|
||||
layout_context: &LayoutContext)
|
||||
-> ImageFragmentInfo {
|
||||
fn convert_length(node: &ThreadSafeLayoutNode, name: &Atom) -> Option<Au> {
|
||||
let element = node.as_element();
|
||||
|
@ -316,32 +315,42 @@ impl ImageFragmentInfo {
|
|||
.map(|pixels| Au::from_px(pixels))
|
||||
}
|
||||
|
||||
let image = url.and_then(|url| layout_context.get_or_request_image(url));
|
||||
|
||||
ImageFragmentInfo {
|
||||
replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node,
|
||||
convert_length(node, &atom!("width")),
|
||||
convert_length(node, &atom!("height"))),
|
||||
image: ImageHolder::new(image_url, local_image_cache)
|
||||
image: image,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the original inline-size of the image.
|
||||
pub fn image_inline_size(&mut self) -> Au {
|
||||
let size = self.image.get_size(self.replaced_image_fragment_info.for_node).unwrap_or(Size2D::zero());
|
||||
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
||||
size.height
|
||||
} else {
|
||||
size.width
|
||||
} as isize)
|
||||
match self.image {
|
||||
Some(ref image) => {
|
||||
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
||||
image.height
|
||||
} else {
|
||||
image.width
|
||||
} as isize)
|
||||
}
|
||||
None => Au(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the original block-size of the image.
|
||||
pub fn image_block_size(&mut self) -> Au {
|
||||
let size = self.image.get_size(self.replaced_image_fragment_info.for_node).unwrap_or(Size2D::zero());
|
||||
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
||||
size.width
|
||||
} else {
|
||||
size.height
|
||||
} as isize)
|
||||
match self.image {
|
||||
Some(ref image) => {
|
||||
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
||||
image.width
|
||||
} else {
|
||||
image.height
|
||||
} as isize)
|
||||
}
|
||||
None => Au(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tile an image
|
||||
|
|
|
@ -46,16 +46,15 @@ use profile::mem::{self, Report, ReportsChan};
|
|||
use profile::time::{self, ProfilerMetadata, profile};
|
||||
use profile::time::{TimerMetadataFrameType, TimerMetadataReflowType};
|
||||
use net_traits::{load_bytes_iter, ResourceTask};
|
||||
use net_traits::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
||||
use net_traits::local_image_cache::{ImageResponder, LocalImageCache};
|
||||
use net_traits::image_cache_task::{ImageCacheTask, ImageCacheResult, ImageCacheChan};
|
||||
use script::dom::bindings::js::LayoutJS;
|
||||
use script::dom::node::{LayoutData, Node};
|
||||
use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse};
|
||||
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC};
|
||||
use script::layout_interface::{MouseOverResponse, Msg, Reflow, ReflowGoal, ReflowQueryType};
|
||||
use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
|
||||
use script_traits::{ConstellationControlMsg, CompositorEvent, OpaqueScriptLayoutChannel};
|
||||
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
|
||||
use script_traits::{ConstellationControlMsg, OpaqueScriptLayoutChannel};
|
||||
use script_traits::ScriptControlChan;
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::Cell;
|
||||
use std::mem::transmute;
|
||||
|
@ -74,7 +73,7 @@ use util::geometry::{Au, MAX_RECT};
|
|||
use util::logical_geometry::LogicalPoint;
|
||||
use util::mem::HeapSizeOf;
|
||||
use util::opts;
|
||||
use util::smallvec::{SmallVec, SmallVec1, VecLike};
|
||||
use util::smallvec::SmallVec;
|
||||
use util::task::spawn_named_with_send_on_failure;
|
||||
use util::task_state;
|
||||
use util::workqueue::WorkQueue;
|
||||
|
@ -86,8 +85,8 @@ pub struct LayoutTaskData {
|
|||
/// The root of the flow tree.
|
||||
pub root_flow: Option<FlowRef>,
|
||||
|
||||
/// The local image cache.
|
||||
pub local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
|
||||
/// The image cache.
|
||||
pub image_cache_task: ImageCacheTask,
|
||||
|
||||
/// The channel on which messages can be sent to the constellation.
|
||||
pub constellation_chan: ConstellationChan,
|
||||
|
@ -145,6 +144,12 @@ pub struct LayoutTask {
|
|||
/// The port on which we receive messages from the constellation
|
||||
pub pipeline_port: Receiver<LayoutControlMsg>,
|
||||
|
||||
/// The port on which we receive messages from the image cache
|
||||
image_cache_receiver: Receiver<ImageCacheResult>,
|
||||
|
||||
/// The channel on which the image cache can send messages to ourself.
|
||||
image_cache_sender: ImageCacheChan,
|
||||
|
||||
/// The channel on which we or others can send messages to ourselves.
|
||||
pub chan: LayoutChan,
|
||||
|
||||
|
@ -169,9 +174,6 @@ pub struct LayoutTask {
|
|||
/// The channel on which messages can be sent to the resource task.
|
||||
pub resource_task: ResourceTask,
|
||||
|
||||
/// The channel on which messages can be sent to the image cache.
|
||||
pub image_cache_task: ImageCacheTask,
|
||||
|
||||
/// Public interface to the font cache task.
|
||||
pub font_cache_task: FontCacheTask,
|
||||
|
||||
|
@ -185,26 +187,6 @@ pub struct LayoutTask {
|
|||
pub rw_data: Arc<Mutex<LayoutTaskData>>,
|
||||
}
|
||||
|
||||
struct LayoutImageResponder {
|
||||
id: PipelineId,
|
||||
script_chan: ScriptControlChan,
|
||||
}
|
||||
|
||||
impl ImageResponder<UntrustedNodeAddress> for LayoutImageResponder {
|
||||
fn respond(&self) -> Box<Fn(ImageResponseMsg, UntrustedNodeAddress)+Send> {
|
||||
let id = self.id.clone();
|
||||
let script_chan = self.script_chan.clone();
|
||||
box move |_, node_address| {
|
||||
let ScriptControlChan(ref chan) = script_chan;
|
||||
debug!("Dirtying {:p}", node_address.0);
|
||||
let mut nodes = SmallVec1::new();
|
||||
nodes.vec_push(node_address);
|
||||
drop(chan.send(ConstellationControlMsg::SendEvent(
|
||||
id, CompositorEvent::ReflowEvent(nodes))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutTaskFactory for LayoutTask {
|
||||
/// Spawns a new layout task.
|
||||
fn create(_phantom: Option<&mut LayoutTask>,
|
||||
|
@ -218,7 +200,7 @@ impl LayoutTaskFactory for LayoutTask {
|
|||
script_chan: ScriptControlChan,
|
||||
paint_chan: PaintChan,
|
||||
resource_task: ResourceTask,
|
||||
img_cache_task: ImageCacheTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
font_cache_task: FontCacheTask,
|
||||
time_profiler_chan: time::ProfilerChan,
|
||||
memory_profiler_chan: mem::ProfilerChan,
|
||||
|
@ -237,7 +219,7 @@ impl LayoutTaskFactory for LayoutTask {
|
|||
script_chan,
|
||||
paint_chan,
|
||||
resource_task,
|
||||
img_cache_task,
|
||||
image_cache_task,
|
||||
font_cache_task,
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan);
|
||||
|
@ -295,8 +277,6 @@ impl LayoutTask {
|
|||
time_profiler_chan: time::ProfilerChan,
|
||||
mem_profiler_chan: mem::ProfilerChan)
|
||||
-> LayoutTask {
|
||||
let local_image_cache =
|
||||
Arc::new(Mutex::new(LocalImageCache::new(image_cache_task.clone())));
|
||||
let screen_size = Size2D(Au(0), Au(0));
|
||||
let device = Device::new(
|
||||
MediaType::Screen,
|
||||
|
@ -317,6 +297,7 @@ impl LayoutTask {
|
|||
|
||||
// Create the channel on which new animations can be sent.
|
||||
let (new_animations_sender, new_animations_receiver) = channel();
|
||||
let (image_cache_sender, image_cache_receiver) = channel();
|
||||
|
||||
LayoutTask {
|
||||
id: id,
|
||||
|
@ -332,13 +313,14 @@ impl LayoutTask {
|
|||
mem_profiler_chan: mem_profiler_chan,
|
||||
reporter_name: reporter_name,
|
||||
resource_task: resource_task,
|
||||
image_cache_task: image_cache_task.clone(),
|
||||
font_cache_task: font_cache_task,
|
||||
first_reflow: Cell::new(true),
|
||||
image_cache_receiver: image_cache_receiver,
|
||||
image_cache_sender: ImageCacheChan(image_cache_sender),
|
||||
rw_data: Arc::new(Mutex::new(
|
||||
LayoutTaskData {
|
||||
root_flow: None,
|
||||
local_image_cache: local_image_cache,
|
||||
image_cache_task: image_cache_task,
|
||||
constellation_chan: constellation_chan,
|
||||
screen_size: screen_size,
|
||||
stacking_context: None,
|
||||
|
@ -371,7 +353,8 @@ impl LayoutTask {
|
|||
url: &Url)
|
||||
-> SharedLayoutContext {
|
||||
SharedLayoutContext {
|
||||
image_cache: rw_data.local_image_cache.clone(),
|
||||
image_cache_task: rw_data.image_cache_task.clone(),
|
||||
image_cache_sender: self.image_cache_sender.clone(),
|
||||
screen_size: rw_data.screen_size.clone(),
|
||||
screen_size_changed: screen_size_changed,
|
||||
constellation_chan: rw_data.constellation_chan.clone(),
|
||||
|
@ -393,21 +376,26 @@ impl LayoutTask {
|
|||
enum PortToRead {
|
||||
Pipeline,
|
||||
Script,
|
||||
ImageCache,
|
||||
}
|
||||
|
||||
let port_to_read = {
|
||||
let sel = Select::new();
|
||||
let mut port1 = sel.handle(&self.port);
|
||||
let mut port2 = sel.handle(&self.pipeline_port);
|
||||
let mut port3 = sel.handle(&self.image_cache_receiver);
|
||||
unsafe {
|
||||
port1.add();
|
||||
port2.add();
|
||||
port3.add();
|
||||
}
|
||||
let ret = sel.wait();
|
||||
if ret == port1.id() {
|
||||
PortToRead::Script
|
||||
} else if ret == port2.id() {
|
||||
PortToRead::Pipeline
|
||||
} else if ret == port3.id() {
|
||||
PortToRead::ImageCache
|
||||
} else {
|
||||
panic!("invalid select result");
|
||||
}
|
||||
|
@ -424,11 +412,15 @@ impl LayoutTask {
|
|||
possibly_locked_rw_data)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
PortToRead::Script => {
|
||||
let msg = self.port.recv().unwrap();
|
||||
self.handle_request_helper(msg, possibly_locked_rw_data)
|
||||
}
|
||||
PortToRead::ImageCache => {
|
||||
let _ = self.image_cache_receiver.recv().unwrap();
|
||||
self.repaint(possibly_locked_rw_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,6 +450,33 @@ impl LayoutTask {
|
|||
}
|
||||
}
|
||||
|
||||
/// Repaint the scene, without performing style matching. This is typically
|
||||
/// used when an image arrives asynchronously and triggers a relayout and
|
||||
/// repaint.
|
||||
/// TODO: In the future we could detect if the image size hasn't changed
|
||||
/// since last time and avoid performing a complete layout pass.
|
||||
fn repaint<'a>(&'a self,
|
||||
possibly_locked_rw_data: &mut Option<MutexGuard<'a, LayoutTaskData>>) -> bool {
|
||||
let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
|
||||
|
||||
let reflow_info = Reflow {
|
||||
goal: ReflowGoal::ForDisplay,
|
||||
page_clip_rect: MAX_RECT,
|
||||
};
|
||||
|
||||
let mut layout_context = self.build_shared_layout_context(&*rw_data,
|
||||
false,
|
||||
None,
|
||||
&self.url);
|
||||
|
||||
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
||||
&mut *rw_data,
|
||||
&mut layout_context);
|
||||
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Receives and dispatches messages from other tasks.
|
||||
fn handle_request_helper<'a>(&'a self,
|
||||
request: Msg,
|
||||
|
@ -823,12 +842,6 @@ impl LayoutTask {
|
|||
|
||||
let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
|
||||
|
||||
{
|
||||
// Reset the image cache.
|
||||
let mut local_image_cache = rw_data.local_image_cache.lock().unwrap();
|
||||
local_image_cache.next_round(self.make_on_image_available_cb());
|
||||
}
|
||||
|
||||
// TODO: Calculate the "actual viewport":
|
||||
// http://www.w3.org/TR/css-device-adapt/#actual-viewport
|
||||
let viewport_size = data.window_size.initial_viewport;
|
||||
|
@ -1039,24 +1052,6 @@ impl LayoutTask {
|
|||
}
|
||||
}
|
||||
|
||||
/// When images can't be loaded in time to display they trigger
|
||||
/// this callback in some task somewhere. This will send a message
|
||||
/// to the script task, and ultimately cause the image to be
|
||||
/// re-requested. We probably don't need to go all the way back to
|
||||
/// the script task for this.
|
||||
///
|
||||
/// FIXME(pcwalton): Rewrite all of this.
|
||||
fn make_on_image_available_cb(&self) -> Box<ImageResponder<UntrustedNodeAddress>+Send> {
|
||||
// This has a crazy signature because the image cache needs to
|
||||
// make multiple copies of the callback, and the dom event
|
||||
// channel is not a copyable type, so this is actually a
|
||||
// little factory to produce callbacks
|
||||
box LayoutImageResponder {
|
||||
id: self.id.clone(),
|
||||
script_chan: self.script_chan.clone(),
|
||||
} as Box<ImageResponder<UntrustedNodeAddress>+Send>
|
||||
}
|
||||
|
||||
/// Handles a message to destroy layout data. Layout data must be destroyed on *this* task
|
||||
/// because the struct type is transmuted to a different type on the script side.
|
||||
unsafe fn handle_reap_layout_data(&self, layout_data: LayoutData) {
|
||||
|
|
|
@ -116,7 +116,7 @@ pub trait TLayoutNode {
|
|||
fn image_url(&self) -> Option<Url> {
|
||||
unsafe {
|
||||
match HTMLImageElementCast::to_layout_js(self.get_jsmanaged()) {
|
||||
Some(elem) => elem.image().as_ref().map(|url| (*url).clone()),
|
||||
Some(elem) => elem.image_url().as_ref().map(|url| (*url).clone()),
|
||||
None => panic!("not an image!")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue