servo_constellation/
constellation_webview.rs1use embedder_traits::user_contents::UserContentManagerId;
6use embedder_traits::{InputEvent, MouseLeftViewportEvent, Theme};
7use euclid::Point2D;
8use log::warn;
9use rustc_hash::FxHashMap;
10use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
11use servo_base::Epoch;
12use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
13use style_traits::CSSPixel;
14
15use crate::browsingcontext::BrowsingContext;
16use crate::pipeline::Pipeline;
17use crate::session_history::JointSessionHistory;
18
19pub(crate) struct ConstellationWebView {
22 webview_id: WebViewId,
24
25 pub active_top_level_pipeline_id: Option<PipelineId>,
27 pub active_top_level_pipeline_epoch: Epoch,
29
30 pub focused_browsing_context_id: BrowsingContextId,
34
35 pub hovered_browsing_context_id: Option<BrowsingContextId>,
38
39 pub last_mouse_move_point: Point2D<f32, CSSPixel>,
42
43 pub session_history: JointSessionHistory,
45
46 pub user_content_manager_id: Option<UserContentManagerId>,
50
51 theme: Theme,
54
55 pub accessibility_active: bool,
62}
63
64impl ConstellationWebView {
65 pub(crate) fn new(
66 webview_id: WebViewId,
67 focused_browsing_context_id: BrowsingContextId,
68 user_content_manager_id: Option<UserContentManagerId>,
69 ) -> Self {
70 Self {
71 webview_id,
72 user_content_manager_id,
73 active_top_level_pipeline_id: None,
74 active_top_level_pipeline_epoch: Epoch::default(),
75 focused_browsing_context_id,
76 hovered_browsing_context_id: None,
77 last_mouse_move_point: Default::default(),
78 session_history: JointSessionHistory::new(),
79 theme: Theme::Light,
80 accessibility_active: false,
81 }
82 }
83
84 pub(crate) fn set_theme(&mut self, new_theme: Theme) -> bool {
86 let old_theme = std::mem::replace(&mut self.theme, new_theme);
87 old_theme != self.theme
88 }
89
90 pub(crate) fn theme(&self) -> Theme {
92 self.theme
93 }
94
95 fn target_pipeline_id_for_input_event(
96 &self,
97 event: &ConstellationInputEvent,
98 browsing_contexts: &FxHashMap<BrowsingContextId, BrowsingContext>,
99 ) -> Option<PipelineId> {
100 if let Some(hit_test_result) = &event.hit_test_result {
101 return Some(hit_test_result.pipeline_id);
102 }
103
104 let browsing_context_id = if matches!(event.event.event, InputEvent::MouseLeftViewport(_)) {
107 self.hovered_browsing_context_id
108 .unwrap_or(self.focused_browsing_context_id)
109 } else {
110 self.focused_browsing_context_id
111 };
112
113 Some(browsing_contexts.get(&browsing_context_id)?.pipeline_id)
114 }
115
116 pub(crate) fn forward_input_event(
119 &mut self,
120 event: ConstellationInputEvent,
121 pipelines: &FxHashMap<PipelineId, Pipeline>,
122 browsing_contexts: &FxHashMap<BrowsingContextId, BrowsingContext>,
123 ) -> bool {
124 let Some(pipeline_id) = self.target_pipeline_id_for_input_event(&event, browsing_contexts)
125 else {
126 warn!("Unknown pipeline for input event. Ignoring.");
127 return false;
128 };
129 let Some(pipeline) = pipelines.get(&pipeline_id) else {
130 warn!("Unknown pipeline id {pipeline_id:?} for input event. Ignoring.");
131 return false;
132 };
133
134 let mut update_hovered_browsing_context =
135 |newly_hovered_browsing_context_id, focus_moving_to_another_iframe: bool| {
136 let old_hovered_context_id = std::mem::replace(
137 &mut self.hovered_browsing_context_id,
138 newly_hovered_browsing_context_id,
139 );
140 if old_hovered_context_id == newly_hovered_browsing_context_id {
141 return;
142 }
143 let Some(old_hovered_context_id) = old_hovered_context_id else {
144 return;
145 };
146 let Some(pipeline) = browsing_contexts
147 .get(&old_hovered_context_id)
148 .and_then(|browsing_context| pipelines.get(&browsing_context.pipeline_id))
149 else {
150 return;
151 };
152
153 let mut synthetic_mouse_leave_event = event.clone();
154 synthetic_mouse_leave_event.event.event =
155 InputEvent::MouseLeftViewport(MouseLeftViewportEvent {
156 focus_moving_to_another_iframe,
157 });
158
159 let _ = pipeline
160 .event_loop
161 .send(ScriptThreadMessage::SendInputEvent(
162 self.webview_id,
163 pipeline.id,
164 synthetic_mouse_leave_event,
165 ));
166 };
167
168 if let InputEvent::MouseLeftViewport(_) = &event.event.event {
169 update_hovered_browsing_context(None, false);
170 return true;
171 }
172
173 if let InputEvent::MouseMove(_) = &event.event.event {
174 update_hovered_browsing_context(Some(pipeline.browsing_context_id), true);
175 self.last_mouse_move_point = event
176 .hit_test_result
177 .as_ref()
178 .expect("MouseMove events should always have hit tests.")
179 .point_in_viewport;
180 }
181
182 let _ = pipeline
183 .event_loop
184 .send(ScriptThreadMessage::SendInputEvent(
185 self.webview_id,
186 pipeline.id,
187 event,
188 ));
189 true
190 }
191}