constellation/
constellation_webview.rs1use base::id::{BrowsingContextId, PipelineId, WebViewId};
6use embedder_traits::{InputEvent, MouseLeftViewportEvent, Theme};
7use euclid::Point2D;
8use log::warn;
9use rustc_hash::FxHashMap;
10use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
11use style_traits::CSSPixel;
12
13use crate::browsingcontext::BrowsingContext;
14use crate::pipeline::Pipeline;
15use crate::session_history::JointSessionHistory;
16
17pub(crate) struct ConstellationWebView {
20 webview_id: WebViewId,
22
23 pub focused_browsing_context_id: BrowsingContextId,
27
28 pub hovered_browsing_context_id: Option<BrowsingContextId>,
31
32 pub last_mouse_move_point: Point2D<f32, CSSPixel>,
35
36 pub session_history: JointSessionHistory,
38
39 theme: Theme,
42}
43
44impl ConstellationWebView {
45 pub(crate) fn new(
46 webview_id: WebViewId,
47 focused_browsing_context_id: BrowsingContextId,
48 ) -> Self {
49 Self {
50 webview_id,
51 focused_browsing_context_id,
52 hovered_browsing_context_id: None,
53 last_mouse_move_point: Default::default(),
54 session_history: JointSessionHistory::new(),
55 theme: Theme::Light,
56 }
57 }
58
59 pub(crate) fn set_theme(&mut self, new_theme: Theme) -> bool {
61 let old_theme = std::mem::replace(&mut self.theme, new_theme);
62 old_theme != self.theme
63 }
64
65 pub(crate) fn theme(&self) -> Theme {
67 self.theme
68 }
69
70 fn target_pipeline_id_for_input_event(
71 &self,
72 event: &ConstellationInputEvent,
73 browsing_contexts: &FxHashMap<BrowsingContextId, BrowsingContext>,
74 ) -> Option<PipelineId> {
75 if let Some(hit_test_result) = &event.hit_test_result {
76 return Some(hit_test_result.pipeline_id);
77 }
78
79 let browsing_context_id = if matches!(event.event.event, InputEvent::MouseLeftViewport(_)) {
82 self.hovered_browsing_context_id
83 .unwrap_or(self.focused_browsing_context_id)
84 } else {
85 self.focused_browsing_context_id
86 };
87
88 Some(browsing_contexts.get(&browsing_context_id)?.pipeline_id)
89 }
90
91 pub(crate) fn forward_input_event(
94 &mut self,
95 event: ConstellationInputEvent,
96 pipelines: &FxHashMap<PipelineId, Pipeline>,
97 browsing_contexts: &FxHashMap<BrowsingContextId, BrowsingContext>,
98 ) -> bool {
99 let Some(pipeline_id) = self.target_pipeline_id_for_input_event(&event, browsing_contexts)
100 else {
101 warn!("Unknown pipeline for input event. Ignoring.");
102 return false;
103 };
104 let Some(pipeline) = pipelines.get(&pipeline_id) else {
105 warn!("Unknown pipeline id {pipeline_id:?} for input event. Ignoring.");
106 return false;
107 };
108
109 let mut update_hovered_browsing_context =
110 |newly_hovered_browsing_context_id, focus_moving_to_another_iframe: bool| {
111 let old_hovered_context_id = std::mem::replace(
112 &mut self.hovered_browsing_context_id,
113 newly_hovered_browsing_context_id,
114 );
115 if old_hovered_context_id == newly_hovered_browsing_context_id {
116 return;
117 }
118 let Some(old_hovered_context_id) = old_hovered_context_id else {
119 return;
120 };
121 let Some(pipeline) = browsing_contexts
122 .get(&old_hovered_context_id)
123 .and_then(|browsing_context| pipelines.get(&browsing_context.pipeline_id))
124 else {
125 return;
126 };
127
128 let mut synthetic_mouse_leave_event = event.clone();
129 synthetic_mouse_leave_event.event.event =
130 InputEvent::MouseLeftViewport(MouseLeftViewportEvent {
131 focus_moving_to_another_iframe,
132 });
133
134 let _ = pipeline
135 .event_loop
136 .send(ScriptThreadMessage::SendInputEvent(
137 self.webview_id,
138 pipeline.id,
139 synthetic_mouse_leave_event,
140 ));
141 };
142
143 if let InputEvent::MouseLeftViewport(_) = &event.event.event {
144 update_hovered_browsing_context(None, false);
145 return true;
146 }
147
148 if let InputEvent::MouseMove(_) = &event.event.event {
149 update_hovered_browsing_context(Some(pipeline.browsing_context_id), true);
150 self.last_mouse_move_point = event
151 .hit_test_result
152 .as_ref()
153 .expect("MouseMove events should always have hit tests.")
154 .point_in_viewport;
155 }
156
157 let _ = pipeline
158 .event_loop
159 .send(ScriptThreadMessage::SendInputEvent(
160 self.webview_id,
161 pipeline.id,
162 event,
163 ));
164 true
165 }
166}