···271271 }
272272 }
273273274274-@@ -657,7 +843,158 @@
274274+@@ -657,6 +843,157 @@
275275 self.webview_id.get()
276276 }
277277···376376+
377377+ /// Returns true if this iframe is hosting an embedded webview (created with "embed" attribute).
378378+ /// Embedded webviews have their own top-level WebViewId and window.parent === window.self.
379379- #[inline]
379379++ #[inline]
380380+ pub(crate) fn is_embedded_webview(&self) -> bool {
381381+ self.is_embedded_webview.get()
382382+ }
···426426+ self.page_zoom.set(zoom);
427427+ }
428428+
429429-+ #[inline]
429429+ #[inline]
430430 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
431431 self.sandboxing_flag_set
432432- .get()
433433-@@ -1018,6 +1355,85 @@
432432+@@ -1018,6 +1355,89 @@
434433435434 // https://html.spec.whatwg.org/multipage/#attr-iframe-loading
436435 make_setter!(SetLoading, "loading");
···513512+ fn RespondToPermissionPrompt(&self, control_id: DOMString, allowed: bool) -> Fallible<()> {
514513+ self.embedded_respond_to_permission_prompt(control_id, allowed)
515514+ }
515515++
516516++ fn MediaSessionAction(&self, action: DOMString) -> Fallible<()> {
517517++ self.embedded_media_session_action(action)
518518++ }
516519 }
517520518521 impl VirtualMethods for HTMLIFrameElement {
519519-@@ -1074,10 +1490,38 @@
522522+@@ -1074,8 +1494,36 @@
520523 // is in a document tree and has a browsing context, which is what causes
521524 // the child browsing context to be created.
522525 if self.upcast::<Node>().is_connected_with_browsing_context() {
···542545+ debug!("iframe src set while in browsing context.");
543546+ self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
544547+ }
545545- }
546546- },
548548++ }
549549++ },
547550+ local_name!("embed") => {
548551+ // The embed attribute determines whether this iframe hosts an embedded webview.
549552+ // Warn if it's changed after the iframe is already connected, as this is not supported.
···552555+ "The 'embed' attribute on iframe should not be changed after insertion. \
553556+ The iframe mode (nested vs embedded webview) is determined at insertion time."
554557+ );
555555-+ }
556556-+ },
558558+ }
559559+ },
557560 local_name!("loading") => {
558558- // https://html.spec.whatwg.org/multipage/#attr-iframe-loading
559559- // > When the loading attribute's state is changed to the Eager state, the user agent must run these steps:
560560-@@ -1140,6 +1584,23 @@
561561+@@ -1140,6 +1588,23 @@
561562562563 debug!("<iframe> running post connection steps");
563564···581582 // Step 1. Create a new child navigable for insertedNode.
582583 self.create_nested_browsing_context(cx);
583584584584-@@ -1163,11 +1624,25 @@
585585+@@ -1163,11 +1628,25 @@
585586 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
586587 self.super_type().unwrap().unbind_from_tree(context, can_gc);
587588···594595+ let global = window.as_global_scope();
595596+ let msg = ScriptToConstellationMessage::RemoveEmbeddedWebView(embedded_webview_id);
596597+ global.script_to_constellation_chan().send(msg).unwrap();
597597-+
598598+599599+- // The iframe HTML element removing steps, given removedNode, are to destroy a child navigable given removedNode
600600+- self.destroy_child_navigable(&mut cx);
598601+ window
599602+ .paint_api()
600603+ .remove_embedded_webview(embedded_webview_id);
···602605+ } else {
603606+ // TODO: https://github.com/servo/servo/issues/42837
604607+ let mut cx = unsafe { temp_cx() };
605605-606606-- // The iframe HTML element removing steps, given removedNode, are to destroy a child navigable given removedNode
607607-- self.destroy_child_navigable(&mut cx);
608608++
608609+ // The iframe HTML element removing steps, given removedNode, are to destroy a child navigable given removedNode
609610+ self.destroy_child_navigable(&mut cx);
610611+ }
···11--- original
22+++ modified
33-@@ -0,0 +1,156 @@
33+@@ -0,0 +1,173 @@
44+/* This Source Code Form is subject to the terms of the Mozilla Public
55+ * License, v. 2.0. If a copy of the MPL was not distributed with this
66+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
···4343+
4444+ // Permission prompt response
4545+ [Throws] undefined respondToPermissionPrompt(DOMString controlId, boolean allowed);
4646++
4747++ // Media session control
4848++ [Throws] undefined mediaSessionAction(DOMString action);
4649+};
4750+
4851+
···157160+ DOMString tag = ""; // Tag for deduplication/replacement
158161+ DOMString? iconUrl = null; // Icon URL
159162+};
163163++
164164++dictionary EmbedderMediaSessionEventDetail {
165165++ required DOMString eventType; // "metadata", "playbackstate", "positionstate"
166166++ // Metadata fields (when eventType == "metadata")
167167++ DOMString? title = null;
168168++ DOMString? artist = null;
169169++ DOMString? album = null;
170170++ // Playback state (when eventType == "playbackstate")
171171++ DOMString? playbackState = null; // "none", "playing", "paused"
172172++ // Position state (when eventType == "positionstate")
173173++ double duration = 0;
174174++ double playbackRate = 0;
175175++ double position = 0;
176176++};
···119119 /// Mark a new document as active
120120 ActivateDocument,
121121 /// Set the document state for a pipeline (used by screenshot / reftests)
122122-@@ -726,6 +775,75 @@
122122+@@ -726,6 +775,77 @@
123123 RespondToScreenshotReadinessRequest(ScreenshotReadinessResponse),
124124 /// Request the constellation to force garbage collection in all `ScriptThread`'s.
125125 TriggerGarbageCollection,
···144144+ >,
145145+ >,
146146+ ),
147147++ /// Send a media session action to the active media session in an embedded webview.
148148++ EmbeddedWebViewMediaSessionAction(embedder_traits::MediaSessionActionType),
147149+ /// Forward an input event to an embedded webview after the parent's DOM hit testing
148150+ /// determined that the event target is an embedded iframe element.
149151+ ForwardEventToEmbeddedWebView(WebViewId, InputEventAndId),