1use std::cell::Cell;
6use std::rc::Rc;
7
8use base::id::{BrowsingContextId, PipelineId, WebViewId};
9use constellation_traits::{
10 IFrameLoadInfo, IFrameLoadInfoWithData, JsEvalResult, LoadData, LoadOrigin,
11 NavigationHistoryBehavior, ScriptToConstellationMessage,
12};
13use content_security_policy::sandboxing_directive::{
14 SandboxingFlagSet, parse_a_sandboxing_directive,
15};
16use dom_struct::dom_struct;
17use embedder_traits::ViewportDetails;
18use html5ever::{LocalName, Prefix, local_name, ns};
19use js::rust::HandleObject;
20use net_traits::ReferrerPolicy;
21use net_traits::request::Destination;
22use profile_traits::ipc as ProfiledIpc;
23use script_traits::{NewPipelineInfo, UpdatePipelineIdReason};
24use servo_url::ServoUrl;
25use style::attr::{AttrValue, LengthOrPercentageOrAuto};
26use stylo_atoms::Atom;
27
28use crate::document_loader::{LoadBlocker, LoadType};
29use crate::dom::attr::Attr;
30use crate::dom::bindings::cell::DomRefCell;
31use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
32use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
33use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
34use crate::dom::bindings::error::Fallible;
35use crate::dom::bindings::inheritance::Castable;
36use crate::dom::bindings::reflector::DomGlobal;
37use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
38use crate::dom::bindings::str::{DOMString, USVString};
39use crate::dom::document::Document;
40use crate::dom::domtokenlist::DOMTokenList;
41use crate::dom::element::{
42 AttributeMutation, Element, LayoutElementHelpers, reflect_referrer_policy_attribute,
43};
44use crate::dom::eventtarget::EventTarget;
45use crate::dom::globalscope::GlobalScope;
46use crate::dom::html::htmlelement::HTMLElement;
47use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
48use crate::dom::trustedhtml::TrustedHTML;
49use crate::dom::virtualmethods::VirtualMethods;
50use crate::dom::windowproxy::WindowProxy;
51use crate::script_runtime::CanGc;
52use crate::script_thread::{ScriptThread, with_script_thread};
53use crate::script_window_proxies::ScriptWindowProxies;
54
55#[derive(PartialEq)]
56enum PipelineType {
57 InitialAboutBlank,
58 Navigation,
59}
60
61#[derive(PartialEq)]
62enum ProcessingMode {
63 FirstTime,
64 NotFirstTime,
65}
66
67#[dom_struct]
68pub(crate) struct HTMLIFrameElement {
69 htmlelement: HTMLElement,
70 #[no_trace]
71 webview_id: Cell<Option<WebViewId>>,
72 #[no_trace]
73 browsing_context_id: Cell<Option<BrowsingContextId>>,
74 #[no_trace]
75 pipeline_id: Cell<Option<PipelineId>>,
76 #[no_trace]
77 pending_pipeline_id: Cell<Option<PipelineId>>,
78 #[no_trace]
79 about_blank_pipeline_id: Cell<Option<PipelineId>>,
80 sandbox: MutNullableDom<DOMTokenList>,
81 #[no_trace]
82 sandboxing_flag_set: Cell<Option<SandboxingFlagSet>>,
83 load_blocker: DomRefCell<Option<LoadBlocker>>,
84 throttled: Cell<bool>,
85 #[conditional_malloc_size_of]
86 script_window_proxies: Rc<ScriptWindowProxies>,
87 pending_navigation: Cell<bool>,
94}
95
96impl HTMLIFrameElement {
97 fn get_url(&self) -> ServoUrl {
100 let element = self.upcast::<Element>();
101 element
102 .get_attribute(&ns!(), &local_name!("src"))
103 .and_then(|src| {
104 let url = src.value();
105 if url.is_empty() {
106 None
107 } else {
108 self.owner_document().base_url().join(&url).ok()
109 }
110 })
111 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap())
112 }
113
114 pub(crate) fn navigate_or_reload_child_browsing_context(
115 &self,
116 load_data: LoadData,
117 history_handling: NavigationHistoryBehavior,
118 can_gc: CanGc,
119 ) {
120 self.start_new_pipeline(
121 load_data,
122 PipelineType::Navigation,
123 history_handling,
124 can_gc,
125 );
126 }
127
128 fn start_new_pipeline(
129 &self,
130 mut load_data: LoadData,
131 pipeline_type: PipelineType,
132 history_handling: NavigationHistoryBehavior,
133 can_gc: CanGc,
134 ) {
135 let browsing_context_id = match self.browsing_context_id() {
136 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
137 Some(id) => id,
138 };
139
140 let webview_id = match self.webview_id() {
141 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
142 Some(id) => id,
143 };
144
145 let document = self.owner_document();
146
147 {
148 let load_blocker = &self.load_blocker;
149 LoadBlocker::terminate(load_blocker, can_gc);
152 }
153
154 if load_data.url.scheme() == "javascript" {
155 let window_proxy = self.GetContentWindow();
156 if let Some(window_proxy) = window_proxy {
157 if !ScriptThread::navigate_to_javascript_url(
158 &document.global(),
159 &window_proxy.global(),
160 &mut load_data,
161 Some(self.upcast()),
162 can_gc,
163 ) {
164 return;
165 }
166 }
167 }
168
169 match load_data.js_eval_result {
170 Some(JsEvalResult::NoContent) => (),
171 _ => {
172 let mut load_blocker = self.load_blocker.borrow_mut();
173 *load_blocker = Some(LoadBlocker::new(
174 &document,
175 LoadType::Subframe(load_data.url.clone()),
176 ));
177 },
178 };
179
180 let window = self.owner_window();
181 let old_pipeline_id = self.pipeline_id();
182 let new_pipeline_id = PipelineId::new();
183 self.pending_pipeline_id.set(Some(new_pipeline_id));
184
185 let load_info = IFrameLoadInfo {
186 parent_pipeline_id: window.pipeline_id(),
187 browsing_context_id,
188 webview_id,
189 new_pipeline_id,
190 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
192 history_handling,
193 };
194
195 let viewport_details = window
196 .get_iframe_viewport_details_if_known(browsing_context_id)
197 .unwrap_or_else(|| ViewportDetails {
198 hidpi_scale_factor: window.device_pixel_ratio(),
199 ..Default::default()
200 });
201
202 match pipeline_type {
203 PipelineType::InitialAboutBlank => {
204 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
205
206 let load_info = IFrameLoadInfoWithData {
207 info: load_info,
208 load_data: load_data.clone(),
209 old_pipeline_id,
210 viewport_details,
211 theme: window.theme(),
212 };
213 window
214 .as_global_scope()
215 .script_to_constellation_chan()
216 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
217 .unwrap();
218
219 let new_pipeline_info = NewPipelineInfo {
220 parent_info: Some(window.pipeline_id()),
221 new_pipeline_id,
222 browsing_context_id,
223 webview_id,
224 opener: None,
225 load_data,
226 viewport_details,
227 theme: window.theme(),
228 };
229
230 self.pipeline_id.set(Some(new_pipeline_id));
231 with_script_thread(|script_thread| {
232 script_thread.spawn_pipeline(new_pipeline_info);
233 });
234 },
235 PipelineType::Navigation => {
236 let load_info = IFrameLoadInfoWithData {
237 info: load_info,
238 load_data,
239 old_pipeline_id,
240 viewport_details,
241 theme: window.theme(),
242 };
243 window
244 .as_global_scope()
245 .script_to_constellation_chan()
246 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
247 load_info,
248 ))
249 .unwrap();
250 },
251 }
252 }
253
254 pub(crate) fn is_initial_blank_document(&self) -> bool {
260 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
261 }
262
263 fn process_the_iframe_attributes(&self, mode: ProcessingMode, can_gc: CanGc) {
265 if self
267 .upcast::<Element>()
268 .has_attribute(&local_name!("srcdoc"))
269 {
270 let url = ServoUrl::parse("about:srcdoc").unwrap();
271 let document = self.owner_document();
272 let window = self.owner_window();
273 let pipeline_id = Some(window.pipeline_id());
274 let mut load_data = LoadData::new(
275 LoadOrigin::Script(document.origin().immutable().clone()),
276 url,
277 pipeline_id,
278 window.as_global_scope().get_referrer(),
279 document.get_referrer_policy(),
280 Some(window.as_global_scope().is_secure_context()),
281 Some(document.insecure_requests_policy()),
282 document.has_trustworthy_ancestor_or_current_origin(),
283 self.sandboxing_flag_set(),
284 );
285 load_data.destination = Destination::IFrame;
286 load_data.policy_container = Some(window.as_global_scope().policy_container());
287 let element = self.upcast::<Element>();
288 load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc")));
289 self.navigate_or_reload_child_browsing_context(
290 load_data,
291 NavigationHistoryBehavior::Push,
292 can_gc,
293 );
294 return;
295 }
296
297 let window = self.owner_window();
298
299 if mode == ProcessingMode::FirstTime {
304 if let Some(window) = self.GetContentWindow() {
305 window.set_name(
306 self.upcast::<Element>()
307 .get_name()
308 .map_or(DOMString::from(""), |n| DOMString::from(&*n)),
309 );
310 }
311 }
312
313 if mode == ProcessingMode::FirstTime &&
314 !self.upcast::<Element>().has_attribute(&local_name!("src"))
315 {
316 return;
317 }
318
319 let url = self.get_url();
324
325 let document = self.owner_document();
328 let referrer_policy_token = self.ReferrerPolicy();
329
330 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
334 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
335 policy => policy,
336 };
337
338 let mut ancestor = window.GetParent();
347 while let Some(a) = ancestor {
348 if let Some(ancestor_url) = a.document().map(|d| d.url()) {
349 if ancestor_url.scheme() == url.scheme() &&
350 ancestor_url.username() == url.username() &&
351 ancestor_url.password() == url.password() &&
352 ancestor_url.host() == url.host() &&
353 ancestor_url.port() == url.port() &&
354 ancestor_url.path() == url.path() &&
355 ancestor_url.query() == url.query()
356 {
357 return;
358 }
359 }
360 ancestor = a.parent().map(DomRoot::from_ref);
361 }
362
363 let creator_pipeline_id = if url.as_str() == "about:blank" {
364 Some(window.pipeline_id())
365 } else {
366 None
367 };
368
369 let mut load_data = LoadData::new(
370 LoadOrigin::Script(document.origin().immutable().clone()),
371 url,
372 creator_pipeline_id,
373 window.as_global_scope().get_referrer(),
374 referrer_policy,
375 Some(window.as_global_scope().is_secure_context()),
376 Some(document.insecure_requests_policy()),
377 document.has_trustworthy_ancestor_or_current_origin(),
378 self.sandboxing_flag_set(),
379 );
380 load_data.destination = Destination::IFrame;
381 load_data.policy_container = Some(window.as_global_scope().policy_container());
382
383 let pipeline_id = self.pipeline_id();
384 let is_about_blank =
387 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
388
389 let history_handling = if is_about_blank {
390 NavigationHistoryBehavior::Replace
391 } else {
392 NavigationHistoryBehavior::Push
393 };
394
395 self.navigate_or_reload_child_browsing_context(load_data, history_handling, can_gc);
396 }
397
398 fn create_nested_browsing_context(&self, can_gc: CanGc) {
406 let url = ServoUrl::parse("about:blank").unwrap();
407 let document = self.owner_document();
408 let window = self.owner_window();
409 let pipeline_id = Some(window.pipeline_id());
410 let mut load_data = LoadData::new(
411 LoadOrigin::Script(document.origin().immutable().clone()),
412 url,
413 pipeline_id,
414 window.as_global_scope().get_referrer(),
415 document.get_referrer_policy(),
416 Some(window.as_global_scope().is_secure_context()),
417 Some(document.insecure_requests_policy()),
418 document.has_trustworthy_ancestor_or_current_origin(),
419 self.sandboxing_flag_set(),
420 );
421 load_data.destination = Destination::IFrame;
422 load_data.policy_container = Some(window.as_global_scope().policy_container());
423 let browsing_context_id = BrowsingContextId::new();
424 let webview_id = window.window_proxy().webview_id();
425 self.pipeline_id.set(None);
426 self.pending_pipeline_id.set(None);
427 self.webview_id.set(Some(webview_id));
428 self.browsing_context_id.set(Some(browsing_context_id));
429 self.start_new_pipeline(
430 load_data,
431 PipelineType::InitialAboutBlank,
432 NavigationHistoryBehavior::Push,
433 can_gc,
434 );
435 }
436
437 fn destroy_nested_browsing_context(&self) {
438 self.pipeline_id.set(None);
439 self.pending_pipeline_id.set(None);
440 self.about_blank_pipeline_id.set(None);
441 self.webview_id.set(None);
442 self.browsing_context_id.set(None);
443 }
444
445 pub(crate) fn update_pipeline_id(
446 &self,
447 new_pipeline_id: PipelineId,
448 reason: UpdatePipelineIdReason,
449 can_gc: CanGc,
450 ) {
451 if !self.is_initial_blank_document() {
456 self.pending_navigation.set(false);
457 }
458 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
459 reason == UpdatePipelineIdReason::Navigation
460 {
461 return;
462 }
463
464 self.pipeline_id.set(Some(new_pipeline_id));
465
466 if reason == UpdatePipelineIdReason::Traversal {
469 let blocker = &self.load_blocker;
470 LoadBlocker::terminate(blocker, can_gc);
471 }
472
473 self.upcast::<Node>().dirty(NodeDamage::Other);
474 }
475
476 fn new_inherited(
477 local_name: LocalName,
478 prefix: Option<Prefix>,
479 document: &Document,
480 ) -> HTMLIFrameElement {
481 HTMLIFrameElement {
482 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
483 browsing_context_id: Cell::new(None),
484 webview_id: Cell::new(None),
485 pipeline_id: Cell::new(None),
486 pending_pipeline_id: Cell::new(None),
487 about_blank_pipeline_id: Cell::new(None),
488 sandbox: Default::default(),
489 sandboxing_flag_set: Cell::new(None),
490 load_blocker: DomRefCell::new(None),
491 throttled: Cell::new(false),
492 script_window_proxies: ScriptThread::window_proxies(),
493 pending_navigation: Default::default(),
494 }
495 }
496
497 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
498 pub(crate) fn new(
499 local_name: LocalName,
500 prefix: Option<Prefix>,
501 document: &Document,
502 proto: Option<HandleObject>,
503 can_gc: CanGc,
504 ) -> DomRoot<HTMLIFrameElement> {
505 Node::reflect_node_with_proto(
506 Box::new(HTMLIFrameElement::new_inherited(
507 local_name, prefix, document,
508 )),
509 document,
510 proto,
511 can_gc,
512 )
513 }
514
515 #[inline]
516 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
517 self.pipeline_id.get()
518 }
519
520 #[inline]
521 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
522 self.browsing_context_id.get()
523 }
524
525 #[inline]
526 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
527 self.webview_id.get()
528 }
529
530 #[inline]
531 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
532 self.sandboxing_flag_set
533 .get()
534 .unwrap_or_else(SandboxingFlagSet::empty)
535 }
536
537 pub(crate) fn set_throttled(&self, throttled: bool) {
538 if self.throttled.get() != throttled {
539 self.throttled.set(throttled);
540 }
541 }
542
543 pub(crate) fn note_pending_navigation(&self) {
547 self.pending_navigation.set(true);
548 }
549
550 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, can_gc: CanGc) {
552 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
555 return;
556 }
557
558 let should_fire_event = if self.is_initial_blank_document() {
587 !self.pending_navigation.get() &&
591 !self.upcast::<Element>().has_attribute(&local_name!("src"))
592 } else {
593 !self.pending_navigation.get()
596 };
597 if should_fire_event {
598 self.upcast::<EventTarget>()
600 .fire_event(atom!("load"), can_gc);
601 }
602
603 let blocker = &self.load_blocker;
604 LoadBlocker::terminate(blocker, can_gc);
605
606 }
608
609 fn parse_sandbox_attribute(&self) {
613 let attribute = self
614 .upcast::<Element>()
615 .get_attribute(&ns!(), &local_name!("sandbox"));
616 self.sandboxing_flag_set
617 .set(attribute.map(|attribute_value| {
618 let tokens: Vec<_> = attribute_value
619 .value()
620 .as_tokens()
621 .iter()
622 .map(|atom| atom.to_string().to_ascii_lowercase())
623 .collect();
624 parse_a_sandboxing_directive(&tokens)
625 }));
626 }
627}
628
629pub(crate) trait HTMLIFrameElementLayoutMethods {
630 fn pipeline_id(self) -> Option<PipelineId>;
631 fn browsing_context_id(self) -> Option<BrowsingContextId>;
632 fn get_width(self) -> LengthOrPercentageOrAuto;
633 fn get_height(self) -> LengthOrPercentageOrAuto;
634}
635
636impl HTMLIFrameElementLayoutMethods for LayoutDom<'_, HTMLIFrameElement> {
637 #[inline]
638 fn pipeline_id(self) -> Option<PipelineId> {
639 (self.unsafe_get()).pipeline_id.get()
640 }
641
642 #[inline]
643 fn browsing_context_id(self) -> Option<BrowsingContextId> {
644 (self.unsafe_get()).browsing_context_id.get()
645 }
646
647 fn get_width(self) -> LengthOrPercentageOrAuto {
648 self.upcast::<Element>()
649 .get_attr_for_layout(&ns!(), &local_name!("width"))
650 .map(AttrValue::as_dimension)
651 .cloned()
652 .unwrap_or(LengthOrPercentageOrAuto::Auto)
653 }
654
655 fn get_height(self) -> LengthOrPercentageOrAuto {
656 self.upcast::<Element>()
657 .get_attr_for_layout(&ns!(), &local_name!("height"))
658 .map(AttrValue::as_dimension)
659 .cloned()
660 .unwrap_or(LengthOrPercentageOrAuto::Auto)
661 }
662}
663
664impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
665 make_url_getter!(Src, "src");
667
668 make_url_setter!(SetSrc, "src");
670
671 fn Srcdoc(&self) -> TrustedHTMLOrString {
673 let element = self.upcast::<Element>();
674 element.get_trusted_html_attribute(&local_name!("srcdoc"))
675 }
676
677 fn SetSrcdoc(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> Fallible<()> {
679 let element = self.upcast::<Element>();
683 let value = TrustedHTML::get_trusted_script_compliant_string(
684 &element.owner_global(),
685 value,
686 "HTMLIFrameElement srcdoc",
687 can_gc,
688 )?;
689 element.set_attribute(
691 &local_name!("srcdoc"),
692 AttrValue::String(value.str().to_owned()),
693 can_gc,
694 );
695 Ok(())
696 }
697
698 fn Sandbox(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
704 self.sandbox.or_init(|| {
705 DOMTokenList::new(
706 self.upcast::<Element>(),
707 &local_name!("sandbox"),
708 Some(vec![
709 Atom::from("allow-downloads"),
710 Atom::from("allow-forms"),
711 Atom::from("allow-modals"),
712 Atom::from("allow-orientation-lock"),
713 Atom::from("allow-pointer-lock"),
714 Atom::from("allow-popups"),
715 Atom::from("allow-popups-to-escape-sandbox"),
716 Atom::from("allow-presentation"),
717 Atom::from("allow-same-origin"),
718 Atom::from("allow-scripts"),
719 Atom::from("allow-top-navigation"),
720 Atom::from("allow-top-navigation-by-user-activation"),
721 Atom::from("allow-top-navigation-to-custom-protocols"),
722 ]),
723 can_gc,
724 )
725 })
726 }
727
728 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
730 self.browsing_context_id
731 .get()
732 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
733 }
734
735 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
738 let pipeline_id = self.pipeline_id.get()?;
740
741 let document = ScriptThread::find_document(pipeline_id)?;
745
746 let current = GlobalScope::current()
748 .expect("No current global object")
749 .as_window()
750 .Document();
751 if !current.origin().same_origin_domain(document.origin()) {
752 return None;
753 }
754 Some(document)
756 }
757
758 fn ReferrerPolicy(&self) -> DOMString {
760 reflect_referrer_policy_attribute(self.upcast::<Element>())
761 }
762
763 make_setter!(SetReferrerPolicy, "referrerpolicy");
765
766 make_bool_getter!(AllowFullscreen, "allowfullscreen");
768 make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
770
771 make_getter!(Width, "width");
773 make_dimension_setter!(SetWidth, "width");
775
776 make_getter!(Height, "height");
778 make_dimension_setter!(SetHeight, "height");
780
781 make_getter!(FrameBorder, "frameborder");
783 make_setter!(SetFrameBorder, "frameborder");
785
786 make_atomic_setter!(SetName, "name");
790
791 make_getter!(Name, "name");
795}
796
797impl VirtualMethods for HTMLIFrameElement {
798 fn super_type(&self) -> Option<&dyn VirtualMethods> {
799 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
800 }
801
802 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
803 self.super_type()
804 .unwrap()
805 .attribute_mutated(attr, mutation, can_gc);
806 match *attr.local_name() {
807 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
818 self.parse_sandbox_attribute();
819 },
820 local_name!("srcdoc") => {
821 if self.upcast::<Node>().is_connected_with_browsing_context() {
832 debug!("iframe srcdoc modified while in browsing context.");
833 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, can_gc);
834 }
835 },
836 local_name!("src") => {
837 if self.upcast::<Node>().is_connected_with_browsing_context() {
846 debug!("iframe src set while in browsing context.");
847 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, can_gc);
848 }
849 },
850 _ => {},
851 }
852 }
853
854 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
855 match attr.local_name() {
856 &local_name!("width") | &local_name!("height") => true,
857 _ => self
858 .super_type()
859 .unwrap()
860 .attribute_affects_presentational_hints(attr),
861 }
862 }
863
864 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
865 match *name {
866 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
867 local_name!("width") => AttrValue::from_dimension(value.into()),
868 local_name!("height") => AttrValue::from_dimension(value.into()),
869 _ => self
870 .super_type()
871 .unwrap()
872 .parse_plain_attribute(name, value),
873 }
874 }
875
876 fn post_connection_steps(&self, can_gc: CanGc) {
878 if let Some(s) = self.super_type() {
879 s.post_connection_steps(can_gc);
880 }
881
882 if !self.upcast::<Node>().is_connected_with_browsing_context() {
886 return;
887 }
888
889 debug!("<iframe> running post connection steps");
890
891 self.create_nested_browsing_context(can_gc);
893
894 self.parse_sandbox_attribute();
897
898 self.process_the_iframe_attributes(ProcessingMode::FirstTime, can_gc);
900 }
901
902 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
903 if let Some(s) = self.super_type() {
904 s.bind_to_tree(context, can_gc);
905 }
906 self.owner_document().invalidate_iframes_collection();
907 }
908
909 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
910 self.super_type().unwrap().unbind_from_tree(context, can_gc);
911
912 let blocker = &self.load_blocker;
913 LoadBlocker::terminate(blocker, CanGc::note());
914
915 let window = self.owner_window();
917 let (sender, receiver) =
918 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
919
920 let browsing_context_id = match self.browsing_context_id() {
923 None => return warn!("Unbinding already unbound iframe."),
924 Some(id) => id,
925 };
926 debug!("Unbinding frame {}.", browsing_context_id);
927
928 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
929 window
930 .as_global_scope()
931 .script_to_constellation_chan()
932 .send(msg)
933 .unwrap();
934 let exited_pipeline_ids = receiver.recv().unwrap();
935
936 for exited_pipeline_id in exited_pipeline_ids {
940 if let Some(exited_document) = ScriptThread::find_document(exited_pipeline_id) {
942 debug!(
943 "Discarding browsing context for pipeline {}",
944 exited_pipeline_id
945 );
946 let exited_window = exited_document.window();
947 exited_window.discard_browsing_context();
948 for exited_iframe in exited_document.iframes().iter() {
949 debug!("Discarding nested browsing context");
950 exited_iframe.destroy_nested_browsing_context();
951 }
952 }
953 }
954
955 self.destroy_nested_browsing_context();
961
962 self.owner_document().invalidate_iframes_collection();
963 }
964}