mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +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 crate::pipeline::Pipeline;
|
||||||
use euclid::TypedSize2D;
|
use euclid::TypedSize2D;
|
||||||
use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
|
use msg::constellation_msg::{
|
||||||
|
BrowsingContextGroupId, BrowsingContextId, PipelineId, TopLevelBrowsingContextId,
|
||||||
|
};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
|
|
||||||
|
@ -34,6 +36,9 @@ pub struct NewBrowsingContextInfo {
|
||||||
/// sorted reverse chronologically: in particular prev.pop() is the latest
|
/// sorted reverse chronologically: in particular prev.pop() is the latest
|
||||||
/// past entry, and next.pop() is the earliest future entry.
|
/// past entry, and next.pop() is the earliest future entry.
|
||||||
pub struct BrowsingContext {
|
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.
|
/// The browsing context id.
|
||||||
pub id: BrowsingContextId,
|
pub id: BrowsingContextId,
|
||||||
|
|
||||||
|
@ -66,6 +71,7 @@ impl BrowsingContext {
|
||||||
/// Create a new browsing context.
|
/// 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.
|
/// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
bc_group_id: BrowsingContextGroupId,
|
||||||
id: BrowsingContextId,
|
id: BrowsingContextId,
|
||||||
top_level_id: TopLevelBrowsingContextId,
|
top_level_id: TopLevelBrowsingContextId,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
|
@ -77,14 +83,15 @@ impl BrowsingContext {
|
||||||
let mut pipelines = HashSet::new();
|
let mut pipelines = HashSet::new();
|
||||||
pipelines.insert(pipeline_id);
|
pipelines.insert(pipeline_id);
|
||||||
BrowsingContext {
|
BrowsingContext {
|
||||||
id: id,
|
bc_group_id,
|
||||||
top_level_id: top_level_id,
|
id,
|
||||||
size: size,
|
top_level_id,
|
||||||
is_private: is_private,
|
size,
|
||||||
is_visible: is_visible,
|
is_private,
|
||||||
pipeline_id: pipeline_id,
|
is_visible,
|
||||||
parent_pipeline_id: parent_pipeline_id,
|
pipeline_id,
|
||||||
pipelines: pipelines,
|
parent_pipeline_id,
|
||||||
|
pipelines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,8 @@ use layout_traits::LayoutThreadFactory;
|
||||||
use log::{Level, LevelFilter, Log, Metadata, Record};
|
use log::{Level, LevelFilter, Log, Metadata, Record};
|
||||||
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
|
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
|
BrowsingContextGroupId, BrowsingContextId, HistoryStateId, PipelineId,
|
||||||
|
TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
|
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
|
||||||
use net_traits::pub_domains::reg_host;
|
use net_traits::pub_domains::reg_host;
|
||||||
|
@ -158,7 +159,7 @@ use servo_remutex::ReentrantMutex;
|
||||||
use servo_url::{Host, ImmutableOrigin, ServoUrl};
|
use servo_url::{Host, ImmutableOrigin, ServoUrl};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
@ -183,6 +184,24 @@ struct Browser {
|
||||||
session_history: JointSessionHistory,
|
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
|
/// The `Constellation` itself. In the servo browser, there is one
|
||||||
/// constellation, which maintains all of the browser global data.
|
/// constellation, which maintains all of the browser global data.
|
||||||
/// In embedded applications, there may be more than one constellation,
|
/// In embedded applications, there may be more than one constellation,
|
||||||
|
@ -312,14 +331,6 @@ pub struct Constellation<Message, LTF, STF> {
|
||||||
/// WebRender thread.
|
/// WebRender thread.
|
||||||
webrender_api_sender: webrender_api::RenderApiSender,
|
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
|
/// The set of all the pipelines in the browser. (See the `pipeline` module
|
||||||
/// for more details.)
|
/// for more details.)
|
||||||
pipelines: HashMap<PipelineId, Pipeline>,
|
pipelines: HashMap<PipelineId, Pipeline>,
|
||||||
|
@ -327,6 +338,14 @@ pub struct Constellation<Message, LTF, STF> {
|
||||||
/// The set of all the browsing contexts in the browser.
|
/// The set of all the browsing contexts in the browser.
|
||||||
browsing_contexts: HashMap<BrowsingContextId, BrowsingContext>,
|
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
|
/// When a navigation is performed, we do not immediately update
|
||||||
/// the session history, instead we ask the event loop to begin loading
|
/// the session history, instead we ask the event loop to begin loading
|
||||||
/// the new document, and do not update the browsing context until the
|
/// the new document, and do not update the browsing context until the
|
||||||
|
@ -675,7 +694,8 @@ where
|
||||||
swmanager_chan: None,
|
swmanager_chan: None,
|
||||||
swmanager_receiver: swmanager_receiver,
|
swmanager_receiver: swmanager_receiver,
|
||||||
swmanager_sender: sw_mgr_clone,
|
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(),
|
pipelines: HashMap::new(),
|
||||||
browsing_contexts: HashMap::new(),
|
browsing_contexts: HashMap::new(),
|
||||||
pending_changes: vec![],
|
pending_changes: vec![],
|
||||||
|
@ -750,6 +770,106 @@ where
|
||||||
namespace_id
|
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
|
/// Helper function for creating a pipeline
|
||||||
fn new_pipeline(
|
fn new_pipeline(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -785,11 +905,22 @@ where
|
||||||
match reg_host(&load_data.url) {
|
match reg_host(&load_data.url) {
|
||||||
None => (None, None),
|
None => (None, None),
|
||||||
Some(host) => {
|
Some(host) => {
|
||||||
let event_loop =
|
match self.get_event_loop(
|
||||||
self.event_loops.get(&host).and_then(|weak| weak.upgrade());
|
&host,
|
||||||
match event_loop {
|
&top_level_browsing_context_id,
|
||||||
None => (None, Some(host)),
|
&opener,
|
||||||
Some(event_loop) => (Some(event_loop), None),
|
) {
|
||||||
|
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 {}.",
|
"Adding new host entry {} for top-level browsing context {}.",
|
||||||
host, top_level_browsing_context_id
|
host, top_level_browsing_context_id
|
||||||
);
|
);
|
||||||
let _ = self
|
self.set_event_loop(
|
||||||
.event_loops
|
Rc::downgrade(&pipeline.pipeline.event_loop),
|
||||||
.insert(host, Rc::downgrade(&pipeline.pipeline.event_loop));
|
host,
|
||||||
|
top_level_browsing_context_id,
|
||||||
|
opener,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(!self.pipelines.contains_key(&pipeline_id));
|
assert!(!self.pipelines.contains_key(&pipeline_id));
|
||||||
|
@ -922,7 +1056,31 @@ where
|
||||||
is_visible: bool,
|
is_visible: bool,
|
||||||
) {
|
) {
|
||||||
debug!("Creating new browsing context {}", browsing_context_id);
|
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(
|
let browsing_context = BrowsingContext::new(
|
||||||
|
bc_group_id,
|
||||||
browsing_context_id,
|
browsing_context_id,
|
||||||
top_level_id,
|
top_level_id,
|
||||||
pipeline_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(
|
self.new_pipeline(
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
browsing_context_id,
|
browsing_context_id,
|
||||||
|
@ -1912,6 +2079,16 @@ where
|
||||||
if self.active_browser_id == Some(top_level_browsing_context_id) {
|
if self.active_browser_id == Some(top_level_browsing_context_id) {
|
||||||
self.active_browser_id = None;
|
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>) {
|
fn handle_iframe_size_msg(&mut self, iframe_sizes: Vec<IFrameSizeMsg>) {
|
||||||
|
@ -2214,6 +2391,26 @@ where
|
||||||
session_history: JointSessionHistory::new(),
|
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 {
|
self.add_pending_change(SessionHistoryChange {
|
||||||
top_level_browsing_context_id: new_top_level_browsing_context_id,
|
top_level_browsing_context_id: new_top_level_browsing_context_id,
|
||||||
browsing_context_id: new_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));
|
thread_local!(pub static TOP_LEVEL_BROWSING_CONTEXT_ID: Cell<Option<TopLevelBrowsingContextId>> = Cell::new(None));
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue