aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/constellation/constellation.rs192
1 files changed, 116 insertions, 76 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 581f72240bd..440deea7877 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -155,6 +155,18 @@ use timer_scheduler::TimerScheduler;
use webrender_api;
use webvr_traits::{WebVREvent, WebVRMsg};
+/// Servo supports tabs (referred to as browsers), so `Constellation` needs to
+/// store browser specific data for bookkeeping.
+struct Browser {
+ /// The currently focused browsing context in this browser for key events.
+ /// The focused pipeline is the current entry of the focused browsing
+ /// context.
+ focused_browsing_context_id: BrowsingContextId,
+
+ /// The joint session history for this browser.
+ session_history: JointSessionHistory,
+}
+
/// The `Constellation` itself. In the servo browser, there is one
/// constellation, which maintains all of the browser global data.
/// In embedded applications, there may be more than one constellation,
@@ -201,13 +213,17 @@ pub struct Constellation<Message, LTF, STF> {
/// constellation to send messages to the compositor thread.
compositor_proxy: CompositorProxy,
- /// The last frame tree sent to WebRender.
+ /// The last frame tree sent to WebRender, denoting the browser (tab) user
+ /// has currently selected. This also serves as the key to retrieve data
+ /// about the current active browser from `browsers`.
active_browser_id: Option<TopLevelBrowsingContextId>,
+ /// Bookkeeping data for all browsers in constellation.
+ browsers: HashMap<TopLevelBrowsingContextId, Browser>,
+
/// Channels for the constellation to send messages to the public
- /// resource-related threads. There are two groups of resource
- /// threads: one for public browsing, and one for private
- /// browsing.
+ /// resource-related threads. There are two groups of resource threads: one
+ /// for public browsing, and one for private browsing.
public_resource_threads: ResourceThreads,
/// Channels for the constellation to send messages to the private
@@ -273,10 +289,8 @@ pub struct Constellation<Message, LTF, STF> {
/// to become same-origin, at which point they can share DOM objects.
event_loops: HashMap<Host, Weak<EventLoop>>,
- joint_session_histories: HashMap<TopLevelBrowsingContextId, JointSessionHistory>,
-
- /// The set of all the pipelines in the browser.
- /// (See the `pipeline` module for more details.)
+ /// The set of all the pipelines in the browser. (See the `pipeline` module
+ /// for more details.)
pipelines: HashMap<PipelineId, Pipeline>,
/// The set of all the browsing contexts in the browser.
@@ -289,11 +303,6 @@ pub struct Constellation<Message, LTF, STF> {
/// we store a `SessionHistoryChange` object for the navigation in progress.
pending_changes: Vec<SessionHistoryChange>,
- /// The currently focused browsing context for key events. The focused
- /// pipeline is the current entry of the focused browsing
- /// context.
- focused_browsing_context_id: Option<BrowsingContextId>,
-
/// Pipeline IDs are namespaced in order to avoid name collisions,
/// and the namespaces are allocated by the constellation.
next_pipeline_namespace_id: PipelineNamespaceId,
@@ -591,6 +600,7 @@ where
embedder_proxy: state.embedder_proxy,
compositor_proxy: state.compositor_proxy,
active_browser_id: None,
+ browsers: HashMap::new(),
debugger_chan: state.debugger_chan,
devtools_chan: state.devtools_chan,
bluetooth_thread: state.bluetooth_thread,
@@ -601,14 +611,12 @@ where
swmanager_receiver: swmanager_receiver,
swmanager_sender: sw_mgr_clone,
event_loops: HashMap::new(),
- joint_session_histories: HashMap::new(),
pipelines: HashMap::new(),
browsing_contexts: HashMap::new(),
pending_changes: vec![],
// We initialize the namespace at 2, since we reserved
// namespace 0 for the embedder, and 0 for the constellation
next_pipeline_namespace_id: PipelineNamespaceId(2),
- focused_browsing_context_id: None,
time_profiler_chan: state.time_profiler_chan,
mem_profiler_chan: state.mem_profiler_chan,
window_size: WindowSizeData {
@@ -964,14 +972,9 @@ where
self.handle_get_pipeline(browsing_context_id, resp_chan);
},
FromCompositorMsg::GetFocusTopLevelBrowsingContext(resp_chan) => {
- let focus_browsing_context = self.focused_browsing_context_id
- .and_then(|ctx_id| self.browsing_contexts.get(&ctx_id))
- .map(|ctx| ctx.top_level_id)
- .filter(|&top_level_ctx_id| {
- let ctx_id = BrowsingContextId::from(top_level_ctx_id);
- self.browsing_contexts.contains_key(&ctx_id)
- });
- let _ = resp_chan.send(focus_browsing_context);
+ // The focused browsing context's top-level browsing context is
+ // the active browser's id itself.
+ let _ = resp_chan.send(self.active_browser_id);
},
FromCompositorMsg::Keyboard(key_event) => {
self.handle_key_msg(key_event);
@@ -1650,12 +1653,13 @@ where
let is_private = false;
let is_visible = true;
- if self.focused_browsing_context_id.is_none() {
- self.focused_browsing_context_id = Some(browsing_context_id);
- }
+ // Register this new top-level browsing context id as a browser and set
+ // its focused browsing context to be itself.
+ self.browsers.insert(top_level_browsing_context_id, Browser {
+ focused_browsing_context_id: browsing_context_id,
+ session_history: JointSessionHistory::new(),
+ });
- self.joint_session_histories
- .insert(top_level_browsing_context_id, JointSessionHistory::new());
self.new_pipeline(
pipeline_id,
browsing_context_id,
@@ -1687,6 +1691,10 @@ where
) {
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
+ self.browsers.remove(&top_level_browsing_context_id);
+ if self.active_browser_id == Some(top_level_browsing_context_id) {
+ self.active_browser_id = None;
+ }
}
fn handle_iframe_size_msg(
@@ -1966,7 +1974,10 @@ where
assert!(!self.pipelines.contains_key(&new_pipeline_id));
self.pipelines.insert(new_pipeline_id, pipeline);
- self.joint_session_histories.insert(new_top_level_browsing_context_id, JointSessionHistory::new());
+ self.browsers.insert(new_top_level_browsing_context_id, Browser {
+ focused_browsing_context_id: new_browsing_context_id,
+ session_history: JointSessionHistory::new(),
+ });
self.add_pending_change(SessionHistoryChange {
top_level_browsing_context_id: new_top_level_browsing_context_id,
browsing_context_id: new_browsing_context_id,
@@ -2558,9 +2569,9 @@ where
sender: IpcSender<u32>,
) {
let length = self
- .joint_session_histories
+ .browsers
.get(&top_level_browsing_context_id)
- .map(JointSessionHistory::history_length)
+ .map(|browser| browser.session_history.history_length())
.unwrap_or(1);
let _ = sender.send(length as u32);
}
@@ -2631,7 +2642,11 @@ where
fn handle_key_msg(&mut self, event: KeyboardEvent) {
// Send to the focused browsing contexts' current pipeline. If it
// doesn't exist, fall back to sending to the compositor.
- match self.focused_browsing_context_id {
+ let focused_browsing_context_id = self
+ .active_browser_id
+ .and_then(|browser_id| self.browsers.get(&browser_id))
+ .map(|browser| browser.focused_browsing_context_id);
+ match focused_browsing_context_id {
Some(browsing_context_id) => {
let event = CompositorEvent::KeyboardEvent(event);
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
@@ -2644,9 +2659,10 @@ where
let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
let result = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.event_loop.send(msg),
- None => {
- return debug!("Pipeline {:?} got key event after closure.", pipeline_id)
- },
+ None => return debug!(
+ "Pipeline {:?} got key event after closure.",
+ pipeline_id
+ ),
};
if let Err(e) = result {
self.handle_send_error(pipeline_id, e);
@@ -2741,11 +2757,25 @@ where
}
fn handle_focus_msg(&mut self, pipeline_id: PipelineId) {
- let browsing_context_id = match self.pipelines.get(&pipeline_id) {
- Some(pipeline) => pipeline.browsing_context_id,
- None => return warn!("Pipeline {:?} focus parent after closure.", pipeline_id),
+ let (browsing_context_id, top_level_browsing_context_id) =
+ match self.pipelines.get(&pipeline_id) {
+ Some(pipeline) => (
+ pipeline.browsing_context_id,
+ pipeline.top_level_browsing_context_id,
+ ),
+ None => return warn!("Pipeline {:?} focus parent after closure.", pipeline_id),
+ };
+
+ // Update the focused browsing context in its browser in `browsers`.
+ match self.browsers.get_mut(&top_level_browsing_context_id) {
+ Some(browser) => {
+ browser.focused_browsing_context_id = browsing_context_id;
+ },
+ None => return warn!(
+ "Browser {} for focus msg does not exist",
+ top_level_browsing_context_id
+ ),
};
- self.focused_browsing_context_id = Some(browsing_context_id);
// Focus parent iframes recursively
self.focus_parent_pipeline(browsing_context_id);
@@ -2970,11 +3000,8 @@ where
// LoadData of inner frames are ignored and replaced with the LoadData
// of the parent.
- let session_history = match self
- .joint_session_histories
- .get(&top_level_browsing_context_id)
- {
- Some(session_history) => session_history,
+ let session_history = match self.browsers.get(&top_level_browsing_context_id) {
+ Some(browser) => &browser.session_history,
None => {
return warn!(
"Session history does not exist for {}",
@@ -3120,9 +3147,13 @@ where
// If the currently focused browsing context is a child of the browsing
// context in which the page is being loaded, then update the focused
- // browsing context to that one.
+ // browsing context to be the one where the page is being loaded.
if self.focused_browsing_context_is_descendant_of(change.browsing_context_id) {
- self.focused_browsing_context_id = Some(change.browsing_context_id);
+ self.browsers
+ .entry(change.top_level_browsing_context_id)
+ .and_modify(|browser| {
+ browser.focused_browsing_context_id = change.browsing_context_id
+ });
}
let (old_pipeline_id, top_level_id) =
@@ -3169,24 +3200,17 @@ where
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_reloader(
- replace_reloader.clone(),
- NeedsToReload::No(change.new_pipeline_id),
- );
+ self.get_joint_session_history(change.top_level_browsing_context_id)
+ .replace_reloader(
+ replace_reloader.clone(),
+ NeedsToReload::No(change.new_pipeline_id),
+ );
match replace_reloader {
NeedsToReload::No(pipeline_id) => (Some(vec![pipeline_id]), None),
NeedsToReload::Yes(..) => (None, None),
}
} else {
- let session_history = self
- .joint_session_histories
- .entry(change.top_level_browsing_context_id)
- .or_insert(JointSessionHistory::new());
let diff = SessionHistoryDiff::BrowsingContextDiff {
browsing_context_id: change.browsing_context_id,
new_reloader: NeedsToReload::No(change.new_pipeline_id),
@@ -3196,7 +3220,9 @@ where
let mut pipelines_to_close = vec![];
let mut states_to_close = HashMap::new();
- let diffs_to_close = session_history.push_diff(diff);
+ let diffs_to_close = self
+ .get_joint_session_history(change.top_level_browsing_context_id)
+ .push_diff(diff);
for diff in diffs_to_close {
match diff {
@@ -3270,7 +3296,11 @@ where
&self,
browsing_context_id: BrowsingContextId
) -> bool {
- self.focused_browsing_context_id.map_or(false, |focus_ctx_id| {
+ let focused_browsing_context_id = self
+ .active_browser_id
+ .and_then(|browser_id| self.browsers.get(&browser_id))
+ .map(|browser| browser.focused_browsing_context_id);
+ focused_browsing_context_id.map_or(false, |focus_ctx_id| {
focus_ctx_id == browsing_context_id || self
.fully_active_descendant_browsing_contexts_iter(browsing_context_id)
.any(|nested_ctx| nested_ctx.id == focus_ctx_id)
@@ -3433,10 +3463,7 @@ where
//
// If there is no focus browsing context yet, the initial page has
// not loaded, so there is nothing to save yet.
- let top_level_browsing_context_id = self.focused_browsing_context_id
- .and_then(|ctx_id| self.browsing_contexts.get(&ctx_id))
- .map(|ctx| ctx.top_level_id);
- let top_level_browsing_context_id = match top_level_browsing_context_id {
+ let top_level_browsing_context_id = match self.active_browser_id {
Some(id) => id,
None => return ReadyToSave::NoTopLevelBrowsingContext,
};
@@ -3719,17 +3746,23 @@ where
top_level_browsing_context_id: TopLevelBrowsingContextId,
pipeline_id: PipelineId,
) {
- let load_data = match self.pipelines.get(&pipeline_id) {
- Some(pipeline) => pipeline.load_data.clone(),
- None => return,
+ match self.browsers.get_mut(&top_level_browsing_context_id) {
+ Some(browser) => {
+ let load_data = match self.pipelines.get(&pipeline_id) {
+ Some(pipeline) => pipeline.load_data.clone(),
+ None => return warn!("Discarding closed pipeline {}", pipeline_id),
+ };
+ browser.session_history.replace_reloader(
+ NeedsToReload::No(pipeline_id),
+ NeedsToReload::Yes(pipeline_id, load_data),
+ );
+ },
+ None => return warn!(
+ "Discarding pipeline {} after browser {} closure",
+ pipeline_id,
+ top_level_browsing_context_id,
+ ),
};
- self.joint_session_histories
- .entry(top_level_browsing_context_id)
- .or_insert(JointSessionHistory::new())
- .replace_reloader(
- NeedsToReload::No(pipeline_id),
- NeedsToReload::Yes(pipeline_id, load_data),
- );
self.close_pipeline(
pipeline_id,
DiscardBrowsingContext::No,
@@ -3848,9 +3881,16 @@ where
&mut self,
top_level_id: TopLevelBrowsingContextId,
) -> &mut JointSessionHistory {
- self.joint_session_histories
+ &mut self.browsers
.entry(top_level_id)
- .or_insert(JointSessionHistory::new())
+ // This shouldn't be necessary since `get_joint_session_history` is
+ // invoked for existing browsers but we need this to satisfy the
+ // type system.
+ .or_insert_with(|| Browser {
+ focused_browsing_context_id: BrowsingContextId::from(top_level_id),
+ session_history: JointSessionHistory::new(),
+ })
+ .session_history
}
// Convert a browsing context to a sendable form to pass to the compositor