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::html::htmlelement::HTMLElement;
46use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
47use crate::dom::trustedhtml::TrustedHTML;
48use crate::dom::virtualmethods::VirtualMethods;
49use crate::dom::windowproxy::WindowProxy;
50use crate::script_runtime::CanGc;
51use crate::script_thread::{ScriptThread, with_script_thread};
52use crate::script_window_proxies::ScriptWindowProxies;
53
54#[derive(PartialEq)]
55enum PipelineType {
56 InitialAboutBlank,
57 Navigation,
58}
59
60#[derive(PartialEq)]
61enum ProcessingMode {
62 FirstTime,
63 NotFirstTime,
64}
65
66#[dom_struct]
67pub(crate) struct HTMLIFrameElement {
68 htmlelement: HTMLElement,
69 #[no_trace]
70 webview_id: Cell<Option<WebViewId>>,
71 #[no_trace]
72 browsing_context_id: Cell<Option<BrowsingContextId>>,
73 #[no_trace]
74 pipeline_id: Cell<Option<PipelineId>>,
75 #[no_trace]
76 pending_pipeline_id: Cell<Option<PipelineId>>,
77 #[no_trace]
78 about_blank_pipeline_id: Cell<Option<PipelineId>>,
79 sandbox: MutNullableDom<DOMTokenList>,
80 #[no_trace]
81 sandboxing_flag_set: Cell<Option<SandboxingFlagSet>>,
82 load_blocker: DomRefCell<Option<LoadBlocker>>,
83 throttled: Cell<bool>,
84 #[conditional_malloc_size_of]
85 script_window_proxies: Rc<ScriptWindowProxies>,
86 pending_navigation: Cell<bool>,
93}
94
95impl HTMLIFrameElement {
96 fn get_url(&self) -> ServoUrl {
99 let element = self.upcast::<Element>();
100 element
101 .get_attribute(&ns!(), &local_name!("src"))
102 .and_then(|src| {
103 let url = src.value();
104 if url.is_empty() {
105 None
106 } else {
107 self.owner_document().base_url().join(&url).ok()
108 }
109 })
110 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap())
111 }
112
113 pub(crate) fn navigate_or_reload_child_browsing_context(
114 &self,
115 load_data: LoadData,
116 history_handling: NavigationHistoryBehavior,
117 can_gc: CanGc,
118 ) {
119 self.start_new_pipeline(
120 load_data,
121 PipelineType::Navigation,
122 history_handling,
123 can_gc,
124 );
125 }
126
127 fn start_new_pipeline(
128 &self,
129 mut load_data: LoadData,
130 pipeline_type: PipelineType,
131 history_handling: NavigationHistoryBehavior,
132 can_gc: CanGc,
133 ) {
134 let browsing_context_id = match self.browsing_context_id() {
135 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
136 Some(id) => id,
137 };
138
139 let webview_id = match self.webview_id() {
140 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
141 Some(id) => id,
142 };
143
144 let document = self.owner_document();
145
146 {
147 let load_blocker = &self.load_blocker;
148 LoadBlocker::terminate(load_blocker, can_gc);
151 }
152
153 if load_data.url.scheme() == "javascript" {
154 let window_proxy = self.GetContentWindow();
155 if let Some(window_proxy) = window_proxy {
156 if !ScriptThread::navigate_to_javascript_url(
157 &document.global(),
158 &window_proxy.global(),
159 &mut load_data,
160 Some(self.upcast()),
161 can_gc,
162 ) {
163 return;
164 }
165 load_data.about_base_url = document.about_base_url();
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 user_content_manager_id: None,
228 theme: window.theme(),
229 };
230
231 self.pipeline_id.set(Some(new_pipeline_id));
232 with_script_thread(|script_thread| {
233 script_thread.spawn_pipeline(new_pipeline_info);
234 });
235 },
236 PipelineType::Navigation => {
237 let load_info = IFrameLoadInfoWithData {
238 info: load_info,
239 load_data,
240 old_pipeline_id,
241 viewport_details,
242 theme: window.theme(),
243 };
244 window
245 .as_global_scope()
246 .script_to_constellation_chan()
247 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
248 load_info,
249 ))
250 .unwrap();
251 },
252 }
253 }
254
255 pub(crate) fn is_initial_blank_document(&self) -> bool {
261 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
262 }
263
264 fn process_the_iframe_attributes(&self, mode: ProcessingMode, can_gc: CanGc) {
266 let element = self.upcast::<Element>();
267 if element.has_attribute(&local_name!("srcdoc")) {
271 let url = ServoUrl::parse("about:srcdoc").unwrap();
272 let document = self.owner_document();
273 let window = self.owner_window();
274 let pipeline_id = Some(window.pipeline_id());
275 let mut load_data = LoadData::new(
276 LoadOrigin::Script(document.origin().snapshot()),
277 url,
278 Some(document.base_url()),
279 pipeline_id,
280 window.as_global_scope().get_referrer(),
281 document.get_referrer_policy(),
282 Some(window.as_global_scope().is_secure_context()),
283 Some(document.insecure_requests_policy()),
284 document.has_trustworthy_ancestor_or_current_origin(),
285 self.sandboxing_flag_set(),
286 );
287 load_data.destination = Destination::IFrame;
288 load_data.policy_container = Some(window.as_global_scope().policy_container());
289 load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc")));
290 self.navigate_or_reload_child_browsing_context(
291 load_data,
292 NavigationHistoryBehavior::Push,
293 can_gc,
294 );
295 return;
296 }
297
298 let window = self.owner_window();
299
300 if mode == ProcessingMode::FirstTime {
305 if let Some(window) = self.GetContentWindow() {
306 window.set_name(
307 element
308 .get_name()
309 .map_or(DOMString::from(""), |n| DOMString::from(&*n)),
310 );
311 }
312 }
313
314 if mode == ProcessingMode::FirstTime && !element.has_attribute(&local_name!("src")) {
315 return;
316 }
317
318 let url = self.get_url();
323
324 let document = self.owner_document();
327 let referrer_policy_token = self.ReferrerPolicy();
328
329 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
333 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
334 policy => policy,
335 };
336
337 let mut ancestor = window.GetParent();
346 while let Some(a) = ancestor {
347 if let Some(ancestor_url) = a.document().map(|d| d.url()) {
348 if ancestor_url.scheme() == url.scheme() &&
349 ancestor_url.username() == url.username() &&
350 ancestor_url.password() == url.password() &&
351 ancestor_url.host() == url.host() &&
352 ancestor_url.port() == url.port() &&
353 ancestor_url.path() == url.path() &&
354 ancestor_url.query() == url.query()
355 {
356 return;
357 }
358 }
359 ancestor = a.parent().map(DomRoot::from_ref);
360 }
361
362 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
363 (Some(window.pipeline_id()), Some(document.base_url()))
364 } else {
365 (None, document.about_base_url())
366 };
367
368 let propagate_encoding_to_child_document = url.origin().same_origin(window.origin());
369 let mut load_data = LoadData::new(
370 LoadOrigin::Script(document.origin().snapshot()),
371 url,
372 about_base_url,
373 creator_pipeline_id,
374 window.as_global_scope().get_referrer(),
375 referrer_policy,
376 Some(window.as_global_scope().is_secure_context()),
377 Some(document.insecure_requests_policy()),
378 document.has_trustworthy_ancestor_or_current_origin(),
379 self.sandboxing_flag_set(),
380 );
381 load_data.destination = Destination::IFrame;
382 load_data.policy_container = Some(window.as_global_scope().policy_container());
383 if propagate_encoding_to_child_document {
384 load_data.container_document_encoding = Some(document.encoding());
385 }
386
387 let pipeline_id = self.pipeline_id();
388 let is_about_blank =
391 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
392
393 let history_handling = if is_about_blank {
394 NavigationHistoryBehavior::Replace
395 } else {
396 NavigationHistoryBehavior::Push
397 };
398
399 self.navigate_or_reload_child_browsing_context(load_data, history_handling, can_gc);
400 }
401
402 fn create_nested_browsing_context(&self, can_gc: CanGc) {
410 let url = ServoUrl::parse("about:blank").unwrap();
411 let document = self.owner_document();
412 let window = self.owner_window();
413 let pipeline_id = Some(window.pipeline_id());
414 let mut load_data = LoadData::new(
415 LoadOrigin::Script(document.origin().snapshot()),
416 url,
417 Some(document.base_url()),
418 pipeline_id,
419 window.as_global_scope().get_referrer(),
420 document.get_referrer_policy(),
421 Some(window.as_global_scope().is_secure_context()),
422 Some(document.insecure_requests_policy()),
423 document.has_trustworthy_ancestor_or_current_origin(),
424 self.sandboxing_flag_set(),
425 );
426 load_data.destination = Destination::IFrame;
427 load_data.policy_container = Some(window.as_global_scope().policy_container());
428
429 let browsing_context_id = BrowsingContextId::new();
430 let webview_id = window.window_proxy().webview_id();
431 self.pipeline_id.set(None);
432 self.pending_pipeline_id.set(None);
433 self.webview_id.set(Some(webview_id));
434 self.browsing_context_id.set(Some(browsing_context_id));
435 self.start_new_pipeline(
436 load_data,
437 PipelineType::InitialAboutBlank,
438 NavigationHistoryBehavior::Push,
439 can_gc,
440 );
441 }
442
443 fn destroy_nested_browsing_context(&self) {
444 self.pipeline_id.set(None);
445 self.pending_pipeline_id.set(None);
446 self.about_blank_pipeline_id.set(None);
447 self.webview_id.set(None);
448 self.browsing_context_id.set(None);
449 }
450
451 pub(crate) fn update_pipeline_id(
452 &self,
453 new_pipeline_id: PipelineId,
454 reason: UpdatePipelineIdReason,
455 can_gc: CanGc,
456 ) {
457 if !self.is_initial_blank_document() {
462 self.pending_navigation.set(false);
463 }
464 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
465 reason == UpdatePipelineIdReason::Navigation
466 {
467 return;
468 }
469
470 self.pipeline_id.set(Some(new_pipeline_id));
471
472 if reason == UpdatePipelineIdReason::Traversal {
475 let blocker = &self.load_blocker;
476 LoadBlocker::terminate(blocker, can_gc);
477 }
478
479 self.upcast::<Node>().dirty(NodeDamage::Other);
480 }
481
482 fn new_inherited(
483 local_name: LocalName,
484 prefix: Option<Prefix>,
485 document: &Document,
486 ) -> HTMLIFrameElement {
487 HTMLIFrameElement {
488 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
489 browsing_context_id: Cell::new(None),
490 webview_id: Cell::new(None),
491 pipeline_id: Cell::new(None),
492 pending_pipeline_id: Cell::new(None),
493 about_blank_pipeline_id: Cell::new(None),
494 sandbox: Default::default(),
495 sandboxing_flag_set: Cell::new(None),
496 load_blocker: DomRefCell::new(None),
497 throttled: Cell::new(false),
498 script_window_proxies: ScriptThread::window_proxies(),
499 pending_navigation: Default::default(),
500 }
501 }
502
503 pub(crate) fn new(
504 local_name: LocalName,
505 prefix: Option<Prefix>,
506 document: &Document,
507 proto: Option<HandleObject>,
508 can_gc: CanGc,
509 ) -> DomRoot<HTMLIFrameElement> {
510 Node::reflect_node_with_proto(
511 Box::new(HTMLIFrameElement::new_inherited(
512 local_name, prefix, document,
513 )),
514 document,
515 proto,
516 can_gc,
517 )
518 }
519
520 #[inline]
521 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
522 self.pipeline_id.get()
523 }
524
525 #[inline]
526 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
527 self.browsing_context_id.get()
528 }
529
530 #[inline]
531 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
532 self.webview_id.get()
533 }
534
535 #[inline]
536 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
537 self.sandboxing_flag_set
538 .get()
539 .unwrap_or_else(SandboxingFlagSet::empty)
540 }
541
542 pub(crate) fn set_throttled(&self, throttled: bool) {
543 if self.throttled.get() != throttled {
544 self.throttled.set(throttled);
545 }
546 }
547
548 pub(crate) fn note_pending_navigation(&self) {
552 self.pending_navigation.set(true);
553 }
554
555 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, can_gc: CanGc) {
557 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
560 return;
561 }
562
563 let should_fire_event = if self.is_initial_blank_document() {
592 !self.pending_navigation.get() &&
596 !self.upcast::<Element>().has_attribute(&local_name!("src"))
597 } else {
598 !self.pending_navigation.get()
601 };
602 if should_fire_event {
603 self.upcast::<EventTarget>()
605 .fire_event(atom!("load"), can_gc);
606 }
607
608 let blocker = &self.load_blocker;
609 LoadBlocker::terminate(blocker, can_gc);
610
611 }
613
614 fn parse_sandbox_attribute(&self) {
618 let attribute = self
619 .upcast::<Element>()
620 .get_attribute(&ns!(), &local_name!("sandbox"));
621 self.sandboxing_flag_set
622 .set(attribute.map(|attribute_value| {
623 let tokens: Vec<_> = attribute_value
624 .value()
625 .as_tokens()
626 .iter()
627 .map(|atom| atom.to_string().to_ascii_lowercase())
628 .collect();
629 parse_a_sandboxing_directive(&tokens)
630 }));
631 }
632
633 pub(crate) fn destroy_document_and_its_descendants(&self, can_gc: CanGc) {
635 let Some(pipeline_id) = self.pipeline_id.get() else {
636 return;
637 };
638 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
640 exited_document.destroy_document_and_its_descendants(can_gc);
641 }
642 self.destroy_nested_browsing_context();
643 }
644
645 fn destroy_child_navigable(&self, can_gc: CanGc) {
647 let blocker = &self.load_blocker;
648 LoadBlocker::terminate(blocker, CanGc::note());
649
650 let Some(browsing_context_id) = self.browsing_context_id() else {
652 return;
654 };
655 let pipeline_id = self.pipeline_id.get();
658
659 self.destroy_nested_browsing_context();
667
668 let (sender, receiver) =
673 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
674 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
675 self.owner_window()
676 .as_global_scope()
677 .script_to_constellation_chan()
678 .send(msg)
679 .unwrap();
680 let _exited_pipeline_ids = receiver.recv().unwrap();
681 let Some(pipeline_id) = pipeline_id else {
682 return;
683 };
684 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
685 exited_document.destroy_document_and_its_descendants(can_gc);
686 }
687
688 }
703}
704
705pub(crate) trait HTMLIFrameElementLayoutMethods {
706 fn pipeline_id(self) -> Option<PipelineId>;
707 fn browsing_context_id(self) -> Option<BrowsingContextId>;
708 fn get_width(self) -> LengthOrPercentageOrAuto;
709 fn get_height(self) -> LengthOrPercentageOrAuto;
710}
711
712impl HTMLIFrameElementLayoutMethods for LayoutDom<'_, HTMLIFrameElement> {
713 #[inline]
714 fn pipeline_id(self) -> Option<PipelineId> {
715 (self.unsafe_get()).pipeline_id.get()
716 }
717
718 #[inline]
719 fn browsing_context_id(self) -> Option<BrowsingContextId> {
720 (self.unsafe_get()).browsing_context_id.get()
721 }
722
723 fn get_width(self) -> LengthOrPercentageOrAuto {
724 self.upcast::<Element>()
725 .get_attr_for_layout(&ns!(), &local_name!("width"))
726 .map(AttrValue::as_dimension)
727 .cloned()
728 .unwrap_or(LengthOrPercentageOrAuto::Auto)
729 }
730
731 fn get_height(self) -> LengthOrPercentageOrAuto {
732 self.upcast::<Element>()
733 .get_attr_for_layout(&ns!(), &local_name!("height"))
734 .map(AttrValue::as_dimension)
735 .cloned()
736 .unwrap_or(LengthOrPercentageOrAuto::Auto)
737 }
738}
739
740impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
741 make_url_getter!(Src, "src");
743
744 make_url_setter!(SetSrc, "src");
746
747 fn Srcdoc(&self) -> TrustedHTMLOrString {
749 let element = self.upcast::<Element>();
750 element.get_trusted_html_attribute(&local_name!("srcdoc"))
751 }
752
753 fn SetSrcdoc(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> Fallible<()> {
755 let element = self.upcast::<Element>();
759 let value = TrustedHTML::get_trusted_script_compliant_string(
760 &element.owner_global(),
761 value,
762 "HTMLIFrameElement srcdoc",
763 can_gc,
764 )?;
765 element.set_attribute(
767 &local_name!("srcdoc"),
768 AttrValue::String(value.str().to_owned()),
769 can_gc,
770 );
771 Ok(())
772 }
773
774 fn Sandbox(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
780 self.sandbox.or_init(|| {
781 DOMTokenList::new(
782 self.upcast::<Element>(),
783 &local_name!("sandbox"),
784 Some(vec![
785 Atom::from("allow-downloads"),
786 Atom::from("allow-forms"),
787 Atom::from("allow-modals"),
788 Atom::from("allow-orientation-lock"),
789 Atom::from("allow-pointer-lock"),
790 Atom::from("allow-popups"),
791 Atom::from("allow-popups-to-escape-sandbox"),
792 Atom::from("allow-presentation"),
793 Atom::from("allow-same-origin"),
794 Atom::from("allow-scripts"),
795 Atom::from("allow-top-navigation"),
796 Atom::from("allow-top-navigation-by-user-activation"),
797 Atom::from("allow-top-navigation-to-custom-protocols"),
798 ]),
799 can_gc,
800 )
801 })
802 }
803
804 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
806 self.browsing_context_id
807 .get()
808 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
809 }
810
811 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
813 let pipeline_id = self.pipeline_id.get()?;
815
816 let document = ScriptThread::find_document(pipeline_id)?;
820 if !self
822 .owner_document()
823 .origin()
824 .same_origin_domain(document.origin())
825 {
826 return None;
827 }
828 Some(document)
830 }
831
832 fn ReferrerPolicy(&self) -> DOMString {
834 reflect_referrer_policy_attribute(self.upcast::<Element>())
835 }
836
837 make_setter!(SetReferrerPolicy, "referrerpolicy");
839
840 make_bool_getter!(AllowFullscreen, "allowfullscreen");
842 make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
844
845 make_getter!(Width, "width");
847 make_dimension_setter!(SetWidth, "width");
849
850 make_getter!(Height, "height");
852 make_dimension_setter!(SetHeight, "height");
854
855 make_getter!(FrameBorder, "frameborder");
857 make_setter!(SetFrameBorder, "frameborder");
859
860 make_atomic_setter!(SetName, "name");
864
865 make_getter!(Name, "name");
869}
870
871impl VirtualMethods for HTMLIFrameElement {
872 fn super_type(&self) -> Option<&dyn VirtualMethods> {
873 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
874 }
875
876 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
877 self.super_type()
878 .unwrap()
879 .attribute_mutated(attr, mutation, can_gc);
880 match *attr.local_name() {
881 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
892 self.parse_sandbox_attribute();
893 },
894 local_name!("srcdoc") => {
895 if self.upcast::<Node>().is_connected_with_browsing_context() {
906 debug!("iframe srcdoc modified while in browsing context.");
907 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, can_gc);
908 }
909 },
910 local_name!("src") => {
911 if self.upcast::<Node>().is_connected_with_browsing_context() {
920 debug!("iframe src set while in browsing context.");
921 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, can_gc);
922 }
923 },
924 _ => {},
925 }
926 }
927
928 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
929 match attr.local_name() {
930 &local_name!("width") | &local_name!("height") => true,
931 _ => self
932 .super_type()
933 .unwrap()
934 .attribute_affects_presentational_hints(attr),
935 }
936 }
937
938 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
939 match *name {
940 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
941 local_name!("width") => AttrValue::from_dimension(value.into()),
942 local_name!("height") => AttrValue::from_dimension(value.into()),
943 _ => self
944 .super_type()
945 .unwrap()
946 .parse_plain_attribute(name, value),
947 }
948 }
949
950 fn post_connection_steps(&self, can_gc: CanGc) {
952 if let Some(s) = self.super_type() {
953 s.post_connection_steps(can_gc);
954 }
955
956 if !self.upcast::<Node>().is_connected_with_browsing_context() {
960 return;
961 }
962
963 debug!("<iframe> running post connection steps");
964
965 self.create_nested_browsing_context(can_gc);
967
968 self.parse_sandbox_attribute();
971
972 self.process_the_iframe_attributes(ProcessingMode::FirstTime, can_gc);
974 }
975
976 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
977 if let Some(s) = self.super_type() {
978 s.bind_to_tree(context, can_gc);
979 }
980 self.owner_document().invalidate_iframes_collection();
981 }
982
983 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
985 self.super_type().unwrap().unbind_from_tree(context, can_gc);
986
987 self.destroy_child_navigable(can_gc);
989
990 self.owner_document().invalidate_iframes_collection();
991 }
992}