compositing/
webview_manager.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::collections::HashMap;
6use std::collections::hash_map::{Entry, Values, ValuesMut};
7
8use base::id::WebViewId;
9
10use crate::webview_renderer::{UnknownWebView, WebViewRenderer};
11
12#[derive(Debug)]
13pub struct WebViewManager<WebView> {
14    /// Our top-level browsing contexts. In the WebRender scene, their pipelines are the children of
15    /// a single root pipeline that also applies any pinch zoom transformation.
16    webviews: HashMap<WebViewId, WebView>,
17
18    /// The order to paint them in, topmost last.
19    pub(crate) painting_order: Vec<WebViewId>,
20}
21
22impl<WebView> Default for WebViewManager<WebView> {
23    fn default() -> Self {
24        Self {
25            webviews: Default::default(),
26            painting_order: Default::default(),
27        }
28    }
29}
30
31impl<WebView> WebViewManager<WebView> {
32    pub fn remove(&mut self, webview_id: WebViewId) -> Result<WebView, UnknownWebView> {
33        self.painting_order.retain(|b| *b != webview_id);
34        self.webviews
35            .remove(&webview_id)
36            .ok_or(UnknownWebView(webview_id))
37    }
38
39    pub fn get(&self, webview_id: WebViewId) -> Option<&WebView> {
40        self.webviews.get(&webview_id)
41    }
42
43    pub fn get_mut(&mut self, webview_id: WebViewId) -> Option<&mut WebView> {
44        self.webviews.get_mut(&webview_id)
45    }
46
47    /// Returns true iff the painting order actually changed.
48    pub fn show(&mut self, webview_id: WebViewId) -> Result<bool, UnknownWebView> {
49        if !self.webviews.contains_key(&webview_id) {
50            return Err(UnknownWebView(webview_id));
51        }
52        if !self.painting_order.contains(&webview_id) {
53            self.painting_order.push(webview_id);
54            return Ok(true);
55        }
56        Ok(false)
57    }
58
59    /// Returns true iff the painting order actually changed.
60    pub fn hide(&mut self, webview_id: WebViewId) -> Result<bool, UnknownWebView> {
61        if !self.webviews.contains_key(&webview_id) {
62            return Err(UnknownWebView(webview_id));
63        }
64        if self.painting_order.contains(&webview_id) {
65            self.painting_order.retain(|b| *b != webview_id);
66            return Ok(true);
67        }
68        Ok(false)
69    }
70
71    /// Returns true iff the painting order actually changed.
72    pub fn hide_all(&mut self) -> bool {
73        if !self.painting_order.is_empty() {
74            self.painting_order.clear();
75            return true;
76        }
77        false
78    }
79
80    /// Returns true iff the painting order actually changed.
81    pub fn raise_to_top(&mut self, webview_id: WebViewId) -> Result<bool, UnknownWebView> {
82        if !self.webviews.contains_key(&webview_id) {
83            return Err(UnknownWebView(webview_id));
84        }
85        if self.painting_order.last() != Some(&webview_id) {
86            self.hide(webview_id)?;
87            self.show(webview_id)?;
88            return Ok(true);
89        }
90        Ok(false)
91    }
92
93    pub fn painting_order(&self) -> impl Iterator<Item = (&WebViewId, &WebView)> {
94        self.painting_order
95            .iter()
96            .flat_map(move |webview_id| self.get(*webview_id).map(|b| (webview_id, b)))
97    }
98
99    pub fn entry(&mut self, webview_id: WebViewId) -> Entry<'_, WebViewId, WebView> {
100        self.webviews.entry(webview_id)
101    }
102
103    pub fn iter(&self) -> Values<'_, WebViewId, WebView> {
104        self.webviews.values()
105    }
106
107    pub fn iter_mut(&mut self) -> ValuesMut<'_, WebViewId, WebView> {
108        self.webviews.values_mut()
109    }
110}
111
112impl WebViewManager<WebViewRenderer> {
113    pub(crate) fn scroll_trees_memory_usage(
114        &self,
115        ops: &mut malloc_size_of::MallocSizeOfOps,
116    ) -> usize {
117        self.iter()
118            .map(|renderer| renderer.scroll_trees_memory_usage(ops))
119            .sum::<usize>()
120    }
121}
122
123#[cfg(test)]
124mod test {
125    use base::id::{BrowsingContextId, Index, PipelineNamespace, PipelineNamespaceId, WebViewId};
126
127    use crate::webview_manager::WebViewManager;
128    use crate::webview_renderer::UnknownWebView;
129
130    fn top_level_id(namespace_id: u32, index: u32) -> WebViewId {
131        WebViewId(BrowsingContextId {
132            namespace_id: PipelineNamespaceId(namespace_id),
133            index: Index::new(index).unwrap(),
134        })
135    }
136
137    fn webviews_sorted<WebView: Clone>(
138        webviews: &WebViewManager<WebView>,
139    ) -> Vec<(WebViewId, WebView)> {
140        let mut keys = webviews.webviews.keys().collect::<Vec<_>>();
141        keys.sort_unstable();
142        keys.iter()
143            .map(|&id| (*id, webviews.webviews.get(id).cloned().unwrap()))
144            .collect()
145    }
146
147    #[test]
148    fn test() {
149        PipelineNamespace::install(PipelineNamespaceId(0));
150        let mut webviews = WebViewManager::default();
151
152        // entry() adds the webview to the map, but not the painting order.
153        webviews.entry(WebViewId::new()).or_insert('a');
154        webviews.entry(WebViewId::new()).or_insert('b');
155        webviews.entry(WebViewId::new()).or_insert('c');
156        assert!(webviews.get(top_level_id(0, 1)).is_some());
157        assert!(webviews.get(top_level_id(0, 2)).is_some());
158        assert!(webviews.get(top_level_id(0, 3)).is_some());
159        assert_eq!(
160            webviews_sorted(&webviews),
161            vec![
162                (top_level_id(0, 1), 'a'),
163                (top_level_id(0, 2), 'b'),
164                (top_level_id(0, 3), 'c'),
165            ]
166        );
167        assert!(webviews.painting_order.is_empty());
168
169        // add() returns WebViewAlreadyExists if the webview id already exists.
170        webviews.entry(top_level_id(0, 3)).or_insert('d');
171        assert!(webviews.get(top_level_id(0, 3)).is_some());
172
173        // Other methods return UnknownWebView or None if the webview id doesn’t exist.
174        assert_eq!(
175            webviews.remove(top_level_id(1, 1)),
176            Err(UnknownWebView(top_level_id(1, 1)))
177        );
178        assert_eq!(webviews.get(top_level_id(1, 1)), None);
179        assert_eq!(webviews.get_mut(top_level_id(1, 1)), None);
180        assert_eq!(
181            webviews.show(top_level_id(1, 1)),
182            Err(UnknownWebView(top_level_id(1, 1)))
183        );
184        assert_eq!(
185            webviews.hide(top_level_id(1, 1)),
186            Err(UnknownWebView(top_level_id(1, 1)))
187        );
188        assert_eq!(
189            webviews.raise_to_top(top_level_id(1, 1)),
190            Err(UnknownWebView(top_level_id(1, 1)))
191        );
192
193        // For webviews not yet visible, both show() and raise_to_top() add the given webview on top.
194        assert_eq!(webviews.show(top_level_id(0, 2)), Ok(true));
195        assert_eq!(webviews.show(top_level_id(0, 2)), Ok(false));
196        assert_eq!(webviews.painting_order, vec![top_level_id(0, 2)]);
197        assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(true));
198        assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(false));
199        assert_eq!(
200            webviews.painting_order,
201            vec![top_level_id(0, 2), top_level_id(0, 1)]
202        );
203        assert_eq!(webviews.show(top_level_id(0, 3)), Ok(true));
204        assert_eq!(webviews.show(top_level_id(0, 3)), Ok(false));
205        assert_eq!(
206            webviews.painting_order,
207            vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)]
208        );
209
210        // For webviews already visible, show() does nothing, while raise_to_top() makes it on top.
211        assert_eq!(webviews.show(top_level_id(0, 1)), Ok(false));
212        assert_eq!(
213            webviews.painting_order,
214            vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)]
215        );
216        assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(true));
217        assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(false));
218        assert_eq!(
219            webviews.painting_order,
220            vec![top_level_id(0, 2), top_level_id(0, 3), top_level_id(0, 1)]
221        );
222
223        // hide() removes the webview from the painting order, but not the map.
224        assert_eq!(webviews.hide(top_level_id(0, 3)), Ok(true));
225        assert_eq!(webviews.hide(top_level_id(0, 3)), Ok(false));
226        assert_eq!(
227            webviews.painting_order,
228            vec![top_level_id(0, 2), top_level_id(0, 1)]
229        );
230        assert_eq!(
231            webviews_sorted(&webviews),
232            vec![
233                (top_level_id(0, 1), 'a'),
234                (top_level_id(0, 2), 'b'),
235                (top_level_id(0, 3), 'c'),
236            ]
237        );
238
239        // painting_order() returns only the visible webviews, in painting order.
240        let mut painting_order = webviews.painting_order();
241        assert_eq!(painting_order.next(), Some((&top_level_id(0, 2), &'b')));
242        assert_eq!(painting_order.next(), Some((&top_level_id(0, 1), &'a')));
243        assert_eq!(painting_order.next(), None);
244        drop(painting_order);
245
246        // remove() removes the given webview from both the map and the painting order.
247        assert!(webviews.remove(top_level_id(0, 1)).is_ok());
248        assert!(webviews.remove(top_level_id(0, 2)).is_ok());
249        assert!(webviews.remove(top_level_id(0, 3)).is_ok());
250        assert!(webviews_sorted(&webviews).is_empty());
251        assert!(webviews.painting_order.is_empty());
252    }
253}