Auto merge of #10242 - asajeffrey:remove-constellation-send-panic, r=metajack

Replace any uses of .send().unwrap() by .send().unwrap_or_else().

This is part of the effort to harden the constellation (#10124), in this case against panics caused by channel send.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10242)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-03-30 19:06:38 +05:30
commit 821afa071e

View file

@ -426,7 +426,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
// //
// Yes, that's all there is to it! // Yes, that's all there is to it!
if opts::multiprocess() { if opts::multiprocess() {
self.spawn_multiprocess(unprivileged_pipeline_content); self.spawn_multiprocess(pipeline_id, unprivileged_pipeline_content);
} else { } else {
unprivileged_pipeline_content.start_all::<LTF, STF>(false); unprivileged_pipeline_content.start_all::<LTF, STF>(false);
} }
@ -437,7 +437,10 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
fn spawn_multiprocess(&mut self, unprivileged_pipeline_content: UnprivilegedPipelineContent) { fn spawn_multiprocess(&mut self,
pipeline_id: PipelineId,
unprivileged_pipeline_content: UnprivilegedPipelineContent)
{
let (server, token) = let (server, token) =
IpcOneShotServer::<IpcSender<UnprivilegedPipelineContent>>::new().unwrap(); IpcOneShotServer::<IpcSender<UnprivilegedPipelineContent>>::new().unwrap();
@ -458,11 +461,12 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
self.child_processes.push(child_process); self.child_processes.push(child_process);
let (_receiver, sender) = server.accept().unwrap(); let (_receiver, sender) = server.accept().unwrap();
sender.send(unprivileged_pipeline_content).unwrap(); sender.send(unprivileged_pipeline_content)
.unwrap_or_else(|_| self.handle_send_error(pipeline_id));
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn spawn_multiprocess(&mut self, _: UnprivilegedPipelineContent) { fn spawn_multiprocess(&mut self, _: PipelineId, _: UnprivilegedPipelineContent) {
panic!("Multiprocess is not supported on Windows."); panic!("Multiprocess is not supported on Windows.");
} }
@ -670,16 +674,18 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
} }
Request::Script(FromScriptMsg::ForwardMouseButtonEvent( Request::Script(FromScriptMsg::ForwardMouseButtonEvent(
pipeline_id, event_type, button, point)) => { pipeline_id, event_type, button, point)) => {
if let Some(pipeline) = self.pipelines.get(&pipeline_id) { let event = CompositorEvent::MouseButtonEvent(event_type, button, point);
pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline_id, let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
CompositorEvent::MouseButtonEvent(event_type, button, point))).unwrap(); self.pipelines.get(&pipeline_id)
} .and_then(|pipeline| pipeline.script_chan.send(msg).ok())
.unwrap_or_else(|| self.handle_send_error(pipeline_id));
} }
Request::Script(FromScriptMsg::ForwardMouseMoveEvent(pipeline_id, point)) => { Request::Script(FromScriptMsg::ForwardMouseMoveEvent(pipeline_id, point)) => {
if let Some(pipeline) = self.pipelines.get(&pipeline_id) { let event = CompositorEvent::MouseMoveEvent(Some(point));
pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline_id, let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
CompositorEvent::MouseMoveEvent(Some(point)))).unwrap(); self.pipelines.get(&pipeline_id)
} .and_then(|pipeline| pipeline.script_chan.send(msg).ok())
.unwrap_or_else(|| self.handle_send_error(pipeline_id));
} }
Request::Script(FromScriptMsg::GetClipboardContents(sender)) => { Request::Script(FromScriptMsg::GetClipboardContents(sender)) => {
let result = self.clipboard_ctx.as_ref().map_or( let result = self.clipboard_ctx.as_ref().map_or(
@ -689,7 +695,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
"".to_owned() "".to_owned()
}) })
); );
sender.send(result).unwrap(); sender.send(result).unwrap_or_else(|e| debug!("Failed to send clipboard ({})", e))
} }
Request::Script(FromScriptMsg::SetClipboardContents(s)) => { Request::Script(FromScriptMsg::SetClipboardContents(s)) => {
if let Some(ref mut ctx) = self.clipboard_ctx { if let Some(ref mut ctx) = self.clipboard_ctx {
@ -767,16 +773,27 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
pipeline.exit(); pipeline.exit();
} }
self.image_cache_thread.exit(); self.image_cache_thread.exit();
self.resource_thread.send(net_traits::ControlMsg::Exit).unwrap(); self.resource_thread.send(net_traits::ControlMsg::Exit)
.unwrap_or_else(|e| debug!("Exit resource thread failed ({})", e));
self.devtools_chan.as_ref().map(|chan| { self.devtools_chan.as_ref().map(|chan| {
chan.send(DevtoolsControlMsg::FromChrome( chan.send(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg))
ChromeToDevtoolsControlMsg::ServerExitMsg)).unwrap(); .unwrap_or_else(|e| debug!("Exit devtools failed ({})", e));
}); });
self.storage_thread.send(StorageThreadMsg::Exit).unwrap(); self.storage_thread.send(StorageThreadMsg::Exit)
.unwrap_or_else(|e| debug!("Exit storage thread failed ({})", e));
self.font_cache_thread.exit(); self.font_cache_thread.exit();
self.compositor_proxy.send(ToCompositorMsg::ShutdownComplete); self.compositor_proxy.send(ToCompositorMsg::ShutdownComplete);
} }
fn handle_send_error(&mut self, pipeline_id: PipelineId) {
let parent_info = match self.pipelines.get(&pipeline_id) {
None => return debug!("Pipeline {:?} send error after closure.", pipeline_id),
Some(pipeline) => pipeline.parent_info,
};
// Treat send error the same as receiving a failure message
self.handle_failure_msg(pipeline_id, parent_info);
}
fn handle_failure_msg(&mut self, fn handle_failure_msg(&mut self,
pipeline_id: PipelineId, pipeline_id: PipelineId,
parent_info: Option<(PipelineId, SubpageId)>) { parent_info: Option<(PipelineId, SubpageId)>) {
@ -840,21 +857,20 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
visible_viewport: *size, visible_viewport: *size,
initial_viewport: *size * ScaleFactor::new(1.0), initial_viewport: *size * ScaleFactor::new(1.0),
device_pixel_ratio: self.window_size.device_pixel_ratio, device_pixel_ratio: self.window_size.device_pixel_ratio,
})).unwrap(); })).unwrap_or_else(|_| self.handle_send_error(pipeline_id));
} }
fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) { fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) {
let subframe_pipeline = self.pipeline(pipeline_id); let subframe_parent_id = match self.pipeline(pipeline_id).parent_info {
let subframe_parent = match subframe_pipeline.parent_info { Some(ref parent) => parent.0,
Some(ref parent) => parent,
None => return, None => return,
}; };
let parent_pipeline = self.pipeline(subframe_parent.0);
let msg = ConstellationControlMsg::DispatchFrameLoadEvent { let msg = ConstellationControlMsg::DispatchFrameLoadEvent {
target: pipeline_id, target: pipeline_id,
parent: subframe_parent.0, parent: subframe_parent_id,
}; };
parent_pipeline.script_chan.send(msg).unwrap(); self.pipeline(subframe_parent_id).script_chan.send(msg)
.unwrap_or_else(|_| self.handle_send_error(subframe_parent_id));
} }
// The script thread associated with pipeline_id has loaded a URL in an iframe via script. This // The script thread associated with pipeline_id has loaded a URL in an iframe via script. This
@ -934,14 +950,14 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
self.pipeline(pipeline_id) self.pipeline(pipeline_id)
.script_chan .script_chan
.send(ConstellationControlMsg::TickAllAnimations(pipeline_id)) .send(ConstellationControlMsg::TickAllAnimations(pipeline_id))
.unwrap(); .unwrap_or_else(|_| self.handle_send_error(pipeline_id));
} }
AnimationTickType::Layout => { AnimationTickType::Layout => {
self.pipeline(pipeline_id) self.pipeline(pipeline_id)
.layout_chan .layout_chan
.0 .0
.send(LayoutControlMsg::TickAnimations) .send(LayoutControlMsg::TickAnimations)
.unwrap(); .unwrap_or_else(|_| self.handle_send_error(pipeline_id));
} }
} }
} }
@ -960,11 +976,9 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
self.handle_load_start_msg(&source_id); self.handle_load_start_msg(&source_id);
// Message the constellation to find the script thread for this iframe // Message the constellation to find the script thread for this iframe
// and issue an iframe load through there. // and issue an iframe load through there.
let parent_pipeline = self.pipeline(parent_pipeline_id); let msg = ConstellationControlMsg::Navigate(parent_pipeline_id, subpage_id, load_data);
let script_channel = &parent_pipeline.script_chan; self.pipeline(parent_pipeline_id).script_chan.send(msg)
script_channel.send(ConstellationControlMsg::Navigate(parent_pipeline_id, .unwrap_or_else(|_| self.handle_send_error(parent_pipeline_id));
subpage_id,
load_data)).unwrap();
Some(source_id) Some(source_id)
} }
None => { None => {
@ -1110,11 +1124,10 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
// Update the owning iframe to point to the new subpage id. // Update the owning iframe to point to the new subpage id.
// This makes things like contentDocument work correctly. // This makes things like contentDocument work correctly.
if let Some((parent_pipeline_id, subpage_id)) = pipeline_info { if let Some((parent_pipeline_id, subpage_id)) = pipeline_info {
let script_chan = &self.pipeline(parent_pipeline_id).script_chan;
let (_, new_subpage_id) = self.pipeline(next_pipeline_id).parent_info.unwrap(); let (_, new_subpage_id) = self.pipeline(next_pipeline_id).parent_info.unwrap();
script_chan.send(ConstellationControlMsg::UpdateSubpageId(parent_pipeline_id, let msg = ConstellationControlMsg::UpdateSubpageId(parent_pipeline_id, subpage_id, new_subpage_id);
subpage_id, self.pipeline(parent_pipeline_id).script_chan.send(msg)
new_subpage_id)).unwrap(); .unwrap_or_else(|_| self.handle_send_error(parent_pipeline_id));
// If this is an iframe, send a mozbrowser location change event. // If this is an iframe, send a mozbrowser location change event.
// This is the result of a back/forward navigation. // This is the result of a back/forward navigation.
@ -1122,7 +1135,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
} }
} }
fn handle_key_msg(&self, key: Key, state: KeyState, mods: KeyModifiers) { fn handle_key_msg(&mut self, key: Key, state: KeyState, mods: KeyModifiers) {
// Send to the explicitly focused pipeline (if it exists), or the root // Send to the explicitly focused pipeline (if it exists), or the root
// frame's current pipeline. If neither exist, fall back to sending to // frame's current pipeline. If neither exist, fall back to sending to
// the compositor below. // the compositor below.
@ -1132,10 +1145,10 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
match target_pipeline_id { match target_pipeline_id {
Some(target_pipeline_id) => { Some(target_pipeline_id) => {
let pipeline = self.pipeline(target_pipeline_id);
let event = CompositorEvent::KeyEvent(key, state, mods); let event = CompositorEvent::KeyEvent(key, state, mods);
pipeline.script_chan.send( let msg = ConstellationControlMsg::SendEvent(target_pipeline_id, event);
ConstellationControlMsg::SendEvent(pipeline.id, event)).unwrap(); self.pipeline(target_pipeline_id).script_chan.send(msg)
.unwrap_or_else(|_| self.handle_send_error(target_pipeline_id));
} }
None => { None => {
let event = ToCompositorMsg::KeyEvent(key, state, mods); let event = ToCompositorMsg::KeyEvent(key, state, mods);
@ -1145,12 +1158,11 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
} }
fn handle_get_pipeline_title_msg(&mut self, pipeline_id: PipelineId) { fn handle_get_pipeline_title_msg(&mut self, pipeline_id: PipelineId) {
match self.pipelines.get(&pipeline_id) { let result = match self.pipelines.get(&pipeline_id) {
None => self.compositor_proxy.send(ToCompositorMsg::ChangePageTitle(pipeline_id, None)), None => return self.compositor_proxy.send(ToCompositorMsg::ChangePageTitle(pipeline_id, None)),
Some(pipeline) => { Some(pipeline) => pipeline.script_chan.send(ConstellationControlMsg::GetTitle(pipeline_id)),
pipeline.script_chan.send(ConstellationControlMsg::GetTitle(pipeline_id)).unwrap(); };
} result.unwrap_or_else(|_| self.handle_send_error(pipeline_id));
}
} }
fn handle_mozbrowser_event_msg(&mut self, fn handle_mozbrowser_event_msg(&mut self,
@ -1172,24 +1184,26 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
let pipeline_id = self.pending_frames.iter().rev() let pipeline_id = self.pending_frames.iter().rev()
.find(|x| x.old_pipeline_id == current_pipeline_id) .find(|x| x.old_pipeline_id == current_pipeline_id)
.map(|x| x.new_pipeline_id).or(current_pipeline_id); .map(|x| x.new_pipeline_id).or(current_pipeline_id);
resp_chan.send(pipeline_id).unwrap(); resp_chan.send(pipeline_id)
.unwrap_or_else(|_| debug!("Failed get_pipeline response."));
} }
fn handle_get_frame(&mut self, fn handle_get_frame(&mut self,
pipeline_id: PipelineId, pipeline_id: PipelineId,
resp_chan: IpcSender<Option<FrameId>>) { resp_chan: IpcSender<Option<FrameId>>) {
let frame_id = self.pipeline_to_frame_map.get(&pipeline_id).map(|x| *x); let frame_id = self.pipeline_to_frame_map.get(&pipeline_id).map(|x| *x);
resp_chan.send(frame_id).unwrap(); resp_chan.send(frame_id)
.unwrap_or_else(|_| debug!("Failed get_pipeline response."));
} }
fn focus_parent_pipeline(&self, pipeline_id: PipelineId) { fn focus_parent_pipeline(&mut self, pipeline_id: PipelineId) {
// Send a message to the parent of the provided pipeline (if it exists) // Send a message to the parent of the provided pipeline (if it exists)
// telling it to mark the iframe element as focused. // telling it to mark the iframe element as focused.
if let Some((containing_pipeline_id, subpage_id)) = self.pipeline(pipeline_id).parent_info { if let Some((containing_pipeline_id, subpage_id)) = self.pipeline(pipeline_id).parent_info {
let pipeline = self.pipeline(containing_pipeline_id);
let event = ConstellationControlMsg::FocusIFrame(containing_pipeline_id, let event = ConstellationControlMsg::FocusIFrame(containing_pipeline_id,
subpage_id); subpage_id);
pipeline.script_chan.send(event).unwrap(); self.pipeline(containing_pipeline_id).script_chan.send(event)
.unwrap_or_else(|_| self.handle_send_error(containing_pipeline_id));
self.focus_parent_pipeline(containing_pipeline_id); self.focus_parent_pipeline(containing_pipeline_id);
} }
@ -1224,7 +1238,8 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
response_sender: IpcSender<IpcSender<CanvasMsg>>) { response_sender: IpcSender<IpcSender<CanvasMsg>>) {
let webrender_api = self.webrender_api_sender.clone(); let webrender_api = self.webrender_api_sender.clone();
let sender = CanvasPaintThread::start(*size, webrender_api); let sender = CanvasPaintThread::start(*size, webrender_api);
response_sender.send(sender).unwrap() response_sender.send(sender)
.unwrap_or_else(|e| debug!("Create canvas paint thread response failed ({})", e))
} }
fn handle_create_webgl_paint_thread_msg( fn handle_create_webgl_paint_thread_msg(
@ -1235,7 +1250,8 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
let webrender_api = self.webrender_api_sender.clone(); let webrender_api = self.webrender_api_sender.clone();
let sender = WebGLPaintThread::start(*size, attributes, webrender_api); let sender = WebGLPaintThread::start(*size, attributes, webrender_api);
response_sender.send(sender).unwrap() response_sender.send(sender)
.unwrap_or_else(|e| debug!("Create WebGL paint thread response failed ({})", e))
} }
fn handle_webdriver_msg(&mut self, msg: WebDriverCommandMsg) { fn handle_webdriver_msg(&mut self, msg: WebDriverCommandMsg) {
@ -1253,16 +1269,16 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
self.load_url_for_webdriver(pipeline_id, load_data, reply); self.load_url_for_webdriver(pipeline_id, load_data, reply);
} }
WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd) => { WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd) => {
let pipeline = self.pipeline(pipeline_id);
let control_msg = ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, cmd); let control_msg = ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, cmd);
pipeline.script_chan.send(control_msg).unwrap(); self.pipeline(pipeline_id).script_chan.send(control_msg)
.unwrap_or_else(|_| self.handle_send_error(pipeline_id));
}, },
WebDriverCommandMsg::SendKeys(pipeline_id, cmd) => { WebDriverCommandMsg::SendKeys(pipeline_id, cmd) => {
let pipeline = self.pipeline(pipeline_id);
for (key, mods, state) in cmd { for (key, mods, state) in cmd {
let event = CompositorEvent::KeyEvent(key, state, mods); let event = CompositorEvent::KeyEvent(key, state, mods);
pipeline.script_chan.send( let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
ConstellationControlMsg::SendEvent(pipeline.id, event)).unwrap(); self.pipeline(pipeline_id).script_chan.send(msg)
.unwrap_or_else(|_| self.handle_send_error(pipeline_id));
} }
}, },
WebDriverCommandMsg::TakeScreenshot(pipeline_id, reply) => { WebDriverCommandMsg::TakeScreenshot(pipeline_id, reply) => {
@ -1271,7 +1287,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
if Some(pipeline_id) == current_pipeline_id { if Some(pipeline_id) == current_pipeline_id {
self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply)); self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply));
} else { } else {
reply.send(None).unwrap(); reply.send(None).unwrap_or_else(|e| debug!("Screenshot reply failed ({})", e));
} }
}, },
} }
@ -1439,6 +1455,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
/// and compares the current layout state to what the compositor has. This is used /// and compares the current layout state to what the compositor has. This is used
/// to check if the output image is "stable" and can be written as a screenshot /// to check if the output image is "stable" and can be written as a screenshot
/// for reftests. /// for reftests.
/// Since this function is only used in reftests, we do not harden it against panic.
fn handle_is_ready_to_save_image(&mut self, fn handle_is_ready_to_save_image(&mut self,
pipeline_states: HashMap<PipelineId, Epoch>) -> ReadyToSave { pipeline_states: HashMap<PipelineId, Epoch>) -> ReadyToSave {
// If there is no root frame yet, the initial page has // If there is no root frame yet, the initial page has