···146146 }
147147 }
148148149149-@@ -571,6 +643,198 @@
149149+@@ -571,6 +643,214 @@
150150 }
151151 }
152152···158158+ hit_test_result: &HitTestResult,
159159+ input_event: &ConstellationInputEvent,
160160+ ) -> bool {
161161-+ // Walk up from the hit node to find if any ancestor is an embedded iframe
162162-+ // We use ShadowIncluding::Yes because embedded iframes may be inside shadow DOM
161161++ // Walk up from the hit node to find if any ancestor is an iframe
162162++ // We use ShadowIncluding::Yes because iframes may be inside shadow DOM
163163+ // (e.g., inside a custom element like <web-view>)
164164-+ let Some(embedded_iframe) = hit_test_result
164164++ let Some(iframe) = hit_test_result
165165+ .node
166166+ .inclusive_ancestors(ShadowIncluding::Yes)
167167-+ .find_map(|ancestor| {
168168-+ let iframe = DomRoot::downcast::<HTMLIFrameElement>(ancestor)?;
169169-+ if iframe.is_embedded_webview() {
170170-+ Some(iframe)
171171-+ } else {
172172-+ None
173173-+ }
174174-+ })
167167++ .find_map(|ancestor| DomRoot::downcast::<HTMLIFrameElement>(ancestor))
175168+ else {
176169+ return false;
177170+ };
178171+
179179-+ // Get the embedded webview ID
180180-+ let Some(embedded_webview_id) = embedded_iframe.embedded_webview_id() else {
181181-+ return false;
172172++ // Determine how to forward: embedded webview or regular cross-origin iframe
173173++ let is_embedded = iframe.is_embedded_webview();
174174++ let embedded_webview_id = if is_embedded {
175175++ iframe.embedded_webview_id()
176176++ } else {
177177++ None
182178+ };
179179++ let iframe_pipeline_id = iframe.pipeline_id();
180180++
181181++ // Must have either an embedded webview ID or a pipeline ID to forward to
182182++ if embedded_webview_id.is_none() && iframe_pipeline_id.is_none() {
183183++ return false;
184184++ }
183185+
184186+ // Get the iframe's border box to transform coordinates from parent to embedded viewport.
185185-+ // The border box origin is in document coordinates (relative to initial containing block).
186186-+ let Some(iframe_border_box) = embedded_iframe.upcast::<Node>().border_box() else {
187187++ let Some(iframe_border_box) = iframe.upcast::<Node>().border_box() else {
187188+ return false;
188189+ };
189190+
···238239+ other => other,
239240+ };
240241+
241241-+ // Create the event with ID to forward to the embedded webview
242242++ // Create the event with ID to forward
242243+ let event_with_id = InputEventAndId::from(transformed_event);
243244+
244244-+ // Forward the event to the embedded webview via the Constellation
245245-+ self.window.send_to_constellation(
246246-+ ScriptToConstellationMessage::ForwardEventToEmbeddedWebView(
247247-+ embedded_webview_id,
248248-+ event_with_id,
249249-+ ),
250250-+ );
245245++ // Forward the event: embedded webview via constellation, regular iframe via pipeline
246246++ if let Some(webview_id) = embedded_webview_id {
247247++ self.window.send_to_constellation(
248248++ ScriptToConstellationMessage::ForwardEventToEmbeddedWebView(
249249++ webview_id,
250250++ event_with_id,
251251++ ),
252252++ );
253253++ } else if let Some(pipeline_id) = iframe_pipeline_id {
254254++ let webview_id = self.window.webview_id();
255255++ self.window.send_to_constellation(
256256++ ScriptToConstellationMessage::ForwardInputEventToIframe(
257257++ webview_id,
258258++ pipeline_id,
259259++ event_with_id,
260260++ ),
261261++ );
262262++ }
251263+
252264+ // Track forwarded touches so subsequent events for the same touch go to the same webview.
253253-+ // This is important because the hit test might return a different result for touchmove/touchend.
254265+ if let InputEvent::Touch(touch) = &input_event.event.event {
255266+ if touch.event_type == TouchEventType::Down {
256256-+ self.forwarded_touches
257257-+ .borrow_mut()
258258-+ .push((touch.touch_id, embedded_webview_id));
267267++ if let Some(webview_id) = embedded_webview_id {
268268++ self.forwarded_touches
269269++ .borrow_mut()
270270++ .push((touch.touch_id, webview_id));
271271++ }
259272+ }
260273+ }
261274+
262262-+ // Notify the parent iframe element that input was received by the embedded webview,
263263-+ // but only for "activation" events (mousedown/touchstart), not for moves or other events.
264264-+ // This allows the parent document to track which embedded webview is "active".
265265-+ let is_activation_event = match &input_event.event.event {
266266-+ InputEvent::MouseButton(mouse_button) => mouse_button.action == MouseButtonAction::Down,
267267-+ InputEvent::Touch(touch) => touch.event_type == TouchEventType::Down,
268268-+ _ => false,
269269-+ };
270270-+ if is_activation_event {
271271-+ embedded_iframe.dispatch_embedded_webview_event(
272272-+ EmbeddedWebViewEventType::InputReceived,
273273-+ CanGc::deprecated_note(),
274274-+ );
275275++ // Notify the parent iframe element that input was received,
276276++ // but only for embedded webviews and activation events.
277277++ if is_embedded {
278278++ let is_activation_event = match &input_event.event.event {
279279++ InputEvent::MouseButton(mouse_button) => {
280280++ mouse_button.action == MouseButtonAction::Down
281281++ },
282282++ InputEvent::Touch(touch) => touch.event_type == TouchEventType::Down,
283283++ _ => false,
284284++ };
285285++ if is_activation_event {
286286++ iframe.dispatch_embedded_webview_event(
287287++ EmbeddedWebViewEventType::InputReceived,
288288++ CanGc::deprecated_note(),
289289++ );
290290++ }
275291+ }
276292+
277293+ true
···345361 /// <https://w3c.github.io/uievents/#handle-native-mouse-move>
346362 fn handle_native_mouse_move_event(
347363 &self,
348348-@@ -589,6 +853,57 @@
364364+@@ -589,6 +869,57 @@
349365 return;
350366 }
351367···403419 // Update the cursor when the mouse moves, if it has changed.
404420 self.set_cursor(Some(hit_test_result.cursor));
405421406406-@@ -824,6 +1139,12 @@
422422+@@ -824,6 +1155,12 @@
407423 return;
408424 };
409425···416432 debug!(
417433 "{:?}: at {:?}",
418434 event.action, hit_test_result.point_in_frame
419419-@@ -941,7 +1262,12 @@
435435+@@ -941,7 +1278,12 @@
420436 // Step 9. If mbutton is the secondary mouse button, then
421437 // Maybe show context menu with native, target.
422438 if let MouseButton::Right = event.button {
···430446 }
431447 },
432448 // https://w3c.github.io/pointerevents/#dfn-handle-native-mouse-up
433433-@@ -1075,8 +1401,29 @@
449449+@@ -1075,8 +1417,29 @@
434450 cx: &mut js::context::JSContext,
435451 target: &EventTarget,
436452 hit_test_result: &HitTestResult,
···461477 // <https://w3c.github.io/pointerevents/#contextmenu>
462478 let menu_event = PointerEvent::new(
463479 &self.window, // window
464464-@@ -1090,25 +1437,25 @@
480480+@@ -1090,25 +1453,25 @@
465481 hit_test_result
466482 .point_relative_to_initial_containing_block
467483 .to_i32(),
···506522 CanGc::from_cx(cx),
507523 );
508524 menu_event.upcast::<Event>().set_composed(true);
509509-@@ -1127,6 +1474,89 @@
525525+@@ -1127,6 +1490,89 @@
510526 };
511527 }
512528···596612 fn handle_touch_event(
597613 &self,
598614 cx: &mut JSContext,
599599-@@ -1133,6 +1563,29 @@
615615+@@ -1133,6 +1579,29 @@
600616 event: EmbedderTouchEvent,
601617 input_event: &ConstellationInputEvent,
602618 ) -> InputEventResult {
···626642 // Ignore all incoming events without a hit test.
627643 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
628644 self.update_active_touch_points_when_early_return(event);
629629-@@ -1139,6 +1592,16 @@
645645+@@ -1139,6 +1608,16 @@
630646 return Default::default();
631647 };
632648···643659 let TouchId(identifier) = event.touch_id;
644660645661 let Some(element) = hit_test_result
646646-@@ -1274,6 +1737,10 @@
662662+@@ -1274,6 +1753,10 @@
647663 // <https://html.spec.whatwg.org/multipage/#selector-active>
648664 // If the element is being actively pointed at the element is being activated.
649665 self.element_for_activation(element).set_active_state(true);
···654670 (current_target, pointer_touch)
655671 },
656672 _ => {
657657-@@ -1306,10 +1773,31 @@
673673+@@ -1306,10 +1789,31 @@
658674 CanGc::from_cx(cx),
659675 );
660676···687703 },
688704 TouchEventType::Up | TouchEventType::Cancel => {
689705 active_touch_points.swap_remove(index);
690690-@@ -1317,6 +1805,17 @@
706706+@@ -1317,6 +1821,17 @@
691707 // <https://html.spec.whatwg.org/multipage/#selector-active>
692708 // If the element is being actively pointed at the element is being activated.
693709 self.element_for_activation(element).set_active_state(false);
···705721 },
706722 TouchEventType::Down => unreachable!("Should have been handled above"),
707723 }
708708-@@ -1364,6 +1863,19 @@
724724+@@ -1364,6 +1879,19 @@
709725 );
710726 let event = touch_event.upcast::<Event>();
711727 event.fire(&touch_dispatch_target, CanGc::from_cx(cx));
···725741 event.flags().into()
726742 }
727743728728-@@ -1498,6 +2010,16 @@
744744+@@ -1498,6 +2026,16 @@
729745 return Default::default();
730746 };
731747···742758 let Some(el) = hit_test_result
743759 .node
744760 .inclusive_ancestors(ShadowIncluding::Yes)
745745-@@ -1959,6 +2481,28 @@
761761+@@ -1959,6 +2497,28 @@
746762 return;
747763 }
748764
···107107+ RevokeOrigin(String),
108108+ /// Get the list of authorized origins.
109109+ GetAuthorizedOrigins,
110110-+}
111111-+
110110+ }
111111+112112+/// Data returned by com.atproto.server.createSession xrpc calls.
113113+#[derive(Clone, Debug, Deserialize, Serialize)]
114114+#[serde(rename_all = "camelCase")]
···152152+ pub email_auth_factor: bool,
153153+ pub active: bool,
154154+ pub status: Option<String>,
155155- }
156156-155155++}
156156++
157157+/// Data returned by com.atproto.server.refreshSession xrpc calls.
158158+#[derive(Clone, Debug, Deserialize, Serialize)]
159159+#[serde(rename_all = "camelCase")]
···214214 /// Mark a new document as active
215215 ActivateDocument,
216216 /// Set the document state for a pipeline (used by screenshot / reftests)
217217-@@ -758,6 +892,175 @@
217217+@@ -758,6 +892,177 @@
218218 /// aggregate lock count and notify the provider only when the count transitions from N to 0.
219219 /// <https://w3c.github.io/screen-wake-lock/#dfn-release-wake-lock>
220220 ReleaseWakeLock(WakeLockType),
···248248+ /// Forward an input event to an embedded webview after the parent's DOM hit testing
249249+ /// determined that the event target is an embedded iframe element.
250250+ ForwardEventToEmbeddedWebView(WebViewId, InputEventAndId),
251251++ /// Forward an input event to a regular cross-origin iframe's pipeline.
252252++ ForwardInputEventToIframe(WebViewId, PipelineId, InputEventAndId),
251253+ /// Inject an input event to the webview that currently has an active IME input.
252254+ /// Used by the virtual keyboard to send keystrokes to the focused input field.
253255+ InjectInputToActiveIme(InputEventAndId),