Make script thread initiate requests for images needed by layout.

In support of this goal, the layout thread collects information about
CSS images that are missing image data and hands it off to the script
thread after layout completes. The script thread stores a list of
nodes that will need to be reflowed after the associated network
request is complete. The script thread ensures that the nodes are
not GCed while a request is ongoing, which the layout thread is
incapable of guaranteeing.

The image cache's API has also been redesigned in support of this
work. No network requests are made by the new image cache, since it
does not possess the document-specific information necessary to
initiate them. Instead, there is now a single, synchronous
query operation that optionally reserves a slot when a cache
entry for a URL cannot be found. This reserved slot is then
the responsibility of the queryer to populate with the contents
of the network response for the URL once it is complete. Any
subsequent queries for the same URL will be informed that the
response is pending until that occurs.

The changes to layout also remove the synchronous image loading
code path, which means that reftests now test the same code
that non-test binaries execute. The decision to take a screenshot
now considers whether there are any outstanding image
requests for layout in order to avoid intermittent failures in
reftests that use CSS images.
This commit is contained in:
Josh Matthews 2016-11-21 18:13:40 -05:00
parent 78e8c31a4d
commit c890c9143c
18 changed files with 528 additions and 388 deletions

View file

@ -75,7 +75,7 @@ use layout::wrapper::LayoutNodeLayoutData;
use layout::wrapper::drop_style_and_layout_data;
use layout_traits::LayoutThreadFactory;
use msg::constellation_msg::{FrameId, PipelineId};
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
use net_traits::image_cache_thread::ImageCacheThread;
use net_traits::image_cache_thread::UsePlaceholder;
use parking_lot::RwLock;
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
@ -98,6 +98,7 @@ use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::mem as std_mem;
use std::ops::{Deref, DerefMut};
use std::process;
use std::sync::{Arc, Mutex, MutexGuard};
@ -137,12 +138,6 @@ pub struct LayoutThread {
/// The port on which we receive messages from the constellation.
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 port on which we receive messages from the font cache thread.
font_cache_receiver: Receiver<()>,
@ -404,11 +399,6 @@ impl LayoutThread {
// Proxy IPC messages from the pipeline to the layout thread.
let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(pipeline_port);
// Ask the router to proxy IPC messages from the image cache thread to the layout thread.
let (ipc_image_cache_sender, ipc_image_cache_receiver) = ipc::channel().unwrap();
let image_cache_receiver =
ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_receiver);
// Ask the router to proxy IPC messages from the font cache thread to the layout thread.
let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap();
let font_cache_receiver =
@ -437,8 +427,6 @@ impl LayoutThread {
image_cache_thread: image_cache_thread,
font_cache_thread: font_cache_thread,
first_reflow: true,
image_cache_receiver: image_cache_receiver,
image_cache_sender: ImageCacheChan(ipc_image_cache_sender),
font_cache_receiver: font_cache_receiver,
font_cache_sender: ipc_font_cache_sender,
parallel_traversal: parallel_traversal,
@ -470,6 +458,7 @@ impl LayoutThread {
margin_style_response: MarginStyleResponse::empty(),
stacking_context_scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None),
pending_images: vec![],
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
@ -530,9 +519,9 @@ impl LayoutThread {
default_computed_values: Arc::new(ComputedValues::initial_values().clone()),
},
image_cache_thread: Mutex::new(self.image_cache_thread.clone()),
image_cache_sender: Mutex::new(self.image_cache_sender.clone()),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: Mutex::new(vec![]),
}
}
@ -541,14 +530,12 @@ impl LayoutThread {
enum Request {
FromPipeline(LayoutControlMsg),
FromScript(Msg),
FromImageCache,
FromFontCache,
}
let request = {
let port_from_script = &self.port;
let port_from_pipeline = &self.pipeline_port;
let port_from_image_cache = &self.image_cache_receiver;
let port_from_font_cache = &self.font_cache_receiver;
select! {
msg = port_from_pipeline.recv() => {
@ -557,10 +544,6 @@ impl LayoutThread {
msg = port_from_script.recv() => {
Request::FromScript(msg.unwrap())
},
msg = port_from_image_cache.recv() => {
msg.unwrap();
Request::FromImageCache
},
msg = port_from_font_cache.recv() => {
msg.unwrap();
Request::FromFontCache
@ -590,9 +573,6 @@ impl LayoutThread {
Request::FromScript(msg) => {
self.handle_request_helper(msg, possibly_locked_rw_data)
},
Request::FromImageCache => {
self.repaint(possibly_locked_rw_data)
},
Request::FromFontCache => {
let _rw_data = possibly_locked_rw_data.lock();
self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
@ -603,37 +583,6 @@ impl LayoutThread {
}
}
/// 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, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) -> bool {
let mut rw_data = possibly_locked_rw_data.lock();
if let Some(mut root_flow) = self.root_flow.clone() {
let flow = flow::mut_base(FlowRef::deref_mut(&mut root_flow));
flow.restyle_damage.insert(REPAINT);
}
let reflow_info = Reflow {
goal: ReflowGoal::ForDisplay,
page_clip_rect: max_rect(),
};
let mut layout_context = self.build_layout_context(&*rw_data,
false,
reflow_info.goal);
self.perform_post_style_recalc_layout_passes(&reflow_info,
None,
None,
&mut *rw_data,
&mut layout_context);
true
}
/// Receives and dispatches messages from other threads.
fn handle_request_helper<'a, 'b>(&mut self,
request: Msg,
@ -1247,6 +1196,9 @@ impl LayoutThread {
query_type: &ReflowQueryType,
rw_data: &mut LayoutThreadData,
context: &mut LayoutContext) {
rw_data.pending_images =
std_mem::replace(&mut context.pending_images.lock().unwrap(), vec![]);
let mut root_flow = match self.root_flow.clone() {
Some(root_flow) => root_flow,
None => return,
@ -1387,6 +1339,14 @@ impl LayoutThread {
None,
&mut *rw_data,
&mut layout_context);
let mut pending_images = layout_context.pending_images.lock().unwrap();
if pending_images.len() > 0 {
//XXXjdm we drop all the images on the floor, but there's no guarantee that
// the node references are valid since the script thread isn't paused.
// need to figure out what to do here!
pending_images.truncate(0);
}
}
fn reflow_with_newly_loaded_web_font<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {