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::hash_map::{Entry, Values, ValuesMut};
6
7use base::id::WebViewId;
8use rustc_hash::FxHashMap;
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: FxHashMap<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::{
126        BrowsingContextId, Index, PipelineNamespace, PipelineNamespaceId, TEST_PAINTER_ID,
127        WebViewId,
128    };
129
130    use crate::webview_manager::WebViewManager;
131    use crate::webview_renderer::UnknownWebView;
132
133    fn webview_id(namespace_id: u32, index: u32) -> WebViewId {
134        WebViewId::mock_for_testing(BrowsingContextId {
135            namespace_id: PipelineNamespaceId(namespace_id),
136            index: Index::new(index).unwrap(),
137        })
138    }
139
140    fn webviews_sorted<WebView: Clone>(
141        webviews: &WebViewManager<WebView>,
142    ) -> Vec<(WebViewId, WebView)> {
143        let mut keys = webviews.webviews.keys().collect::<Vec<_>>();
144        keys.sort_unstable();
145        keys.iter()
146            .map(|&id| (*id, webviews.webviews.get(id).cloned().unwrap()))
147            .collect()
148    }
149
150    #[test]
151    fn test() {
152        PipelineNamespace::install(PipelineNamespaceId(0));
153        let mut webviews = WebViewManager::default();
154
155        // entry() adds the webview to the map, but not the painting order.
156        let painter_id = TEST_PAINTER_ID;
157        webviews.entry(WebViewId::new(painter_id)).or_insert('a');
158        webviews.entry(WebViewId::new(painter_id)).or_insert('b');
159        webviews.entry(WebViewId::new(painter_id)).or_insert('c');
160        assert!(webviews.get(webview_id(0, 1)).is_some());
161        assert!(webviews.get(webview_id(0, 2)).is_some());
162        assert!(webviews.get(webview_id(0, 3)).is_some());
163        assert_eq!(
164            webviews_sorted(&webviews),
165            vec![
166                (webview_id(0, 1), 'a'),
167                (webview_id(0, 2), 'b'),
168                (webview_id(0, 3), 'c'),
169            ]
170        );
171        assert!(webviews.painting_order.is_empty());
172
173        // add() returns WebViewAlreadyExists if the webview id already exists.
174        webviews.entry(webview_id(0, 3)).or_insert('d');
175        assert!(webviews.get(webview_id(0, 3)).is_some());
176
177        // Other methods return UnknownWebView or None if the webview id doesn’t exist.
178        assert_eq!(
179            webviews.remove(webview_id(1, 1)),
180            Err(UnknownWebView(webview_id(1, 1)))
181        );
182        assert_eq!(webviews.get(webview_id(1, 1)), None);
183        assert_eq!(webviews.get_mut(webview_id(1, 1)), None);
184        assert_eq!(
185            webviews.show(webview_id(1, 1)),
186            Err(UnknownWebView(webview_id(1, 1)))
187        );
188        assert_eq!(
189            webviews.hide(webview_id(1, 1)),
190            Err(UnknownWebView(webview_id(1, 1)))
191        );
192        assert_eq!(
193            webviews.raise_to_top(webview_id(1, 1)),
194            Err(UnknownWebView(webview_id(1, 1)))
195        );
196
197        // For webviews not yet visible, both show() and raise_to_top() add the given webview on top.
198        assert_eq!(webviews.show(webview_id(0, 2)), Ok(true));
199        assert_eq!(webviews.show(webview_id(0, 2)), Ok(false));
200        assert_eq!(webviews.painting_order, vec![webview_id(0, 2)]);
201        assert_eq!(webviews.raise_to_top(webview_id(0, 1)), Ok(true));
202        assert_eq!(webviews.raise_to_top(webview_id(0, 1)), Ok(false));
203        assert_eq!(
204            webviews.painting_order,
205            vec![webview_id(0, 2), webview_id(0, 1)]
206        );
207        assert_eq!(webviews.show(webview_id(0, 3)), Ok(true));
208        assert_eq!(webviews.show(webview_id(0, 3)), Ok(false));
209        assert_eq!(
210            webviews.painting_order,
211            vec![webview_id(0, 2), webview_id(0, 1), webview_id(0, 3)]
212        );
213
214        // For webviews already visible, show() does nothing, while raise_to_top() makes it on top.
215        assert_eq!(webviews.show(webview_id(0, 1)), Ok(false));
216        assert_eq!(
217            webviews.painting_order,
218            vec![webview_id(0, 2), webview_id(0, 1), webview_id(0, 3)]
219        );
220        assert_eq!(webviews.raise_to_top(webview_id(0, 1)), Ok(true));
221        assert_eq!(webviews.raise_to_top(webview_id(0, 1)), Ok(false));
222        assert_eq!(
223            webviews.painting_order,
224            vec![webview_id(0, 2), webview_id(0, 3), webview_id(0, 1)]
225        );
226
227        // hide() removes the webview from the painting order, but not the map.
228        assert_eq!(webviews.hide(webview_id(0, 3)), Ok(true));
229        assert_eq!(webviews.hide(webview_id(0, 3)), Ok(false));
230        assert_eq!(
231            webviews.painting_order,
232            vec![webview_id(0, 2), webview_id(0, 1)]
233        );
234        assert_eq!(
235            webviews_sorted(&webviews),
236            vec![
237                (webview_id(0, 1), 'a'),
238                (webview_id(0, 2), 'b'),
239                (webview_id(0, 3), 'c'),
240            ]
241        );
242
243        // painting_order() returns only the visible webviews, in painting order.
244        let mut painting_order = webviews.painting_order();
245        assert_eq!(painting_order.next(), Some((&webview_id(0, 2), &'b')));
246        assert_eq!(painting_order.next(), Some((&webview_id(0, 1), &'a')));
247        assert_eq!(painting_order.next(), None);
248        drop(painting_order);
249
250        // remove() removes the given webview from both the map and the painting order.
251        assert!(webviews.remove(webview_id(0, 1)).is_ok());
252        assert!(webviews.remove(webview_id(0, 2)).is_ok());
253        assert!(webviews.remove(webview_id(0, 3)).is_ok());
254        assert!(webviews_sorted(&webviews).is_empty());
255        assert!(webviews.painting_order.is_empty());
256    }
257}