Make non-initial about:blank loads asynchronous

Don't update iframe pipeline until load completes

To preserve the previous functionality of delaying load events when a
new navigation is triggered, pending pipeline id represents the
current pending load. The load event is only fired if the load message's
pipeline id matches the pending pipeline id.

Track frame size on Frame instead of Pipeline

Disabled matchMedia test

Track creator pipeline id
This commit is contained in:
Connor Brewster 2017-04-17 18:48:11 -05:00
parent f579405510
commit d004db95cf
18 changed files with 305 additions and 200 deletions

View file

@ -101,7 +101,7 @@ use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState,
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{LogEntry, ServiceWorkerMsg, webdriver_msg};
use script_traits::{MozBrowserErrorType, MozBrowserEvent, WebDriverCommandMsg, WindowSizeData};
use script_traits::{SWManagerMsg, ScopeThings, WindowSizeType};
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WindowSizeType};
use serde::{Deserialize, Serialize};
use servo_config::opts;
use servo_config::prefs::PREFS;
@ -599,17 +599,31 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let (event_loop, host) = match sandbox {
IFrameSandboxState::IFrameSandboxed => (None, None),
IFrameSandboxState::IFrameUnsandboxed => match reg_host(&load_data.url) {
None => (None, None),
Some(host) => {
let event_loop = self.event_loops.get(&top_level_frame_id)
.and_then(|map| map.get(&host))
.and_then(|weak| weak.upgrade());
match event_loop {
None => (None, Some(host)),
Some(event_loop) => (Some(event_loop.clone()), None),
IFrameSandboxState::IFrameUnsandboxed => {
// If this is an about:blank load, it must share the creator's event loop.
// This must match the logic in the script thread when determining the proper origin.
if load_data.url.as_str() != "about:blank" {
match reg_host(&load_data.url) {
None => (None, None),
Some(host) => {
let event_loop = self.event_loops.get(&top_level_frame_id)
.and_then(|map| map.get(&host))
.and_then(|weak| weak.upgrade());
match event_loop {
None => (None, Some(host)),
Some(event_loop) => (Some(event_loop.clone()), None),
}
},
}
},
} else if let Some(parent) = parent_info
.and_then(|(pipeline_id, _)| self.pipelines.get(&pipeline_id)) {
(Some(parent.event_loop.clone()), None)
} else if let Some(creator) = load_data.creator_pipeline_id
.and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) {
(Some(creator.event_loop.clone()), None)
} else {
(None, None)
}
},
};
@ -735,7 +749,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
/// Create a new frame and update the internal bookkeeping.
fn new_frame(&mut self, frame_id: FrameId, pipeline_id: PipelineId, load_data: LoadData) {
fn new_frame(&mut self,
frame_id: FrameId,
pipeline_id: PipelineId,
load_data: LoadData) {
let frame = Frame::new(frame_id, pipeline_id, load_data);
self.frames.insert(frame_id, frame);
@ -915,11 +932,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
load_info.info.new_pipeline_id);
self.handle_script_loaded_url_in_iframe_msg(load_info);
}
FromScriptMsg::ScriptLoadedAboutBlankInIFrame(load_info, lc) => {
FromScriptMsg::ScriptNewIFrame(load_info, layout_sender) => {
debug!("constellation got loaded `about:blank` in iframe message {:?} {:?}",
load_info.parent_pipeline_id,
load_info.new_pipeline_id);
self.handle_script_loaded_about_blank_in_iframe_msg(load_info, lc);
self.handle_script_new_iframe(load_info, layout_sender);
}
FromScriptMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => {
self.handle_change_running_animations_state(pipeline_id, animation_state)
@ -1277,10 +1294,19 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Notify the browser chrome that the pipeline has failed
self.trigger_mozbrowsererror(top_level_frame_id, reason, backtrace);
let pipeline_id = self.frames.get(&top_level_frame_id).map(|frame| frame.pipeline_id);
let pipeline_url = pipeline_id.and_then(|id| self.pipelines.get(&id).map(|pipeline| pipeline.url.clone()));
let parent_info = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.parent_info));
let window_size = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.size));
let (window_size, pipeline_id) = {
let frame = self.frames.get(&top_level_frame_id);
let window_size = frame.and_then(|frame| frame.size);
let pipeline_id = frame.map(|frame| frame.pipeline_id);
(window_size, pipeline_id)
};
let (pipeline_url, parent_info) = {
let pipeline = pipeline_id.and_then(|id| self.pipelines.get(&id));
let pipeline_url = pipeline.map(|pipeline| pipeline.url.clone());
let parent_info = pipeline.and_then(|pipeline| pipeline.parent_info);
(pipeline_url, parent_info)
};
self.close_frame_children(top_level_frame_id, DiscardBrowsingContext::No, ExitPipelineMode::Force);
@ -1295,7 +1321,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
warn!("creating replacement pipeline for about:failure");
let new_pipeline_id = PipelineId::new();
let load_data = LoadData::new(failure_url, None, None);
let load_data = LoadData::new(failure_url, None, None, None);
let sandbox = IFrameSandboxState::IFrameSandboxed;
self.new_pipeline(new_pipeline_id, top_level_frame_id, parent_info,
window_size, load_data.clone(), sandbox, false);
@ -1340,7 +1366,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let window_size = self.window_size.initial_viewport;
let root_pipeline_id = PipelineId::new();
let root_frame_id = self.root_frame_id;
let load_data = LoadData::new(url.clone(), None, None);
let load_data = LoadData::new(url.clone(), None, None, None);
let sandbox = IFrameSandboxState::IFrameUnsandboxed;
self.new_pipeline(root_pipeline_id, root_frame_id, None, Some(window_size), load_data.clone(), sandbox, false);
self.handle_load_start_msg(root_pipeline_id);
@ -1353,29 +1379,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
fn handle_frame_size_msg(&mut self,
iframe_sizes: Vec<(PipelineId, TypedSize2D<f32, CSSPixel>)>) {
for (pipeline_id, size) in iframe_sizes {
let result = {
let pipeline = match self.pipelines.get_mut(&pipeline_id) {
Some(pipeline) => pipeline,
None => continue,
};
if pipeline.size == Some(size) {
continue;
}
pipeline.size = Some(size);
let msg = ConstellationControlMsg::Resize(pipeline_id, WindowSizeData {
initial_viewport: size,
device_pixel_ratio: self.window_size.device_pixel_ratio,
}, WindowSizeType::Initial);
pipeline.event_loop.send(msg)
iframe_sizes: Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>) {
for (frame_id, size) in iframe_sizes {
let window_size = WindowSizeData {
initial_viewport: size,
device_pixel_ratio: self.window_size.device_pixel_ratio,
};
if let Err(e) = result {
self.handle_send_error(pipeline_id, e);
}
self.resize_frame(window_size, WindowSizeType::Initial, frame_id);
}
}
@ -1423,12 +1434,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
};
// TODO - loaddata here should have referrer info (not None, None)
LoadData::new(url, None, None)
LoadData::new(url, Some(source_pipeline.id), None, None)
});
let is_private = load_info.info.is_private || source_pipeline.is_private;
let window_size = old_pipeline.and_then(|old_pipeline| old_pipeline.size);
let window_size = self.frames.get(&load_info.info.frame_id).and_then(|frame| frame.size);
(load_data, window_size, is_private)
};
@ -1456,9 +1467,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
is_private);
}
fn handle_script_loaded_about_blank_in_iframe_msg(&mut self,
load_info: IFrameLoadInfo,
layout_sender: IpcSender<LayoutControlMsg>) {
fn handle_script_new_iframe(&mut self,
load_info: IFrameLoadInfo,
layout_sender: IpcSender<LayoutControlMsg>) {
let IFrameLoadInfo {
parent_pipeline_id,
new_pipeline_id,
@ -1486,12 +1497,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.compositor_proxy.clone_compositor_proxy(),
is_private || parent_pipeline.is_private,
url.clone(),
None,
parent_pipeline.visible)
};
// TODO: Referrer?
let load_data = LoadData::new(url, None, None);
let load_data = LoadData::new(url, Some(parent_pipeline_id), None, None);
let replace_instant = if replace {
self.frames.get(&frame_id).map(|frame| frame.instant)
@ -1641,7 +1651,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// changes would be overridden by changing the subframe associated with source_id.
// Create the new pipeline
let window_size = self.pipelines.get(&source_id).and_then(|source| source.size);
let window_size = self.frames.get(&root_frame_id).and_then(|frame| frame.size);
let new_pipeline_id = PipelineId::new();
let sandbox = IFrameSandboxState::IFrameUnsandboxed;
let replace_instant = if replace {
@ -1959,7 +1969,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
},
WebDriverCommandMsg::Refresh(pipeline_id, reply) => {
let load_data = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => LoadData::new(pipeline.url.clone(), None, None),
Some(pipeline) => LoadData::new(pipeline.url.clone(), None, None, None),
None => return warn!("Pipeline {:?} Refresh after closure.", pipeline_id),
};
self.load_url_for_webdriver(pipeline_id, load_data, reply, true);
@ -2019,8 +2029,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let load_data = entry.load_data;
let (parent_info, window_size, is_private) = match self.frames.get(&frame_id) {
Some(frame) => match self.pipelines.get(&frame.pipeline_id) {
Some(pipeline) => (pipeline.parent_info, pipeline.size, pipeline.is_private),
None => (None, None, false),
Some(pipeline) => (pipeline.parent_info, frame.size, pipeline.is_private),
None => (None, frame.size, false),
},
None => return warn!("no frame to traverse"),
};
@ -2101,7 +2111,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Update the owning iframe to point to the new pipeline id.
// This makes things like contentDocument work correctly.
if let Some((parent_pipeline_id, _)) = parent_info {
let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id, frame_id, pipeline_id);
let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id,
frame_id, pipeline_id, UpdatePipelineIdReason::Traversal);
let result = match self.pipelines.get(&parent_pipeline_id) {
None => return warn!("Pipeline {:?} child traversed after closure.", parent_pipeline_id),
Some(pipeline) => pipeline.event_loop.send(msg),
@ -2245,7 +2256,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
if new_frame {
self.new_frame(frame_change.frame_id, frame_change.new_pipeline_id, frame_change.load_data);
self.new_frame(frame_change.frame_id,
frame_change.new_pipeline_id,
frame_change.load_data);
self.update_activity(frame_change.new_pipeline_id);
self.notify_history_changed(frame_change.new_pipeline_id);
};
@ -2275,7 +2288,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
if let Some((parent_pipeline_id, _)) = pipeline.parent_info {
if let Some(parent_pipeline) = self.pipelines.get(&parent_pipeline_id) {
let msg = ConstellationControlMsg::FramedContentChanged(parent_pipeline_id, pipeline.frame_id);
let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id,
pipeline.frame_id, pipeline_id, UpdatePipelineIdReason::Navigation);
let _ = parent_pipeline.event_loop.send(msg);
}
}
@ -2298,45 +2312,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
fn handle_window_size_msg(&mut self, new_size: WindowSizeData, size_type: WindowSizeType) {
debug!("handle_window_size_msg: {:?}", new_size.initial_viewport.to_untyped());
if let Some(frame) = self.frames.get(&self.root_frame_id) {
// Send Resize (or ResizeInactive) messages to each
// pipeline in the frame tree.
let pipeline_id = frame.pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => return warn!("Pipeline {:?} resized after closing.", pipeline_id),
Some(pipeline) => pipeline,
};
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
let pipelines = frame.prev.iter().chain(frame.next.iter())
.filter_map(|entry| entry.pipeline_id)
.filter_map(|pipeline_id| self.pipelines.get(&pipeline_id));
for pipeline in pipelines {
let _ = pipeline.event_loop.send(ConstellationControlMsg::ResizeInactive(
pipeline.id,
new_size
));
}
}
// Send resize message to any pending pipelines that aren't loaded yet.
for pending_frame in &self.pending_frames {
let pipeline_id = pending_frame.new_pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => { warn!("Pending pipeline {:?} is closed", pipeline_id); continue; }
Some(pipeline) => pipeline,
};
if pipeline.parent_info.is_none() {
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
}
}
let frame_id = self.root_frame_id;
self.resize_frame(new_size, size_type, frame_id);
if let Some(resize_channel) = self.webdriver.resize_channel.take() {
let _ = resize_channel.send(new_size);
@ -2422,7 +2399,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// size for the pipeline, then its painting should be up to date. If the constellation
// *hasn't* received a size, it could be that the layer was hidden by script before the
// compositor discovered it, so we just don't check the layer.
if let Some(size) = pipeline.size {
if let Some(size) = frame.size {
// If the rectangle for this pipeline is zero sized, it will
// never be painted. In this case, don't query the layout
// thread as it won't contribute to the final output image.
@ -2509,6 +2486,54 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.set_activity(pipeline_id, self.get_activity(pipeline_id));
}
/// Handle updating the size of a frame. This notifies every pipeline in the frame of the new
/// size.
fn resize_frame(&mut self, new_size: WindowSizeData, size_type: WindowSizeType, frame_id: FrameId) {
if let Some(frame) = self.frames.get_mut(&frame_id) {
frame.size = Some(new_size.initial_viewport);
}
if let Some(frame) = self.frames.get(&frame_id) {
// Send Resize (or ResizeInactive) messages to each
// pipeline in the frame tree.
let pipeline_id = frame.pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => return warn!("Pipeline {:?} resized after closing.", pipeline_id),
Some(pipeline) => pipeline,
};
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
let pipelines = frame.prev.iter().chain(frame.next.iter())
.filter_map(|entry| entry.pipeline_id)
.filter_map(|pipeline_id| self.pipelines.get(&pipeline_id));
for pipeline in pipelines {
let _ = pipeline.event_loop.send(ConstellationControlMsg::ResizeInactive(
pipeline.id,
new_size
));
}
}
// Send resize message to any pending pipelines that aren't loaded yet.
for pending_frame in &self.pending_frames {
let pipeline_id = pending_frame.new_pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => { warn!("Pending pipeline {:?} is closed", pipeline_id); continue; }
Some(pipeline) => pipeline,
};
if pipeline.frame_id == frame_id {
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
}
}
}
fn clear_joint_session_future(&mut self, frame_id: FrameId) {
let frame_ids: Vec<FrameId> = self.full_frame_tree_iter(frame_id)
.map(|frame| frame.id)
@ -2652,7 +2677,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.pipelines.get(&frame.pipeline_id).map(|pipeline: &Pipeline| {
let mut frame_tree = SendableFrameTree {
pipeline: pipeline.to_sendable(),
size: pipeline.size,
size: frame.size,
children: vec!(),
};

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use euclid::size::TypedSize2D;
use msg::constellation_msg::{FrameId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
@ -9,6 +10,7 @@ use std::collections::HashMap;
use std::iter::once;
use std::mem::replace;
use std::time::Instant;
use style_traits::CSSPixel;
/// A frame in the frame tree.
/// Each frame is the constellation's view of a browsing context.
@ -22,13 +24,16 @@ pub struct Frame {
/// The frame id.
pub id: FrameId,
/// The timestamp for the current session history entry
/// The size of the frame.
pub size: Option<TypedSize2D<f32, CSSPixel>>,
/// The timestamp for the current session history entry.
pub instant: Instant,
/// The pipeline for the current session history entry
/// The pipeline for the current session history entry.
pub pipeline_id: PipelineId,
/// The load data for the current session history entry
/// The load data for the current session history entry.
pub load_data: LoadData,
/// The past session history, ordered chronologically.
@ -44,6 +49,7 @@ impl Frame {
pub fn new(id: FrameId, pipeline_id: PipelineId, load_data: LoadData) -> Frame {
Frame {
id: id,
size: None,
pipeline_id: pipeline_id,
instant: Instant::now(),
load_data: load_data,

View file

@ -76,10 +76,6 @@ pub struct Pipeline {
/// The title of the most recently-loaded page.
pub title: Option<String>,
/// The size of the frame.
/// TODO: move this field to `Frame`.
pub size: Option<TypedSize2D<f32, CSSPixel>>,
/// Whether this pipeline is currently running animations. Pipelines that are running
/// animations cause composites to be continually scheduled.
pub running_animations: bool,
@ -291,7 +287,6 @@ impl Pipeline {
state.compositor_proxy,
state.is_private,
url,
state.window_size,
state.prev_visibility.unwrap_or(true)))
}
@ -305,7 +300,6 @@ impl Pipeline {
compositor_proxy: Box<CompositorProxy + 'static + Send>,
is_private: bool,
url: ServoUrl,
size: Option<TypedSize2D<f32, CSSPixel>>,
visible: bool)
-> Pipeline {
let pipeline = Pipeline {
@ -318,7 +312,6 @@ impl Pipeline {
url: url,
title: None,
children: vec!(),
size: size,
running_animations: false,
visible: visible,
is_private: is_private,