Skip to content
Permalink
Browse files

implement browsing context group and set

  • Loading branch information...
gterzian committed Jun 4, 2019
1 parent d544c18 commit 8c28852e900063fcf5d25b9a0ce658ad3bd3e28a
@@ -4,7 +4,9 @@

use crate::pipeline::Pipeline;
use euclid::TypedSize2D;
use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
use msg::constellation_msg::{
BrowsingContextGroupId, BrowsingContextId, PipelineId, TopLevelBrowsingContextId,
};
use std::collections::{HashMap, HashSet};
use style_traits::CSSPixel;

@@ -34,6 +36,9 @@ pub struct NewBrowsingContextInfo {
/// sorted reverse chronologically: in particular prev.pop() is the latest
/// past entry, and next.pop() is the earliest future entry.
pub struct BrowsingContext {
/// The browsing context group id where the top-level of this bc is found.
pub bc_group_id: BrowsingContextGroupId,

/// The browsing context id.
pub id: BrowsingContextId,

@@ -66,6 +71,7 @@ impl BrowsingContext {
/// Create a new browsing context.
/// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts.
pub fn new(
bc_group_id: BrowsingContextGroupId,
id: BrowsingContextId,
top_level_id: TopLevelBrowsingContextId,
pipeline_id: PipelineId,
@@ -77,14 +83,15 @@ impl BrowsingContext {
let mut pipelines = HashSet::new();
pipelines.insert(pipeline_id);
BrowsingContext {
id: id,
top_level_id: top_level_id,
size: size,
is_private: is_private,
is_visible: is_visible,
pipeline_id: pipeline_id,
parent_pipeline_id: parent_pipeline_id,
pipelines: pipelines,
bc_group_id,
id,
top_level_id,
size,
is_private,
is_visible,
pipeline_id,
parent_pipeline_id,
pipelines,
}
}

@@ -126,7 +126,8 @@ use layout_traits::LayoutThreadFactory;
use log::{Level, LevelFilter, Log, Metadata, Record};
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
use msg::constellation_msg::{
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
BrowsingContextGroupId, BrowsingContextId, HistoryStateId, PipelineId,
TopLevelBrowsingContextId,
};
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
use net_traits::pub_domains::reg_host;
@@ -158,7 +159,7 @@ use servo_remutex::ReentrantMutex;
use servo_url::{Host, ImmutableOrigin, ServoUrl};
use std::borrow::ToOwned;
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::collections::{HashMap, HashSet, VecDeque};
use std::marker::PhantomData;
use std::mem::replace;
use std::process;
@@ -183,6 +184,24 @@ struct Browser {
session_history: JointSessionHistory,
}

/// A browsing context group.
///
/// https://html.spec.whatwg.org/multipage/#browsing-context-group
#[derive(Clone, Default)]
struct BrowsingContextGroup {
/// A browsing context group holds a set of top-level browsing contexts.
top_level_browsing_context_set: HashSet<TopLevelBrowsingContextId>,

/// The set of all event loops in this BrowsingContextGroup.
/// We store the event loops in a map
/// indexed by registered domain name (as a `Host`) to event loops.
/// It is important that scripts with the same eTLD+1,
/// who are part of the same browsing-context group
/// share an event loop, since they can use `document.domain`
/// to become same-origin, at which point they can share DOM objects.
event_loops: HashMap<Host, Weak<EventLoop>>,
}

/// 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,
@@ -312,21 +331,21 @@ pub struct Constellation<Message, LTF, STF> {
/// WebRender thread.
webrender_api_sender: webrender_api::RenderApiSender,

/// The set of all event loops in the browser.
/// We store the event loops in a map
/// indexed by registered domain name (as a `Host`) to event loops.
/// It is important that scripts with the same eTLD+1
/// share an event loop, since they can use `document.domain`
/// to become same-origin, at which point they can share DOM objects.
event_loops: HashMap<Host, Weak<EventLoop>>,

/// 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.
browsing_contexts: HashMap<BrowsingContextId, BrowsingContext>,

/// A user agent holds a a set of browsing context groups.
///
/// https://html.spec.whatwg.org/multipage/#browsing-context-group-set
browsing_context_group_set: HashMap<BrowsingContextGroupId, BrowsingContextGroup>,

/// The Id counter for BrowsingContextGroup.
browsing_context_group_next_id: u32,

/// When a navigation is performed, we do not immediately update
/// the session history, instead we ask the event loop to begin loading
/// the new document, and do not update the browsing context until the
@@ -675,7 +694,8 @@ where
swmanager_chan: None,
swmanager_receiver: swmanager_receiver,
swmanager_sender: sw_mgr_clone,
event_loops: HashMap::new(),
browsing_context_group_set: Default::default(),
browsing_context_group_next_id: Default::default(),
pipelines: HashMap::new(),
browsing_contexts: HashMap::new(),
pending_changes: vec![],
@@ -750,6 +770,106 @@ where
namespace_id
}

fn next_browsing_context_group_id(&mut self) -> BrowsingContextGroupId {
let id = self.browsing_context_group_next_id;
self.browsing_context_group_next_id += 1;
BrowsingContextGroupId(id)
}

fn get_event_loop(
&mut self,
host: &Host,
top_level_browsing_context_id: &TopLevelBrowsingContextId,
opener: &Option<BrowsingContextId>,
) -> Result<Weak<EventLoop>, &'static str> {
let bc_group = match opener {
Some(browsing_context_id) => {
let opener = self
.browsing_contexts
.get(&browsing_context_id)
.ok_or("Opener was closed before the openee started")?;
self.browsing_context_group_set
.get(&opener.bc_group_id)
.ok_or("Opener belongs to an unknow BC group")?
},
None => self
.browsing_context_group_set
.iter()
.filter_map(|(_, bc_group)| {
if bc_group
.top_level_browsing_context_set
.contains(&top_level_browsing_context_id)
{
Some(bc_group)
} else {
None
}
})
.last()
.ok_or(
"Trying to get an event-loop for a top-level belonging to an unknown BC group",
)?,
};
bc_group
.event_loops
.get(host)
.ok_or("Trying to get an event-loop from an unknown BC group")
.map(|event_loop| event_loop.clone())
}

fn set_event_loop(
&mut self,
event_loop: Weak<EventLoop>,
host: Host,
top_level_browsing_context_id: TopLevelBrowsingContextId,
opener: Option<BrowsingContextId>,
) {
let relevant_top_level = if let Some(opener) = opener {
match self.browsing_contexts.get(&opener) {
Some(opener) => opener.top_level_id,
None => {
warn!("Setting event-loop for an unknown auxiliary");
return;
},
}
} else {
top_level_browsing_context_id
};
let maybe_bc_group_id = self
.browsing_context_group_set
.iter()
.filter_map(|(id, bc_group)| {
if bc_group
.top_level_browsing_context_set
.contains(&top_level_browsing_context_id)
{
Some(id.clone())
} else {
None
}
})
.last();
let bc_group_id = match maybe_bc_group_id {
Some(id) => id,
None => {
warn!("Trying to add an event-loop to an unknown BC group");
return;
},
};
if let Some(bc_group) = self.browsing_context_group_set.get_mut(&bc_group_id) {
if !bc_group
.event_loops
.insert(host.clone(), event_loop)
.is_none()
{
warn!(
"Double-setting an event-loop for {:?} at {:?}",
host, relevant_top_level
);
}
}
}

/// Helper function for creating a pipeline
fn new_pipeline(
&mut self,
@@ -785,11 +905,22 @@ where
match reg_host(&load_data.url) {
None => (None, None),
Some(host) => {
let event_loop =
self.event_loops.get(&host).and_then(|weak| weak.upgrade());
match event_loop {
None => (None, Some(host)),
Some(event_loop) => (Some(event_loop), None),
match self.get_event_loop(
&host,
&top_level_browsing_context_id,
&opener,
) {
Err(err) => {
warn!("{}", err);
(None, Some(host))
},
Ok(event_loop) => {
if let Some(event_loop) = event_loop.upgrade() {
(Some(event_loop), None)
} else {
(None, Some(host))
}
},
}
},
}
@@ -867,9 +998,12 @@ where
"Adding new host entry {} for top-level browsing context {}.",
host, top_level_browsing_context_id
);
let _ = self
.event_loops
.insert(host, Rc::downgrade(&pipeline.pipeline.event_loop));
self.set_event_loop(
Rc::downgrade(&pipeline.pipeline.event_loop),
host,
top_level_browsing_context_id,
opener,
);
}

assert!(!self.pipelines.contains_key(&pipeline_id));
@@ -922,7 +1056,31 @@ where
is_visible: bool,
) {
debug!("Creating new browsing context {}", browsing_context_id);
let bc_group_id = match self
.browsing_context_group_set
.iter_mut()
.filter_map(|(id, bc_group)| {
if bc_group
.top_level_browsing_context_set
.contains(&top_level_id)
{
Some(id)
} else {
None
}
})
.last()
{
Some(id) => id.clone(),
None => {
warn!(
"Top-level was unpexpectedly removed from its top_level_browsing_context_set."
);
return;
},
};
let browsing_context = BrowsingContext::new(
bc_group_id,
browsing_context_id,
top_level_id,
pipeline_id,
@@ -1877,6 +2035,15 @@ where
},
);

// https://html.spec.whatwg.org/multipage/#creating-a-new-browsing-context-group
let mut new_bc_group: BrowsingContextGroup = Default::default();
let new_bc_group_id = self.next_browsing_context_group_id();
new_bc_group
.top_level_browsing_context_set
.insert(top_level_browsing_context_id.clone());
self.browsing_context_group_set
.insert(new_bc_group_id, new_bc_group);

self.new_pipeline(
pipeline_id,
browsing_context_id,
@@ -1912,6 +2079,16 @@ where
if self.active_browser_id == Some(top_level_browsing_context_id) {
self.active_browser_id = None;
}
let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
Some(bc) => bc,
None => {
warn!("BC has closed before it has started");
return;
},
};
// https://html.spec.whatwg.org/multipage/#bcg-remove
self.browsing_context_group_set
.remove(&browsing_context.bc_group_id);
}

fn handle_iframe_size_msg(&mut self, iframe_sizes: Vec<IFrameSizeMsg>) {
@@ -2214,6 +2391,26 @@ where
session_history: JointSessionHistory::new(),
},
);

// https://html.spec.whatwg.org/multipage/#bcg-append
let opener = match self.browsing_contexts.get(&opener_browsing_context_id) {
Some(id) => id,
None => {
warn!("Trying to append an unknow auxiliary to a BC group");
return;
},
};
let bc_group = match self.browsing_context_group_set.get_mut(&opener.bc_group_id) {
Some(bc_group) => bc_group,
None => {
warn!("Trying to add a top-level to an unknown group.");
return;
},
};
bc_group
.top_level_browsing_context_set
.insert(new_top_level_browsing_context_id.clone());

self.add_pending_change(SessionHistoryChange {
top_level_browsing_context_id: new_top_level_browsing_context_id,
browsing_context_id: new_browsing_context_id,
@@ -174,6 +174,9 @@ impl fmt::Display for BrowsingContextId {
}
}

#[derive(Clone, Default, Eq, Hash, PartialEq)]
pub struct BrowsingContextGroupId(pub u32);

thread_local!(pub static TOP_LEVEL_BROWSING_CONTEXT_ID: Cell<Option<TopLevelBrowsingContextId>> = Cell::new(None));

#[derive(

0 comments on commit 8c28852

Please sign in to comment.
You can’t perform that action at this time.