Rewild Your Web
web
browser
dweb
1--- original
2+++ modified
3@@ -5,12 +5,15 @@
4 use std::cell::Cell;
5
6 use base::Epoch;
7-use base::generic_channel::GenericSend;
8-use constellation_traits::{LoadData, NavigationHistoryBehavior};
9+use base::generic_channel::{GenericSend, GenericSender};
10+use constellation_traits::{
11+ EmbeddedWebViewEventType, LoadData, NavigationHistoryBehavior, ScriptToConstellationMessage,
12+};
13 use embedder_traits::{
14- ContextMenuAction, ContextMenuElementInformation, ContextMenuElementInformationFlags,
15- ContextMenuItem, ContextMenuRequest, EditingActionEvent, EmbedderControlId,
16- EmbedderControlRequest, EmbedderControlResponse, EmbedderMsg,
17+ AllowOrDeny, ContextMenuAction, ContextMenuElementInformation,
18+ ContextMenuElementInformationFlags, ContextMenuItem, ContextMenuRequest, EditingActionEvent,
19+ EmbedderControlId, EmbedderControlRequest, EmbedderControlResponse, EmbedderMsg,
20+ PermissionFeature, PermissionPromptRequest, SimpleDialogRequest,
21 };
22 use euclid::{Point2D, Rect, Size2D};
23 use ipc_channel::router::ROUTER;
24@@ -35,8 +38,8 @@
25 use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
26 use crate::dom::textcontrol::TextControlElement;
27 use crate::dom::types::{
28- Element, HTMLAnchorElement, HTMLElement, HTMLImageElement, HTMLInputElement, HTMLSelectElement,
29- HTMLTextAreaElement, Window,
30+ Document, Element, HTMLAnchorElement, HTMLElement, HTMLImageElement, HTMLInputElement,
31+ HTMLSelectElement, HTMLTextAreaElement, Window,
32 };
33 use crate::messaging::MainThreadScriptMsg;
34
35@@ -47,6 +50,7 @@
36 FileInput(DomRoot<HTMLInputElement>),
37 Ime(DomRoot<HTMLElement>),
38 ContextMenu(ContextMenuNodes),
39+ PermissionPrompt(DomRoot<Document>),
40 }
41
42 impl ControlElement {
43@@ -57,6 +61,7 @@
44 ControlElement::FileInput(element) => element.upcast::<Node>(),
45 ControlElement::Ime(element) => element.upcast::<Node>(),
46 ControlElement::ContextMenu(context_menu_nodes) => &context_menu_nodes.node,
47+ ControlElement::PermissionPrompt(document) => document.upcast::<Node>(),
48 }
49 }
50 }
51@@ -129,39 +134,77 @@
52 EmbedderControlRequest::SelectElement(..) |
53 EmbedderControlRequest::ColorPicker(..) |
54 EmbedderControlRequest::InputMethod(..) |
55- EmbedderControlRequest::ContextMenu(..) => self
56- .window
57- .send_to_embedder(EmbedderMsg::ShowEmbedderControl(id, rect, request)),
58+ EmbedderControlRequest::ContextMenu(..) |
59+ EmbedderControlRequest::PermissionPrompt(..) => {
60+ // If this is an embedded webview, route the control request to the parent
61+ // iframe element via the Constellation. Otherwise, send directly to the embedder.
62+ if self.window.as_global_scope().is_embedded_webview() {
63+ self.window.send_to_constellation(
64+ ScriptToConstellationMessage::EmbeddedWebViewNotification(
65+ EmbeddedWebViewEventType::EmbedderControlShow { id, rect, request },
66+ ),
67+ );
68+ } else {
69+ // Check if this is an InputMethod request before consuming it
70+ let is_input_method =
71+ matches!(request, EmbedderControlRequest::InputMethod(..));
72+ self.window
73+ .send_to_embedder(EmbedderMsg::ShowEmbedderControl(id, rect, request));
74+ // Also notify constellation for IME tracking if this is an InputMethod control
75+ if is_input_method {
76+ self.window.send_to_constellation(
77+ ScriptToConstellationMessage::SetActiveImeWebView(
78+ self.window.webview_id(),
79+ ),
80+ );
81+ }
82+ }
83+ },
84 EmbedderControlRequest::FilePicker(file_picker_request) => {
85- let (sender, receiver) = profile_traits::ipc::channel(
86- self.window.as_global_scope().time_profiler_chan().clone(),
87- )
88- .expect("Error initializing channel");
89- let main_thread_sender = self.window.main_thread_script_chan().clone();
90- ROUTER.add_typed_route(
91- receiver.to_ipc_receiver(),
92- Box::new(move |result| {
93- let Ok(embedder_control_response) = result else {
94- return;
95- };
96- if let Err(error) = main_thread_sender.send(
97- MainThreadScriptMsg::ForwardEmbedderControlResponseFromFileManager(
98+ // If this is an embedded webview, route the control request to the parent
99+ // iframe element via the Constellation.
100+ if self.window.as_global_scope().is_embedded_webview() {
101+ self.window.send_to_constellation(
102+ ScriptToConstellationMessage::EmbeddedWebViewNotification(
103+ EmbeddedWebViewEventType::EmbedderControlShow {
104 id,
105- embedder_control_response,
106- ),
107- ) {
108- warn!("Could not send FileManager response to main thread: {error}")
109- }
110- }),
111- );
112- self.window
113- .as_global_scope()
114- .resource_threads()
115- .sender()
116- .send(CoreResourceMsg::ToFileManager(
117- FileManagerThreadMsg::SelectFiles(id, file_picker_request, sender),
118- ))
119- .unwrap();
120+ rect,
121+ request: EmbedderControlRequest::FilePicker(file_picker_request),
122+ },
123+ ),
124+ );
125+ } else {
126+ // For non-embedded webviews, use the FileManager directly
127+ let (sender, receiver) = profile_traits::ipc::channel(
128+ self.window.as_global_scope().time_profiler_chan().clone(),
129+ )
130+ .expect("Error initializing channel");
131+ let main_thread_sender = self.window.main_thread_script_chan().clone();
132+ ROUTER.add_typed_route(
133+ receiver.to_ipc_receiver(),
134+ Box::new(move |result| {
135+ let Ok(embedder_control_response) = result else {
136+ return;
137+ };
138+ if let Err(error) = main_thread_sender.send(
139+ MainThreadScriptMsg::ForwardEmbedderControlResponseFromFileManager(
140+ id,
141+ embedder_control_response,
142+ ),
143+ ) {
144+ warn!("Could not send FileManager response to main thread: {error}")
145+ }
146+ }),
147+ );
148+ self.window
149+ .as_global_scope()
150+ .resource_threads()
151+ .sender()
152+ .send(CoreResourceMsg::ToFileManager(
153+ FileManagerThreadMsg::SelectFiles(id, file_picker_request, sender),
154+ ))
155+ .unwrap();
156+ }
157 },
158 }
159
160@@ -180,12 +223,67 @@
161 pipeline_id: self.window.pipeline_id(),
162 index: index.0,
163 };
164- self.window
165- .send_to_embedder(EmbedderMsg::HideEmbedderControl(id));
166+ // If this is an embedded webview, route the hide request to the parent
167+ // iframe element via the Constellation. Otherwise, send directly to the embedder.
168+ if self.window.as_global_scope().is_embedded_webview() {
169+ self.window.send_to_constellation(
170+ ScriptToConstellationMessage::EmbeddedWebViewNotification(
171+ EmbeddedWebViewEventType::EmbedderControlHide { id },
172+ ),
173+ );
174+ } else {
175+ self.window
176+ .send_to_embedder(EmbedderMsg::HideEmbedderControl(id));
177+ // Also notify constellation to clear IME tracking if this is an Ime control
178+ if matches!(control_element, ControlElement::Ime(_)) {
179+ self.window.send_to_constellation(
180+ ScriptToConstellationMessage::ClearActiveImeWebView(
181+ self.window.webview_id(),
182+ ),
183+ );
184+ }
185+ }
186 false
187 });
188 }
189
190+ /// Hide all visible embedder controls. This is called when the document becomes inactive
191+ /// (e.g., when navigating to a new page) to ensure controls like the virtual keyboard
192+ /// are hidden.
193+ pub(crate) fn hide_all_controls(&self) {
194+ let has_ime_control = self
195+ .visible_elements
196+ .borrow()
197+ .values()
198+ .any(|c| matches!(c, ControlElement::Ime(_)));
199+ let indices: Vec<_> = self.visible_elements.borrow().keys().cloned().collect();
200+ for index in indices {
201+ let id = EmbedderControlId {
202+ webview_id: self.window.webview_id(),
203+ pipeline_id: self.window.pipeline_id(),
204+ index: index.0,
205+ };
206+ if self.window.as_global_scope().is_embedded_webview() {
207+ self.window.send_to_constellation(
208+ ScriptToConstellationMessage::EmbeddedWebViewNotification(
209+ EmbeddedWebViewEventType::EmbedderControlHide { id },
210+ ),
211+ );
212+ } else {
213+ self.window
214+ .send_to_embedder(EmbedderMsg::HideEmbedderControl(id));
215+ }
216+ }
217+ // Also notify constellation to clear IME tracking if we had an Ime control
218+ if has_ime_control && !self.window.as_global_scope().is_embedded_webview() {
219+ self.window
220+ .send_to_constellation(ScriptToConstellationMessage::ClearActiveImeWebView(
221+ self.window.webview_id(),
222+ ));
223+ }
224+ self.visible_elements.borrow_mut().clear();
225+ }
226+
227 pub(crate) fn handle_embedder_control_response(
228 &self,
229 id: EmbedderControlId,
230@@ -229,6 +327,10 @@
231 ) => {
232 context_menu_nodes.handle_context_menu_action(action, can_gc);
233 },
234+ (ControlElement::PermissionPrompt(_), EmbedderControlResponse::PermissionPrompt(_)) => {
235+ // Permission prompt responses go directly through the IPC sender
236+ // included in the request, so this path is not used.
237+ },
238 (_, _) => unreachable!(
239 "The response to a form control should always match it's originating type."
240 ),
241@@ -235,6 +337,36 @@
242 }
243 }
244
245+ /// Show a simple dialog (alert, confirm, prompt) for an embedded webview.
246+ /// The request includes an IPC sender that the parent iframe will use to respond directly.
247+ pub(crate) fn show_simple_dialog_for_embedded_webview(&self, request: SimpleDialogRequest) {
248+ // Send the request (with IPC sender) to parent via constellation.
249+ // The parent HTMLIFrameElement will store the sender and use it to respond.
250+ self.window.send_to_constellation(
251+ ScriptToConstellationMessage::EmbeddedWebViewNotification(
252+ EmbeddedWebViewEventType::SimpleDialogShow(request),
253+ ),
254+ );
255+ }
256+
257+ /// Show a permission prompt for an embedded webview.
258+ /// The request includes an IPC sender that the parent iframe will use to respond directly.
259+ pub(crate) fn show_permission_prompt(
260+ &self,
261+ feature: PermissionFeature,
262+ response_sender: GenericSender<AllowOrDeny>,
263+ ) -> EmbedderControlId {
264+ let request = EmbedderControlRequest::PermissionPrompt(PermissionPromptRequest {
265+ feature,
266+ response_sender,
267+ });
268+ self.show_embedder_control(
269+ ControlElement::PermissionPrompt(self.window.Document()),
270+ request,
271+ None,
272+ )
273+ }
274+
275 pub(crate) fn show_context_menu(&self, hit_test_result: &HitTestResult) {
276 let mut anchor_element = None;
277 let mut image_element = None;
278@@ -410,8 +542,11 @@
279 let Some(browsing_context) = document.browsing_context() else {
280 return;
281 };
282- let (browsing_context, new) = browsing_context
283- .choose_browsing_context("_blank".into(), true /* nooopener */);
284+ let (browsing_context, new) = browsing_context.choose_browsing_context(
285+ "_blank".into(),
286+ true, /* nooopener */
287+ Some(url.clone()),
288+ );
289 let Some(browsing_context) = browsing_context else {
290 return;
291 };