Report errors using the top-level frame id rather than the pipeline id.

This commit is contained in:
Alan Jeffrey 2016-11-10 15:34:04 -06:00
parent 22aebdf5d4
commit c228a4cf03
9 changed files with 203 additions and 195 deletions

View file

@ -419,9 +419,9 @@ impl Log for FromScriptLogger {
fn log(&self, record: &LogRecord) { fn log(&self, record: &LogRecord) {
if let Some(entry) = log_entry(record) { if let Some(entry) = log_entry(record) {
debug!("Sending log entry {:?}.", entry); debug!("Sending log entry {:?}.", entry);
let pipeline_id = PipelineId::installed(); let top_level_frame_id = FrameId::installed();
let thread_name = thread::current().name().map(ToOwned::to_owned); let thread_name = thread::current().name().map(ToOwned::to_owned);
let msg = FromScriptMsg::LogEntry(pipeline_id, thread_name, entry); let msg = FromScriptMsg::LogEntry(top_level_frame_id, thread_name, entry);
let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner()); let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner());
let _ = chan.send(msg); let _ = chan.send(msg);
} }
@ -457,9 +457,9 @@ impl Log for FromCompositorLogger {
fn log(&self, record: &LogRecord) { fn log(&self, record: &LogRecord) {
if let Some(entry) = log_entry(record) { if let Some(entry) = log_entry(record) {
debug!("Sending log entry {:?}.", entry); debug!("Sending log entry {:?}.", entry);
let pipeline_id = PipelineId::installed(); let top_level_frame_id = FrameId::installed();
let thread_name = thread::current().name().map(ToOwned::to_owned); let thread_name = thread::current().name().map(ToOwned::to_owned);
let msg = FromCompositorMsg::LogEntry(pipeline_id, thread_name, entry); let msg = FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry);
let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner()); let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner());
let _ = chan.send(msg); let _ = chan.send(msg);
} }
@ -605,9 +605,18 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
None None
}; };
// TODO: think about the case where the child pipeline is created
// before the parent is part of the frame tree.
let top_level_frame_id = match parent_info {
Some((_, FrameType::MozBrowserIFrame)) => frame_id,
Some((parent_id, _)) => self.get_top_level_frame_for_pipeline(parent_id),
None => self.root_frame_id,
};
let result = Pipeline::spawn::<Message, LTF, STF>(InitialPipelineState { let result = Pipeline::spawn::<Message, LTF, STF>(InitialPipelineState {
id: pipeline_id, id: pipeline_id,
frame_id: frame_id, frame_id: frame_id,
top_level_frame_id: top_level_frame_id,
parent_info: parent_info, parent_info: parent_info,
constellation_chan: self.script_sender.clone(), constellation_chan: self.script_sender.clone(),
layout_to_constellation_chan: self.layout_sender.clone(), layout_to_constellation_chan: self.layout_sender.clone(),
@ -847,8 +856,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
debug!("constellation got reload message"); debug!("constellation got reload message");
self.handle_reload_msg(); self.handle_reload_msg();
} }
FromCompositorMsg::LogEntry(pipeline_id, thread_name, entry) => { FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry) => {
self.handle_log_entry(pipeline_id, thread_name, entry); self.handle_log_entry(top_level_frame_id, thread_name, entry);
} }
} }
} }
@ -999,8 +1008,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
FromScriptMsg::Exit => { FromScriptMsg::Exit => {
self.compositor_proxy.send(ToCompositorMsg::Exit); self.compositor_proxy.send(ToCompositorMsg::Exit);
} }
FromScriptMsg::LogEntry(pipeline_id, thread_name, entry) => { FromScriptMsg::LogEntry(top_level_frame_id, thread_name, entry) => {
self.handle_log_entry(pipeline_id, thread_name, entry); self.handle_log_entry(top_level_frame_id, thread_name, entry);
} }
FromScriptMsg::SetTitle(pipeline_id, title) => { FromScriptMsg::SetTitle(pipeline_id, title) => {
@ -1172,10 +1181,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
fn handle_send_error(&mut self, pipeline_id: PipelineId, err: IOError) { fn handle_send_error(&mut self, pipeline_id: PipelineId, err: IOError) {
// Treat send error the same as receiving a panic message // Treat send error the same as receiving a panic message
debug!("Pipeline {:?} send error ({}).", pipeline_id, err); debug!("Pipeline {:?} send error ({}).", pipeline_id, err);
self.handle_panic(Some(pipeline_id), format!("Send failed ({})", err), None); let top_level_frame_id = self.get_top_level_frame_for_pipeline(pipeline_id);
let reason = format!("Send failed ({})", err);
self.handle_panic(top_level_frame_id, reason, None);
} }
fn handle_panic(&mut self, pipeline_id: Option<PipelineId>, reason: String, backtrace: Option<String>) { fn handle_panic(&mut self, top_level_frame_id: FrameId, reason: String, backtrace: Option<String>) {
if opts::get().hard_fail { if opts::get().hard_fail {
// It's quite difficult to make Servo exit cleanly if some threads have failed. // It's quite difficult to make Servo exit cleanly if some threads have failed.
// Hard fail exists for test runners so we crash and that's good enough. // Hard fail exists for test runners so we crash and that's good enough.
@ -1183,26 +1194,18 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
process::exit(1); process::exit(1);
} }
debug!("Panic handler for pipeline {:?}: {}.", pipeline_id, reason); debug!("Panic handler for top-level frame {}: {}.", top_level_frame_id, reason);
// Notify the browser chrome that the pipeline has failed // Notify the browser chrome that the pipeline has failed
self.trigger_mozbrowsererror(pipeline_id, reason, backtrace); self.trigger_mozbrowsererror(top_level_frame_id, reason, backtrace);
if let Some(pipeline_id) = pipeline_id { let frame_id = FrameId::from(top_level_frame_id);
let pipeline_url = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.url.clone()); let pipeline_id = self.frames.get(&frame_id).map(|frame| frame.current.pipeline_id);
let parent_info = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info); let pipeline_url = pipeline_id.and_then(|id| self.pipelines.get(&id).map(|pipeline| pipeline.url.clone()));
let window_size = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.size); let parent_info = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.parent_info));
let frame_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); let window_size = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.size));
self.close_pipeline(pipeline_id, ExitPipelineMode::Force); self.close_frame_children(frame_id, ExitPipelineMode::Force);
self.pipelines.remove(&pipeline_id);
while let Some(pending_pipeline_id) = self.pending_frames.iter().find(|pending| {
pending.old_pipeline_id == Some(pipeline_id)
}).map(|frame| frame.new_pipeline_id) {
warn!("removing pending frame change for failed pipeline");
self.close_pipeline(pending_pipeline_id, ExitPipelineMode::Force);
}
let failure_url = ServoUrl::parse("about:failure").expect("infallible"); let failure_url = ServoUrl::parse("about:failure").expect("infallible");
@ -1214,27 +1217,27 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
warn!("creating replacement pipeline for about:failure"); warn!("creating replacement pipeline for about:failure");
if let Some(frame_id) = frame_id {
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = PipelineId::new();
let load_data = LoadData::new(failure_url, None, None); let load_data = LoadData::new(failure_url, None, None);
self.new_pipeline(new_pipeline_id, frame_id, parent_info, Some(pipeline_id), self.new_pipeline(new_pipeline_id, frame_id, parent_info, pipeline_id,
window_size, None, load_data, false); window_size, None, load_data, false);
self.pending_frames.push(FrameChange { self.pending_frames.push(FrameChange {
frame_id: frame_id, frame_id: frame_id,
old_pipeline_id: Some(pipeline_id), old_pipeline_id: pipeline_id,
new_pipeline_id: new_pipeline_id, new_pipeline_id: new_pipeline_id,
document_ready: false, document_ready: false,
replace: false, replace: false,
}); });
} }
}
}
fn handle_log_entry(&mut self, pipeline_id: Option<PipelineId>, thread_name: Option<String>, entry: LogEntry) { fn handle_log_entry(&mut self, top_level_frame_id: Option<FrameId>, thread_name: Option<String>, entry: LogEntry) {
debug!("Received log entry {:?}.", entry); debug!("Received log entry {:?}.", entry);
match entry { match entry {
LogEntry::Panic(reason, backtrace) => self.handle_panic(pipeline_id, reason, Some(backtrace)), LogEntry::Panic(reason, backtrace) => {
let top_level_frame_id = top_level_frame_id.unwrap_or(self.root_frame_id);
self.handle_panic(top_level_frame_id, reason, Some(backtrace));
},
LogEntry::Error(reason) | LogEntry::Warn(reason) => { LogEntry::Error(reason) | LogEntry::Warn(reason) => {
// VecDeque::truncate is unstable // VecDeque::truncate is unstable
if WARNINGS_BUFFER_SIZE <= self.handled_warnings.len() { if WARNINGS_BUFFER_SIZE <= self.handled_warnings.len() {
@ -1291,25 +1294,24 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) { fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) {
let (frame_id, parent_info) = match self.pipelines.get(&pipeline_id) { let (frame_id, parent_id) = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => (pipeline.frame_id, pipeline.parent_info), Some(pipeline) => match pipeline.parent_info {
None => return warn!("Pipeline {:?} loaded after closure.", pipeline_id), Some((parent_id, _)) => (pipeline.frame_id, parent_id),
}; None => return warn!("Pipeline {} has no parent.", pipeline_id),
let subframe_parent_id = match parent_info { },
Some(ref parent) => parent.0, None => return warn!("Pipeline {} loaded after closure.", pipeline_id),
None => return warn!("Pipeline {:?} has no parent.", pipeline_id),
}; };
let msg = ConstellationControlMsg::DispatchFrameLoadEvent { let msg = ConstellationControlMsg::DispatchFrameLoadEvent {
target: frame_id, target: frame_id,
parent: subframe_parent_id, parent: parent_id,
child: pipeline_id, child: pipeline_id,
}; };
let result = match self.pipelines.get(&subframe_parent_id) { let result = match self.pipelines.get(&parent_id) {
Some(pipeline) => pipeline.script_chan.send(msg), Some(parent) => parent.script_chan.send(msg),
None => return warn!("Pipeline {:?} subframe loaded after closure.", subframe_parent_id), None => return warn!("Parent {} frame loaded after closure.", parent_id),
}; };
if let Err(e) = result { if let Err(e) = result {
self.handle_send_error(subframe_parent_id, e); self.handle_send_error(parent_id, e);
} }
} }
@ -1371,7 +1373,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
(load_data, script_chan, window_size, is_private) (load_data, script_chan, window_size, is_private)
}; };
// Create the new pipeline, attached to the parent and push to pending frames // Create the new pipeline, attached to the parent and push to pending frames
self.new_pipeline(load_info.new_pipeline_id, self.new_pipeline(load_info.new_pipeline_id,
load_info.frame_id, load_info.frame_id,
@ -1428,37 +1429,27 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
pipeline_id: PipelineId, pipeline_id: PipelineId,
message: String, message: String,
sender: IpcSender<bool>) { sender: IpcSender<bool>) {
let display_alert_dialog = if PREFS.is_mozbrowser_enabled() { let pipeline_isnt_root = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info).is_some();
let parent_pipeline_info = self.pipelines.get(&pipeline_id).and_then(|source| source.parent_info); let mozbrowser_modal_prompt = pipeline_isnt_root && PREFS.is_mozbrowser_enabled();
if parent_pipeline_info.is_some() {
let root_pipeline_id = self.frames.get(&self.root_frame_id)
.map(|root_frame| root_frame.current.pipeline_id);
let ancestor_info = self.get_mozbrowser_ancestor_info(pipeline_id); if mozbrowser_modal_prompt {
if let Some((ancestor_id, mozbrowser_iframe_id)) = ancestor_info {
if root_pipeline_id == Some(ancestor_id) {
match root_pipeline_id.and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) {
Some(root_pipeline) => {
// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsershowmodalprompt // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsershowmodalprompt
let event = MozBrowserEvent::ShowModalPrompt("alert".to_owned(), "Alert".to_owned(), let prompt_type = String::from("alert");
String::from(message), "".to_owned()); let title = String::from("Alert");
root_pipeline.trigger_mozbrowser_event(Some(mozbrowser_iframe_id), event); let return_value = String::from("");
} let event = MozBrowserEvent::ShowModalPrompt(prompt_type, title, message, return_value);
None => return warn!("Alert sent to Pipeline {:?} after closure.", root_pipeline_id), let top_level_frame_id = self.get_top_level_frame_for_pipeline(pipeline_id);
}
} else {
warn!("A non-current frame is trying to show an alert.")
}
}
false
} else {
true
}
} else {
true
};
let result = sender.send(display_alert_dialog); match self.frames.get(&self.root_frame_id) {
None => warn!("Alert sent after root frame closure."),
Some(root_frame) => match self.pipelines.get(&root_frame.current.pipeline_id) {
None => warn!("Alert sent after root pipeline closure."),
Some(root_pipeline) => root_pipeline.trigger_mozbrowser_event(Some(top_level_frame_id), event),
}
}
}
let result = sender.send(!mozbrowser_modal_prompt);
if let Err(e) = result { if let Err(e) = result {
self.handle_send_error(pipeline_id, e); self.handle_send_error(pipeline_id, e);
} }
@ -1546,7 +1537,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
fn handle_load_start_msg(&mut self, pipeline_id: PipelineId) { fn handle_load_start_msg(&mut self, pipeline_id: PipelineId) {
let frame_id = self.get_top_level_frame_for_pipeline(Some(pipeline_id)); let frame_id = self.get_top_level_frame_for_pipeline(pipeline_id);
let forward = !self.joint_session_future_is_empty(frame_id); let forward = !self.joint_session_future_is_empty(frame_id);
let back = !self.joint_session_past_is_empty(frame_id); let back = !self.joint_session_past_is_empty(frame_id);
self.compositor_proxy.send(ToCompositorMsg::LoadStart(back, forward)); self.compositor_proxy.send(ToCompositorMsg::LoadStart(back, forward));
@ -1564,7 +1555,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
if webdriver_reset { if webdriver_reset {
self.webdriver.load_channel = None; self.webdriver.load_channel = None;
} }
let frame_id = self.get_top_level_frame_for_pipeline(Some(pipeline_id)); let frame_id = self.get_top_level_frame_for_pipeline(pipeline_id);
let forward = !self.joint_session_future_is_empty(frame_id); let forward = !self.joint_session_future_is_empty(frame_id);
let back = !self.joint_session_past_is_empty(frame_id); let back = !self.joint_session_past_is_empty(frame_id);
let root = self.root_frame_id == frame_id; let root = self.root_frame_id == frame_id;
@ -1575,13 +1566,15 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
fn handle_traverse_history_msg(&mut self, fn handle_traverse_history_msg(&mut self,
pipeline_id: Option<PipelineId>, pipeline_id: Option<PipelineId>,
direction: TraversalDirection) { direction: TraversalDirection) {
let frame_id = self.get_top_level_frame_for_pipeline(pipeline_id); let top_level_frame_id = pipeline_id
.map(|pipeline_id| self.get_top_level_frame_for_pipeline(pipeline_id))
.unwrap_or(self.root_frame_id);
let mut traversal_info = HashMap::new(); let mut traversal_info = HashMap::new();
match direction { match direction {
TraversalDirection::Forward(delta) => { TraversalDirection::Forward(delta) => {
let mut future = self.joint_session_future(frame_id); let mut future = self.joint_session_future(top_level_frame_id);
for _ in 0..delta { for _ in 0..delta {
match future.pop() { match future.pop() {
Some((_, frame_id, pipeline_id)) => { Some((_, frame_id, pipeline_id)) => {
@ -1592,7 +1585,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
}, },
TraversalDirection::Back(delta) => { TraversalDirection::Back(delta) => {
let mut past = self.joint_session_past(frame_id); let mut past = self.joint_session_past(top_level_frame_id);
for _ in 0..delta { for _ in 0..delta {
match past.pop() { match past.pop() {
Some((_, frame_id, pipeline_id)) => { Some((_, frame_id, pipeline_id)) => {
@ -1609,7 +1602,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender<u32>) { fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender<u32>) {
let frame_id = self.get_top_level_frame_for_pipeline(Some(pipeline_id)); let frame_id = self.get_top_level_frame_for_pipeline(pipeline_id);
// Initialize length at 1 to count for the current active entry // Initialize length at 1 to count for the current active entry
let mut length = 1; let mut length = 1;
@ -1966,11 +1959,21 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
} }
fn get_top_level_frame_for_pipeline(&self, pipeline_id: Option<PipelineId>) -> FrameId { fn get_top_level_frame_for_pipeline(&self, mut pipeline_id: PipelineId) -> FrameId {
if PREFS.is_mozbrowser_enabled() { if PREFS.is_mozbrowser_enabled() {
pipeline_id.and_then(|id| self.get_mozbrowser_ancestor_info(id)) loop {
.map(|(_, mozbrowser_iframe_id)| mozbrowser_iframe_id) match self.pipelines.get(&pipeline_id) {
.unwrap_or(self.root_frame_id) Some(pipeline) => match pipeline.parent_info {
Some((_, FrameType::MozBrowserIFrame)) => return pipeline.frame_id,
Some((parent_id, _)) => pipeline_id = parent_id,
None => return self.root_frame_id,
},
None => {
warn!("Finding top-level ancestor for pipeline {} after closure.", pipeline_id);
return self.root_frame_id;
},
}
}
} else { } else {
// If mozbrowser is not enabled, the root frame is the only top-level frame // If mozbrowser is not enabled, the root frame is the only top-level frame
self.root_frame_id self.root_frame_id
@ -2025,7 +2028,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// This is the result of a link being clicked and a navigation completing. // This is the result of a link being clicked and a navigation completing.
self.trigger_mozbrowserlocationchange(frame_change.new_pipeline_id); self.trigger_mozbrowserlocationchange(frame_change.new_pipeline_id);
let top_level_frame_id = self.get_top_level_frame_for_pipeline(Some(frame_change.new_pipeline_id)); let top_level_frame_id = self.get_top_level_frame_for_pipeline(frame_change.new_pipeline_id);
self.clear_joint_session_future(top_level_frame_id); self.clear_joint_session_future(top_level_frame_id);
} }
@ -2262,30 +2265,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Close a frame (and all children) // Close a frame (and all children)
fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) { fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
debug!("Closing frame {:?}.", frame_id); debug!("Closing frame {}.", frame_id);
// Store information about the pipelines to be closed. Then close the
// pipelines, before removing ourself from the frames hash map. This
// ordering is vital - so that if close_pipeline() ends up closing
// any child frames, they can be removed from the parent frame correctly.
let parent_info = self.frames.get(&frame_id) let parent_info = self.frames.get(&frame_id)
.and_then(|frame| self.pipelines.get(&frame.current.pipeline_id)) .and_then(|frame| self.pipelines.get(&frame.current.pipeline_id))
.and_then(|pipeline| pipeline.parent_info); .and_then(|pipeline| pipeline.parent_info);
let pipelines_to_close = { self.close_frame_children(frame_id, exit_mode);
let mut pipelines_to_close = vec!();
if let Some(frame) = self.frames.get(&frame_id) {
pipelines_to_close.extend_from_slice(&frame.next);
pipelines_to_close.push(frame.current.clone());
pipelines_to_close.extend_from_slice(&frame.prev);
}
pipelines_to_close
};
for entry in pipelines_to_close {
self.close_pipeline(entry.pipeline_id, exit_mode);
}
if self.frames.remove(&frame_id).is_none() { if self.frames.remove(&frame_id).is_none() {
warn!("Closing frame {:?} twice.", frame_id); warn!("Closing frame {:?} twice.", frame_id);
@ -2301,6 +2286,31 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
debug!("Closed frame {:?}.", frame_id); debug!("Closed frame {:?}.", frame_id);
} }
// Close the children of a frame
fn close_frame_children(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
debug!("Closing frame children {}.", frame_id);
// Store information about the pipelines to be closed. Then close the
// pipelines, before removing ourself from the frames hash map. This
// ordering is vital - so that if close_pipeline() ends up closing
// any child frames, they can be removed from the parent frame correctly.
let mut pipelines_to_close: Vec<PipelineId> = self.pending_frames.iter()
.filter(|frame_change| frame_change.frame_id == frame_id)
.map(|frame_change| frame_change.new_pipeline_id)
.collect();
if let Some(frame) = self.frames.get(&frame_id) {
pipelines_to_close.extend(frame.next.iter().map(|state| state.pipeline_id));
pipelines_to_close.push(frame.current.pipeline_id);
pipelines_to_close.extend(frame.prev.iter().map(|state| state.pipeline_id));
}
for pipeline_id in pipelines_to_close {
self.close_pipeline(pipeline_id, exit_mode);
}
debug!("Closed frame children {}.", frame_id);
}
// Close all pipelines at and beneath a given frame // Close all pipelines at and beneath a given frame
fn close_pipeline(&mut self, pipeline_id: PipelineId, exit_mode: ExitPipelineMode) { fn close_pipeline(&mut self, pipeline_id: PipelineId, exit_mode: ExitPipelineMode) {
debug!("Closing pipeline {:?}.", pipeline_id); debug!("Closing pipeline {:?}.", pipeline_id);
@ -2410,49 +2420,29 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
} }
/// For a given pipeline, determine the mozbrowser iframe that transitively contains
/// it. There could be arbitrary levels of nested iframes in between them.
fn get_mozbrowser_ancestor_info(&self, original_pipeline_id: PipelineId) -> Option<(PipelineId, FrameId)> {
let mut pipeline_id = original_pipeline_id;
loop {
match self.pipelines.get(&pipeline_id) {
Some(pipeline) => match pipeline.parent_info {
Some((parent_id, FrameType::MozBrowserIFrame)) => return Some((parent_id, pipeline.frame_id)),
Some((parent_id, _)) => pipeline_id = parent_id,
None => return None,
},
None => {
warn!("Finding mozbrowser ancestor for pipeline {} after closure.", pipeline_id);
return None;
},
}
}
}
// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserlocationchange // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserlocationchange
// Note that this is a no-op if the pipeline is not a mozbrowser iframe // Note that this is a no-op if the pipeline is not a mozbrowser iframe
fn trigger_mozbrowserlocationchange(&self, pipeline_id: PipelineId) { fn trigger_mozbrowserlocationchange(&self, pipeline_id: PipelineId) {
if !PREFS.is_mozbrowser_enabled() { return; } match self.pipelines.get(&pipeline_id) {
Some(pipeline) => if let Some((parent_id, FrameType::MozBrowserIFrame)) = pipeline.parent_info {
let url = match self.pipelines.get(&pipeline_id) { match self.pipelines.get(&parent_id) {
Some(pipeline) => pipeline.url.to_string(), Some(parent) => {
None => return warn!("triggered mozbrowser location change on closed pipeline {:?}", pipeline_id), let can_go_forward = !self.joint_session_future_is_empty(pipeline.frame_id);
}; let can_go_back = !self.joint_session_past_is_empty(pipeline.frame_id);
let url = pipeline.url.to_string();
// If this is a mozbrowser iframe, then send the event with new url
if let Some((ancestor_id, mozbrowser_frame_id)) = self.get_mozbrowser_ancestor_info(pipeline_id) {
if let Some(ancestor) = self.pipelines.get(&ancestor_id) {
let can_go_forward = !self.joint_session_future(mozbrowser_frame_id).is_empty();
let can_go_back = !self.joint_session_past(mozbrowser_frame_id).is_empty();
let event = MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward); let event = MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward);
ancestor.trigger_mozbrowser_event(Some(mozbrowser_frame_id), event); parent.trigger_mozbrowser_event(Some(pipeline.frame_id), event);
},
None => warn!("triggered mozbrowser location change on closed parent {}", parent_id),
} }
},
None => warn!("triggered mozbrowser location change on closed pipeline {}", pipeline_id),
} }
} }
// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsererror // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsererror
// Note that this does not require the pipeline to be an immediate child of the root // Note that this does not require the pipeline to be an immediate child of the root
fn trigger_mozbrowsererror(&mut self, pipeline_id: Option<PipelineId>, reason: String, backtrace: Option<String>) { fn trigger_mozbrowsererror(&mut self, top_level_frame_id: FrameId, reason: String, backtrace: Option<String>) {
if !PREFS.is_mozbrowser_enabled() { return; } if !PREFS.is_mozbrowser_enabled() { return; }
let mut report = String::new(); let mut report = String::new();
@ -2474,21 +2464,19 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, reason, report); let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, reason, report);
if let Some(pipeline_id) = pipeline_id { match self.frames.get(&top_level_frame_id) {
if let Some((ancestor_id, mozbrowser_iframe_id)) = self.get_mozbrowser_ancestor_info(pipeline_id) { None => warn!("Mozbrowser error after top-level frame closed."),
if let Some(ancestor) = self.pipelines.get(&ancestor_id) { Some(frame) => match self.pipelines.get(&frame.current.pipeline_id) {
return ancestor.trigger_mozbrowser_event(Some(mozbrowser_iframe_id), event); None => warn!("Mozbrowser error after top-level pipeline closed."),
} Some(pipeline) => match pipeline.parent_info {
} None => pipeline.trigger_mozbrowser_event(None, event),
} Some((parent_id, _)) => match self.pipelines.get(&parent_id) {
None => warn!("Mozbrowser error after root pipeline closed."),
if let Some(root_frame) = self.frames.get(&self.root_frame_id) { Some(parent) => parent.trigger_mozbrowser_event(Some(top_level_frame_id), event),
if let Some(root_pipeline) = self.pipelines.get(&root_frame.current.pipeline_id) { },
return root_pipeline.trigger_mozbrowser_event(None, event); },
} },
} };
warn!("Mozbrowser error after root pipeline closed.");
} }
fn focused_pipeline_in_tree(&self, frame_id: FrameId) -> bool { fn focused_pipeline_in_tree(&self, frame_id: FrameId) -> bool {

View file

@ -82,6 +82,8 @@ pub struct InitialPipelineState {
pub id: PipelineId, pub id: PipelineId,
/// The ID of the frame that contains this Pipeline. /// The ID of the frame that contains this Pipeline.
pub frame_id: FrameId, pub frame_id: FrameId,
/// The ID of the top-level frame that contains this Pipeline.
pub top_level_frame_id: FrameId,
/// The ID of the parent pipeline and frame type, if any. /// The ID of the parent pipeline and frame type, if any.
/// If `None`, this is the root. /// If `None`, this is the root.
pub parent_info: Option<(PipelineId, FrameType)>, pub parent_info: Option<(PipelineId, FrameType)>,
@ -204,6 +206,7 @@ impl Pipeline {
let unprivileged_pipeline_content = UnprivilegedPipelineContent { let unprivileged_pipeline_content = UnprivilegedPipelineContent {
id: state.id, id: state.id,
frame_id: state.frame_id, frame_id: state.frame_id,
top_level_frame_id: state.top_level_frame_id,
parent_info: state.parent_info, parent_info: state.parent_info,
constellation_chan: state.constellation_chan, constellation_chan: state.constellation_chan,
scheduler_chan: state.scheduler_chan, scheduler_chan: state.scheduler_chan,
@ -381,6 +384,7 @@ impl Pipeline {
pub struct UnprivilegedPipelineContent { pub struct UnprivilegedPipelineContent {
id: PipelineId, id: PipelineId,
frame_id: FrameId, frame_id: FrameId,
top_level_frame_id: FrameId,
parent_info: Option<(PipelineId, FrameType)>, parent_info: Option<(PipelineId, FrameType)>,
constellation_chan: IpcSender<ScriptMsg>, constellation_chan: IpcSender<ScriptMsg>,
layout_to_constellation_chan: IpcSender<LayoutMsg>, layout_to_constellation_chan: IpcSender<LayoutMsg>,
@ -416,6 +420,7 @@ impl UnprivilegedPipelineContent {
let layout_pair = STF::create(InitialScriptState { let layout_pair = STF::create(InitialScriptState {
id: self.id, id: self.id,
frame_id: self.frame_id, frame_id: self.frame_id,
top_level_frame_id: self.top_level_frame_id,
parent_info: self.parent_info, parent_info: self.parent_info,
control_chan: self.script_chan.clone(), control_chan: self.script_chan.clone(),
control_port: self.script_port, control_port: self.script_port,
@ -433,6 +438,7 @@ impl UnprivilegedPipelineContent {
}, self.load_data.clone()); }, self.load_data.clone());
LTF::create(self.id, LTF::create(self.id,
Some(self.top_level_frame_id),
self.load_data.url, self.load_data.url,
self.parent_info.is_some(), self.parent_info.is_some(),
layout_pair, layout_pair,

View file

@ -42,7 +42,6 @@ extern crate selectors;
extern crate serde_json; extern crate serde_json;
extern crate servo_url; extern crate servo_url;
extern crate style; extern crate style;
extern crate style_traits;
extern crate util; extern crate util;
extern crate webrender_traits; extern crate webrender_traits;
@ -80,7 +79,7 @@ use layout::webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBui
use layout::wrapper::LayoutNodeLayoutData; use layout::wrapper::LayoutNodeLayoutData;
use layout::wrapper::drop_style_and_layout_data; use layout::wrapper::drop_style_and_layout_data;
use layout_traits::LayoutThreadFactory; use layout_traits::LayoutThreadFactory;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::{FrameId, PipelineId};
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
use net_traits::image_cache_thread::UsePlaceholder; use net_traits::image_cache_thread::UsePlaceholder;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -235,6 +234,7 @@ impl LayoutThreadFactory for LayoutThread {
/// Spawns a new layout thread. /// Spawns a new layout thread.
fn create(id: PipelineId, fn create(id: PipelineId,
top_level_frame_id: Option<FrameId>,
url: ServoUrl, url: ServoUrl,
is_iframe: bool, is_iframe: bool,
chan: (Sender<Msg>, Receiver<Msg>), chan: (Sender<Msg>, Receiver<Msg>),
@ -251,7 +251,11 @@ impl LayoutThreadFactory for LayoutThread {
thread::spawn_named(format!("LayoutThread {:?}", id), thread::spawn_named(format!("LayoutThread {:?}", id),
move || { move || {
thread_state::initialize(thread_state::LAYOUT); thread_state::initialize(thread_state::LAYOUT);
PipelineId::install(id);
if let Some(top_level_frame_id) = top_level_frame_id {
FrameId::install(top_level_frame_id);
}
{ // Ensures layout thread is destroyed before we send shutdown message { // Ensures layout thread is destroyed before we send shutdown message
let sender = chan.0; let sender = chan.0;
let layout = LayoutThread::new(id, let layout = LayoutThread::new(id,
@ -718,6 +722,7 @@ impl LayoutThread {
fn create_layout_thread(&self, info: NewLayoutThreadInfo) { fn create_layout_thread(&self, info: NewLayoutThreadInfo) {
LayoutThread::create(info.id, LayoutThread::create(info.id,
FrameId::installed(),
info.url.clone(), info.url.clone(),
info.is_parent, info.is_parent,
info.layout_pair, info.layout_pair,

View file

@ -20,7 +20,7 @@ extern crate webrender_traits;
use gfx::font_cache_thread::FontCacheThread; use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::ipc::{IpcReceiver, IpcSender};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::{FrameId, PipelineId};
use net_traits::image_cache_thread::ImageCacheThread; use net_traits::image_cache_thread::ImageCacheThread;
use profile_traits::{mem, time}; use profile_traits::{mem, time};
use script_traits::{ConstellationControlMsg, LayoutControlMsg}; use script_traits::{ConstellationControlMsg, LayoutControlMsg};
@ -33,6 +33,7 @@ use std::sync::mpsc::{Receiver, Sender};
pub trait LayoutThreadFactory { pub trait LayoutThreadFactory {
type Message; type Message;
fn create(id: PipelineId, fn create(id: PipelineId,
top_level_frame_id: Option<FrameId>,
url: ServoUrl, url: ServoUrl,
is_iframe: bool, is_iframe: bool,
chan: (Sender<Self::Message>, Receiver<Self::Message>), chan: (Sender<Self::Message>, Receiver<Self::Message>),

View file

@ -217,8 +217,6 @@ impl PipelineNamespace {
thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = Cell::new(None)); thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = Cell::new(None));
thread_local!(pub static PIPELINE_ID: Cell<Option<PipelineId>> = Cell::new(None));
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
pub struct PipelineNamespaceId(pub u32); pub struct PipelineNamespaceId(pub u32);
@ -246,15 +244,6 @@ impl PipelineId {
let PipelineIndex(index) = self.index; let PipelineIndex(index) = self.index;
webrender_traits::PipelineId(namespace_id, index) webrender_traits::PipelineId(namespace_id, index)
} }
pub fn install(id: PipelineId) {
PIPELINE_ID.with(|tls| tls.set(Some(id)))
}
pub fn installed() -> Option<PipelineId> {
PIPELINE_ID.with(|tls| tls.get())
}
} }
impl fmt::Display for PipelineId { impl fmt::Display for PipelineId {
@ -265,6 +254,8 @@ impl fmt::Display for PipelineId {
} }
} }
thread_local!(pub static TOP_LEVEL_FRAME_ID: Cell<Option<FrameId>> = Cell::new(None));
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
pub struct FrameIndex(pub u32); pub struct FrameIndex(pub u32);
@ -283,6 +274,16 @@ impl FrameId {
new_frame_id new_frame_id
}) })
} }
/// Each script and layout thread should have the top-level frame id installed,
/// since it is used by crash reporting.
pub fn install(id: FrameId) {
TOP_LEVEL_FRAME_ID.with(|tls| tls.set(Some(id)))
}
pub fn installed() -> Option<FrameId> {
TOP_LEVEL_FRAME_ID.with(|tls| tls.get())
}
} }
impl fmt::Display for FrameId { impl fmt::Display for FrameId {

View file

@ -26,7 +26,7 @@ use js::jsapi::{HandleValue, JS_SetInterruptCallback};
use js::jsapi::{JSAutoCompartment, JSContext}; use js::jsapi::{JSAutoCompartment, JSContext};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::Runtime; use js::rust::Runtime;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::FrameId;
use net_traits::{IpcSend, load_whole_resource}; use net_traits::{IpcSend, load_whole_resource};
use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType};
use rand::random; use rand::random;
@ -158,9 +158,14 @@ impl DedicatedWorkerGlobalScope {
closing: Arc<AtomicBool>) { closing: Arc<AtomicBool>) {
let serialized_worker_url = worker_url.to_string(); let serialized_worker_url = worker_url.to_string();
let name = format!("WebWorker for {}", serialized_worker_url); let name = format!("WebWorker for {}", serialized_worker_url);
let top_level_frame_id = FrameId::installed();
spawn_named(name, move || { spawn_named(name, move || {
thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER); thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER);
PipelineId::install(init.pipeline_id);
if let Some(top_level_frame_id) = top_level_frame_id {
FrameId::install(top_level_frame_id);
}
let roots = RootCollection::new(); let roots = RootCollection::new();
let _stack_roots_tls = StackRootTLS::new(&roots); let _stack_roots_tls = StackRootTLS::new(&roots);

View file

@ -517,8 +517,8 @@ impl ScriptThreadFactory for ScriptThread {
thread::spawn_named(format!("ScriptThread {:?}", state.id), thread::spawn_named(format!("ScriptThread {:?}", state.id),
move || { move || {
thread_state::initialize(thread_state::SCRIPT); thread_state::initialize(thread_state::SCRIPT);
PipelineId::install(state.id);
PipelineNamespace::install(state.pipeline_namespace_id); PipelineNamespace::install(state.pipeline_namespace_id);
FrameId::install(state.top_level_frame_id);
let roots = RootCollection::new(); let roots = RootCollection::new();
let _stack_roots_tls = StackRootTLS::new(&roots); let _stack_roots_tls = StackRootTLS::new(&roots);
let id = state.id; let id = state.id;

View file

@ -442,6 +442,8 @@ pub struct InitialScriptState {
pub parent_info: Option<(PipelineId, FrameType)>, pub parent_info: Option<(PipelineId, FrameType)>,
/// The ID of the frame this script is part of. /// The ID of the frame this script is part of.
pub frame_id: FrameId, pub frame_id: FrameId,
/// The ID of the top-level frame this script is part of.
pub top_level_frame_id: FrameId,
/// A channel with which messages can be sent to us (the script thread). /// A channel with which messages can be sent to us (the script thread).
pub control_chan: IpcSender<ConstellationControlMsg>, pub control_chan: IpcSender<ConstellationControlMsg>,
/// A port on which messages sent by the constellation to script can be received. /// A port on which messages sent by the constellation to script can be received.
@ -699,8 +701,8 @@ pub enum ConstellationMsg {
WebDriverCommand(WebDriverCommandMsg), WebDriverCommand(WebDriverCommandMsg),
/// Reload the current page. /// Reload the current page.
Reload, Reload,
/// A log entry, with the pipeline id and thread name /// A log entry, with the top-level frame id and thread name
LogEntry(Option<PipelineId>, Option<String>, LogEntry), LogEntry(Option<FrameId>, Option<String>, LogEntry),
} }
/// Resources required by workerglobalscopes /// Resources required by workerglobalscopes

View file

@ -15,8 +15,8 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use euclid::point::Point2D; use euclid::point::Point2D;
use euclid::size::Size2D; use euclid::size::Size2D;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::{FrameId, PipelineId, TraversalDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use msg::constellation_msg::{PipelineId, TraversalDirection};
use net_traits::CoreResourceMsg; use net_traits::CoreResourceMsg;
use net_traits::storage_thread::StorageType; use net_traits::storage_thread::StorageType;
use offscreen_gl_context::{GLContextAttributes, GLLimits}; use offscreen_gl_context::{GLContextAttributes, GLLimits};
@ -131,8 +131,8 @@ pub enum ScriptMsg {
ResizeTo(Size2D<u32>), ResizeTo(Size2D<u32>),
/// Script has handled a touch event, and either prevented or allowed default actions. /// Script has handled a touch event, and either prevented or allowed default actions.
TouchEventProcessed(EventResult), TouchEventProcessed(EventResult),
/// A log entry, with the pipeline id and thread name /// A log entry, with the top-level frame id and thread name
LogEntry(Option<PipelineId>, Option<String>, LogEntry), LogEntry(Option<FrameId>, Option<String>, LogEntry),
/// Notifies the constellation that this pipeline has exited. /// Notifies the constellation that this pipeline has exited.
PipelineExited(PipelineId), PipelineExited(PipelineId),
/// Send messages from postMessage calls from serviceworker /// Send messages from postMessage calls from serviceworker