From 556d83738aed76dc3a207323356fc7754212a24f Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Fri, 9 Sep 2016 11:13:22 +0200 Subject: [PATCH] Purge pipelines from distant history. --- components/constellation/constellation.rs | 237 ++++++++++++++++------ 1 file changed, 171 insertions(+), 66 deletions(-) diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index fa10a76bbdc6..69e0045ed879 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -216,21 +216,46 @@ pub struct InitialConstellationState { pub webrender_api_sender: Option, } + +#[derive(Debug, Clone, PartialEq)] +enum HistoryEntry { + Alive { + id: PipelineId, + }, + Dead { + url: Url, + is_private: bool, + } +} + +impl HistoryEntry { + fn pipeline(&self) -> Option { + match *self { + HistoryEntry::Alive { id } => Some(id), + HistoryEntry::Dead { .. } => None, + } + } +} + #[derive(Debug, Clone)] struct FrameState { instant: Instant, - pipeline_id: PipelineId, + entry: HistoryEntry, frame_id: FrameId, } impl FrameState { - fn new(pipeline_id: PipelineId, frame_id: FrameId) -> FrameState { + fn new(entry: HistoryEntry, frame_id: FrameId) -> FrameState { FrameState { instant: Instant::now(), - pipeline_id: pipeline_id, + entry: entry, frame_id: frame_id, } } + + fn pipeline(&self) -> Option { + self.entry.pipeline() + } } /// Stores the navigation context for a single frame in the frame tree. @@ -246,22 +271,66 @@ impl Frame { Frame { id: id, prev: vec!(), - current: FrameState::new(pipeline_id, id), + current: FrameState::new(HistoryEntry::Alive { id: pipeline_id }, id), next: vec!(), } } fn load(&mut self, pipeline_id: PipelineId) { - self.prev.push(self.current.clone()); - self.current = FrameState::new(pipeline_id, self.id); - } + let prev = replace(&mut self.current, + FrameState::new(HistoryEntry::Alive { id: pipeline_id }, self.id)); + self.prev.push(prev); + } + + fn maybe_purge(&mut self, pipelines: &HashMap) { + fn unload(entries: &mut [FrameState], pipelines: &HashMap) { + for &mut FrameState { ref mut entry, .. } in entries { + let (url, is_private) = match *entry { + HistoryEntry::Alive { ref id } => { + let pipeline = match pipelines.get(id) { + Some(pipeline) => pipeline, + None => continue, + }; + pipeline.exit(); + (pipeline.url.clone(), pipeline.is_private) + }, + HistoryEntry::Dead { .. } => continue, + }; + *entry = HistoryEntry::Dead { + url: url, + is_private: is_private, + }; + } + } + + const HISTORY_TO_KEEP: usize = 3; + + if self.prev.len() > HISTORY_TO_KEEP { + let boundary = self.prev.len() - HISTORY_TO_KEEP; + unload(&mut self.prev[..boundary], pipelines); + } + + if self.next.len() > HISTORY_TO_KEEP { + unload(&mut self.next[HISTORY_TO_KEEP..], pipelines); + } + } fn remove_forward_entries(&mut self) -> Vec { replace(&mut self.next, vec!()) } - fn replace_current(&mut self, pipeline_id: PipelineId) -> FrameState { - replace(&mut self.current, FrameState::new(pipeline_id, self.id)) + fn replace_current(&mut self, + pipeline_id: PipelineId, + pipelines: &HashMap) + -> FrameState { + let next = FrameState::new(HistoryEntry::Alive { id: pipeline_id }, self.id); + let previous = replace(&mut self.current, next); + self.maybe_purge(pipelines); + previous + } + + fn current_pipeline(&self) -> PipelineId { + self.current.pipeline().expect("Current pipeline should always be alive") } } @@ -298,10 +367,10 @@ impl<'a> Iterator for FrameTreeIterator<'a> { continue; }, }; - let pipeline = match self.pipelines.get(&frame.current.pipeline_id) { + let pipeline = match self.pipelines.get(&frame.current_pipeline()) { Some(pipeline) => pipeline, None => { - warn!("Pipeline {:?} iterated after closure.", frame.current.pipeline_id); + warn!("Pipeline {:?} iterated after closure.", frame.current_pipeline()); continue; }, }; @@ -333,8 +402,10 @@ impl<'a> Iterator for FullFrameTreeIterator<'a> { }, }; for entry in frame.prev.iter().chain(frame.next.iter()).chain(once(&frame.current)) { - if let Some(pipeline) = self.pipelines.get(&entry.pipeline_id) { - self.stack.extend(pipeline.children.iter().map(|&c| c)); + if let Some(pipeline_id) = entry.pipeline() { + if let Some(pipeline) = self.pipelines.get(&pipeline_id) { + self.stack.extend(pipeline.children.iter().map(|&c| c)); + } } } return Some(frame) @@ -630,28 +701,28 @@ impl Constellation } } - fn joint_session_future(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> { + fn joint_session_future(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, HistoryEntry)> { let mut future = vec!(); for frame in self.full_frame_tree_iter(frame_id_root) { - future.extend(frame.next.iter().map(|entry| (entry.instant, entry.frame_id, entry.pipeline_id))); + future.extend(frame.next.iter().map(|entry| (entry.instant, entry.frame_id, entry.entry.clone()))); } // reverse sorting - future.sort_by(|a, b| b.cmp(a)); + future.sort_by(|a, b| b.0.cmp(&a.0)); future } - fn joint_session_past(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> { + fn joint_session_past(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, HistoryEntry)> { let mut past = vec!(); for frame in self.full_frame_tree_iter(frame_id_root) { let mut prev_instant = frame.current.instant; for entry in frame.prev.iter().rev() { - past.push((prev_instant, entry.frame_id, entry.pipeline_id)); + past.push((prev_instant, entry.frame_id, entry.entry.clone())); prev_instant = entry.instant; } } - past.sort(); + past.sort_by(|a, b| a.0.cmp(&b.0)); past } @@ -1353,7 +1424,7 @@ impl Constellation if parent_pipeline_info.is_some() { let root_pipeline_id = self.root_frame_id .and_then(|root_frame_id| self.frames.get(&root_frame_id)) - .map(|root_frame| root_frame.current.pipeline_id); + .map(|root_frame| root_frame.current_pipeline()); let ancestor_info = self.get_mozbrowser_ancestor_info(pipeline_id); if let Some((ancestor_id, mozbrowser_iframe_id)) = ancestor_info { @@ -1550,7 +1621,7 @@ impl Constellation // the compositor below. let root_pipeline_id = self.root_frame_id .and_then(|root_frame_id| self.frames.get(&root_frame_id)) - .map(|root_frame| root_frame.current.pipeline_id); + .map(|root_frame| root_frame.current_pipeline()); let pipeline_id = self.focus_pipeline_id.or(root_pipeline_id); match pipeline_id { @@ -1576,7 +1647,7 @@ impl Constellation // Send Reload constellation msg to root script channel. let root_pipeline_id = self.root_frame_id .and_then(|root_frame_id| self.frames.get(&root_frame_id)) - .map(|root_frame| root_frame.current.pipeline_id); + .map(|root_frame| root_frame.current_pipeline()); if let Some(pipeline_id) = root_pipeline_id { let msg = ConstellationControlMsg::Reload(pipeline_id); @@ -1620,7 +1691,7 @@ impl Constellation resp_chan: IpcSender>) { let current_pipeline_id = frame_id.or(self.root_frame_id) .and_then(|frame_id| self.frames.get(&frame_id)) - .map(|frame| frame.current.pipeline_id); + .map(|frame| frame.current_pipeline()); let current_pipeline_id_loaded = current_pipeline_id .map(|id| (id, true)); let pipeline_id_loaded = self.pending_frames.iter().rev() @@ -1693,12 +1764,14 @@ impl Constellation None => return warn!("No frame associated with pipeline {:?}", pipeline_id), }; - let child_pipeline_ids: Vec = self.full_frame_tree_iter(frame_id) - .flat_map(|frame| frame.next.iter() - .chain(frame.prev.iter()) - .chain(once(&frame.current))) - .map(|state| state.pipeline_id) - .collect(); + let child_pipeline_ids = self.full_frame_tree_iter(frame_id) + .flat_map(|frame| { + frame.next.iter() + .chain(frame.prev.iter()) + .chain(once(&frame.current)) + }) + .filter_map(|state| state.pipeline()) + .collect::>(); for id in child_pipeline_ids { if let Some(pipeline) = self.pipelines.get_mut(&id) { pipeline.change_visibility(visible); @@ -1795,7 +1868,7 @@ impl Constellation WebDriverCommandMsg::TakeScreenshot(pipeline_id, reply) => { let current_pipeline_id = self.root_frame_id .and_then(|root_frame_id| self.frames.get(&root_frame_id)) - .map(|root_frame| root_frame.current.pipeline_id); + .map(|root_frame| root_frame.current_pipeline()); if Some(pipeline_id) == current_pipeline_id { self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply)); } else { @@ -1807,7 +1880,7 @@ impl Constellation } } - fn traverse_frame_to_pipeline(&mut self, frame_id: FrameId, next_pipeline_id: PipelineId) { + fn traverse_frame_to_pipeline(&mut self, frame_id: FrameId, next_pipeline_id: HistoryEntry) { // Check if the currently focused pipeline is the pipeline being replaced // (or a child of it). This has to be done here, before the current // frame tree is modified below. @@ -1815,17 +1888,19 @@ impl Constellation let prev_pipeline_id = match self.frames.get_mut(&frame_id) { Some(frame) => { - let prev = frame.current.pipeline_id; + let prev = frame.current_pipeline(); // Check that this frame contains the pipeline passed in, so that this does not // change Frame's state before realizing `next_pipeline_id` is invalid. let mut contains_pipeline = false; - if frame.next.iter().find(|entry| next_pipeline_id == entry.pipeline_id).is_some() { + if frame.next.iter().any(|entry| next_pipeline_id == entry.entry) { contains_pipeline = true; frame.prev.push(frame.current.clone()); while let Some(entry) = frame.next.pop() { - if entry.pipeline_id == next_pipeline_id { - frame.current = entry; + if entry.entry == next_pipeline_id { + if let HistoryEntry::Alive { .. } = entry.entry { + frame.current = entry; + } break; } else { frame.prev.push(entry); @@ -1834,12 +1909,14 @@ impl Constellation } if !contains_pipeline && - frame.prev.iter().find(|entry| next_pipeline_id == entry.pipeline_id).is_some() { + frame.prev.iter().any(|entry| next_pipeline_id == entry.entry) { contains_pipeline = true; frame.next.push(frame.current.clone()); while let Some(entry) = frame.prev.pop() { - if entry.pipeline_id == next_pipeline_id { - frame.current = entry; + if entry.entry == next_pipeline_id { + if let HistoryEntry::Alive { .. } = entry.entry { + frame.current = entry; + } break; } else { frame.next.push(entry); @@ -1857,6 +1934,25 @@ impl Constellation None => return warn!("no frame to traverse"), }; + let next_pipeline_id = match next_pipeline_id { + HistoryEntry::Alive { id } => id, + HistoryEntry::Dead { url, is_private } => { + let window_size = self.window_size.visible_viewport; + let pipeline_id = PipelineId::new(); + self.new_pipeline(pipeline_id, None, None, Some(window_size), None, + LoadData::new(url.clone(), None, None), is_private); + self.handle_load_start_msg(pipeline_id); + self.pending_frames.push(FrameChange { + old_pipeline_id: Some(prev_pipeline_id), + new_pipeline_id: pipeline_id, + document_ready: false, + replace: false, + }); + self.compositor_proxy.send(ToCompositorMsg::ChangePageUrl(pipeline_id, url)); + return; + }, + }; + let pipeline_info = self.pipelines.get(&prev_pipeline_id).and_then(|p| p.parent_info); // If the currently focused pipeline is the one being changed (or a child @@ -1949,15 +2045,19 @@ impl Constellation } if frame_change.replace { - let evicted = self.frames.get_mut(&frame_id).map(|frame| { - frame.replace_current(frame_change.new_pipeline_id) - }); + let evicted = { + let pipelines = &mut self.pipelines; + self.frames.get_mut(&frame_id).map(|frame| { + frame.replace_current(frame_change.new_pipeline_id, pipelines) + }) + }; if let Some(evicted) = evicted { - self.close_pipeline(evicted.pipeline_id, ExitPipelineMode::Normal); + self.close_pipeline(evicted.pipeline().expect("Should be alive"), + ExitPipelineMode::Normal); } } else { if let Some(ref mut frame) = self.frames.get_mut(&frame_id) { - frame.load(frame_change.new_pipeline_id); + frame.load(frame_change.new_pipeline_id) } } } @@ -2059,7 +2159,7 @@ impl Constellation None => return warn!("Frame {:?} resized after closing.", root_frame_id), Some(frame) => frame, }; - let pipeline_id = frame.current.pipeline_id; + let pipeline_id = frame.current_pipeline(); let pipeline = match self.pipelines.get(&pipeline_id) { None => return warn!("Pipeline {:?} resized after closing.", pipeline_id), Some(pipeline) => pipeline, @@ -2069,8 +2169,8 @@ impl Constellation new_size, size_type )); - for entry in frame.prev.iter().chain(&frame.next) { - let pipeline = match self.pipelines.get(&entry.pipeline_id) { + for id in frame.prev.iter().chain(&frame.next).filter_map(FrameState::pipeline) { + let pipeline = match self.pipelines.get(&id) { None => { warn!("Inactive pipeline {:?} resized after closing.", pipeline_id); continue; @@ -2145,7 +2245,7 @@ impl Constellation // are met, then the output image should not change and a reftest // screenshot can safely be written. for frame in self.current_frame_tree_iter(self.root_frame_id) { - let pipeline_id = frame.current.pipeline_id; + let pipeline_id = frame.current_pipeline(); let pipeline = match self.pipelines.get(&pipeline_id) { None => { @@ -2172,7 +2272,7 @@ impl Constellation } // See if this pipeline has reached idle script state yet. - match self.document_states.get(&frame.current.pipeline_id) { + match self.document_states.get(&frame.current_pipeline()) { Some(&DocumentState::Idle) => {} Some(&DocumentState::Pending) | None => { return ReadyToSave::DocumentLoading; @@ -2192,7 +2292,7 @@ impl Constellation } // Get the epoch that the compositor has drawn for this pipeline. - let compositor_epoch = pipeline_states.get(&frame.current.pipeline_id); + let compositor_epoch = pipeline_states.get(&frame.current_pipeline()); match compositor_epoch { Some(compositor_epoch) => { // Synchronously query the layout thread to see if the current @@ -2234,20 +2334,25 @@ impl Constellation continue; } }; - evicted_pipelines.extend(frame.remove_forward_entries()); - for entry in frame.next.iter().chain(frame.prev.iter()).chain(once(&frame.current)) { - let pipeline = match self.pipelines.get(&entry.pipeline_id) { + evicted_pipelines.extend(frame.remove_forward_entries().iter().filter_map(FrameState::pipeline)); + let iter = frame.prev + .iter() + .chain(&frame.next) + .chain(once(&frame.current)) + .filter_map(FrameState::pipeline); + for id in iter { + let pipeline = match self.pipelines.get(&id) { Some(pipeline) => pipeline, None => { - warn!("Removed forward history after pipeline {:?} closure.", entry.pipeline_id); + warn!("Removed forward history after pipeline {:?} closure.", id); continue; } }; frames_to_clear.extend_from_slice(&pipeline.children); } } - for entry in evicted_pipelines { - self.close_pipeline(entry.pipeline_id, ExitPipelineMode::Normal); + for pipeline_id in evicted_pipelines { + self.close_pipeline(pipeline_id, ExitPipelineMode::Normal); } } @@ -2258,23 +2363,23 @@ impl Constellation // 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) - .and_then(|frame| self.pipelines.get(&frame.current.pipeline_id)) + .and_then(|frame| self.pipelines.get(&frame.current_pipeline())) .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.extend(frame.next.iter().filter_map(FrameState::pipeline)); + pipelines_to_close.push(frame.current_pipeline()); + pipelines_to_close.extend(frame.prev.iter().filter_map(FrameState::pipeline)); } pipelines_to_close }; - for entry in pipelines_to_close { - self.close_pipeline(entry.pipeline_id, exit_mode); + for pipeline_id in pipelines_to_close { + self.close_pipeline(pipeline_id, exit_mode); } if self.frames.remove(&frame_id).is_none() { @@ -2364,7 +2469,7 @@ impl Constellation // Convert a frame to a sendable form to pass to the compositor fn frame_to_sendable(&self, frame_id: FrameId) -> Option { self.frames.get(&frame_id).and_then(|frame: &Frame| { - self.pipelines.get(&frame.current.pipeline_id).map(|pipeline: &Pipeline| { + self.pipelines.get(&frame.current_pipeline()).map(|pipeline: &Pipeline| { let mut frame_tree = SendableFrameTree { pipeline: pipeline.to_sendable(), size: pipeline.size, @@ -2386,7 +2491,7 @@ impl Constellation fn revoke_paint_permission(&self, pipeline_id: PipelineId) { let frame_id = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame); for frame in self.current_frame_tree_iter(frame_id) { - self.pipelines.get(&frame.current.pipeline_id).map(|pipeline| pipeline.revoke_paint_permission()); + self.pipelines.get(&frame.current_pipeline()).map(|pipeline| pipeline.revoke_paint_permission()); } } @@ -2409,7 +2514,7 @@ impl Constellation } for frame in self.current_frame_tree_iter(self.root_frame_id) { - self.pipelines.get(&frame.current.pipeline_id).map(|pipeline| pipeline.grant_paint_permission()); + self.pipelines.get(&frame.current_pipeline()).map(|pipeline| pipeline.grant_paint_permission()); } } @@ -2491,7 +2596,7 @@ impl Constellation if let Some(root_frame_id) = self.root_frame_id { if let Some(root_frame) = self.frames.get(&root_frame_id) { - if let Some(root_pipeline) = self.pipelines.get(&root_frame.current.pipeline_id) { + if let Some(root_pipeline) = self.pipelines.get(&root_frame.current_pipeline()) { return root_pipeline.trigger_mozbrowser_event(None, event); } } @@ -2514,7 +2619,7 @@ impl Constellation pipeline_id: PipelineId, root_frame_id: Option) -> bool { self.current_frame_tree_iter(root_frame_id) - .any(|current_frame| current_frame.current.pipeline_id == pipeline_id) + .any(|current_frame| current_frame.current_pipeline() == pipeline_id) } }