Rewild Your Web
web browser dweb
at main 238 lines 10 kB view raw
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 {