diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 408d4df6463..52976bf1daf 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -419,9 +419,9 @@ impl Log for FromScriptLogger { fn log(&self, record: &LogRecord) { if let Some(entry) = log_entry(record) { 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 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.send(msg); } @@ -457,9 +457,9 @@ impl Log for FromCompositorLogger { fn log(&self, record: &LogRecord) { if let Some(entry) = log_entry(record) { 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 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.send(msg); } @@ -605,9 +605,18 @@ impl Constellation 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::(InitialPipelineState { id: pipeline_id, frame_id: frame_id, + top_level_frame_id: top_level_frame_id, parent_info: parent_info, constellation_chan: self.script_sender.clone(), layout_to_constellation_chan: self.layout_sender.clone(), @@ -847,8 +856,8 @@ impl Constellation debug!("constellation got reload message"); self.handle_reload_msg(); } - FromCompositorMsg::LogEntry(pipeline_id, thread_name, entry) => { - self.handle_log_entry(pipeline_id, thread_name, entry); + FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry) => { + self.handle_log_entry(top_level_frame_id, thread_name, entry); } } } @@ -999,8 +1008,8 @@ impl Constellation FromScriptMsg::Exit => { self.compositor_proxy.send(ToCompositorMsg::Exit); } - FromScriptMsg::LogEntry(pipeline_id, thread_name, entry) => { - self.handle_log_entry(pipeline_id, thread_name, entry); + FromScriptMsg::LogEntry(top_level_frame_id, thread_name, entry) => { + self.handle_log_entry(top_level_frame_id, thread_name, entry); } FromScriptMsg::SetTitle(pipeline_id, title) => { @@ -1172,10 +1181,12 @@ impl Constellation fn handle_send_error(&mut self, pipeline_id: PipelineId, err: IOError) { // Treat send error the same as receiving a panic message 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, reason: String, backtrace: Option) { + fn handle_panic(&mut self, top_level_frame_id: FrameId, reason: String, backtrace: Option) { if opts::get().hard_fail { // 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. @@ -1183,58 +1194,50 @@ impl Constellation 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 - self.trigger_mozbrowsererror(pipeline_id, reason, backtrace); + self.trigger_mozbrowsererror(top_level_frame_id, reason, backtrace); - if let Some(pipeline_id) = pipeline_id { - let pipeline_url = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.url.clone()); - let parent_info = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info); - let window_size = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.size); - let frame_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); + let frame_id = FrameId::from(top_level_frame_id); + let pipeline_id = self.frames.get(&frame_id).map(|frame| frame.current.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)); - self.close_pipeline(pipeline_id, ExitPipelineMode::Force); - self.pipelines.remove(&pipeline_id); + self.close_frame_children(frame_id, ExitPipelineMode::Force); - 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"); - - if let Some(pipeline_url) = pipeline_url { - if pipeline_url == failure_url { - return error!("about:failure failed"); - } - } - - warn!("creating replacement pipeline for about:failure"); - - if let Some(frame_id) = frame_id { - let new_pipeline_id = PipelineId::new(); - let load_data = LoadData::new(failure_url, None, None); - self.new_pipeline(new_pipeline_id, frame_id, parent_info, Some(pipeline_id), - window_size, None, load_data, false); - - self.pending_frames.push(FrameChange { - frame_id: frame_id, - old_pipeline_id: Some(pipeline_id), - new_pipeline_id: new_pipeline_id, - document_ready: false, - replace: false, - }); + if let Some(pipeline_url) = pipeline_url { + if pipeline_url == failure_url { + return error!("about:failure failed"); } } + + warn!("creating replacement pipeline for about:failure"); + + let new_pipeline_id = PipelineId::new(); + let load_data = LoadData::new(failure_url, None, None); + self.new_pipeline(new_pipeline_id, frame_id, parent_info, pipeline_id, + window_size, None, load_data, false); + + self.pending_frames.push(FrameChange { + frame_id: frame_id, + old_pipeline_id: pipeline_id, + new_pipeline_id: new_pipeline_id, + document_ready: false, + replace: false, + }); } - fn handle_log_entry(&mut self, pipeline_id: Option, thread_name: Option, entry: LogEntry) { + fn handle_log_entry(&mut self, top_level_frame_id: Option, thread_name: Option, entry: LogEntry) { debug!("Received log entry {:?}.", 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) => { // VecDeque::truncate is unstable if WARNINGS_BUFFER_SIZE <= self.handled_warnings.len() { @@ -1291,25 +1294,24 @@ impl Constellation } fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) { - let (frame_id, parent_info) = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => (pipeline.frame_id, pipeline.parent_info), - None => return warn!("Pipeline {:?} loaded after closure.", pipeline_id), - }; - let subframe_parent_id = match parent_info { - Some(ref parent) => parent.0, - None => return warn!("Pipeline {:?} has no parent.", pipeline_id), + let (frame_id, parent_id) = match self.pipelines.get(&pipeline_id) { + Some(pipeline) => match pipeline.parent_info { + Some((parent_id, _)) => (pipeline.frame_id, parent_id), + None => return warn!("Pipeline {} has no parent.", pipeline_id), + }, + None => return warn!("Pipeline {} loaded after closure.", pipeline_id), }; let msg = ConstellationControlMsg::DispatchFrameLoadEvent { target: frame_id, - parent: subframe_parent_id, + parent: parent_id, child: pipeline_id, }; - let result = match self.pipelines.get(&subframe_parent_id) { - Some(pipeline) => pipeline.script_chan.send(msg), - None => return warn!("Pipeline {:?} subframe loaded after closure.", subframe_parent_id), + let result = match self.pipelines.get(&parent_id) { + Some(parent) => parent.script_chan.send(msg), + None => return warn!("Parent {} frame loaded after closure.", parent_id), }; if let Err(e) = result { - self.handle_send_error(subframe_parent_id, e); + self.handle_send_error(parent_id, e); } } @@ -1371,11 +1373,10 @@ impl Constellation (load_data, script_chan, window_size, is_private) }; - // Create the new pipeline, attached to the parent and push to pending frames self.new_pipeline(load_info.new_pipeline_id, load_info.frame_id, - Some((load_info.parent_pipeline_id, load_info.frame_type)), + Some((load_info.parent_pipeline_id, load_info.frame_type)), load_info.old_pipeline_id, window_size, script_chan, @@ -1428,37 +1429,27 @@ impl Constellation pipeline_id: PipelineId, message: String, sender: IpcSender) { - let display_alert_dialog = if PREFS.is_mozbrowser_enabled() { - let parent_pipeline_info = self.pipelines.get(&pipeline_id).and_then(|source| source.parent_info); - 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 pipeline_isnt_root = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info).is_some(); + let mozbrowser_modal_prompt = pipeline_isnt_root && PREFS.is_mozbrowser_enabled(); - let ancestor_info = self.get_mozbrowser_ancestor_info(pipeline_id); - 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 - let event = MozBrowserEvent::ShowModalPrompt("alert".to_owned(), "Alert".to_owned(), - String::from(message), "".to_owned()); - root_pipeline.trigger_mozbrowser_event(Some(mozbrowser_iframe_id), event); - } - None => return warn!("Alert sent to Pipeline {:?} after closure.", root_pipeline_id), - } - } else { - warn!("A non-current frame is trying to show an alert.") - } + if mozbrowser_modal_prompt { + // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsershowmodalprompt + let prompt_type = String::from("alert"); + let title = String::from("Alert"); + let return_value = String::from(""); + let event = MozBrowserEvent::ShowModalPrompt(prompt_type, title, message, return_value); + let top_level_frame_id = self.get_top_level_frame_for_pipeline(pipeline_id); + + 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), } - false - } else { - true } - } else { - true - }; + } - let result = sender.send(display_alert_dialog); + let result = sender.send(!mozbrowser_modal_prompt); if let Err(e) = result { self.handle_send_error(pipeline_id, e); } @@ -1546,7 +1537,7 @@ impl Constellation } 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 back = !self.joint_session_past_is_empty(frame_id); self.compositor_proxy.send(ToCompositorMsg::LoadStart(back, forward)); @@ -1564,7 +1555,7 @@ impl Constellation if webdriver_reset { 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 back = !self.joint_session_past_is_empty(frame_id); let root = self.root_frame_id == frame_id; @@ -1575,13 +1566,15 @@ impl Constellation fn handle_traverse_history_msg(&mut self, pipeline_id: Option, 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(); match direction { 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 { match future.pop() { Some((_, frame_id, pipeline_id)) => { @@ -1592,7 +1585,7 @@ impl Constellation } }, 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 { match past.pop() { Some((_, frame_id, pipeline_id)) => { @@ -1609,7 +1602,7 @@ impl Constellation } fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender) { - 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 let mut length = 1; @@ -1966,11 +1959,21 @@ impl Constellation } } - fn get_top_level_frame_for_pipeline(&self, pipeline_id: Option) -> FrameId { + fn get_top_level_frame_for_pipeline(&self, mut pipeline_id: PipelineId) -> FrameId { if PREFS.is_mozbrowser_enabled() { - pipeline_id.and_then(|id| self.get_mozbrowser_ancestor_info(id)) - .map(|(_, mozbrowser_iframe_id)| mozbrowser_iframe_id) - .unwrap_or(self.root_frame_id) + loop { + match self.pipelines.get(&pipeline_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 { // If mozbrowser is not enabled, the root frame is the only top-level frame self.root_frame_id @@ -2025,7 +2028,7 @@ impl Constellation // This is the result of a link being clicked and a navigation completing. 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); } @@ -2262,30 +2265,12 @@ impl Constellation // Close a frame (and all children) fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) { - 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. + debug!("Closing frame {}.", frame_id); let parent_info = self.frames.get(&frame_id) .and_then(|frame| self.pipelines.get(&frame.current.pipeline_id)) .and_then(|pipeline| pipeline.parent_info); - let pipelines_to_close = { - 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); - } + self.close_frame_children(frame_id, exit_mode); if self.frames.remove(&frame_id).is_none() { warn!("Closing frame {:?} twice.", frame_id); @@ -2301,6 +2286,31 @@ impl Constellation 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 = 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 fn close_pipeline(&mut self, pipeline_id: PipelineId, exit_mode: ExitPipelineMode) { debug!("Closing pipeline {:?}.", pipeline_id); @@ -2410,49 +2420,29 @@ impl Constellation } } - /// 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 // Note that this is a no-op if the pipeline is not a mozbrowser iframe fn trigger_mozbrowserlocationchange(&self, pipeline_id: PipelineId) { - if !PREFS.is_mozbrowser_enabled() { return; } - - let url = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => pipeline.url.to_string(), - None => return warn!("triggered mozbrowser location change on closed pipeline {:?}", pipeline_id), - }; - - // 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); - ancestor.trigger_mozbrowser_event(Some(mozbrowser_frame_id), event); - } + match self.pipelines.get(&pipeline_id) { + Some(pipeline) => if let Some((parent_id, FrameType::MozBrowserIFrame)) = pipeline.parent_info { + match self.pipelines.get(&parent_id) { + Some(parent) => { + 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(); + let event = MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward); + 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 // Note that this does not require the pipeline to be an immediate child of the root - fn trigger_mozbrowsererror(&mut self, pipeline_id: Option, reason: String, backtrace: Option) { + fn trigger_mozbrowsererror(&mut self, top_level_frame_id: FrameId, reason: String, backtrace: Option) { if !PREFS.is_mozbrowser_enabled() { return; } let mut report = String::new(); @@ -2474,21 +2464,19 @@ impl Constellation let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, reason, report); - if let Some(pipeline_id) = pipeline_id { - if let Some((ancestor_id, mozbrowser_iframe_id)) = self.get_mozbrowser_ancestor_info(pipeline_id) { - if let Some(ancestor) = self.pipelines.get(&ancestor_id) { - return ancestor.trigger_mozbrowser_event(Some(mozbrowser_iframe_id), event); - } - } - } - - if let Some(root_frame) = self.frames.get(&self.root_frame_id) { - 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."); + match self.frames.get(&top_level_frame_id) { + None => warn!("Mozbrowser error after top-level frame closed."), + Some(frame) => match self.pipelines.get(&frame.current.pipeline_id) { + 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."), + Some(parent) => parent.trigger_mozbrowser_event(Some(top_level_frame_id), event), + }, + }, + }, + }; } fn focused_pipeline_in_tree(&self, frame_id: FrameId) -> bool { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 0e76b4bcc88..44739fd6191 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -82,6 +82,8 @@ pub struct InitialPipelineState { pub id: PipelineId, /// The ID of the frame that contains this Pipeline. 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. /// If `None`, this is the root. pub parent_info: Option<(PipelineId, FrameType)>, @@ -204,6 +206,7 @@ impl Pipeline { let unprivileged_pipeline_content = UnprivilegedPipelineContent { id: state.id, frame_id: state.frame_id, + top_level_frame_id: state.top_level_frame_id, parent_info: state.parent_info, constellation_chan: state.constellation_chan, scheduler_chan: state.scheduler_chan, @@ -381,6 +384,7 @@ impl Pipeline { pub struct UnprivilegedPipelineContent { id: PipelineId, frame_id: FrameId, + top_level_frame_id: FrameId, parent_info: Option<(PipelineId, FrameType)>, constellation_chan: IpcSender, layout_to_constellation_chan: IpcSender, @@ -416,6 +420,7 @@ impl UnprivilegedPipelineContent { let layout_pair = STF::create(InitialScriptState { id: self.id, frame_id: self.frame_id, + top_level_frame_id: self.top_level_frame_id, parent_info: self.parent_info, control_chan: self.script_chan.clone(), control_port: self.script_port, @@ -433,6 +438,7 @@ impl UnprivilegedPipelineContent { }, self.load_data.clone()); LTF::create(self.id, + Some(self.top_level_frame_id), self.load_data.url, self.parent_info.is_some(), layout_pair, diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 5b9722d8a10..fbf95b091ea 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -42,7 +42,6 @@ extern crate selectors; extern crate serde_json; extern crate servo_url; extern crate style; -extern crate style_traits; extern crate util; extern crate webrender_traits; @@ -80,7 +79,7 @@ use layout::webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBui use layout::wrapper::LayoutNodeLayoutData; use layout::wrapper::drop_style_and_layout_data; 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::UsePlaceholder; use parking_lot::RwLock; @@ -235,6 +234,7 @@ impl LayoutThreadFactory for LayoutThread { /// Spawns a new layout thread. fn create(id: PipelineId, + top_level_frame_id: Option, url: ServoUrl, is_iframe: bool, chan: (Sender, Receiver), @@ -251,7 +251,11 @@ impl LayoutThreadFactory for LayoutThread { thread::spawn_named(format!("LayoutThread {:?}", id), move || { 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 let sender = chan.0; let layout = LayoutThread::new(id, @@ -718,6 +722,7 @@ impl LayoutThread { fn create_layout_thread(&self, info: NewLayoutThreadInfo) { LayoutThread::create(info.id, + FrameId::installed(), info.url.clone(), info.is_parent, info.layout_pair, diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index a38fbc8e1f4..70d6eab10ff 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -20,7 +20,7 @@ extern crate webrender_traits; use gfx::font_cache_thread::FontCacheThread; 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 profile_traits::{mem, time}; use script_traits::{ConstellationControlMsg, LayoutControlMsg}; @@ -33,6 +33,7 @@ use std::sync::mpsc::{Receiver, Sender}; pub trait LayoutThreadFactory { type Message; fn create(id: PipelineId, + top_level_frame_id: Option, url: ServoUrl, is_iframe: bool, chan: (Sender, Receiver), diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index dba47caa4bf..dddfb0c0042 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -217,8 +217,6 @@ impl PipelineNamespace { thread_local!(pub static PIPELINE_NAMESPACE: Cell> = Cell::new(None)); -thread_local!(pub static PIPELINE_ID: Cell> = Cell::new(None)); - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] pub struct PipelineNamespaceId(pub u32); @@ -246,15 +244,6 @@ impl PipelineId { let PipelineIndex(index) = self.index; webrender_traits::PipelineId(namespace_id, index) } - - pub fn install(id: PipelineId) { - PIPELINE_ID.with(|tls| tls.set(Some(id))) - } - - pub fn installed() -> Option { - PIPELINE_ID.with(|tls| tls.get()) - } - } impl fmt::Display for PipelineId { @@ -265,6 +254,8 @@ impl fmt::Display for PipelineId { } } +thread_local!(pub static TOP_LEVEL_FRAME_ID: Cell> = Cell::new(None)); + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] pub struct FrameIndex(pub u32); @@ -283,6 +274,16 @@ impl FrameId { 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 { + TOP_LEVEL_FRAME_ID.with(|tls| tls.get()) + } } impl fmt::Display for FrameId { diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 85f4db49e02..6fbbd25f97b 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -26,7 +26,7 @@ use js::jsapi::{HandleValue, JS_SetInterruptCallback}; use js::jsapi::{JSAutoCompartment, JSContext}; use js::jsval::UndefinedValue; use js::rust::Runtime; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::FrameId; use net_traits::{IpcSend, load_whole_resource}; use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; use rand::random; @@ -158,9 +158,14 @@ impl DedicatedWorkerGlobalScope { closing: Arc) { let serialized_worker_url = worker_url.to_string(); let name = format!("WebWorker for {}", serialized_worker_url); + let top_level_frame_id = FrameId::installed(); + spawn_named(name, move || { 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 _stack_roots_tls = StackRootTLS::new(&roots); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index e5eaf30a5d8..8a5fd03c479 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -518,8 +518,8 @@ impl ScriptThreadFactory for ScriptThread { thread::spawn_named(format!("ScriptThread {:?}", state.id), move || { thread_state::initialize(thread_state::SCRIPT); - PipelineId::install(state.id); PipelineNamespace::install(state.pipeline_namespace_id); + FrameId::install(state.top_level_frame_id); let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); let id = state.id; diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index cfab945f99b..8a437ff24ee 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -442,6 +442,8 @@ pub struct InitialScriptState { pub parent_info: Option<(PipelineId, FrameType)>, /// The ID of the frame this script is part of. 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). pub control_chan: IpcSender, /// A port on which messages sent by the constellation to script can be received. @@ -699,8 +701,8 @@ pub enum ConstellationMsg { WebDriverCommand(WebDriverCommandMsg), /// Reload the current page. Reload, - /// A log entry, with the pipeline id and thread name - LogEntry(Option, Option, LogEntry), + /// A log entry, with the top-level frame id and thread name + LogEntry(Option, Option, LogEntry), } /// Resources required by workerglobalscopes diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 61c64bbc750..d4a9fc7442b 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -15,8 +15,8 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use euclid::point::Point2D; use euclid::size::Size2D; use ipc_channel::ipc::IpcSender; +use msg::constellation_msg::{FrameId, PipelineId, TraversalDirection}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; -use msg::constellation_msg::{PipelineId, TraversalDirection}; use net_traits::CoreResourceMsg; use net_traits::storage_thread::StorageType; use offscreen_gl_context::{GLContextAttributes, GLLimits}; @@ -131,8 +131,8 @@ pub enum ScriptMsg { ResizeTo(Size2D), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), - /// A log entry, with the pipeline id and thread name - LogEntry(Option, Option, LogEntry), + /// A log entry, with the top-level frame id and thread name + LogEntry(Option, Option, LogEntry), /// Notifies the constellation that this pipeline has exited. PipelineExited(PipelineId), /// Send messages from postMessage calls from serviceworker