mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Rather than creating unique types for each pipeline-namespaced index type (eg. MessagePortId, DomExceptionId, etc.), we can create a generic common type that uses a marker to prevent type confusion. This change allows us to reduce the boilerplate code required when implementing serializable/transferable interfaces, since the structured clone implementation can rely on the common type. Testing: Existing WPT tests for serialization and transferring provide coverage. --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
242 lines
8.8 KiB
Rust
242 lines
8.8 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||
|
||
use std::collections::HashMap;
|
||
use std::collections::hash_map::{Entry, Values, ValuesMut};
|
||
|
||
use base::id::WebViewId;
|
||
|
||
use crate::webview_renderer::UnknownWebView;
|
||
|
||
#[derive(Debug)]
|
||
pub struct WebViewManager<WebView> {
|
||
/// Our top-level browsing contexts. In the WebRender scene, their pipelines are the children of
|
||
/// a single root pipeline that also applies any pinch zoom transformation.
|
||
webviews: HashMap<WebViewId, WebView>,
|
||
|
||
/// The order to paint them in, topmost last.
|
||
pub(crate) painting_order: Vec<WebViewId>,
|
||
}
|
||
|
||
impl<WebView> Default for WebViewManager<WebView> {
|
||
fn default() -> Self {
|
||
Self {
|
||
webviews: Default::default(),
|
||
painting_order: Default::default(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<WebView> WebViewManager<WebView> {
|
||
pub fn remove(&mut self, webview_id: WebViewId) -> Result<WebView, UnknownWebView> {
|
||
self.painting_order.retain(|b| *b != webview_id);
|
||
self.webviews
|
||
.remove(&webview_id)
|
||
.ok_or(UnknownWebView(webview_id))
|
||
}
|
||
|
||
pub fn get(&self, webview_id: WebViewId) -> Option<&WebView> {
|
||
self.webviews.get(&webview_id)
|
||
}
|
||
|
||
pub fn get_mut(&mut self, webview_id: WebViewId) -> Option<&mut WebView> {
|
||
self.webviews.get_mut(&webview_id)
|
||
}
|
||
|
||
/// Returns true iff the painting order actually changed.
|
||
pub fn show(&mut self, webview_id: WebViewId) -> Result<bool, UnknownWebView> {
|
||
if !self.webviews.contains_key(&webview_id) {
|
||
return Err(UnknownWebView(webview_id));
|
||
}
|
||
if !self.painting_order.contains(&webview_id) {
|
||
self.painting_order.push(webview_id);
|
||
return Ok(true);
|
||
}
|
||
Ok(false)
|
||
}
|
||
|
||
/// Returns true iff the painting order actually changed.
|
||
pub fn hide(&mut self, webview_id: WebViewId) -> Result<bool, UnknownWebView> {
|
||
if !self.webviews.contains_key(&webview_id) {
|
||
return Err(UnknownWebView(webview_id));
|
||
}
|
||
if self.painting_order.contains(&webview_id) {
|
||
self.painting_order.retain(|b| *b != webview_id);
|
||
return Ok(true);
|
||
}
|
||
Ok(false)
|
||
}
|
||
|
||
/// Returns true iff the painting order actually changed.
|
||
pub fn hide_all(&mut self) -> bool {
|
||
if !self.painting_order.is_empty() {
|
||
self.painting_order.clear();
|
||
return true;
|
||
}
|
||
false
|
||
}
|
||
|
||
/// Returns true iff the painting order actually changed.
|
||
pub fn raise_to_top(&mut self, webview_id: WebViewId) -> Result<bool, UnknownWebView> {
|
||
if !self.webviews.contains_key(&webview_id) {
|
||
return Err(UnknownWebView(webview_id));
|
||
}
|
||
if self.painting_order.last() != Some(&webview_id) {
|
||
self.hide(webview_id)?;
|
||
self.show(webview_id)?;
|
||
return Ok(true);
|
||
}
|
||
Ok(false)
|
||
}
|
||
|
||
pub fn painting_order(&self) -> impl Iterator<Item = (&WebViewId, &WebView)> {
|
||
self.painting_order
|
||
.iter()
|
||
.flat_map(move |webview_id| self.get(*webview_id).map(|b| (webview_id, b)))
|
||
}
|
||
|
||
pub fn entry(&mut self, webview_id: WebViewId) -> Entry<'_, WebViewId, WebView> {
|
||
self.webviews.entry(webview_id)
|
||
}
|
||
|
||
pub fn iter(&self) -> Values<'_, WebViewId, WebView> {
|
||
self.webviews.values()
|
||
}
|
||
|
||
pub fn iter_mut(&mut self) -> ValuesMut<'_, WebViewId, WebView> {
|
||
self.webviews.values_mut()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod test {
|
||
use base::id::{BrowsingContextId, Index, PipelineNamespace, PipelineNamespaceId, WebViewId};
|
||
|
||
use crate::webview_manager::WebViewManager;
|
||
use crate::webview_renderer::UnknownWebView;
|
||
|
||
fn top_level_id(namespace_id: u32, index: u32) -> WebViewId {
|
||
WebViewId(BrowsingContextId {
|
||
namespace_id: PipelineNamespaceId(namespace_id),
|
||
index: Index::new(index).unwrap(),
|
||
})
|
||
}
|
||
|
||
fn webviews_sorted<WebView: Clone>(
|
||
webviews: &WebViewManager<WebView>,
|
||
) -> Vec<(WebViewId, WebView)> {
|
||
let mut keys = webviews.webviews.keys().collect::<Vec<_>>();
|
||
keys.sort();
|
||
keys.iter()
|
||
.map(|&id| (*id, webviews.webviews.get(id).cloned().unwrap()))
|
||
.collect()
|
||
}
|
||
|
||
#[test]
|
||
fn test() {
|
||
PipelineNamespace::install(PipelineNamespaceId(0));
|
||
let mut webviews = WebViewManager::default();
|
||
|
||
// entry() adds the webview to the map, but not the painting order.
|
||
webviews.entry(WebViewId::new()).or_insert('a');
|
||
webviews.entry(WebViewId::new()).or_insert('b');
|
||
webviews.entry(WebViewId::new()).or_insert('c');
|
||
assert!(webviews.get(top_level_id(0, 1)).is_some());
|
||
assert!(webviews.get(top_level_id(0, 2)).is_some());
|
||
assert!(webviews.get(top_level_id(0, 3)).is_some());
|
||
assert_eq!(
|
||
webviews_sorted(&webviews),
|
||
vec![
|
||
(top_level_id(0, 1), 'a'),
|
||
(top_level_id(0, 2), 'b'),
|
||
(top_level_id(0, 3), 'c'),
|
||
]
|
||
);
|
||
assert!(webviews.painting_order.is_empty());
|
||
|
||
// add() returns WebViewAlreadyExists if the webview id already exists.
|
||
webviews.entry(top_level_id(0, 3)).or_insert('d');
|
||
assert!(webviews.get(top_level_id(0, 3)).is_some());
|
||
|
||
// Other methods return UnknownWebView or None if the webview id doesn’t exist.
|
||
assert_eq!(
|
||
webviews.remove(top_level_id(1, 1)),
|
||
Err(UnknownWebView(top_level_id(1, 1)))
|
||
);
|
||
assert_eq!(webviews.get(top_level_id(1, 1)), None);
|
||
assert_eq!(webviews.get_mut(top_level_id(1, 1)), None);
|
||
assert_eq!(
|
||
webviews.show(top_level_id(1, 1)),
|
||
Err(UnknownWebView(top_level_id(1, 1)))
|
||
);
|
||
assert_eq!(
|
||
webviews.hide(top_level_id(1, 1)),
|
||
Err(UnknownWebView(top_level_id(1, 1)))
|
||
);
|
||
assert_eq!(
|
||
webviews.raise_to_top(top_level_id(1, 1)),
|
||
Err(UnknownWebView(top_level_id(1, 1)))
|
||
);
|
||
|
||
// For webviews not yet visible, both show() and raise_to_top() add the given webview on top.
|
||
assert_eq!(webviews.show(top_level_id(0, 2)), Ok(true));
|
||
assert_eq!(webviews.show(top_level_id(0, 2)), Ok(false));
|
||
assert_eq!(webviews.painting_order, vec![top_level_id(0, 2)]);
|
||
assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(true));
|
||
assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(false));
|
||
assert_eq!(
|
||
webviews.painting_order,
|
||
vec![top_level_id(0, 2), top_level_id(0, 1)]
|
||
);
|
||
assert_eq!(webviews.show(top_level_id(0, 3)), Ok(true));
|
||
assert_eq!(webviews.show(top_level_id(0, 3)), Ok(false));
|
||
assert_eq!(
|
||
webviews.painting_order,
|
||
vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)]
|
||
);
|
||
|
||
// For webviews already visible, show() does nothing, while raise_to_top() makes it on top.
|
||
assert_eq!(webviews.show(top_level_id(0, 1)), Ok(false));
|
||
assert_eq!(
|
||
webviews.painting_order,
|
||
vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)]
|
||
);
|
||
assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(true));
|
||
assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(false));
|
||
assert_eq!(
|
||
webviews.painting_order,
|
||
vec![top_level_id(0, 2), top_level_id(0, 3), top_level_id(0, 1)]
|
||
);
|
||
|
||
// hide() removes the webview from the painting order, but not the map.
|
||
assert_eq!(webviews.hide(top_level_id(0, 3)), Ok(true));
|
||
assert_eq!(webviews.hide(top_level_id(0, 3)), Ok(false));
|
||
assert_eq!(
|
||
webviews.painting_order,
|
||
vec![top_level_id(0, 2), top_level_id(0, 1)]
|
||
);
|
||
assert_eq!(
|
||
webviews_sorted(&webviews),
|
||
vec![
|
||
(top_level_id(0, 1), 'a'),
|
||
(top_level_id(0, 2), 'b'),
|
||
(top_level_id(0, 3), 'c'),
|
||
]
|
||
);
|
||
|
||
// painting_order() returns only the visible webviews, in painting order.
|
||
let mut painting_order = webviews.painting_order();
|
||
assert_eq!(painting_order.next(), Some((&top_level_id(0, 2), &'b')));
|
||
assert_eq!(painting_order.next(), Some((&top_level_id(0, 1), &'a')));
|
||
assert_eq!(painting_order.next(), None);
|
||
drop(painting_order);
|
||
|
||
// remove() removes the given webview from both the map and the painting order.
|
||
assert!(webviews.remove(top_level_id(0, 1)).is_ok());
|
||
assert!(webviews.remove(top_level_id(0, 2)).is_ok());
|
||
assert!(webviews.remove(top_level_id(0, 3)).is_ok());
|
||
assert!(webviews_sorted(&webviews).is_empty());
|
||
assert!(webviews.painting_order.is_empty());
|
||
}
|
||
}
|