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