forked from
me.webbeef.org/browser.html
Rewild Your Web
1--- original
2+++ modified
3@@ -35,15 +35,15 @@
4 use base::cross_process_instant::CrossProcessInstant;
5 use base::generic_channel;
6 use base::id::{
7- BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespace, ScriptEventLoopId,
8- TEST_WEBVIEW_ID, WebViewId,
9+ BrowsingContextId, HistoryStateId, MessagePortId, PipelineId, PipelineNamespace,
10+ ScriptEventLoopId, TEST_WEBVIEW_ID, WebViewId,
11 };
12 use canvas_traits::webgl::WebGLPipeline;
13 use chrono::{DateTime, Local};
14 use constellation_traits::{
15- JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior, ScreenshotReadinessResponse,
16- ScriptToConstellationChan, ScriptToConstellationMessage, ScrollStateUpdate,
17- StructuredSerializedData, TraversalDirection, WindowSizeType,
18+ EmbeddedWebViewEventType, JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior,
19+ ScreenshotReadinessResponse, ScriptToConstellationChan, ScriptToConstellationMessage,
20+ ScrollStateUpdate, StructuredSerializedData, TraversalDirection, WindowSizeType,
21 };
22 use crossbeam_channel::unbounded;
23 use data_url::mime::Mime;
24@@ -55,7 +55,7 @@
25 use embedder_traits::{
26 EmbedderControlId, EmbedderControlResponse, EmbedderMsg, FocusSequenceNumber,
27 InputEventOutcome, JavaScriptEvaluationError, JavaScriptEvaluationId, MediaSessionActionType,
28- Theme, ViewportDetails, WebDriverScriptCommand,
29+ ServoErrorType, Theme, ViewportDetails, WebDriverScriptCommand,
30 };
31 use encoding_rs::Encoding;
32 use fonts::{FontContext, SystemFontServiceProxy};
33@@ -92,6 +92,7 @@
34 UpdatePipelineIdReason,
35 };
36 use servo_arc::Arc as ServoArc;
37+use servo_config::pref_util::PrefValue;
38 use servo_config::{opts, pref, prefs};
39 use servo_url::{ImmutableOrigin, MutableOrigin, OriginSnapshot, ServoUrl};
40 use storage_traits::StorageThreads;
41@@ -115,6 +116,8 @@
42 use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
43 DocumentMethods, DocumentReadyState,
44 };
45+use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
46+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::MessagePortMethods;
47 use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods;
48 use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
49 use crate::dom::bindings::conversions::{
50@@ -134,9 +137,13 @@
51 RenderingUpdateReason,
52 };
53 use crate::dom::element::Element;
54+use crate::dom::event::Event;
55+use crate::dom::eventtarget::EventTarget;
56 use crate::dom::globalscope::GlobalScope;
57 use crate::dom::html::htmliframeelement::{HTMLIFrameElement, IframeContext};
58+use crate::dom::messageport::MessagePort;
59 use crate::dom::node::{Node, NodeTraits};
60+use crate::dom::peerstreamevent::PeerStreamEvent;
61 use crate::dom::servoparser::{ParserContext, ServoParser};
62 use crate::dom::types::DebuggerGlobalScope;
63 #[cfg(feature = "webgpu")]
64@@ -1943,11 +1950,22 @@
65 self.handle_refresh_cursor(pipeline_id);
66 },
67 ScriptThreadMessage::PreferencesUpdated(updates) => {
68- let mut current_preferences = prefs::get().clone();
69- for (name, value) in updates {
70- current_preferences.set_value(&name, value);
71+ // Only update core preferences (those without namespace separator)
72+ // Embedder preferences are already stored in the embedder prefs registry
73+ let core_updates: Vec<_> = updates
74+ .iter()
75+ .filter(|(name, _)| !name.contains('.'))
76+ .collect();
77+ if !core_updates.is_empty() {
78+ let mut current_preferences = prefs::get().clone();
79+ for (name, value) in core_updates {
80+ current_preferences.set_value(name, value.clone());
81+ }
82+ prefs::set(current_preferences);
83 }
84- prefs::set(current_preferences);
85+
86+ // Dispatch preferencechanged events to all Embedder instances
87+ self.dispatch_preference_changed_to_embedders(&updates, CanGc::from_cx(cx));
88 },
89 ScriptThreadMessage::ForwardKeyboardScroll(pipeline_id, scroll) => {
90 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
91@@ -1988,6 +2006,35 @@
92 ScriptThreadMessage::TriggerGarbageCollection => unsafe {
93 JS_GC(*GlobalScope::get_cx(), GCReason::API);
94 },
95+ ScriptThreadMessage::DispatchEmbeddedWebViewEvent {
96+ target,
97+ parent,
98+ event,
99+ } => {
100+ self.handle_embedded_webview_event(parent, target, event, CanGc::from_cx(cx));
101+ },
102+ ScriptThreadMessage::DispatchServoError(error_type, message) => {
103+ self.handle_dispatch_servo_error(error_type, message, CanGc::from_cx(cx));
104+ },
105+ ScriptThreadMessage::DispatchPairingEvent(event) => {
106+ self.handle_dispatch_pairing_event(event, CanGc::from_cx(cx));
107+ },
108+ ScriptThreadMessage::DispatchPeerStream(
109+ peer_id,
110+ remote_port_id_bytes,
111+ stream_id,
112+ from_peer,
113+ target_url,
114+ ) => {
115+ self.handle_dispatch_peer_stream(
116+ peer_id,
117+ remote_port_id_bytes,
118+ stream_id,
119+ from_peer,
120+ target_url,
121+ CanGc::from_cx(cx),
122+ );
123+ },
124 }
125 }
126
127@@ -3059,6 +3106,9 @@
128 .documents
129 .borrow()
130 .find_iframe(parent_pipeline_id, browsing_context_id);
131+ let is_embedded_webview = frame_element
132+ .as_ref()
133+ .is_some_and(|iframe| iframe.is_embedded_webview());
134 if let Some(frame_element) = frame_element {
135 frame_element.update_pipeline_id(new_pipeline_id, reason, cx);
136 }
137@@ -3078,6 +3128,7 @@
138 // is no need to pass along existing opener information that
139 // will be discarded.
140 None,
141+ is_embedded_webview,
142 );
143 }
144 }
145@@ -3354,6 +3405,157 @@
146 }
147 }
148
149+ /// Handle an embedded webview event that should be dispatched on an iframe element.
150+ fn handle_embedded_webview_event(
151+ &self,
152+ parent_id: PipelineId,
153+ browsing_context_id: BrowsingContextId,
154+ event: EmbeddedWebViewEventType,
155+ can_gc: CanGc,
156+ ) {
157+ let iframe = self
158+ .documents
159+ .borrow()
160+ .find_iframe(parent_id, browsing_context_id);
161+ match iframe {
162+ Some(iframe) => iframe.dispatch_embedded_webview_event(event, can_gc),
163+ None => warn!(
164+ "Embedded webview event sent to closed pipeline {}.",
165+ parent_id
166+ ),
167+ }
168+ }
169+
170+ /// Handle a servo error by dispatching servoerror events to all navigator.embedder instances.
171+ fn handle_dispatch_servo_error(
172+ &self,
173+ error_type: ServoErrorType,
174+ message: String,
175+ can_gc: CanGc,
176+ ) {
177+ // Dispatch servoerror event to windows that already have an Embedder created
178+ for (_, document) in self.documents.borrow().iter() {
179+ if let Some(embedder) = document.window().Navigator().get_embedder() {
180+ // Enter the realm of the embedder's global before dispatching JS events
181+ let _ac = enter_realm(&*embedder);
182+ embedder.dispatch_servo_error(&error_type, &message, can_gc);
183+ }
184+ }
185+ }
186+
187+ /// Handle a pairing event by dispatching to all navigator.embedder.pairing instances.
188+ fn handle_dispatch_pairing_event(
189+ &self,
190+ event: constellation_traits::PairingEvent,
191+ can_gc: CanGc,
192+ ) {
193+ for (_, document) in self.documents.borrow().iter() {
194+ if let Some(embedder) = document.window().Navigator().get_embedder() {
195+ if let Some(pairing) = embedder.get_pairing() {
196+ let _ac = enter_realm(&*pairing);
197+ pairing.dispatch_pairing_event(&event, can_gc);
198+ }
199+ }
200+ }
201+ }
202+
203+ /// Handle an incoming peer stream: create a local MessagePort and fire "peerstream" on Window.
204+ /// Only fires on documents whose URL matches the target_url specified by the sender.
205+ /// If preventDefault() is called on the event, deny the offer.
206+ fn handle_dispatch_peer_stream(
207+ &self,
208+ peer_id: String,
209+ remote_port_id_bytes: Vec<u8>,
210+ stream_id: String,
211+ from_peer: String,
212+ target_url: String,
213+ can_gc: CanGc,
214+ ) {
215+ log::debug!(
216+ "handle_dispatch_peer_stream: peer_id={peer_id}, stream_id={stream_id}, target_url={target_url}"
217+ );
218+ let Ok(remote_port_id) = postcard::from_bytes::<MessagePortId>(&remote_port_id_bytes)
219+ else {
220+ log::warn!("Failed to deserialize remote port ID in DispatchPeerStream");
221+ return;
222+ };
223+
224+ // Find the first document whose URL matches the target.
225+ let mut accepted = false;
226+ let mut responding_global = None;
227+ let doc_count = self.documents.borrow().iter().count();
228+ log::debug!("Searching {doc_count} documents for URL match with {target_url}");
229+ for (_, document) in self.documents.borrow().iter() {
230+ let window = document.window();
231+ let global = window.upcast::<crate::dom::globalscope::GlobalScope>();
232+ let doc_url = global.get_url();
233+ log::debug!(
234+ "Document url={} vs target={} match={}",
235+ doc_url.as_str(),
236+ target_url,
237+ doc_url.as_str() == target_url
238+ );
239+ if doc_url.as_str() != target_url {
240+ continue;
241+ }
242+ let _ac = enter_realm(window);
243+
244+ log::debug!("URL matched! Creating local port and firing peerstream event");
245+
246+ // Create a new local port and set its entanglement to the remote port.
247+ let local_port = MessagePort::new(global, can_gc);
248+ global.track_message_port(&local_port, None);
249+ global.set_port_entanglement(*local_port.message_port_id(), remote_port_id);
250+ global.start_message_port(local_port.message_port_id(), can_gc);
251+
252+ // Tell the constellation to set bidirectional entanglement.
253+ let _ = global.script_to_constellation_chan().send(
254+ ScriptToConstellationMessage::EntanglePorts(
255+ *local_port.message_port_id(),
256+ remote_port_id,
257+ ),
258+ );
259+
260+ // Fire the cancelable "peerstream" event on Window.
261+ let event = PeerStreamEvent::new(
262+ global,
263+ Atom::from("peerstream"),
264+ false,
265+ true, // cancelable
266+ &local_port,
267+ peer_id.clone(),
268+ can_gc,
269+ );
270+ event
271+ .upcast::<Event>()
272+ .fire(window.upcast::<EventTarget>(), can_gc);
273+
274+ log::debug!(
275+ "peerstream event fired, defaultPrevented={}",
276+ event.upcast::<Event>().DefaultPrevented()
277+ );
278+
279+ if !event.upcast::<Event>().DefaultPrevented() {
280+ accepted = true;
281+ } else {
282+ // Clean up the port — the offer was denied.
283+ local_port.Close(can_gc);
284+ }
285+ responding_global = Some(global.script_to_constellation_chan().clone());
286+ break;
287+ }
288+
289+ // Report back to the constellation.
290+ if let Some(chan) = responding_global {
291+ log::debug!("Sending PeerStreamResponse: accepted={accepted}");
292+ let _ = chan.send(ScriptToConstellationMessage::PeerStreamResponse(
293+ stream_id, from_peer, accepted,
294+ ));
295+ } else {
296+ log::warn!("No document matched target_url {target_url} — peerstream event not fired");
297+ }
298+ }
299+
300 fn ask_constellation_for_top_level_info(
301 &self,
302 sender_webview_id: WebViewId,
303@@ -3468,7 +3670,13 @@
304 self.senders.pipeline_to_embedder_sender.clone(),
305 self.senders.constellation_sender.clone(),
306 incomplete.pipeline_id,
307- incomplete.parent_info,
308+ // For embedded webviews, don't pass parent_info to Window so that
309+ // is_top_level() returns true (they are top-level browsing contexts).
310+ if incomplete.is_embedded_webview {
311+ None
312+ } else {
313+ incomplete.parent_info
314+ },
315 incomplete.viewport_details,
316 origin.clone(),
317 final_url.clone(),
318@@ -3490,6 +3698,8 @@
319 #[cfg(feature = "webgpu")]
320 self.gpu_id_hub.clone(),
321 incomplete.load_data.inherited_secure_context,
322+ incomplete.is_embedded_webview,
323+ incomplete.hide_focus,
324 incomplete.theme,
325 self.this.clone(),
326 );
327@@ -3513,6 +3723,7 @@
328 incomplete.webview_id,
329 incomplete.parent_info,
330 incomplete.opener,
331+ incomplete.is_embedded_webview,
332 );
333 if window_proxy.parent().is_some() {
334 // https://html.spec.whatwg.org/multipage/#navigating-across-documents:delaying-load-events-mode-2
335@@ -4286,6 +4497,24 @@
336 document.event_handler().handle_refresh_cursor();
337 }
338
339+ /// Dispatch preferencechanged events to all Embedder instances in this script thread.
340+ fn dispatch_preference_changed_to_embedders(
341+ &self,
342+ changes: &[(String, PrefValue)],
343+ can_gc: CanGc,
344+ ) {
345+ // Dispatch preferencechanged event to windows that already have an Embedder created
346+ for (_, document) in self.documents.borrow().iter() {
347+ if let Some(embedder) = document.window().Navigator().get_embedder() {
348+ // Enter the realm of the embedder's global before dispatching JS events
349+ let _ac = enter_realm(&*embedder);
350+ for (name, value) in changes {
351+ embedder.dispatch_preference_changed(name, value, can_gc);
352+ }
353+ }
354+ }
355+ }
356+
357 pub(crate) fn is_servo_privileged(url: ServoUrl) -> bool {
358 with_script_thread(|script_thread| script_thread.privileged_urls.contains(&url))
359 }