mirror of
https://github.com/servo/servo.git
synced 2025-07-22 14:53:49 +01:00
implement browsing context group and set
This commit is contained in:
parent
d544c186b9
commit
8c28852e90
3 changed files with 235 additions and 28 deletions
|
@ -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,14 +331,6 @@ 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>,
|
||||
|
@ -327,6 +338,14 @@ pub struct Constellation<Message, LTF, STF> {
|
|||
/// 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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue