forked from
me.webbeef.org/browser.html
Rewild Your Web
1--- original
2+++ modified
3@@ -7,8 +7,8 @@
4
5 use base::id::{BrowsingContextId, PipelineId, WebViewId};
6 use constellation_traits::{
7- IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData, LoadOrigin,
8- NavigationHistoryBehavior, ScriptToConstellationMessage,
9+ EmbeddedWebViewCreationRequest, IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData,
10+ LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationMessage,
11 };
12 use content_security_policy::sandboxing_directive::{
13 SandboxingFlagSet, parse_a_sandboxing_directive,
14@@ -16,6 +16,7 @@
15 use dom_struct::dom_struct;
16 use embedder_traits::ViewportDetails;
17 use html5ever::{LocalName, Prefix, local_name, ns};
18+use ipc_channel::ipc;
19 use js::context::JSContext;
20 use js::rust::HandleObject;
21 use net_traits::ReferrerPolicy;
22@@ -24,30 +25,35 @@
23 use script_bindings::script_runtime::temp_cx;
24 use script_traits::{NewPipelineInfo, UpdatePipelineIdReason};
25 use servo_url::ServoUrl;
26+use style::Atom;
27 use style::attr::{AttrValue, LengthOrPercentageOrAuto};
28-use stylo_atoms::Atom;
29
30 use crate::document_loader::{LoadBlocker, LoadType};
31 use crate::dom::attr::Attr;
32 use crate::dom::bindings::cell::DomRefCell;
33+use crate::dom::bindings::codegen::Bindings::EmbeddedWebViewBinding::ScreenshotOptions;
34 use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
35 use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
36 use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
37 use crate::dom::bindings::error::Fallible;
38 use crate::dom::bindings::inheritance::Castable;
39+use crate::dom::bindings::num::Finite;
40 use crate::dom::bindings::reflector::DomGlobal;
41 use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
42 use crate::dom::bindings::str::{DOMString, USVString};
43+use crate::dom::console::Console;
44 use crate::dom::document::Document;
45 use crate::dom::domtokenlist::DOMTokenList;
46 use crate::dom::element::{
47 AttributeMutation, Element, LayoutElementHelpers, reflect_referrer_policy_attribute,
48 };
49+use crate::dom::embedder::Embedder;
50 use crate::dom::eventtarget::EventTarget;
51 use crate::dom::globalscope::GlobalScope;
52 use crate::dom::html::htmlelement::HTMLElement;
53 use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
54 use crate::dom::performance::performanceresourcetiming::InitiatorType;
55+use crate::dom::promise::Promise;
56 use crate::dom::trustedhtml::TrustedHTML;
57 use crate::dom::virtualmethods::VirtualMethods;
58 use crate::dom::windowproxy::WindowProxy;
59@@ -76,6 +82,12 @@
60 SrcDoc,
61 }
62
63+// Re-export PendingDialogSender from the embedded webview module
64+pub(crate) use super::htmlembeddedwebview::PendingDialogSender;
65+// Type alias for pending permission senders
66+pub(crate) type PendingPermissionSender =
67+ base::generic_channel::GenericSender<embedder_traits::AllowOrDeny>;
68+
69 #[dom_struct]
70 pub(crate) struct HTMLIFrameElement {
71 htmlelement: HTMLElement,
72@@ -112,6 +124,30 @@
73 /// an empty iframe is attached. In that case, we shouldn't fire a
74 /// subsequent asynchronous load event.
75 already_fired_synchronous_load_event: Cell<bool>,
76+ /// Whether this iframe is in "embed" mode (hosting a top-level webview).
77+ is_embedded_webview: Cell<bool>,
78+ /// The embedded webview ID when in embed mode.
79+ #[no_trace]
80+ embedded_webview_id: Cell<Option<WebViewId>>,
81+ /// History tracking for embedded webviews - the list of URLs in the session history.
82+ #[no_trace]
83+ embedded_history: DomRefCell<Vec<ServoUrl>>,
84+ /// The current index in the embedded webview history.
85+ embedded_history_index: Cell<usize>,
86+ /// Pending dialog sender for embedded webviews.
87+ /// When an embedded webview calls alert/confirm/prompt, the IPC sender is stored here
88+ /// so the parent shell can respond directly via respondToAlert/Confirm/Prompt.
89+ #[ignore_malloc_size_of = "Channels are hard"]
90+ #[no_trace]
91+ pending_dialog: DomRefCell<Option<PendingDialogSender>>,
92+ /// Pending permission sender for embedded webviews.
93+ /// When an embedded webview requests a permission, the IPC sender is stored here
94+ /// so the parent shell can respond directly via respondToPermissionPrompt.
95+ #[ignore_malloc_size_of = "Channels are hard"]
96+ #[no_trace]
97+ pending_permission_sender: DomRefCell<Option<PendingPermissionSender>>,
98+ /// Current page zoom level for embedded webviews (default 1.0 = 100%).
99+ page_zoom: Cell<f64>,
100 }
101
102 impl HTMLIFrameElement {
103@@ -265,6 +301,8 @@
104 viewport_details,
105 user_content_manager_id: None,
106 theme: window.theme(),
107+ is_embedded_webview: false,
108+ hide_focus: false,
109 };
110
111 self.pipeline_id.set(Some(new_pipeline_id));
112@@ -560,6 +598,147 @@
113 );
114 }
115
116+ /// Create an embedded webview for this iframe when the "embed" attribute is present.
117+ /// This creates a new top-level WebView instead of a nested browsing context.
118+ fn create_embedded_webview(&self) {
119+ let Some(url) = self.shared_attribute_processing_steps_for_iframe_and_frame_elements()
120+ else {
121+ error!("Can't create embedded webview without url");
122+ return;
123+ };
124+ let document = self.owner_document();
125+ let window = self.owner_window();
126+
127+ // Check if the parent document's origin is allowed to create embedded webviews
128+ let parent_url = document.url();
129+ if !Embedder::is_allowed_to_embed_for_url(&parent_url) {
130+ let message = format!(
131+ "Embedded webview creation blocked: the 'embed' attribute on iframes is only \
132+ allowed for privileged origins. Current origin '{}' is not allowed.",
133+ parent_url.origin().ascii_serialization()
134+ );
135+ // Log error to web console so developers can see why it failed
136+ Console::internal_warn(window.as_global_scope(), DOMString::from(message.clone()));
137+ warn!("{}", message);
138+
139+ // Fall back to non-functional state (iframe won't navigate)
140+ self.is_embedded_webview.set(false);
141+ return;
142+ }
143+
144+ let pipeline_id = window.pipeline_id();
145+ let parent_webview_id = window.window_proxy().webview_id();
146+
147+ // Create load data for the embedded webview
148+ let mut load_data = LoadData::new(
149+ LoadOrigin::Script(document.origin().snapshot()),
150+ url,
151+ Some(document.base_url()),
152+ Some(pipeline_id),
153+ window.as_global_scope().get_referrer(),
154+ document.get_referrer_policy(),
155+ Some(window.as_global_scope().is_secure_context()),
156+ Some(document.insecure_requests_policy()),
157+ document.has_trustworthy_ancestor_or_current_origin(),
158+ self.sandboxing_flag_set(),
159+ );
160+ load_data.destination = Destination::IFrame;
161+ load_data.policy_container = Some(window.as_global_scope().policy_container());
162+
163+ // Clone load_data for spawning the pipeline later
164+ let load_data_for_spawn = load_data.clone();
165+ let theme = window.theme();
166+
167+ // Get the iframe's size to use as the viewport for the embedded webview.
168+ // We use border_box which gives us the size in CSS pixels.
169+ let hidpi_scale_factor = window.device_pixel_ratio();
170+ let viewport_details = self
171+ .upcast::<Node>()
172+ .border_box()
173+ .map(|border_box| ViewportDetails {
174+ size: euclid::Size2D::new(
175+ border_box.size.width.to_f32_px(),
176+ border_box.size.height.to_f32_px(),
177+ ),
178+ hidpi_scale_factor,
179+ page_zoom_for_rendering: None,
180+ })
181+ .unwrap_or_else(|| ViewportDetails {
182+ hidpi_scale_factor,
183+ ..Default::default()
184+ });
185+
186+ // Create an IPC channel for the response
187+ let (response_sender, response_receiver) =
188+ ipc::channel().expect("Failed to create IPC channel for embedded webview");
189+
190+ let hide_focus = self.has_hide_focus();
191+ let request = EmbeddedWebViewCreationRequest {
192+ load_data,
193+ parent_pipeline_id: pipeline_id,
194+ parent_webview_id,
195+ viewport_details,
196+ theme,
197+ hide_focus,
198+ response_sender,
199+ };
200+
201+ // Send the request to the constellation
202+ let global = window.as_global_scope();
203+ let msg = ScriptToConstellationMessage::CreateEmbeddedWebView(request);
204+ global.script_to_constellation_chan().send(msg).unwrap();
205+
206+ // Block waiting for the response
207+ match response_receiver.recv() {
208+ Ok(Some(response)) => {
209+ debug!(
210+ "Embedded webview created: webview_id={:?}, browsing_context_id={:?}, pipeline_id={:?}",
211+ response.new_webview_id,
212+ response.new_browsing_context_id,
213+ response.new_pipeline_id
214+ );
215+ // Store the embedded webview state
216+ self.is_embedded_webview.set(true);
217+ self.embedded_webview_id.set(Some(response.new_webview_id));
218+ self.browsing_context_id
219+ .set(Some(response.new_browsing_context_id));
220+ self.pipeline_id.set(Some(response.new_pipeline_id));
221+ self.webview_id.set(Some(response.new_webview_id));
222+
223+ // Spawn the pipeline in the script thread
224+ // Embedded webviews are top-level, so parent_info is None
225+ let new_pipeline_info = NewPipelineInfo {
226+ parent_info: None,
227+ new_pipeline_id: response.new_pipeline_id,
228+ browsing_context_id: response.new_browsing_context_id,
229+ webview_id: response.new_webview_id,
230+ opener: None,
231+ load_data: load_data_for_spawn,
232+ viewport_details,
233+ user_content_manager_id: None,
234+ theme,
235+ is_embedded_webview: true,
236+ hide_focus,
237+ };
238+
239+ with_script_thread(|script_thread| {
240+ script_thread.spawn_pipeline(new_pipeline_info);
241+ });
242+ },
243+ Ok(None) => {
244+ warn!("Embedded webview creation was rejected by embedder");
245+ // Fall back to not being an embedded webview (just a static iframe)
246+ self.is_embedded_webview.set(false);
247+ self.embedded_webview_id.set(None);
248+ },
249+ Err(e) => {
250+ warn!("Failed to receive embedded webview response: {:?}", e);
251+ self.is_embedded_webview.set(false);
252+ self.embedded_webview_id.set(None);
253+ },
254+ }
255+ }
256+
257 fn destroy_nested_browsing_context(&self) {
258 self.pipeline_id.set(None);
259 self.pending_pipeline_id.set(None);
260@@ -622,6 +801,13 @@
261 lazy_load_resumption_steps: Default::default(),
262 pending_navigation: Default::default(),
263 already_fired_synchronous_load_event: Default::default(),
264+ is_embedded_webview: Cell::new(false),
265+ embedded_webview_id: Cell::new(None),
266+ embedded_history: DomRefCell::new(Vec::new()),
267+ embedded_history_index: Cell::new(0),
268+ pending_dialog: DomRefCell::new(None),
269+ pending_permission_sender: DomRefCell::new(None),
270+ page_zoom: Cell::new(1.0),
271 }
272 }
273
274@@ -657,7 +843,158 @@
275 self.webview_id.get()
276 }
277
278+ /// Check whether this iframe has the "embed" attribute set.
279+ fn is_embed_mode(&self) -> bool {
280+ self.upcast::<Element>()
281+ .has_attribute(&local_name!("embed"))
282+ }
283+
284+ /// Check whether this iframe has the "hidefocus" attribute set.
285+ /// When set, the embedded webview should never receive focus.
286+ fn has_hide_focus(&self) -> bool {
287+ self.upcast::<Element>()
288+ .has_attribute(&local_name!("hidefocus"))
289+ }
290+
291+ /// Check if this iframe should adopt a pre-created embedded webview.
292+ /// This is set via the `adopt-webview-id`, `adopt-browsing-context-id`,
293+ /// and `adopt-pipeline-id` attributes, which are used when the constellation
294+ /// has already created an embedded webview (e.g., from window.open in an embedded context).
295+ fn get_adopt_ids(&self) -> Option<(WebViewId, BrowsingContextId, PipelineId)> {
296+ let element = self.upcast::<Element>();
297+
298+ // All three attributes must be present
299+ let webview_id_str = element.get_string_attribute(&LocalName::from("adopt-webview-id"));
300+ let browsing_context_id_str =
301+ element.get_string_attribute(&LocalName::from("adopt-browsing-context-id"));
302+ let pipeline_id_str = element.get_string_attribute(&LocalName::from("adopt-pipeline-id"));
303+
304+ if webview_id_str.is_empty() ||
305+ browsing_context_id_str.is_empty() ||
306+ pipeline_id_str.is_empty()
307+ {
308+ return None;
309+ }
310+
311+ // Parse the IDs (they are in the format "TypeName(namespace,index)")
312+ let browsing_context_id = BrowsingContextId::from_string(&browsing_context_id_str.str())?;
313+ let pipeline_id = PipelineId::from_string(&pipeline_id_str.str())?;
314+
315+ // WebViewId is "(painter_id, browsing_context_id)" - we derive it from the browsing_context_id
316+ // The WebViewId is constructed from the same BrowsingContextId
317+ let webview_id = WebViewId::from_string(&webview_id_str.str())?;
318+
319+ Some((webview_id, browsing_context_id, pipeline_id))
320+ }
321+
322+ /// Adopt a pre-created embedded webview. This is called when the iframe has
323+ /// adopt attributes set, indicating that constellation has already created
324+ /// the webview and we just need to associate it with this iframe.
325+ fn adopt_embedded_webview(
326+ &self,
327+ webview_id: WebViewId,
328+ browsing_context_id: BrowsingContextId,
329+ pipeline_id: PipelineId,
330+ cx: &mut JSContext,
331+ ) {
332+ debug!(
333+ "<iframe> adopting embedded webview: webview_id={:?}, browsing_context_id={:?}, pipeline_id={:?}",
334+ webview_id, browsing_context_id, pipeline_id
335+ );
336+
337+ // Store the embedded webview state
338+ self.is_embedded_webview.set(true);
339+ self.embedded_webview_id.set(Some(webview_id));
340+ self.browsing_context_id.set(Some(browsing_context_id));
341+ self.pipeline_id.set(Some(pipeline_id));
342+ self.webview_id.set(Some(webview_id));
343+
344+ // The pipeline is already spawned by the constellation, so we don't need to
345+ // call spawn_pipeline here. The webview is already running.
346+
347+ // Remove the adopt attributes now that we've used them
348+ let element = self.upcast::<Element>();
349+ element.remove_attribute(
350+ &ns!(),
351+ &LocalName::from("adopt-webview-id"),
352+ CanGc::from_cx(cx),
353+ );
354+ element.remove_attribute(
355+ &ns!(),
356+ &LocalName::from("adopt-browsing-context-id"),
357+ CanGc::from_cx(cx),
358+ );
359+ element.remove_attribute(
360+ &ns!(),
361+ &LocalName::from("adopt-pipeline-id"),
362+ CanGc::from_cx(cx),
363+ );
364+ }
365+
366+ /// Get the effective webview ID, taking into account embedded webview mode.
367+ /// Returns the embedded webview ID if in embed mode, otherwise the parent webview ID.
368+ #[inline]
369+ pub(crate) fn embedded_webview_id(&self) -> Option<WebViewId> {
370+ if self.is_embedded_webview.get() {
371+ self.embedded_webview_id.get()
372+ } else {
373+ None
374+ }
375+ }
376+
377+ /// Returns true if this iframe is hosting an embedded webview (created with "embed" attribute).
378+ /// Embedded webviews have their own top-level WebViewId and window.parent === window.self.
379 #[inline]
380+ pub(crate) fn is_embedded_webview(&self) -> bool {
381+ self.is_embedded_webview.get()
382+ }
383+
384+ /// Set the embedded history entries and current index.
385+ /// Called when the constellation sends history change notifications.
386+ pub(crate) fn set_embedded_history(&self, entries: Vec<ServoUrl>, index: usize) {
387+ *self.embedded_history.borrow_mut() = entries;
388+ self.embedded_history_index.set(index);
389+ }
390+
391+ /// Get the embedded history state as (can_go_back, can_go_forward).
392+ pub(crate) fn embedded_history_state(&self) -> (bool, bool) {
393+ let can_go_back = self.embedded_history_index.get() > 0;
394+ let history = self.embedded_history.borrow();
395+ let can_go_forward = history.len() > self.embedded_history_index.get() + 1;
396+ (can_go_back, can_go_forward)
397+ }
398+
399+ /// Set the pending dialog sender for embedded webview dialog responses.
400+ pub(crate) fn set_pending_dialog(&self, sender: Option<PendingDialogSender>) {
401+ *self.pending_dialog.borrow_mut() = sender;
402+ }
403+
404+ /// Take the pending dialog sender, removing it from storage.
405+ pub(crate) fn take_pending_dialog(&self) -> Option<PendingDialogSender> {
406+ self.pending_dialog.borrow_mut().take()
407+ }
408+
409+ /// Set the pending permission sender for embedded webview permission prompt responses.
410+ pub(crate) fn set_pending_permission_sender(&self, sender: Option<PendingPermissionSender>) {
411+ *self.pending_permission_sender.borrow_mut() = sender;
412+ }
413+
414+ /// Take the pending permission sender, removing it from storage.
415+ pub(crate) fn take_pending_permission_sender(&self) -> Option<PendingPermissionSender> {
416+ self.pending_permission_sender.borrow_mut().take()
417+ }
418+
419+ /// Get the current page zoom level.
420+ pub(crate) fn get_page_zoom(&self) -> f64 {
421+ self.page_zoom.get()
422+ }
423+
424+ /// Set the page zoom level.
425+ pub(crate) fn set_page_zoom(&self, zoom: f64) {
426+ self.page_zoom.set(zoom);
427+ }
428+
429+ #[inline]
430 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
431 self.sandboxing_flag_set
432 .get()
433@@ -1014,6 +1351,85 @@
434
435 // https://html.spec.whatwg.org/multipage/#attr-iframe-loading
436 make_setter!(SetLoading, "loading");
437+
438+ // Servo extension: Embedded WebView methods
439+ // These delegate to helper methods in htmlembeddedwebview.rs
440+
441+ fn Load(&self, url: USVString) -> Fallible<()> {
442+ self.embedded_load(url)
443+ }
444+
445+ fn Reload(&self) -> Fallible<()> {
446+ self.embedded_reload()
447+ }
448+
449+ fn GetPageZoom(&self) -> Fallible<Finite<f64>> {
450+ self.embedded_page_zoom().map(Finite::wrap)
451+ }
452+
453+ fn SetPageZoom(&self, zoom: Finite<f64>) -> Fallible<()> {
454+ self.embedded_set_page_zoom(*zoom)
455+ }
456+
457+ fn CanGoBack(&self) -> Fallible<bool> {
458+ self.embedded_can_go_back()
459+ }
460+
461+ fn GoBack(&self) -> Fallible<DOMString> {
462+ self.embedded_go_back()
463+ }
464+
465+ fn CanGoForward(&self) -> Fallible<bool> {
466+ self.embedded_can_go_forward()
467+ }
468+
469+ fn GoForward(&self) -> Fallible<DOMString> {
470+ self.embedded_go_forward()
471+ }
472+
473+ fn TakeScreenshot(&self, options: &ScreenshotOptions) -> Fallible<Rc<Promise>> {
474+ self.embedded_take_screenshot(options)
475+ }
476+
477+ fn RespondToSelectControl(&self, control_id: DOMString, selected_index: i32) -> Fallible<()> {
478+ self.embedded_respond_to_select_control(control_id, selected_index)
479+ }
480+
481+ fn RespondToColorPicker(
482+ &self,
483+ control_id: DOMString,
484+ color: Option<DOMString>,
485+ ) -> Fallible<()> {
486+ self.embedded_respond_to_color_picker(control_id, color)
487+ }
488+
489+ fn RespondToContextMenu(
490+ &self,
491+ control_id: DOMString,
492+ action_id: Option<DOMString>,
493+ ) -> Fallible<()> {
494+ self.embedded_respond_to_context_menu(control_id, action_id)
495+ }
496+
497+ fn CancelEmbedderControl(&self, control_id: DOMString) -> Fallible<()> {
498+ self.embedded_cancel_embedder_control(control_id)
499+ }
500+
501+ fn RespondToAlert(&self, control_id: DOMString) -> Fallible<()> {
502+ self.embedded_respond_to_alert(control_id)
503+ }
504+
505+ fn RespondToConfirm(&self, control_id: DOMString, confirmed: bool) -> Fallible<()> {
506+ self.embedded_respond_to_confirm(control_id, confirmed)
507+ }
508+
509+ fn RespondToPrompt(&self, control_id: DOMString, value: Option<DOMString>) -> Fallible<()> {
510+ self.embedded_respond_to_prompt(control_id, value)
511+ }
512+
513+ fn RespondToPermissionPrompt(&self, control_id: DOMString, allowed: bool) -> Fallible<()> {
514+ self.embedded_respond_to_permission_prompt(control_id, allowed)
515+ }
516 }
517
518 impl VirtualMethods for HTMLIFrameElement {
519@@ -1069,10 +1485,38 @@
520 // is in a document tree and has a browsing context, which is what causes
521 // the child browsing context to be created.
522 if self.upcast::<Node>().is_connected_with_browsing_context() {
523- debug!("iframe src set while in browsing context.");
524- self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
525+ // For embedded webviews, navigate using the load() method instead of
526+ // processing iframe attributes (which is for regular nested iframes).
527+ if self.is_embedded_webview.get() {
528+ if let Some(webview_id) = self.embedded_webview_id.get() {
529+ let url = self
530+ .shared_attribute_processing_steps_for_iframe_and_frame_elements()
531+ .expect("Failed to get embedding iframe url");
532+ let window = self.owner_window();
533+ window
534+ .as_global_scope()
535+ .script_to_constellation_chan()
536+ .send(ScriptToConstellationMessage::EmbeddedWebViewLoad(
537+ webview_id, url,
538+ ))
539+ .unwrap();
540+ }
541+ } else {
542+ debug!("iframe src set while in browsing context.");
543+ self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
544+ }
545 }
546 },
547+ local_name!("embed") => {
548+ // The embed attribute determines whether this iframe hosts an embedded webview.
549+ // Warn if it's changed after the iframe is already connected, as this is not supported.
550+ if self.upcast::<Node>().is_connected_with_browsing_context() {
551+ warn!(
552+ "The 'embed' attribute on iframe should not be changed after insertion. \
553+ The iframe mode (nested vs embedded webview) is determined at insertion time."
554+ );
555+ }
556+ },
557 local_name!("loading") => {
558 // https://html.spec.whatwg.org/multipage/#attr-iframe-loading
559 // > When the loading attribute's state is changed to the Eager state, the user agent must run these steps:
560@@ -1135,6 +1579,23 @@
561
562 debug!("<iframe> running post connection steps");
563
564+ // Check if this iframe should adopt a pre-created embedded webview
565+ // (e.g., from window.open() in an embedded context)
566+ if let Some((webview_id, browsing_context_id, pipeline_id)) = self.get_adopt_ids() {
567+ debug!("<iframe> adopting pre-created embedded webview");
568+ self.adopt_embedded_webview(webview_id, browsing_context_id, pipeline_id, cx);
569+ return;
570+ }
571+
572+ // Check if this iframe has the "embed" attribute for embedded webview mode
573+ if self.is_embed_mode() {
574+ debug!("<iframe> in embed mode, creating embedded webview");
575+ self.create_embedded_webview();
576+ // For embedded webviews, we don't process sandbox or iframe attributes
577+ // the same way - the embedded webview handles its own security context
578+ return;
579+ }
580+
581 // Step 1. Create a new child navigable for insertedNode.
582 self.create_nested_browsing_context(cx);
583
584@@ -1158,11 +1619,25 @@
585 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
586 self.super_type().unwrap().unbind_from_tree(context, can_gc);
587
588- // TODO: https://github.com/servo/servo/issues/42837
589- let mut cx = unsafe { temp_cx() };
590+ // If this is an embedded webview, notify the compositor to stop tracking its rect
591+ if self.is_embedded_webview.get() {
592+ if let Some(embedded_webview_id) = self.embedded_webview_id.get() {
593+ let window = self.owner_window();
594+ let global = window.as_global_scope();
595+ let msg = ScriptToConstellationMessage::RemoveEmbeddedWebView(embedded_webview_id);
596+ global.script_to_constellation_chan().send(msg).unwrap();
597+
598+ window
599+ .paint_api()
600+ .remove_embedded_webview(embedded_webview_id);
601+ }
602+ } else {
603+ // TODO: https://github.com/servo/servo/issues/42837
604+ let mut cx = unsafe { temp_cx() };
605
606- // The iframe HTML element removing steps, given removedNode, are to destroy a child navigable given removedNode
607- self.destroy_child_navigable(&mut cx);
608+ // The iframe HTML element removing steps, given removedNode, are to destroy a child navigable given removedNode
609+ self.destroy_child_navigable(&mut cx);
610+ }
611
612 self.owner_document().invalidate_iframes_collection();
613 }