forked from
me.webbeef.org/browser.html
Rewild Your Web
1--- original
2+++ modified
3@@ -15,7 +15,9 @@
4
5 use bitflags::bitflags;
6 use chrono::Local;
7-use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
8+use constellation_traits::{
9+ EmbeddedWebViewEventType, NavigationHistoryBehavior, ScriptToConstellationMessage,
10+};
11 use content_security_policy::sandboxing_directive::SandboxingFlagSet;
12 use content_security_policy::{CspList, Policy as CspPolicy, PolicyDisposition};
13 use cookie::Cookie;
14@@ -27,6 +29,7 @@
15 Image, LoadStatus,
16 };
17 use encoding_rs::{Encoding, UTF_8};
18+use euclid::Size2D;
19 use fonts::WebFontDocumentContext;
20 use html5ever::{LocalName, Namespace, QualName, local_name, ns};
21 use hyper_serde::Serde;
22@@ -55,7 +58,7 @@
23 use script_traits::{DocumentActivity, ProgressiveWebMetricType};
24 use servo_arc::Arc;
25 use servo_base::cross_process_instant::CrossProcessInstant;
26-use servo_base::generic_channel::GenericSend;
27+use servo_base::generic_channel::{GenericSend, GenericSharedMemory};
28 use servo_base::id::WebViewId;
29 use servo_base::{Epoch, generic_channel};
30 use servo_config::pref;
31@@ -635,6 +638,9 @@
32 #[no_trace]
33 favicon: RefCell<Option<Image>>,
34
35+ /// The cached theme color for that document.
36+ theme_color: RefCell<Option<String>>,
37+
38 /// All websockets created that are associated with this document.
39 websockets: DOMTracker<WebSocket>,
40
41@@ -882,6 +888,12 @@
42
43 // Set the document's activity level, reflow if necessary, and suspend or resume timers.
44 self.activity.set(activity);
45+
46+ // When document becomes inactive, hide all embedder controls (keyboard, etc.)
47+ if activity == DocumentActivity::Inactive {
48+ self.embedder_controls().hide_all_controls();
49+ }
50+
51 let media = ServoMedia::get();
52 let pipeline_id = self.window().pipeline_id();
53 let client_context_id =
54@@ -895,6 +907,7 @@
55
56 self.title_changed();
57 self.notify_embedder_favicon();
58+ self.notify_embedder_theme_color();
59 self.dirty_all_nodes();
60 self.window().resume(CanGc::from_cx(cx));
61 media.resume(&client_context_id);
62@@ -1307,6 +1320,9 @@
63 LoadStatus::Started,
64 ));
65 self.send_to_embedder(EmbedderMsg::Status(self.webview_id(), None));
66+ self.notify_embedded_webview_parent(
67+ EmbeddedWebViewEventType::LoadStatusChanged(LoadStatus::Started),
68+ );
69 }
70 },
71 DocumentReadyState::Complete => {
72@@ -1315,6 +1331,9 @@
73 self.webview_id(),
74 LoadStatus::Complete,
75 ));
76+ self.notify_embedded_webview_parent(
77+ EmbeddedWebViewEventType::LoadStatusChanged(LoadStatus::Complete),
78+ );
79 }
80 update_with_current_instant(&self.dom_complete);
81 },
82@@ -1721,7 +1740,13 @@
83 let window = self.window();
84 if window.is_top_level() {
85 let title = self.title().map(String::from);
86- self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
87+ self.send_to_embedder(EmbedderMsg::ChangePageTitle(
88+ self.webview_id(),
89+ title.clone(),
90+ ));
91+ // Also notify parent iframe if this is an embedded webview.
92+ // The constellation will filter and only forward if this is actually an embedded webview.
93+ self.notify_embedded_webview_parent(EmbeddedWebViewEventType::TitleChanged(title));
94 }
95 }
96
97@@ -1730,6 +1755,18 @@
98 window.send_to_embedder(msg);
99 }
100
101+ /// Sends a notification to the parent iframe element if this document is in an embedded webview.
102+ /// The constellation checks if the webview is embedded and forwards the event to the parent pipeline.
103+ pub(crate) fn notify_embedded_webview_parent(&self, event: EmbeddedWebViewEventType) {
104+ let window = self.window();
105+ // Only top-level windows can be embedded webviews
106+ if window.is_top_level() {
107+ window.send_to_constellation(
108+ ScriptToConstellationMessage::EmbeddedWebViewNotification(event),
109+ );
110+ }
111+ }
112+
113 pub(crate) fn dirty_all_nodes(&self) {
114 let root = match self.GetDocumentElement() {
115 Some(root) => root,
116@@ -3315,9 +3352,59 @@
117 current_rendering_epoch,
118 );
119
120+ // After reflow, update embedded webview rects for input event routing
121+ self.update_embedded_webview_rects();
122+
123 (phases, statistics)
124 }
125
126+ /// Update the rects of embedded webviews for input event routing.
127+ /// This sends the current position/size of each embedded webview iframe
128+ /// to the compositor so it can route input events to the correct webview.
129+ /// Also updates visibility state when iframes have display:none.
130+ fn update_embedded_webview_rects(&self) {
131+ let parent_webview_id = self.webview_id();
132+ let paint_api = self.window().paint_api();
133+ let device_pixel_ratio = self.window().device_pixel_ratio().get();
134+
135+ // Collect embedded webview iframes first to avoid holding the borrow
136+ // during border_box() calls which can trigger reflow and need iframes_mut().
137+ let embedded_iframes: Vec<(DomRoot<HTMLIFrameElement>, WebViewId)> = self
138+ .iframes()
139+ .iter()
140+ .filter(|iframe| iframe.is_embedded_webview())
141+ .filter_map(|iframe| iframe.embedded_webview_id().map(|id| (iframe, id)))
142+ .collect();
143+
144+ for (iframe, embedded_webview_id) in embedded_iframes {
145+ // Get the iframe's border box (in CSS pixels relative to the initial containing block)
146+ // This is equivalent to getBoundingClientRect() which is viewport-relative.
147+ // If the iframe has display:none, border_box() returns None.
148+ let Some(border_box) = iframe.upcast::<Node>().border_box() else {
149+ // Iframe is not visible (display:none), notify compositor to hide it
150+ paint_api.set_embedded_webview_hidden(embedded_webview_id, parent_webview_id, true);
151+ continue;
152+ };
153+
154+ // Convert to device pixels
155+ // Note: border_box coordinates are viewport-relative (like getBoundingClientRect)
156+ let rect = webrender_api::units::DeviceRect::from_origin_and_size(
157+ webrender_api::units::DevicePoint::new(
158+ border_box.origin.x.to_f32_px() * device_pixel_ratio,
159+ border_box.origin.y.to_f32_px() * device_pixel_ratio,
160+ ),
161+ webrender_api::units::DeviceSize::new(
162+ border_box.size.width.to_f32_px() * device_pixel_ratio,
163+ border_box.size.height.to_f32_px() * device_pixel_ratio,
164+ ),
165+ );
166+
167+ // Iframe is visible, notify compositor to show it and update its rect
168+ paint_api.set_embedded_webview_hidden(embedded_webview_id, parent_webview_id, false);
169+ paint_api.update_embedded_webview_rect(embedded_webview_id, parent_webview_id, rect);
170+ }
171+ }
172+
173 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
174 self.waiting_on_canvas_image_updates.set(false);
175 }
176@@ -4060,6 +4147,7 @@
177 active_sandboxing_flag_set: Cell::new(SandboxingFlagSet::empty()),
178 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
179 favicon: RefCell::new(None),
180+ theme_color: RefCell::new(None),
181 websockets: DOMTracker::new(),
182 details_name_groups: Default::default(),
183 protocol_handler_automation_mode: Default::default(),
184@@ -5174,6 +5262,36 @@
185
186 pub(crate) fn notify_embedder_favicon(&self) {
187 if let Some(ref image) = *self.favicon.borrow() {
188+ // Encode the raw pixel data as PNG for the embedded webview event
189+ let pixel_format = match image.format {
190+ embedder_traits::PixelFormat::RGBA8 => pixels::SnapshotPixelFormat::RGBA,
191+ embedder_traits::PixelFormat::BGRA8 => pixels::SnapshotPixelFormat::BGRA,
192+ _ => pixels::SnapshotPixelFormat::RGBA, // Fallback
193+ };
194+
195+ let mut snapshot = pixels::Snapshot::from_vec(
196+ Size2D::new(image.width, image.height),
197+ pixel_format,
198+ pixels::SnapshotAlphaMode::Transparent {
199+ premultiplied: false,
200+ },
201+ image.data().to_vec(),
202+ );
203+
204+ let mut png_bytes = Vec::new();
205+ if snapshot
206+ .encode_for_mime_type(&pixels::EncodedImageType::Png, None, &mut png_bytes)
207+ .is_ok()
208+ {
209+ // Notify parent iframe about favicon change (for embedded webviews)
210+ self.notify_embedded_webview_parent(EmbeddedWebViewEventType::FaviconChanged {
211+ bytes: GenericSharedMemory::from_bytes(&png_bytes),
212+ width: image.width,
213+ height: image.height,
214+ });
215+ }
216+
217+ // Notify embedder
218 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
219 }
220 }
221@@ -5233,6 +5351,20 @@
222 pub(crate) fn set_css_styling_flag(&self, value: bool) {
223 self.css_styling_flag.set(value)
224 }
225+
226+ pub(crate) fn notify_embedder_theme_color(&self) {
227+ if let Some(ref theme_color) = *self.theme_color.borrow() {
228+ // Notify parent iframe about theme color change (for embedded webviews)
229+ self.notify_embedded_webview_parent(EmbeddedWebViewEventType::ThemeColorChanged(
230+ theme_color.clone(),
231+ ));
232+ }
233+ }
234+
235+ pub(crate) fn set_theme_color(&self, theme_color: String) {
236+ *self.theme_color.borrow_mut() = Some(theme_color);
237+ self.notify_embedder_theme_color();
238+ }
239 }
240
241 impl DocumentMethods<crate::DomTypeHolder> for Document {