diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 620e4b614601..261ed6e265bd 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -2490,15 +2490,15 @@ impl Constellation }, Some(old_pipeline_id) => { // Deactivate the old pipeline, and activate the new one. - let pipelines_to_close = if let Some(replace_reloader) = change.replace { + let (pipelines_to_close, states_to_close) = if let Some(replace_reloader) = change.replace { let session_history = self.joint_session_histories .entry(change.top_level_browsing_context_id).or_insert(JointSessionHistory::new()); session_history.replace(replace_reloader.clone(), NeedsToReload::No(change.new_pipeline_id)); match replace_reloader { - NeedsToReload::No(pipeline_id) => vec![pipeline_id], - NeedsToReload::Yes(..) => vec![], + NeedsToReload::No(pipeline_id) => (Some(vec![pipeline_id]), None), + NeedsToReload::Yes(..) => (None, None), } } else { let session_history = self.joint_session_histories @@ -2509,20 +2509,50 @@ impl Constellation old_reloader: NeedsToReload::No(old_pipeline_id), }; - session_history.push_diff(diff).into_iter() - .filter_map(|diff| match diff { - SessionHistoryDiff::BrowsingContextDiff { new_reloader, .. } => Some(new_reloader), - SessionHistoryDiff::PipelineDiff { .. } => None, - }) - .filter_map(|pipeline_id| pipeline_id.alive_pipeline_id()) - .collect::>() + let mut pipelines_to_close = vec![]; + let mut states_to_close = HashMap::new(); + + let diffs_to_close = session_history.push_diff(diff); + + for diff in diffs_to_close { + match diff { + SessionHistoryDiff::BrowsingContextDiff { new_reloader, .. } => { + if let Some(pipeline_id) = new_reloader.alive_pipeline_id() { + pipelines_to_close.push(pipeline_id); + } + } + SessionHistoryDiff::PipelineDiff { pipeline_reloader, new_history_state_id, .. } => { + if let Some(pipeline_id) = pipeline_reloader.alive_pipeline_id() { + let states = states_to_close.entry(pipeline_id).or_insert(Vec::new()); + states.push(new_history_state_id); + } + } + } + } + + (Some(pipelines_to_close), Some(states_to_close)) }; self.update_activity(old_pipeline_id); self.update_activity(change.new_pipeline_id); - for pipeline_id in pipelines_to_close { - self.close_pipeline(pipeline_id, DiscardBrowsingContext::No, ExitPipelineMode::Normal); + if let Some(states_to_close) = states_to_close { + for (pipeline_id, states) in states_to_close { + let msg = ConstellationControlMsg::RemoveHistoryStates(pipeline_id, states); + let result = match self.pipelines.get(&pipeline_id) { + None => return warn!("Pipeline {} removed history states after closure", pipeline_id), + Some(pipeline) => pipeline.event_loop.send(msg), + }; + if let Err(e) = result { + self.handle_send_error(pipeline_id, e); + } + } + } + + if let Some(pipelines_to_close) = pipelines_to_close { + for pipeline_id in pipelines_to_close { + self.close_pipeline(pipeline_id, DiscardBrowsingContext::No, ExitPipelineMode::Normal); + } } self.notify_history_changed(change.top_level_browsing_context_id); diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index f070f58235d5..1192a96f9497 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -252,6 +252,12 @@ impl ResourceChannelManager { let mut history_states = http_state.history_states.write().unwrap(); history_states.insert(history_state_id, history_state); } + CoreResourceMsg::RemoveHistoryStates(states_to_remove) => { + let mut history_states = http_state.history_states.write().unwrap(); + for history_state in states_to_remove { + history_states.remove(&history_state); + } + } CoreResourceMsg::Synchronize(sender) => { let _ = sender.send(()); } diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index e831892e144a..2a9b791a02fb 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -368,6 +368,8 @@ pub enum CoreResourceMsg { GetHistoryState(HistoryStateId, IpcSender>>), /// Set a history state for a given history state id SetHistoryState(HistoryStateId, Vec), + /// Removes history states for the given ids + RemoveHistoryStates(Vec), /// Synchronization message solely for knowing the state of the ResourceChannelManager loop Synchronize(IpcSender<()>), /// Send the network sender in constellation to CoreResourceThread diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index b158a9d44fd7..e18e06cecea9 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -102,6 +102,13 @@ impl History { } } + pub fn remove_states(&self, states: Vec) { + let _ = self.window + .upcast::() + .resource_threads() + .send(CoreResourceMsg::RemoveHistoryStates(states)); + } + // https://html.spec.whatwg.org/multipage/#dom-history-pushstate // https://html.spec.whatwg.org/multipage/#dom-history-replacestate fn push_or_replace_state(&self, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index c188caed3532..4e89ba4ceff4 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1170,6 +1170,7 @@ impl ScriptThread { PostMessage(id, ..) => Some(id), UpdatePipelineId(_, _, id, _) => Some(id), UpdateHistoryStateId(id, ..) => Some(id), + RemoveHistoryStates(id, ..) => Some(id), FocusIFrame(id, ..) => Some(id), WebDriverScriptCommand(id, ..) => Some(id), TickAllAnimations(id) => Some(id), @@ -1298,6 +1299,8 @@ impl ScriptThread { reason), ConstellationControlMsg::UpdateHistoryStateId(pipeline_id, history_state_id) => self.handle_update_history_state_id_msg(pipeline_id, history_state_id), + ConstellationControlMsg::RemoveHistoryStates(pipeline_id, history_states) => + self.handle_remove_history_states(pipeline_id, history_states), ConstellationControlMsg::FocusIFrame(parent_pipeline_id, frame_id) => self.handle_focus_iframe_msg(parent_pipeline_id, frame_id), ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, msg) => @@ -1683,6 +1686,13 @@ impl ScriptThread { } } + fn handle_remove_history_states(&self, pipeline_id: PipelineId, history_states: Vec) { + match { self.documents.borrow().find_window(pipeline_id) } { + None => return warn!("update history state after pipeline {} closed.", pipeline_id), + Some(window) => window.History().r().remove_states(history_states), + } + } + /// Window was resized, but this script was not active, so don't reflow yet fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) { let window = self.documents.borrow().find_window(id) diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index d270d87a50c1..272cfe3cedbe 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -291,6 +291,8 @@ pub enum ConstellationControlMsg { UpdatePipelineId(PipelineId, BrowsingContextId, PipelineId, UpdatePipelineIdReason), /// Updates the history state of a given pipeline. UpdateHistoryStateId(PipelineId, Option), + /// Removes inaccesible history states. + RemoveHistoryStates(PipelineId, Vec), /// Set an iframe to be focused. Used when an element in an iframe gains focus. /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context FocusIFrame(PipelineId, BrowsingContextId), @@ -346,6 +348,7 @@ impl fmt::Debug for ConstellationControlMsg { PostMessage(..) => "PostMessage", UpdatePipelineId(..) => "UpdatePipelineId", UpdateHistoryStateId(..) => "UpdateHistoryStateId", + RemoveHistoryStates(..) => "RemoveHistoryStates", FocusIFrame(..) => "FocusIFrame", WebDriverScriptCommand(..) => "WebDriverScriptCommand", TickAllAnimations(..) => "TickAllAnimations",