auto merge of #917 : larsbergstrom/servo/task_failure, r=metajack

Pipeline construction now creates a task that supervises all of the child tasks created by script, render, and layout. If any of those fail, it sends a message back to the constellation, which re-creates the pipeline and threads it into the appropriate spot in either the FrameTree or set of pending frame updates.

Also, turn on debug_info for symbols in the Makefile.
This commit is contained in:
bors-servo 2013-09-12 15:27:52 -07:00
commit 49aa4a568c
11 changed files with 211 additions and 72 deletions

View file

@ -32,7 +32,7 @@ MKFILE_DEPS := config.stamp $(call rwildcard,$(S)mk/,*)
# Enable debug!() etc even without configure --enable-debug
# The evaluation of these prints & their arguments is controlled
# at runtime by the environment variable RUST_LOG.
CFG_RUSTC_FLAGS += --cfg debug
CFG_RUSTC_FLAGS += --cfg debug -Z debug-info
ifdef CFG_DISABLE_OPTIMIZE
$(info cfg: disabling rustc optimization (CFG_DISABLE_OPTIMIZE))

View file

@ -486,7 +486,12 @@ impl CompositorLayer {
} else {
// ID does not match ours, so recurse on descendents (including hidden children).
self.children.mut_iter().map(|x| &mut x.child)
.any(|x| x.add_buffers(pipeline_id, cell.take(), epoch))
.any(|x| {
let buffers = cell.take();
let result = x.add_buffers(pipeline_id, buffers.clone(), epoch);
cell.put_back(buffers);
result
})
}
}

View file

@ -13,8 +13,8 @@ use geom::size::Size2D;
use geom::rect::Rect;
use gfx::opts::Opts;
use pipeline::Pipeline;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FrameRectMsg, IFrameSandboxState};
use servo_msg::constellation_msg::{InitLoadUrlMsg, LoadIframeUrlMsg, LoadUrlMsg};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, FrameRectMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, InitLoadUrlMsg, LoadIframeUrlMsg, LoadUrlMsg};
use servo_msg::constellation_msg::{Msg, NavigateMsg, NavigationType, IFrameUnsandboxed};
use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowMsg, SubpageId};
use servo_msg::constellation_msg;
@ -23,6 +23,7 @@ use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask;
use servo_net::resource_task;
use servo_util::time::ProfilerChan;
use servo_util::url::make_url;
use std::hashmap::{HashMap, HashSet};
use std::util::replace;
use extra::url::Url;
@ -312,6 +313,15 @@ impl Constellation {
&self.navigation_context.current
}
/// Returns both the navigation context and pending frame trees whose keys are pipeline_id.
pub fn find_all(&mut self, pipeline_id: PipelineId) -> ~[@mut FrameTree] {
let matching_navi_frames = self.navigation_context.find_all(pipeline_id);
let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| {
frame_change.after.find_mut(pipeline_id)
};
matching_navi_frames.move_iter().chain(matching_pending_frames).collect()
}
/// Handles loading pages, navigation, and granting access to the compositor
fn handle_request(&mut self, request: Msg) -> bool {
match request {
@ -319,6 +329,9 @@ impl Constellation {
self.handle_exit(sender);
return false;
}
FailureMsg(pipeline_id, subpage_id) => {
self.handle_failure_msg(pipeline_id, subpage_id);
}
// This should only be called once per constellation, and only by the browser
InitLoadUrlMsg(url) => {
self.handle_init_load(url);
@ -363,6 +376,32 @@ impl Constellation {
sender.send(());
}
fn handle_failure_msg(&mut self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>) {
let new_id = self.get_next_pipeline_id();
let pipeline = @mut Pipeline::create(new_id,
subpage_id,
self.chan.clone(),
self.compositor_chan.clone(),
self.image_cache_task.clone(),
self.resource_task.clone(),
self.profiler_chan.clone(),
self.opts.clone(),
{
let size = self.compositor_chan.get_size();
from_value(Size2D(size.width as uint, size.height as uint))
});
let failure = ~"about:failure";
let url = make_url(failure, None);
pipeline.load(url);
let frames = self.find_all(pipeline_id);
for frame_tree in frames.iter() {
frame_tree.pipeline = pipeline;
};
self.pipelines.insert(pipeline_id, pipeline);
}
fn handle_init_load(&mut self, url: Url) {
let pipeline = @mut Pipeline::create(self.get_next_pipeline_id(),
None,
@ -420,14 +459,8 @@ impl Constellation {
}
}
// Traverse the navigation context and pending frames and tell each associated pipeline to resize.
let frame_trees: ~[@mut FrameTree] = {
let matching_navi_frames = self.navigation_context.find_all(pipeline_id);
let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| {
frame_change.after.find_mut(pipeline_id)
};
matching_navi_frames.move_iter().chain(matching_pending_frames).collect()
};
for frame_tree in frame_trees.iter() {
let frames = self.find_all(pipeline_id);
for frame_tree in frames.iter() {
for child_frame_tree in frame_tree.children.mut_iter() {
let pipeline = &child_frame_tree.frame_tree.pipeline;
if pipeline.subpage_id.expect("Constellation: child frame does not have a

View file

@ -11,7 +11,7 @@ use gfx::opts::Opts;
use layout::layout_task::LayoutTask;
use script::layout_interface::LayoutChan;
use script::script_task::{ExecuteMsg, LoadMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId};
use servo_msg::constellation_msg::{ConstellationChan, FailureMsg, PipelineId, SubpageId};
use script::dom::node::AbstractNode;
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
use script::script_task;
@ -20,9 +20,11 @@ use servo_net::resource_task::ResourceTask;
use servo_util::time::ProfilerChan;
use geom::size::Size2D;
use extra::future::Future;
use std::cell::Cell;
use std::comm;
use std::task;
/// A uniquely-identifiable pipeline of stript task, layout task, and render task.
/// A uniquely-identifiable pipeline of script task, layout task, and render task.
#[deriving(Clone)]
pub struct Pipeline {
id: PipelineId,
@ -94,37 +96,81 @@ impl Pipeline {
let (script_port, script_chan) = special_stream!(ScriptChan);
let (layout_port, layout_chan) = special_stream!(LayoutChan);
let (render_port, render_chan) = special_stream!(RenderChan);
let pipeline = Pipeline::new(id,
subpage_id,
script_chan.clone(),
layout_chan.clone(),
render_chan.clone());
let (port, chan) = stream::<task::TaskResult>();
let script_port = Cell::new(script_port);
let resource_task = Cell::new(resource_task);
let size = Cell::new(size);
let render_port = Cell::new(render_port);
let layout_port = Cell::new(layout_port);
let constellation_chan_handler = Cell::new(constellation_chan.clone());
let constellation_chan = Cell::new(constellation_chan);
let image_cache_task = Cell::new(image_cache_task);
let profiler_chan = Cell::new(profiler_chan);
ScriptTask::create(id,
compositor_chan.clone(),
layout_chan.clone(),
script_port,
script_chan.clone(),
constellation_chan.clone(),
resource_task,
image_cache_task.clone(),
size);
do Pipeline::spawn(chan) {
let script_port = script_port.take();
let resource_task = resource_task.take();
let size = size.take();
let render_port = render_port.take();
let layout_port = layout_port.take();
let constellation_chan = constellation_chan.take();
let image_cache_task = image_cache_task.take();
let profiler_chan = profiler_chan.take();
ScriptTask::create(id,
compositor_chan.clone(),
layout_chan.clone(),
script_port,
script_chan.clone(),
constellation_chan.clone(),
resource_task,
image_cache_task.clone(),
size);
RenderTask::create(id,
render_port,
compositor_chan.clone(),
opts.clone(),
profiler_chan.clone());
RenderTask::create(id,
render_port,
compositor_chan.clone(),
opts.clone(),
profiler_chan.clone());
LayoutTask::create(id,
layout_port,
constellation_chan,
script_chan.clone(),
render_chan.clone(),
image_cache_task,
opts.clone(),
profiler_chan);
Pipeline::new(id,
subpage_id,
script_chan,
layout_chan,
render_chan)
LayoutTask::create(id,
layout_port,
constellation_chan,
script_chan.clone(),
render_chan.clone(),
image_cache_task,
opts.clone(),
profiler_chan);
};
do spawn {
match port.recv() {
task::Success => (),
task::Failure => {
let constellation_chan = constellation_chan_handler.take();
constellation_chan.send(FailureMsg(id, subpage_id));
}
}
};
pipeline
}
/// This function wraps the task creation within a supervised task
/// so that failure will only tear down those tasks instead of ours.
pub fn spawn(chan: Chan<task::TaskResult>, f: ~fn()) {
let mut task = task::task();
task.opts.notify_chan = Some(chan);
task.supervised();
do task.spawn {
f();
};
}
pub fn new(id: PipelineId,

View file

@ -35,6 +35,7 @@ pub enum IFrameSandboxState {
pub enum Msg {
ExitMsg(Chan<()>),
FailureMsg(PipelineId, Option<SubpageId>),
InitLoadUrlMsg(Url),
FrameRectMsg(PipelineId, SubpageId, Rect<f32>),
LoadUrlMsg(PipelineId, Url, Future<Size2D<uint>>),

View file

@ -16,42 +16,55 @@ Create a URL object from a string. Does various helpful browsery things like
is based off the current url
*/
// TODO: about:failure->
pub fn make_url(str_url: ~str, current_url: Option<Url>) -> Url {
let schm = url::get_scheme(str_url);
let str_url = if schm.is_err() {
if current_url.is_none() {
// Assume we've been given a file path. If it's absolute just return
// it, otherwise make it absolute with the cwd.
if str_url.starts_with("/") {
~"file://" + str_url
} else {
~"file://" + os::getcwd().push(str_url).to_str()
}
} else {
let current_url = current_url.unwrap();
debug!("make_url: current_url: %?", current_url);
if str_url.starts_with("//") {
current_url.scheme + ":" + str_url
} else if current_url.path.is_empty() ||
str_url.starts_with("/") {
current_url.scheme + "://" +
current_url.host + "/" +
str_url.trim_left_chars(&'/')
} else {
let mut path = ~[];
for p in current_url.path.split_iter('/') {
path.push(p.to_str());
let str_url = match schm {
Err(_) => {
if current_url.is_none() {
// Assume we've been given a file path. If it's absolute just return
// it, otherwise make it absolute with the cwd.
if str_url.starts_with("/") {
~"file://" + str_url
} else {
~"file://" + os::getcwd().push(str_url).to_str()
}
let path = path.init();
let mut path = path.iter().map(|x| (*x).clone()).collect::<~[~str]>();
path.push(str_url);
let path = path.connect("/");
current_url.scheme + "://" + current_url.host + path
} else {
let current_url = current_url.unwrap();
debug!("make_url: current_url: %?", current_url);
if str_url.starts_with("//") {
current_url.scheme + ":" + str_url
} else if current_url.path.is_empty() ||
str_url.starts_with("/") {
current_url.scheme + "://" +
current_url.host + "/" +
str_url.trim_left_chars(&'/')
} else {
let mut path = ~[];
for p in current_url.path.split_iter('/') {
path.push(p.to_str());
}
let path = path.init();
let mut path = path.iter().map(|x| (*x).clone()).collect::<~[~str]>();
path.push(str_url);
let path = path.connect("/");
current_url.scheme + "://" + current_url.host + path
}
}
},
Ok((scheme, page)) => {
match scheme {
~"about" => {
match page {
~"failure" => ~"file://" + os::getcwd().push("../src/test/html/failure.html").to_str(),
// TODO: handle the rest of the about: pages
_ => str_url
}
},
_ => str_url
}
}
} else {
str_url
};
// FIXME: Need to handle errors

BIN
src/test/html/andreas.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View file

@ -0,0 +1,8 @@
<html>
<head>
<title>about:failure</title>
</head>
<body>
<img src="andreas.jpeg"/>
</body>
</html>

View file

@ -0,0 +1,12 @@
<html>
<head>
<title>Summit demo crash page</title>
</head>
<body>
<audio>
<source src="horse.ogg" type="audio/ogg">
<source src="horse.mp3" type="audio/mpeg">
</audio>
<pre>pre</pre>
</body>
</html>

View file

@ -0,0 +1,9 @@
<html>
<head>
<title>Summit page linking to the crash page</title>
</head>
<body>
<a href="summit-crash.html">Load a crashing page</a>
</body>
</html>

12
src/test/html/summit.html Normal file
View file

@ -0,0 +1,12 @@
<html>
<head>
<title>Summit demo page</title>
</head>
<body>
<iframe src="about-mozilla.html" style="display:block; border: 1px; width: 400px; height: 400px"
frameborder="yes" scrolling="yes"></iframe>
<iframe src="summit-link.html" style="display:block; border: 1px; width: 400px; height: 400px"
frameborder="yes" scrolling="yes"></iframe>
</body>
</html>