Rewild Your Web
web browser dweb
at main 278 lines 12 kB view raw
1--- original 2+++ modified 3@@ -3,6 +3,7 @@ 4 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 5 6 use std::cell::Cell; 7+use std::collections::BTreeMap; 8 use std::collections::hash_map::Entry; 9 use std::rc::Rc; 10 use std::sync::Arc; 11@@ -80,8 +81,15 @@ 12 pub(crate) painter_id: PainterId, 13 14 /// Our [`WebViewRenderer`]s, one for every `WebView`. 15- pub(crate) webview_renderers: FxHashMap<WebViewId, WebViewRenderer>, 16+ /// Using BTreeMap to ensure deterministic iteration order by WebViewId, 17+ /// which is important for proper z-ordering in the display list (parents before children). 18+ pub(crate) webview_renderers: BTreeMap<WebViewId, WebViewRenderer>, 19 20+ /// Set of WebViewIds that are embedded webviews. These should not be rendered 21+ /// as top-level iframes in the root display list, as they are already referenced 22+ /// by their parent's display list through IFrameFragment. 23+ pub(crate) embedded_webview_ids: FxHashSet<WebViewId>, 24+ 25 /// Tracks whether or not the view needs to be repainted. 26 pub(crate) needs_repaint: Cell<RepaintReason>, 27 28@@ -258,6 +266,7 @@ 29 painter_id, 30 embedder_to_constellation_sender, 31 webview_renderers: Default::default(), 32+ embedded_webview_ids: Default::default(), 33 rendering_context, 34 needs_repaint: Cell::default(), 35 pending_frames: Default::default(), 36@@ -278,7 +287,12 @@ 37 painter 38 } 39 40- pub(crate) fn perform_updates(&mut self) { 41+ /// Process pending scroll and zoom events for all webview renderers. 42+ /// Returns a list of (webview_id, unconsumed_scroll) tuples for scroll events 43+ /// that were not consumed by embedded webviews and should be forwarded to parents. 44+ pub(crate) fn perform_updates( 45+ &mut self, 46+ ) -> Vec<(WebViewId, crate::webview_renderer::ScrollEvent)> { 47 // The WebXR thread may make a different context current 48 if let Err(err) = self.rendering_context.make_current() { 49 warn!("Failed to make the rendering context current: {:?}", err); 50@@ -285,18 +299,23 @@ 51 } 52 53 let mut need_zoom = false; 54- let scroll_offset_updates: Vec<_> = self 55- .webview_renderers 56- .values_mut() 57- .filter_map(|webview_renderer| { 58- let (zoom, scroll_result) = webview_renderer 59- .process_pending_scroll_and_pinch_zoom_events(&self.webrender_api); 60- need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom); 61- scroll_result 62- }) 63- .collect(); 64+ let mut unconsumed_scrolls = Vec::new(); 65+ let mut scroll_offset_updates = Vec::new(); 66+ 67+ for (webview_id, webview_renderer) in self.webview_renderers.iter_mut() { 68+ let result = 69+ webview_renderer.process_pending_scroll_and_pinch_zoom_events(&self.webrender_api); 70+ need_zoom = need_zoom || (result.pinch_zoom_result == PinchZoomResult::DidPinchZoom); 71+ if let Some(scroll_result) = result.scroll_result { 72+ scroll_offset_updates.push(scroll_result); 73+ } 74+ if let Some(unconsumed_scroll) = result.unconsumed_scroll { 75+ unconsumed_scrolls.push((*webview_id, unconsumed_scroll)); 76+ } 77+ } 78 79 self.send_zoom_and_scroll_offset_updates(need_zoom, scroll_offset_updates); 80+ unconsumed_scrolls 81 } 82 83 #[track_caller] 84@@ -591,7 +610,16 @@ 85 86 let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect); 87 let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]); 88+ 89+ // Iterate over webview_renderers in order. BTreeMap ensures deterministic ordering 90+ // by WebViewId, so parents (e.g., (0,1)) come before children (e.g., (0,2)). 91+ // This ensures children are rendered on top for proper hit testing. 92 for webview_renderer in self.webview_renderers.values() { 93+ // Skip embedded webviews - they are rendered as part of their parent's 94+ // display list through IFrameFragment, not as top-level iframes. 95+ if self.embedded_webview_ids.contains(&webview_renderer.id) { 96+ continue; 97+ } 98 if webview_renderer.hidden() { 99 continue; 100 } 101@@ -652,7 +680,7 @@ 102 /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe 103 /// for each visible top-level browsing context, applying a transformation on the root for 104 /// pinch zoom, page zoom, and HiDPI scaling. 105- fn send_root_pipeline_display_list(&mut self) { 106+ pub(crate) fn send_root_pipeline_display_list(&mut self) { 107 let mut transaction = Transaction::new(); 108 self.send_root_pipeline_display_list_in_transaction(&mut transaction); 109 self.generate_frame(&mut transaction, RenderReasons::SCENE); 110@@ -718,6 +746,21 @@ 111 self.send_transaction(transaction); 112 } 113 114+ /// Send a single scroll result to WebRender. This is used when forwarding 115+ /// unconsumed scroll events from embedded webviews to their parent. 116+ pub(crate) fn send_scroll_result_to_webrender(&mut self, scroll_result: ScrollResult) { 117+ let mut transaction = Transaction::new(); 118+ transaction.set_scroll_offsets( 119+ scroll_result.external_scroll_id, 120+ vec![SampledScrollOffset { 121+ offset: scroll_result.offset, 122+ generation: 0, 123+ }], 124+ ); 125+ self.generate_frame(&mut transaction, RenderReasons::APZ); 126+ self.send_transaction(transaction); 127+ } 128+ 129 pub(crate) fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) { 130 let Some(renderer) = self.webrender_renderer.as_mut() else { 131 return; 132@@ -788,6 +831,26 @@ 133 self.send_root_pipeline_display_list(); 134 } 135 136+ /// Mark a webview as an embedded webview. Embedded webviews are not rendered 137+ /// as top-level iframes in the root display list, as they are already referenced 138+ /// by their parent's display list through IFrameFragment. 139+ pub(crate) fn register_embedded_webview(&mut self, embedded_webview_id: WebViewId) { 140+ self.embedded_webview_ids.insert(embedded_webview_id); 141+ // Also set the flag on the webview renderer so it handles zoom correctly 142+ if let Some(webview_renderer) = self.webview_renderers.get_mut(&embedded_webview_id) { 143+ webview_renderer.set_is_embedded_webview(true); 144+ } 145+ } 146+ 147+ /// Remove a webview from the embedded webview set. 148+ pub(crate) fn unregister_embedded_webview(&mut self, embedded_webview_id: WebViewId) { 149+ self.embedded_webview_ids.remove(&embedded_webview_id); 150+ // Also clear the flag on the webview renderer 151+ if let Some(webview_renderer) = self.webview_renderers.get_mut(&embedded_webview_id) { 152+ webview_renderer.set_is_embedded_webview(false); 153+ } 154+ } 155+ 156 pub(crate) fn set_throttled( 157 &mut self, 158 webview_id: WebViewId, 159@@ -1181,15 +1244,23 @@ 160 webview: Box<dyn WebViewTrait>, 161 viewport_details: ViewportDetails, 162 ) { 163- self.webview_renderers 164- .entry(webview.id()) 165- .or_insert(WebViewRenderer::new( 166+ let webview_id = webview.id(); 167+ let is_embedded = self.embedded_webview_ids.contains(&webview_id); 168+ self.webview_renderers.entry(webview_id).or_insert_with(|| { 169+ let mut renderer = WebViewRenderer::new( 170 webview, 171 viewport_details, 172 self.embedder_to_constellation_sender.clone(), 173 self.refresh_driver.clone(), 174 self.webrender_document, 175- )); 176+ ); 177+ // If this webview was already registered as embedded before being created, 178+ // set the flag now 179+ if is_embedded { 180+ renderer.set_is_embedded_webview(true); 181+ } 182+ renderer 183+ }); 184 } 185 186 pub(crate) fn remove_webview(&mut self, webview_id: WebViewId) { 187@@ -1276,25 +1347,26 @@ 188 } 189 190 pub(crate) fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEventAndId) { 191- if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) { 192- match &event.event { 193- InputEvent::MouseMove(event) => { 194- // We only track the last mouse move position for non-touch events. 195- if !event.is_compatibility_event_for_touch { 196- let event_point = event 197- .point 198- .as_device_point(webview_renderer.device_pixels_per_page_pixel()); 199- self.last_mouse_move_position = Some(event_point); 200- } 201- }, 202- InputEvent::MouseLeftViewport(_) => { 203- self.last_mouse_move_position = None; 204- }, 205- _ => {}, 206- } 207- 208- webview_renderer.notify_input_event(&self.webrender_api, &self.needs_repaint, event); 209+ let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else { 210+ return; 211+ }; 212+ match &event.event { 213+ InputEvent::MouseMove(event) => { 214+ // We only track the last mouse move position for non-touch events. 215+ if !event.is_compatibility_event_for_touch { 216+ let event_point = event 217+ .point 218+ .as_device_point(webview_renderer.device_pixels_per_page_pixel()); 219+ self.last_mouse_move_position = Some(event_point); 220+ } 221+ }, 222+ InputEvent::MouseLeftViewport(_) => { 223+ self.last_mouse_move_position = None; 224+ }, 225+ _ => {}, 226 } 227+ 228+ webview_renderer.notify_input_event(&self.webrender_api, &self.needs_repaint, event); 229 self.disable_lcp_calculation_for_webview(webview_id); 230 } 231 232@@ -1310,6 +1382,38 @@ 233 self.disable_lcp_calculation_for_webview(webview_id); 234 } 235 236+ /// Attempt to scroll at the given point. Returns true if scroll was consumed. 237+ /// This is used for embedded webviews to check if the scroll should bubble up to the parent. 238+ pub(crate) fn try_scroll_at_point( 239+ &mut self, 240+ webview_id: WebViewId, 241+ scroll: Scroll, 242+ point: WebViewPoint, 243+ ) -> bool { 244+ let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else { 245+ return false; 246+ }; 247+ let device_point = point.as_device_point(webview_renderer.device_pixels_per_page_pixel()); 248+ webview_renderer 249+ .scroll_node_at_device_point(&self.webrender_api, device_point, scroll) 250+ .is_some() 251+ } 252+ 253+ /// Try to scroll any scrollable node in the webview and send the result to WebRender. 254+ /// This is used for bubbling scroll events from embedded iframes when hit-testing fails. 255+ pub(crate) fn try_scroll_any_and_send_to_webrender( 256+ &mut self, 257+ webview_id: WebViewId, 258+ scroll: Scroll, 259+ ) { 260+ let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else { 261+ return; 262+ }; 263+ if let Some(scroll_result) = webview_renderer.try_scroll_any(scroll) { 264+ self.send_scroll_result_to_webrender(scroll_result); 265+ } 266+ } 267+ 268 pub(crate) fn pinch_zoom( 269 &mut self, 270 webview_id: WebViewId, 271@@ -1356,7 +1460,6 @@ 272 result: InputEventResult, 273 ) { 274 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else { 275- warn!("Handled input event for unknown webview: {webview_id}"); 276 return; 277 }; 278 webview_renderer.notify_input_event_handled(