1use std::cell::Cell;
6
7use base::id::{BrowsingContextId, PipelineId, WebViewId};
8use bitflags::bitflags;
9use constellation_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
10use constellation_traits::{
11 IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData, LoadOrigin,
12 NavigationHistoryBehavior, ScriptToConstellationMessage,
13};
14use dom_struct::dom_struct;
15use embedder_traits::ViewportDetails;
16use html5ever::{LocalName, Prefix, local_name, ns};
17use js::rust::HandleObject;
18use net_traits::ReferrerPolicy;
19use net_traits::request::Destination;
20use profile_traits::ipc as ProfiledIpc;
21use script_traits::{NewLayoutInfo, UpdatePipelineIdReason};
22use servo_url::ServoUrl;
23use style::attr::{AttrValue, LengthOrPercentageOrAuto};
24use stylo_atoms::Atom;
25
26use crate::document_loader::{LoadBlocker, LoadType};
27use crate::dom::attr::Attr;
28use crate::dom::bindings::cell::DomRefCell;
29use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
30use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
31use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
32use crate::dom::bindings::error::Fallible;
33use crate::dom::bindings::inheritance::Castable;
34use crate::dom::bindings::reflector::DomGlobal;
35use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
36use crate::dom::bindings::str::{DOMString, USVString};
37use crate::dom::document::{Document, determine_policy_for_token};
38use crate::dom::domtokenlist::DOMTokenList;
39use crate::dom::element::{
40 AttributeMutation, Element, LayoutElementHelpers, reflect_referrer_policy_attribute,
41};
42use crate::dom::eventtarget::EventTarget;
43use crate::dom::globalscope::GlobalScope;
44use crate::dom::html::htmlelement::HTMLElement;
45use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
46use crate::dom::trustedhtml::TrustedHTML;
47use crate::dom::virtualmethods::VirtualMethods;
48use crate::dom::windowproxy::WindowProxy;
49use crate::script_runtime::CanGc;
50use crate::script_thread::ScriptThread;
51
52#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
53struct SandboxAllowance(u8);
54
55bitflags! {
56 impl SandboxAllowance: u8 {
57 const ALLOW_NOTHING = 0x00;
58 const ALLOW_SAME_ORIGIN = 0x01;
59 const ALLOW_TOP_NAVIGATION = 0x02;
60 const ALLOW_FORMS = 0x04;
61 const ALLOW_SCRIPTS = 0x08;
62 const ALLOW_POINTER_LOCK = 0x10;
63 const ALLOW_POPUPS = 0x20;
64 }
65}
66
67#[derive(PartialEq)]
68enum PipelineType {
69 InitialAboutBlank,
70 Navigation,
71}
72
73#[derive(PartialEq)]
74enum ProcessingMode {
75 FirstTime,
76 NotFirstTime,
77}
78
79#[dom_struct]
80pub(crate) struct HTMLIFrameElement {
81 htmlelement: HTMLElement,
82 #[no_trace]
83 webview_id: Cell<Option<WebViewId>>,
84 #[no_trace]
85 browsing_context_id: Cell<Option<BrowsingContextId>>,
86 #[no_trace]
87 pipeline_id: Cell<Option<PipelineId>>,
88 #[no_trace]
89 pending_pipeline_id: Cell<Option<PipelineId>>,
90 #[no_trace]
91 about_blank_pipeline_id: Cell<Option<PipelineId>>,
92 sandbox: MutNullableDom<DOMTokenList>,
93 sandbox_allowance: Cell<Option<SandboxAllowance>>,
94 load_blocker: DomRefCell<Option<LoadBlocker>>,
95 throttled: Cell<bool>,
96}
97
98impl HTMLIFrameElement {
99 pub(crate) fn is_sandboxed(&self) -> bool {
100 self.sandbox_allowance.get().is_some()
101 }
102
103 fn get_url(&self) -> ServoUrl {
106 let element = self.upcast::<Element>();
107 element
108 .get_attribute(&ns!(), &local_name!("src"))
109 .and_then(|src| {
110 let url = src.value();
111 if url.is_empty() {
112 None
113 } else {
114 self.owner_document().base_url().join(&url).ok()
115 }
116 })
117 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap())
118 }
119
120 pub(crate) fn navigate_or_reload_child_browsing_context(
121 &self,
122 load_data: LoadData,
123 history_handling: NavigationHistoryBehavior,
124 can_gc: CanGc,
125 ) {
126 self.start_new_pipeline(
127 load_data,
128 PipelineType::Navigation,
129 history_handling,
130 can_gc,
131 );
132 }
133
134 fn start_new_pipeline(
135 &self,
136 mut load_data: LoadData,
137 pipeline_type: PipelineType,
138 history_handling: NavigationHistoryBehavior,
139 can_gc: CanGc,
140 ) {
141 let sandboxed = if self.is_sandboxed() {
142 IFrameSandboxed
143 } else {
144 IFrameUnsandboxed
145 };
146
147 let browsing_context_id = match self.browsing_context_id() {
148 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
149 Some(id) => id,
150 };
151
152 let webview_id = match self.webview_id() {
153 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
154 Some(id) => id,
155 };
156
157 let document = self.owner_document();
158
159 {
160 let load_blocker = &self.load_blocker;
161 LoadBlocker::terminate(load_blocker, can_gc);
164 }
165
166 if load_data.url.scheme() == "javascript" {
167 let window_proxy = self.GetContentWindow();
168 if let Some(window_proxy) = window_proxy {
169 if !ScriptThread::navigate_to_javascript_url(
170 &document.global(),
171 &window_proxy.global(),
172 &mut load_data,
173 Some(self.upcast()),
174 can_gc,
175 ) {
176 return;
177 }
178 }
179 }
180
181 match load_data.js_eval_result {
182 Some(JsEvalResult::NoContent) => (),
183 _ => {
184 let mut load_blocker = self.load_blocker.borrow_mut();
185 *load_blocker = Some(LoadBlocker::new(
186 &document,
187 LoadType::Subframe(load_data.url.clone()),
188 ));
189 },
190 };
191
192 let window = self.owner_window();
193 let old_pipeline_id = self.pipeline_id();
194 let new_pipeline_id = PipelineId::new();
195 self.pending_pipeline_id.set(Some(new_pipeline_id));
196
197 let load_info = IFrameLoadInfo {
198 parent_pipeline_id: window.pipeline_id(),
199 browsing_context_id,
200 webview_id,
201 new_pipeline_id,
202 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
204 history_handling,
205 };
206
207 let viewport_details = window
208 .get_iframe_viewport_details_if_known(browsing_context_id)
209 .unwrap_or_else(|| ViewportDetails {
210 hidpi_scale_factor: window.device_pixel_ratio(),
211 ..Default::default()
212 });
213
214 match pipeline_type {
215 PipelineType::InitialAboutBlank => {
216 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
217
218 let load_info = IFrameLoadInfoWithData {
219 info: load_info,
220 load_data: load_data.clone(),
221 old_pipeline_id,
222 sandbox: sandboxed,
223 viewport_details,
224 theme: window.theme(),
225 };
226 window
227 .as_global_scope()
228 .script_to_constellation_chan()
229 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
230 .unwrap();
231
232 let new_layout_info = NewLayoutInfo {
233 parent_info: Some(window.pipeline_id()),
234 new_pipeline_id,
235 browsing_context_id,
236 webview_id,
237 opener: None,
238 load_data,
239 viewport_details,
240 theme: window.theme(),
241 };
242
243 self.pipeline_id.set(Some(new_pipeline_id));
244 ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
245 },
246 PipelineType::Navigation => {
247 let load_info = IFrameLoadInfoWithData {
248 info: load_info,
249 load_data,
250 old_pipeline_id,
251 sandbox: sandboxed,
252 viewport_details,
253 theme: window.theme(),
254 };
255 window
256 .as_global_scope()
257 .script_to_constellation_chan()
258 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
259 load_info,
260 ))
261 .unwrap();
262 },
263 }
264 }
265
266 fn process_the_iframe_attributes(&self, mode: ProcessingMode, can_gc: CanGc) {
268 if self
270 .upcast::<Element>()
271 .has_attribute(&local_name!("srcdoc"))
272 {
273 let url = ServoUrl::parse("about:srcdoc").unwrap();
274 let document = self.owner_document();
275 let window = self.owner_window();
276 let pipeline_id = Some(window.pipeline_id());
277 let mut load_data = LoadData::new(
278 LoadOrigin::Script(document.origin().immutable().clone()),
279 url,
280 pipeline_id,
281 window.as_global_scope().get_referrer(),
282 document.get_referrer_policy(),
283 Some(window.as_global_scope().is_secure_context()),
284 Some(document.insecure_requests_policy()),
285 document.has_trustworthy_ancestor_or_current_origin(),
286 );
287 load_data.destination = Destination::IFrame;
288 load_data.policy_container = Some(window.as_global_scope().policy_container());
289 let element = self.upcast::<Element>();
290 load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc")));
291 self.navigate_or_reload_child_browsing_context(
292 load_data,
293 NavigationHistoryBehavior::Push,
294 can_gc,
295 );
296 return;
297 }
298
299 let window = self.owner_window();
300
301 if mode == ProcessingMode::FirstTime {
306 if let Some(window) = self.GetContentWindow() {
307 window.set_name(
308 self.upcast::<Element>()
309 .get_name()
310 .map_or(DOMString::from(""), |n| DOMString::from(&*n)),
311 );
312 }
313 }
314
315 if mode == ProcessingMode::FirstTime &&
316 !self.upcast::<Element>().has_attribute(&local_name!("src"))
317 {
318 return;
319 }
320
321 let url = self.get_url();
326
327 let document = self.owner_document();
330 let referrer_policy_token = self.ReferrerPolicy();
331
332 let referrer_policy = match determine_policy_for_token(referrer_policy_token.str()) {
336 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
337 policy => policy,
338 };
339
340 let mut ancestor = window.GetParent();
349 while let Some(a) = ancestor {
350 if let Some(ancestor_url) = a.document().map(|d| d.url()) {
351 if ancestor_url.scheme() == url.scheme() &&
352 ancestor_url.username() == url.username() &&
353 ancestor_url.password() == url.password() &&
354 ancestor_url.host() == url.host() &&
355 ancestor_url.port() == url.port() &&
356 ancestor_url.path() == url.path() &&
357 ancestor_url.query() == url.query()
358 {
359 return;
360 }
361 }
362 ancestor = a.parent().map(DomRoot::from_ref);
363 }
364
365 let creator_pipeline_id = if url.as_str() == "about:blank" {
366 Some(window.pipeline_id())
367 } else {
368 None
369 };
370
371 let mut load_data = LoadData::new(
372 LoadOrigin::Script(document.origin().immutable().clone()),
373 url,
374 creator_pipeline_id,
375 window.as_global_scope().get_referrer(),
376 referrer_policy,
377 Some(window.as_global_scope().is_secure_context()),
378 Some(document.insecure_requests_policy()),
379 document.has_trustworthy_ancestor_or_current_origin(),
380 );
381 load_data.destination = Destination::IFrame;
382 load_data.policy_container = Some(window.as_global_scope().policy_container());
383
384 let pipeline_id = self.pipeline_id();
385 let is_about_blank =
388 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
389
390 let history_handling = if is_about_blank {
391 NavigationHistoryBehavior::Replace
392 } else {
393 NavigationHistoryBehavior::Push
394 };
395
396 self.navigate_or_reload_child_browsing_context(load_data, history_handling, can_gc);
397 }
398
399 fn create_nested_browsing_context(&self, can_gc: CanGc) {
400 let url = ServoUrl::parse("about:blank").unwrap();
416 let document = self.owner_document();
417 let window = self.owner_window();
418 let pipeline_id = Some(window.pipeline_id());
419 let mut load_data = LoadData::new(
420 LoadOrigin::Script(document.origin().immutable().clone()),
421 url,
422 pipeline_id,
423 window.as_global_scope().get_referrer(),
424 document.get_referrer_policy(),
425 Some(window.as_global_scope().is_secure_context()),
426 Some(document.insecure_requests_policy()),
427 document.has_trustworthy_ancestor_or_current_origin(),
428 );
429 load_data.destination = Destination::IFrame;
430 load_data.policy_container = Some(window.as_global_scope().policy_container());
431 let browsing_context_id = BrowsingContextId::new();
432 let webview_id = window.window_proxy().webview_id();
433 self.pipeline_id.set(None);
434 self.pending_pipeline_id.set(None);
435 self.webview_id.set(Some(webview_id));
436 self.browsing_context_id.set(Some(browsing_context_id));
437 self.start_new_pipeline(
438 load_data,
439 PipelineType::InitialAboutBlank,
440 NavigationHistoryBehavior::Push,
441 can_gc,
442 );
443 }
444
445 fn destroy_nested_browsing_context(&self) {
446 self.pipeline_id.set(None);
447 self.pending_pipeline_id.set(None);
448 self.about_blank_pipeline_id.set(None);
449 self.webview_id.set(None);
450 self.browsing_context_id.set(None);
451 }
452
453 pub(crate) fn update_pipeline_id(
454 &self,
455 new_pipeline_id: PipelineId,
456 reason: UpdatePipelineIdReason,
457 can_gc: CanGc,
458 ) {
459 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
460 reason == UpdatePipelineIdReason::Navigation
461 {
462 return;
463 }
464
465 self.pipeline_id.set(Some(new_pipeline_id));
466
467 if reason == UpdatePipelineIdReason::Traversal {
470 let blocker = &self.load_blocker;
471 LoadBlocker::terminate(blocker, can_gc);
472 }
473
474 self.upcast::<Node>().dirty(NodeDamage::Other);
475 }
476
477 fn new_inherited(
478 local_name: LocalName,
479 prefix: Option<Prefix>,
480 document: &Document,
481 ) -> HTMLIFrameElement {
482 HTMLIFrameElement {
483 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
484 browsing_context_id: Cell::new(None),
485 webview_id: Cell::new(None),
486 pipeline_id: Cell::new(None),
487 pending_pipeline_id: Cell::new(None),
488 about_blank_pipeline_id: Cell::new(None),
489 sandbox: Default::default(),
490 sandbox_allowance: Cell::new(None),
491 load_blocker: DomRefCell::new(None),
492 throttled: Cell::new(false),
493 }
494 }
495
496 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
497 pub(crate) fn new(
498 local_name: LocalName,
499 prefix: Option<Prefix>,
500 document: &Document,
501 proto: Option<HandleObject>,
502 can_gc: CanGc,
503 ) -> DomRoot<HTMLIFrameElement> {
504 Node::reflect_node_with_proto(
505 Box::new(HTMLIFrameElement::new_inherited(
506 local_name, prefix, document,
507 )),
508 document,
509 proto,
510 can_gc,
511 )
512 }
513
514 #[inline]
515 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
516 self.pipeline_id.get()
517 }
518
519 #[inline]
520 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
521 self.browsing_context_id.get()
522 }
523
524 #[inline]
525 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
526 self.webview_id.get()
527 }
528
529 pub(crate) fn set_throttled(&self, throttled: bool) {
530 if self.throttled.get() != throttled {
531 self.throttled.set(throttled);
532 }
533 }
534
535 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, can_gc: CanGc) {
537 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
540 return;
541 }
542
543 self.upcast::<EventTarget>()
551 .fire_event(atom!("load"), can_gc);
552
553 let blocker = &self.load_blocker;
554 LoadBlocker::terminate(blocker, can_gc);
555
556 }
558}
559
560pub(crate) trait HTMLIFrameElementLayoutMethods {
561 fn pipeline_id(self) -> Option<PipelineId>;
562 fn browsing_context_id(self) -> Option<BrowsingContextId>;
563 fn get_width(self) -> LengthOrPercentageOrAuto;
564 fn get_height(self) -> LengthOrPercentageOrAuto;
565}
566
567impl HTMLIFrameElementLayoutMethods for LayoutDom<'_, HTMLIFrameElement> {
568 #[inline]
569 fn pipeline_id(self) -> Option<PipelineId> {
570 (self.unsafe_get()).pipeline_id.get()
571 }
572
573 #[inline]
574 fn browsing_context_id(self) -> Option<BrowsingContextId> {
575 (self.unsafe_get()).browsing_context_id.get()
576 }
577
578 fn get_width(self) -> LengthOrPercentageOrAuto {
579 self.upcast::<Element>()
580 .get_attr_for_layout(&ns!(), &local_name!("width"))
581 .map(AttrValue::as_dimension)
582 .cloned()
583 .unwrap_or(LengthOrPercentageOrAuto::Auto)
584 }
585
586 fn get_height(self) -> LengthOrPercentageOrAuto {
587 self.upcast::<Element>()
588 .get_attr_for_layout(&ns!(), &local_name!("height"))
589 .map(AttrValue::as_dimension)
590 .cloned()
591 .unwrap_or(LengthOrPercentageOrAuto::Auto)
592 }
593}
594
595impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
596 make_url_getter!(Src, "src");
598
599 make_url_setter!(SetSrc, "src");
601
602 fn Srcdoc(&self) -> TrustedHTMLOrString {
604 let element = self.upcast::<Element>();
605 element.get_trusted_html_attribute(&local_name!("srcdoc"))
606 }
607
608 fn SetSrcdoc(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> Fallible<()> {
610 let element = self.upcast::<Element>();
614 let value = TrustedHTML::get_trusted_script_compliant_string(
615 &element.owner_global(),
616 value,
617 "HTMLIFrameElement srcdoc",
618 can_gc,
619 )?;
620 element.set_attribute(
622 &local_name!("srcdoc"),
623 AttrValue::String(value.as_ref().to_owned()),
624 can_gc,
625 );
626 Ok(())
627 }
628
629 fn Sandbox(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
631 self.sandbox.or_init(|| {
632 DOMTokenList::new(
633 self.upcast::<Element>(),
634 &local_name!("sandbox"),
635 Some(vec![
636 Atom::from("allow-same-origin"),
637 Atom::from("allow-forms"),
638 Atom::from("allow-pointer-lock"),
639 Atom::from("allow-popups"),
640 Atom::from("allow-scripts"),
641 Atom::from("allow-top-navigation"),
642 ]),
643 can_gc,
644 )
645 })
646 }
647
648 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
650 self.browsing_context_id
651 .get()
652 .and_then(ScriptThread::find_window_proxy)
653 }
654
655 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
658 let pipeline_id = self.pipeline_id.get()?;
660
661 let document = ScriptThread::find_document(pipeline_id)?;
665
666 let current = GlobalScope::current()
668 .expect("No current global object")
669 .as_window()
670 .Document();
671 if !current.origin().same_origin_domain(document.origin()) {
672 return None;
673 }
674 Some(document)
676 }
677
678 fn ReferrerPolicy(&self) -> DOMString {
680 reflect_referrer_policy_attribute(self.upcast::<Element>())
681 }
682
683 make_setter!(SetReferrerPolicy, "referrerpolicy");
685
686 make_bool_getter!(AllowFullscreen, "allowfullscreen");
688 make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
690
691 make_getter!(Width, "width");
693 make_dimension_setter!(SetWidth, "width");
695
696 make_getter!(Height, "height");
698 make_dimension_setter!(SetHeight, "height");
700
701 make_getter!(FrameBorder, "frameborder");
703 make_setter!(SetFrameBorder, "frameborder");
705
706 make_atomic_setter!(SetName, "name");
710
711 make_getter!(Name, "name");
715}
716
717impl VirtualMethods for HTMLIFrameElement {
718 fn super_type(&self) -> Option<&dyn VirtualMethods> {
719 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
720 }
721
722 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
723 self.super_type()
724 .unwrap()
725 .attribute_mutated(attr, mutation, can_gc);
726 match *attr.local_name() {
727 local_name!("sandbox") => {
728 self.sandbox_allowance
729 .set(mutation.new_value(attr).map(|value| {
730 let mut modes = SandboxAllowance::ALLOW_NOTHING;
731 for token in value.as_tokens() {
732 modes |= match &*token.to_ascii_lowercase() {
733 "allow-same-origin" => SandboxAllowance::ALLOW_SAME_ORIGIN,
734 "allow-forms" => SandboxAllowance::ALLOW_FORMS,
735 "allow-pointer-lock" => SandboxAllowance::ALLOW_POINTER_LOCK,
736 "allow-popups" => SandboxAllowance::ALLOW_POPUPS,
737 "allow-scripts" => SandboxAllowance::ALLOW_SCRIPTS,
738 "allow-top-navigation" => SandboxAllowance::ALLOW_TOP_NAVIGATION,
739 _ => SandboxAllowance::ALLOW_NOTHING,
740 };
741 }
742 modes
743 }));
744 },
745 local_name!("srcdoc") => {
746 if self.upcast::<Node>().is_connected_with_browsing_context() {
757 debug!("iframe srcdoc modified while in browsing context.");
758 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, can_gc);
759 }
760 },
761 local_name!("src") => {
762 if self.upcast::<Node>().is_connected_with_browsing_context() {
771 debug!("iframe src set while in browsing context.");
772 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, can_gc);
773 }
774 },
775 _ => {},
776 }
777 }
778
779 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
780 match *name {
781 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
782 local_name!("width") => AttrValue::from_dimension(value.into()),
783 local_name!("height") => AttrValue::from_dimension(value.into()),
784 _ => self
785 .super_type()
786 .unwrap()
787 .parse_plain_attribute(name, value),
788 }
789 }
790
791 fn post_connection_steps(&self) {
792 if let Some(s) = self.super_type() {
793 s.post_connection_steps();
794 }
795
796 if self.upcast::<Node>().is_connected_with_browsing_context() {
803 debug!("iframe bound to browsing context.");
804 self.create_nested_browsing_context(CanGc::note());
805 self.process_the_iframe_attributes(ProcessingMode::FirstTime, CanGc::note());
806 }
807 }
808
809 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
810 if let Some(s) = self.super_type() {
811 s.bind_to_tree(context, can_gc);
812 }
813 self.owner_document().invalidate_iframes_collection();
814 }
815
816 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
817 self.super_type().unwrap().unbind_from_tree(context, can_gc);
818
819 let blocker = &self.load_blocker;
820 LoadBlocker::terminate(blocker, CanGc::note());
821
822 let window = self.owner_window();
824 let (sender, receiver) =
825 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
826
827 let browsing_context_id = match self.browsing_context_id() {
830 None => return warn!("Unbinding already unbound iframe."),
831 Some(id) => id,
832 };
833 debug!("Unbinding frame {}.", browsing_context_id);
834
835 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
836 window
837 .as_global_scope()
838 .script_to_constellation_chan()
839 .send(msg)
840 .unwrap();
841 let exited_pipeline_ids = receiver.recv().unwrap();
842
843 for exited_pipeline_id in exited_pipeline_ids {
847 if let Some(exited_document) = ScriptThread::find_document(exited_pipeline_id) {
849 debug!(
850 "Discarding browsing context for pipeline {}",
851 exited_pipeline_id
852 );
853 let exited_window = exited_document.window();
854 exited_window.discard_browsing_context();
855 for exited_iframe in exited_document.iframes().iter() {
856 debug!("Discarding nested browsing context");
857 exited_iframe.destroy_nested_browsing_context();
858 }
859 }
860 }
861
862 self.destroy_nested_browsing_context();
868
869 self.owner_document().invalidate_iframes_collection();
870 }
871}