diff --git a/patches/components/constellation/constellation.rs.patch b/patches/components/constellation/constellation.rs.patch index 2017542..53f1fdb 100644 --- a/patches/components/constellation/constellation.rs.patch +++ b/patches/components/constellation/constellation.rs.patch @@ -99,7 +99,21 @@ } #[derive(Debug)] -@@ -522,6 +539,40 @@ +@@ -469,6 +486,13 @@ + /// currently being pressed. + pressed_mouse_buttons: u16, + ++ /// True while a mouse button that went down over the chrome (root) webview is ++ /// still held. Pins the whole drag (moves + up) to the chrome so a fast ++ /// pointermove that crosses an embedded view is not re-hit-tested and stolen, ++ /// which would strand a resize / move drag. Mirrors the touch path's ++ /// forwarded_touches sequence pinning; only real mouse takes this path. ++ mouse_drag_pinned_to_chrome: bool, ++ + /// The currently activated keyboard modifiers. + active_keyboard_modifiers: Modifiers, + +@@ -522,6 +546,40 @@ /// to the `UserContents` need to be forwared to all the `ScriptThread`s that host /// the relevant `WebView`. pub(crate) user_contents_for_manager_id: FxHashMap, @@ -140,7 +154,7 @@ } /// State needed to construct a constellation. -@@ -585,6 +636,9 @@ +@@ -585,6 +643,9 @@ /// The wake lock provider for acquiring and releasing OS-level screen wake locks. pub wake_lock_provider: Box, @@ -150,7 +164,7 @@ } /// When we are exiting a pipeline, we can either force exiting or not. A normal exit -@@ -667,6 +721,11 @@ +@@ -667,6 +728,11 @@ let broken_image_icon_data = resources::read_bytes(Resource::BrokenImageIcon); @@ -162,7 +176,7 @@ let mut constellation: Constellation = Constellation { event_loops: Default::default(), namespace_receiver, -@@ -688,7 +747,7 @@ +@@ -688,7 +754,7 @@ script_to_devtools_callback: Default::default(), #[cfg(feature = "bluetooth")] bluetooth_ipc_sender: state.bluetooth_thread, @@ -171,7 +185,15 @@ private_resource_threads: state.private_resource_threads, public_storage_threads: state.public_storage_threads, private_storage_threads: state.private_storage_threads, -@@ -741,6 +800,21 @@ +@@ -725,6 +791,7 @@ + canvas: OnceCell::new(), + pending_approval_navigations: Default::default(), + pressed_mouse_buttons: 0, ++ mouse_drag_pinned_to_chrome: false, + active_keyboard_modifiers: Modifiers::empty(), + hard_fail, + active_media_session: None, +@@ -741,6 +808,21 @@ pending_viewport_changes: Default::default(), screenshot_readiness_requests: Vec::new(), user_contents_for_manager_id: Default::default(), @@ -193,7 +215,7 @@ }; constellation.run(); -@@ -766,6 +840,18 @@ +@@ -766,6 +848,18 @@ fn clean_up_finished_script_event_loops(&mut self) { self.event_loop_join_handles .retain(|join_handle| !join_handle.is_finished()); @@ -212,7 +234,7 @@ self.event_loops .retain(|event_loop| event_loop.upgrade().is_some()); } -@@ -1053,6 +1139,11 @@ +@@ -1053,6 +1147,11 @@ .get(&webview_id) .and_then(|webview| webview.user_content_manager_id); @@ -224,7 +246,7 @@ let new_pipeline_info = NewPipelineInfo { parent_info: parent_pipeline_id, new_pipeline_id, -@@ -1064,6 +1155,13 @@ +@@ -1064,6 +1163,13 @@ user_content_manager_id, theme, target_snapshot_params, @@ -238,7 +260,7 @@ }; let pipeline = match Pipeline::spawn(new_pipeline_info, event_loop, self, throttled) { Ok(pipeline) => pipeline, -@@ -1229,6 +1327,9 @@ +@@ -1229,6 +1335,9 @@ Script((WebViewId, PipelineId, ScriptToConstellationMessage)), BackgroundHangMonitor(HangMonitorAlert), Embedder(EmbedderToConstellationMessage), @@ -248,7 +270,7 @@ RemoveProcess(usize), } // Get one incoming request. -@@ -1248,6 +1349,28 @@ +@@ -1248,6 +1357,28 @@ sel.recv(&self.background_hang_monitor_receiver); sel.recv(&self.embedder_to_constellation_receiver); @@ -277,7 +299,7 @@ self.process_manager.register(&mut sel); let request = { -@@ -1274,9 +1397,30 @@ +@@ -1274,9 +1405,30 @@ oper.recv(&self.embedder_to_constellation_receiver) .expect("Unexpected embedder channel panic in constellation"), )), @@ -309,7 +331,7 @@ let _ = oper.recv(self.process_manager.receiver_at(process_index)); Ok(Request::RemoveProcess(process_index)) }, -@@ -1299,6 +1443,37 @@ +@@ -1299,6 +1451,37 @@ Request::BackgroundHangMonitor(message) => { self.handle_request_from_background_hang_monitor(message); }, @@ -347,7 +369,7 @@ Request::RemoveProcess(index) => self.process_manager.remove(index), } } -@@ -1522,11 +1697,7 @@ +@@ -1522,11 +1705,7 @@ } }, EmbedderToConstellationMessage::PreferencesUpdated(updates) => { @@ -360,7 +382,7 @@ let _ = event_loop.send(ScriptThreadMessage::PreferencesUpdated( updates .iter() -@@ -1553,6 +1724,18 @@ +@@ -1553,6 +1732,18 @@ EmbedderToConstellationMessage::SetAccessibilityActive(webview_id, active) => { self.set_accessibility_active(webview_id, active); }, @@ -379,7 +401,7 @@ } } -@@ -1750,7 +1933,13 @@ +@@ -1750,7 +1941,13 @@ return warn!("Attempt to add channel name from an unexpected origin."); } self.broadcast_channels @@ -394,7 +416,7 @@ }, ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter( router_id, -@@ -1764,7 +1953,13 @@ +@@ -1764,7 +1961,13 @@ return warn!("Attempt to remove channel name from an unexpected origin."); } self.broadcast_channels @@ -409,7 +431,7 @@ }, ScriptToConstellationMessage::RemoveBroadcastChannelRouter(router_id, origin) => { if self -@@ -1776,6 +1971,12 @@ +@@ -1776,6 +1979,12 @@ self.broadcast_channels .remove_broadcast_channel_router(router_id); }, @@ -422,7 +444,7 @@ ScriptToConstellationMessage::ScheduleBroadcast(router_id, message) => { if self .check_origin_against_pipeline(&source_pipeline_id, &message.origin) -@@ -1785,8 +1986,15 @@ +@@ -1785,8 +1994,15 @@ "Attempt to schedule broadcast from an origin not matching the origin of the msg." ); } @@ -439,7 +461,7 @@ }, ScriptToConstellationMessage::PipelineExited => { self.handle_pipeline_exited(source_pipeline_id); -@@ -1806,6 +2014,12 @@ +@@ -1806,6 +2022,12 @@ ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info) => { self.handle_script_new_auxiliary(load_info); }, @@ -452,7 +474,7 @@ ScriptToConstellationMessage::ChangeRunningAnimationsState(animation_state) => { self.handle_change_running_animations_state(source_pipeline_id, animation_state) }, -@@ -1857,7 +2071,7 @@ +@@ -1857,7 +2079,7 @@ ScriptToConstellationMessage::SetFinalUrl(final_url) => { // The script may have finished loading after we already started shutting down. if let Some(ref mut pipeline) = self.pipelines.get_mut(&source_pipeline_id) { @@ -461,7 +483,7 @@ } else { warn!("constellation got set final url message for dead pipeline"); } -@@ -2007,6 +2221,29 @@ +@@ -2007,6 +2229,29 @@ new_value, ); }, @@ -491,21 +513,22 @@ ScriptToConstellationMessage::MediaSessionEvent(pipeline_id, event) => { // Unlikely at this point, but we may receive events coming from // different media sessions, so we set the active media session based -@@ -2025,7 +2262,12 @@ +@@ -2025,8 +2270,13 @@ }; self.active_media_session = Some(pipeline_id); self.constellation_to_embedder_proxy.send( - ConstellationToEmbedderMsg::MediaSessionEvent(webview_id, event), + ConstellationToEmbedderMsg::MediaSessionEvent(webview_id, event.clone()), -+ ); + ); + // Also route to embedded webview parent iframe. + self.handle_embedded_webview_notification( + webview_id, + EmbeddedWebViewEventType::MediaSessionEvent(event), - ); ++ ); }, #[cfg(feature = "webgpu")] -@@ -2099,9 +2341,1018 @@ + ScriptToConstellationMessage::RequestAdapter(response_sender, options, ids) => self +@@ -2099,9 +2349,1018 @@ } }, }, @@ -1524,7 +1547,7 @@ /// Check the origin of a message against that of the pipeline it came from. /// Note: this is still limited as a security check, /// see -@@ -2418,6 +3669,55 @@ +@@ -2418,6 +3677,55 @@ TransferState::TransferInProgress(queue) => queue.push_back(task), TransferState::CompletionFailed(queue) => queue.push_back(task), TransferState::CompletionRequested(_, queue) => queue.push_back(task), @@ -1580,7 +1603,7 @@ } } -@@ -3204,6 +4504,101 @@ +@@ -3204,6 +4512,101 @@ ); } @@ -1682,7 +1705,7 @@ fn forward_input_event( &mut self, webview_id: WebViewId, -@@ -3223,6 +4618,34 @@ +@@ -3223,6 +4626,66 @@ let pressed_mouse_buttons = self.pressed_mouse_buttons; let active_keyboard_modifiers = self.active_keyboard_modifiers; @@ -1693,8 +1716,40 @@ + // parent's push_iframe), deliver the pointer event straight to it using the + // WebRender-localized point. This supersedes the chrome-side + // forward_event_to_embedded_iframe_if_needed coordinate-transform path. -+ let (webview_id, event) = -+ self.redirect_pointer_event_to_embedded_webview(webview_id, event, &hit_test_result); ++ // ++ // Exception: implicit pointer capture for a chrome-initiated drag. Once a mouse ++ // button goes down over the chrome (e.g. a resize / move handle), the rest of the ++ // drag (moves + up) stays pinned to the chrome (root) webview. Re-hit-testing each ++ // move would redirect a fast pointermove that crosses an embedded view into that ++ // view and strand the drag (the chrome stops receiving move / up and never ++ // recovers). The point is already in root coordinates, so the pinned event is ++ // delivered unchanged. Mirrors the touch path's forwarded_touches sequence pinning. ++ let is_mouse_down = matches!( ++ &event.event, ++ InputEvent::MouseButton(button) if button.action == MouseButtonAction::Down ++ ); ++ let is_mouse_up = matches!( ++ &event.event, ++ InputEvent::MouseButton(button) if button.action == MouseButtonAction::Up ++ ); ++ let pin_to_chrome = self.mouse_drag_pinned_to_chrome && ++ matches!( ++ &event.event, ++ InputEvent::MouseMove(_) | InputEvent::MouseButton(_) ++ ); ++ let (webview_id, event) = if pin_to_chrome { ++ (webview_id, event) ++ } else { ++ self.redirect_pointer_event_to_embedded_webview(webview_id, event, &hit_test_result) ++ }; ++ if is_mouse_down { ++ // Pin only when the press stayed on the chrome (webview_id is the post-redirect ++ // target here; an embedded press keeps normal per-move routing, no regression). ++ self.mouse_drag_pinned_to_chrome = ++ !self.embedded_webview_to_iframe.contains_key(&webview_id); ++ } else if is_mouse_up && self.pressed_mouse_buttons == 0 { ++ self.mouse_drag_pinned_to_chrome = false; ++ } + + // If a mouse Down landed on an embedded webview, re-fire the activation event on + // the chrome's element so it can take focus and update the topbar. @@ -1717,7 +1772,7 @@ let event_id = event.id; let Some(webview) = self.webviews.get_mut(&webview_id) else { warn!("Got input event for unknown WebViewId: {webview_id:?}"); -@@ -3325,6 +4748,40 @@ +@@ -3325,6 +4788,40 @@ /// fn handle_close_top_level_browsing_context(&mut self, webview_id: WebViewId) { debug!("{webview_id}: Closing"); @@ -1758,7 +1813,7 @@ let browsing_context_id = BrowsingContextId::from(webview_id); // Step 5. Remove traversable from the user agent's top-level traversable set. let browsing_context = -@@ -3601,8 +5058,27 @@ +@@ -3601,8 +5098,27 @@ opener_webview_id, opener_pipeline_id, response_sender, @@ -1786,7 +1841,7 @@ let Some((webview_id_sender, webview_id_receiver)) = generic_channel::channel() else { warn!("Failed to create channel"); let _ = response_sender.send(None); -@@ -3701,6 +5177,402 @@ +@@ -3701,6 +5217,402 @@ }); } @@ -2189,7 +2244,7 @@ #[servo_tracing::instrument(skip_all)] fn handle_refresh_cursor(&self, pipeline_id: PipelineId) { let Some(pipeline) = self.pipelines.get(&pipeline_id) else { -@@ -4250,7 +6122,7 @@ +@@ -4250,7 +6162,7 @@ }, }; @@ -2198,7 +2253,7 @@ match self.browsing_contexts.get_mut(&browsing_context_id) { Some(browsing_context) => { let old_pipeline_id = browsing_context.pipeline_id; -@@ -4259,6 +6131,7 @@ +@@ -4259,6 +6171,7 @@ old_pipeline_id, browsing_context.parent_pipeline_id, browsing_context.webview_id, @@ -2206,7 +2261,7 @@ ) }, None => { -@@ -4268,6 +6141,15 @@ +@@ -4268,6 +6181,15 @@ self.unload_document(old_pipeline_id); @@ -2222,7 +2277,7 @@ if let Some(new_pipeline) = self.pipelines.get(&new_pipeline_id) { if let Some(ref chan) = self.devtools_sender { let state = NavigationState::Start(new_pipeline.url.clone()); -@@ -4836,7 +6718,7 @@ +@@ -4836,7 +6758,7 @@ } #[servo_tracing::instrument(skip_all)] @@ -2231,7 +2286,7 @@ // Send a flat projection of the history to embedder. // The final vector is a concatenation of the URLs of the past // entries, the current entry and the future entries. -@@ -4948,9 +6830,22 @@ +@@ -4948,9 +6870,22 @@ self.constellation_to_embedder_proxy .send(ConstellationToEmbedderMsg::HistoryChanged( webview_id, @@ -2255,7 +2310,7 @@ } #[servo_tracing::instrument(skip_all)] -@@ -4969,7 +6864,7 @@ +@@ -4969,7 +6904,7 @@ webview.focused_browsing_context_id = change.browsing_context_id; } @@ -2264,7 +2319,7 @@ match self.browsing_contexts.get_mut(&change.browsing_context_id) { Some(browsing_context) => { debug!("Adding pipeline to existing browsing context."); -@@ -4976,11 +6871,15 @@ +@@ -4976,11 +6911,15 @@ let old_pipeline_id = browsing_context.pipeline_id; browsing_context.pipelines.insert(change.new_pipeline_id); browsing_context.update_current_entry(change.new_pipeline_id); @@ -2282,7 +2337,7 @@ }, }; -@@ -4988,6 +6887,18 @@ +@@ -4988,6 +6927,18 @@ self.unload_document(old_pipeline_id); } diff --git a/ui/system/desktop/working.css b/ui/system/desktop/working.css index 927c79d..726e784 100644 --- a/ui/system/desktop/working.css +++ b/ui/system/desktop/working.css @@ -72,8 +72,10 @@ /* Suppress iframe pointer events on ALL webviews so a pointer interaction over a desk view reaches the desktop shell instead of the webview. Two independent states want this: - - workshop-resizing: a resize drag is in flight, so iframe content can't grab - the pointer mid-drag (TODO: replace with pointer capture). + - workshop-resizing: a resize / move drag is in flight, so view content can't + grab the pointer mid-drag. (Real mouse is also pinned to the shell at the + compositor level in constellation forward_input_event; this rule still + covers touch drags, which take the DOM-forward path.) - popover-open: a dock/topbar popover is open, so an outside-click over a desk view falls through to its document-level outside-click handler (clicks over chrome already reach it). */