1use std::cell::Cell;
6use std::rc::Rc;
7
8use content_security_policy::sandboxing_directive::{
9 SandboxingFlagSet, parse_a_sandboxing_directive,
10};
11use dom_struct::dom_struct;
12use embedder_traits::ViewportDetails;
13use html5ever::{LocalName, Prefix, local_name, ns};
14use js::context::JSContext;
15use js::rust::HandleObject;
16use net_traits::ReferrerPolicy;
17use net_traits::request::Destination;
18use profile_traits::ipc as ProfiledIpc;
19use script_traits::{NewPipelineInfo, UpdatePipelineIdReason};
20use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
21use servo_constellation_traits::{
22 IFrameLoadInfo, IFrameLoadInfoWithData, LoadData, LoadOrigin, NavigationHistoryBehavior,
23 ScriptToConstellationMessage, TargetSnapshotParams,
24};
25use servo_url::ServoUrl;
26use style::attr::{AttrValue, LengthOrPercentageOrAuto};
27use stylo_atoms::Atom;
28
29use crate::document_loader::{LoadBlocker, LoadType};
30use crate::dom::attr::Attr;
31use crate::dom::bindings::cell::DomRefCell;
32use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
33use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
34use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
35use crate::dom::bindings::error::Fallible;
36use crate::dom::bindings::inheritance::Castable;
37use crate::dom::bindings::refcounted::Trusted;
38use crate::dom::bindings::reflector::DomGlobal;
39use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
40use crate::dom::bindings::str::{DOMString, USVString};
41use crate::dom::document::Document;
42use crate::dom::domtokenlist::DOMTokenList;
43use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
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::performance::performanceresourcetiming::InitiatorType;
49use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
50use crate::dom::virtualmethods::VirtualMethods;
51use crate::dom::windowproxy::WindowProxy;
52use crate::navigation::{
53 determine_creation_sandboxing_flags, determine_iframe_element_referrer_policy,
54};
55use crate::network_listener::ResourceTimingListener;
56use crate::script_runtime::CanGc;
57use crate::script_thread::{ScriptThread, with_script_thread};
58use crate::script_window_proxies::ScriptWindowProxies;
59
60#[derive(PartialEq)]
61enum PipelineType {
62 InitialAboutBlank,
63 Navigation,
64}
65
66#[derive(Clone, Copy, PartialEq)]
67pub(crate) enum ProcessingMode {
68 FirstTime,
69 NotFirstTime,
70}
71
72#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
74enum LazyLoadResumptionSteps {
75 #[default]
76 None,
77 SrcDoc,
78}
79
80#[dom_struct]
81pub(crate) struct HTMLIFrameElement {
82 htmlelement: HTMLElement,
83 #[no_trace]
84 webview_id: Cell<Option<WebViewId>>,
85 #[no_trace]
86 browsing_context_id: Cell<Option<BrowsingContextId>>,
87 #[no_trace]
88 pipeline_id: Cell<Option<PipelineId>>,
89 #[no_trace]
90 pending_pipeline_id: Cell<Option<PipelineId>>,
91 #[no_trace]
92 about_blank_pipeline_id: Cell<Option<PipelineId>>,
93 sandbox: MutNullableDom<DOMTokenList>,
94 #[no_trace]
95 sandboxing_flag_set: Cell<Option<SandboxingFlagSet>>,
96 load_blocker: DomRefCell<Option<LoadBlocker>>,
97 throttled: Cell<bool>,
98 #[conditional_malloc_size_of]
99 script_window_proxies: Rc<ScriptWindowProxies>,
100 current_navigation_was_lazy_loaded: Cell<bool>,
102 #[no_trace]
104 lazy_load_resumption_steps: Cell<LazyLoadResumptionSteps>,
105 pending_navigation: Cell<bool>,
112 already_fired_synchronous_load_event: Cell<bool>,
116}
117
118impl HTMLIFrameElement {
119 fn shared_attribute_processing_steps_for_iframe_and_frame_elements(
121 &self,
122 _mode: ProcessingMode,
123 ) -> Option<ServoUrl> {
124 let element = self.upcast::<Element>();
125 let url = element
127 .get_attribute(&local_name!("src"))
128 .and_then(|src| {
129 let url = src.value();
130 if url.is_empty() {
131 None
132 } else {
133 self.owner_document().encoding_parse_a_url(&url).ok()
137 }
138 })
139 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
141 Some(url)
151 }
152
153 pub(crate) fn navigate_or_reload_child_browsing_context(
154 &self,
155 load_data: LoadData,
156 history_handling: NavigationHistoryBehavior,
157 mode: ProcessingMode,
158 target_snapshot_params: TargetSnapshotParams,
159 cx: &mut JSContext,
160 ) {
161 self.already_fired_synchronous_load_event.set(false);
168
169 self.start_new_pipeline(
170 load_data,
171 PipelineType::Navigation,
172 history_handling,
173 mode,
174 target_snapshot_params,
175 cx,
176 );
177 }
178
179 fn start_new_pipeline(
180 &self,
181 mut load_data: LoadData,
182 pipeline_type: PipelineType,
183 history_handling: NavigationHistoryBehavior,
184 mode: ProcessingMode,
185 target_snapshot_params: TargetSnapshotParams,
186 cx: &mut JSContext,
187 ) {
188 let document = self.owner_document();
189
190 {
191 let load_blocker = &self.load_blocker;
192 LoadBlocker::terminate(load_blocker, cx);
195
196 *load_blocker.borrow_mut() = Some(LoadBlocker::new(
197 &document,
198 LoadType::Subframe(load_data.url.clone()),
199 ));
200 }
201
202 if load_data.url.scheme() != "javascript" {
203 self.continue_navigation(
204 load_data,
205 pipeline_type,
206 history_handling,
207 target_snapshot_params,
208 );
209 return;
210 }
211
212 let iframe = Trusted::new(self);
216 let doc = Trusted::new(&*document);
217 document
218 .global()
219 .task_manager()
220 .networking_task_source()
221 .queue(task!(navigate_to_javascript: move |cx| {
222 let this = iframe.root();
223 let window_proxy = this.GetContentWindow();
224 if let Some(window_proxy) = window_proxy {
225 if !ScriptThread::navigate_to_javascript_url(
228 cx,
229 &this.owner_global(),
230 &window_proxy.global(),
231 &mut load_data,
232 Some(this.upcast()),
233 Some(mode == ProcessingMode::FirstTime),
234 ) {
235 LoadBlocker::terminate(&this.load_blocker, cx);
236 return;
237 }
238 load_data.about_base_url = doc.root().about_base_url();
239 }
240 this.continue_navigation(load_data, pipeline_type, history_handling, target_snapshot_params);
241 }));
242 }
243
244 fn continue_navigation(
245 &self,
246 load_data: LoadData,
247 pipeline_type: PipelineType,
248 history_handling: NavigationHistoryBehavior,
249 target_snapshot_params: TargetSnapshotParams,
250 ) {
251 let browsing_context_id = match self.browsing_context_id() {
252 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
253 Some(id) => id,
254 };
255
256 let webview_id = match self.webview_id() {
257 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
258 Some(id) => id,
259 };
260
261 let window = self.owner_window();
262 let old_pipeline_id = self.pipeline_id();
263 let new_pipeline_id = PipelineId::new();
264 self.pending_pipeline_id.set(Some(new_pipeline_id));
265
266 let load_info = IFrameLoadInfo {
267 parent_pipeline_id: window.pipeline_id(),
268 browsing_context_id,
269 webview_id,
270 new_pipeline_id,
271 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
273 history_handling,
274 target_snapshot_params,
275 };
276
277 let viewport_details = window
278 .get_iframe_viewport_details_if_known(browsing_context_id)
279 .unwrap_or_else(|| ViewportDetails {
280 hidpi_scale_factor: window.device_pixel_ratio(),
281 ..Default::default()
282 });
283
284 match pipeline_type {
285 PipelineType::InitialAboutBlank => {
286 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
287
288 let load_info = IFrameLoadInfoWithData {
289 info: load_info,
290 load_data: load_data.clone(),
291 old_pipeline_id,
292 viewport_details,
293 theme: window.theme(),
294 };
295 window
296 .as_global_scope()
297 .script_to_constellation_chan()
298 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
299 .unwrap();
300
301 let new_pipeline_info = NewPipelineInfo {
302 parent_info: Some(window.pipeline_id()),
303 new_pipeline_id,
304 browsing_context_id,
305 webview_id,
306 opener: None,
307 load_data,
308 viewport_details,
309 user_content_manager_id: None,
310 theme: window.theme(),
311 target_snapshot_params,
312 };
313
314 self.pipeline_id.set(Some(new_pipeline_id));
315 with_script_thread(|script_thread| {
316 script_thread.spawn_pipeline(new_pipeline_info);
317 });
318 },
319 PipelineType::Navigation => {
320 let load_info = IFrameLoadInfoWithData {
321 info: load_info,
322 load_data,
323 old_pipeline_id,
324 viewport_details,
325 theme: window.theme(),
326 };
327 window
328 .as_global_scope()
329 .script_to_constellation_chan()
330 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
331 load_info,
332 ))
333 .unwrap();
334 },
335 }
336 }
337
338 pub(crate) fn is_initial_blank_document(&self) -> bool {
344 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
345 }
346
347 fn navigate_an_iframe_or_frame(
349 &self,
350 cx: &mut JSContext,
351 load_data: LoadData,
352 mode: ProcessingMode,
353 ) {
354 let history_handling = if !self
357 .GetContentDocument()
358 .is_some_and(|doc| doc.completely_loaded())
359 {
360 NavigationHistoryBehavior::Replace
361 } else {
362 NavigationHistoryBehavior::Auto
364 };
365 let target_snapshot_params = snapshot_self(self);
373 self.navigate_or_reload_child_browsing_context(
374 load_data,
375 history_handling,
376 mode,
377 target_snapshot_params,
378 cx,
379 );
380 }
381
382 fn will_lazy_load_element_steps(&self) -> bool {
384 if !self.owner_document().scripting_enabled() {
386 return false;
387 }
388 self.Loading() == "lazy"
391 }
392
393 fn navigate_to_the_srcdoc_resource(&self, mode: ProcessingMode, cx: &mut JSContext) {
395 let url = ServoUrl::parse("about:srcdoc").unwrap();
398 let document = self.owner_document();
399 let window = self.owner_window();
400 let pipeline_id = Some(window.pipeline_id());
401 let mut load_data = LoadData::new(
402 LoadOrigin::Script(document.origin().snapshot()),
403 url,
404 Some(document.base_url()),
405 pipeline_id,
406 window.as_global_scope().get_referrer(),
407 document.get_referrer_policy(),
408 Some(window.as_global_scope().is_secure_context()),
409 Some(document.insecure_requests_policy()),
410 document.has_trustworthy_ancestor_or_current_origin(),
411 self.sandboxing_flag_set(),
412 );
413 load_data.destination = Destination::IFrame;
414 load_data.policy_container = Some(window.as_global_scope().policy_container());
415 load_data.srcdoc = String::from(
416 self.upcast::<Element>()
417 .get_string_attribute(&local_name!("srcdoc")),
418 );
419
420 self.navigate_an_iframe_or_frame(cx, load_data, mode);
421 }
422
423 fn mark_navigation_as_lazy_loaded(&self, cx: &mut JSContext) {
425 self.current_navigation_was_lazy_loaded.set(true);
427 let blocker = &self.load_blocker;
428 LoadBlocker::terminate(blocker, cx);
429 }
430
431 fn process_the_iframe_attributes(&self, mode: ProcessingMode, cx: &mut JSContext) {
433 let element = self.upcast::<Element>();
434
435 if element.has_attribute(&local_name!("srcdoc")) {
439 self.current_navigation_was_lazy_loaded.set(false);
441 if self.will_lazy_load_element_steps() {
443 self.lazy_load_resumption_steps
446 .set(LazyLoadResumptionSteps::SrcDoc);
447 self.mark_navigation_as_lazy_loaded(cx);
449 return;
453 }
454 self.navigate_to_the_srcdoc_resource(mode, cx);
457 return;
458 }
459
460 let window = self.owner_window();
461
462 if mode == ProcessingMode::FirstTime {
467 if let Some(window) = self.GetContentWindow() {
468 window.set_name(
469 element
470 .get_name()
471 .map_or(DOMString::from(""), |n| DOMString::from(&*n)),
472 );
473 }
474 }
475
476 let Some(url) = self.shared_attribute_processing_steps_for_iframe_and_frame_elements(mode)
479 else {
480 return;
482 };
483
484 if url.matches_about_blank() && mode == ProcessingMode::FirstTime {
486 self.already_fired_synchronous_load_event.set(true);
488 self.run_iframe_load_event_steps(cx);
490 return;
492 }
493
494 let document = self.owner_document();
497 let referrer_policy_token = self.ReferrerPolicy();
498
499 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
503 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
504 policy => policy,
505 };
506
507 let mut ancestor = window.GetParent();
516 while let Some(a) = ancestor {
517 if let Some(ancestor_url) = a.document().map(|d| d.url()) {
518 if ancestor_url.scheme() == url.scheme() &&
519 ancestor_url.username() == url.username() &&
520 ancestor_url.password() == url.password() &&
521 ancestor_url.host() == url.host() &&
522 ancestor_url.port() == url.port() &&
523 ancestor_url.path() == url.path() &&
524 ancestor_url.query() == url.query()
525 {
526 return;
527 }
528 }
529 ancestor = a.parent().map(DomRoot::from_ref);
530 }
531
532 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
533 (Some(window.pipeline_id()), Some(document.base_url()))
534 } else {
535 (None, document.about_base_url())
536 };
537
538 let propagate_encoding_to_child_document = url.origin().same_origin(window.origin());
539 let mut load_data = LoadData::new(
540 LoadOrigin::Script(document.origin().snapshot()),
541 url,
542 about_base_url,
543 creator_pipeline_id,
544 window.as_global_scope().get_referrer(),
545 referrer_policy,
546 Some(window.as_global_scope().is_secure_context()),
547 Some(document.insecure_requests_policy()),
548 document.has_trustworthy_ancestor_or_current_origin(),
549 self.sandboxing_flag_set(),
550 );
551 load_data.destination = Destination::IFrame;
552 load_data.policy_container = Some(window.as_global_scope().policy_container());
553 if propagate_encoding_to_child_document {
554 load_data.container_document_encoding = Some(document.encoding());
555 }
556
557 let pipeline_id = self.pipeline_id();
558 let is_about_blank =
561 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
562
563 let history_handling = if is_about_blank {
564 NavigationHistoryBehavior::Replace
565 } else {
566 NavigationHistoryBehavior::Push
567 };
568
569 let target_snapshot_params = snapshot_self(self);
570 self.navigate_or_reload_child_browsing_context(
571 load_data,
572 history_handling,
573 mode,
574 target_snapshot_params,
575 cx,
576 );
577 }
578
579 fn create_nested_browsing_context(&self, cx: &mut JSContext) {
587 let url = ServoUrl::parse("about:blank").unwrap();
588 let document = self.owner_document();
589 let window = self.owner_window();
590 let pipeline_id = Some(window.pipeline_id());
591 let mut load_data = LoadData::new(
592 LoadOrigin::Script(document.origin().snapshot()),
593 url,
594 Some(document.base_url()),
595 pipeline_id,
596 window.as_global_scope().get_referrer(),
597 document.get_referrer_policy(),
598 Some(window.as_global_scope().is_secure_context()),
599 Some(document.insecure_requests_policy()),
600 document.has_trustworthy_ancestor_or_current_origin(),
601 self.sandboxing_flag_set(),
602 );
603 load_data.destination = Destination::IFrame;
604 load_data.policy_container = Some(window.as_global_scope().policy_container());
605
606 let browsing_context_id = BrowsingContextId::new();
607 let webview_id = window.window_proxy().webview_id();
608 self.pipeline_id.set(None);
609 self.pending_pipeline_id.set(None);
610 self.webview_id.set(Some(webview_id));
611 self.browsing_context_id.set(Some(browsing_context_id));
612 self.start_new_pipeline(
613 load_data,
614 PipelineType::InitialAboutBlank,
615 NavigationHistoryBehavior::Push,
616 ProcessingMode::FirstTime,
617 snapshot_self(self),
618 cx,
619 );
620 }
621
622 fn destroy_nested_browsing_context(&self) {
623 self.pipeline_id.set(None);
624 self.pending_pipeline_id.set(None);
625 self.about_blank_pipeline_id.set(None);
626 self.webview_id.set(None);
627 if let Some(browsing_context_id) = self.browsing_context_id.take() {
628 self.script_window_proxies.remove(browsing_context_id)
629 }
630 }
631
632 pub(crate) fn update_pipeline_id(
636 &self,
637 new_pipeline_id: PipelineId,
638 reason: UpdatePipelineIdReason,
639 cx: &mut JSContext,
640 ) -> bool {
641 if !self.is_initial_blank_document() {
646 self.pending_navigation.set(false);
647 }
648 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
649 reason == UpdatePipelineIdReason::Navigation
650 {
651 return false;
652 }
653
654 self.pipeline_id.set(Some(new_pipeline_id));
655
656 if reason == UpdatePipelineIdReason::Traversal {
659 let blocker = &self.load_blocker;
660 LoadBlocker::terminate(blocker, cx);
661 }
662
663 self.upcast::<Node>().dirty(NodeDamage::Other);
664 true
665 }
666
667 fn new_inherited(
668 local_name: LocalName,
669 prefix: Option<Prefix>,
670 document: &Document,
671 ) -> HTMLIFrameElement {
672 HTMLIFrameElement {
673 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
674 browsing_context_id: Cell::new(None),
675 webview_id: Cell::new(None),
676 pipeline_id: Cell::new(None),
677 pending_pipeline_id: Cell::new(None),
678 about_blank_pipeline_id: Cell::new(None),
679 sandbox: Default::default(),
680 sandboxing_flag_set: Cell::new(None),
681 load_blocker: DomRefCell::new(None),
682 throttled: Cell::new(false),
683 script_window_proxies: ScriptThread::window_proxies(),
684 current_navigation_was_lazy_loaded: Default::default(),
685 lazy_load_resumption_steps: Default::default(),
686 pending_navigation: Default::default(),
687 already_fired_synchronous_load_event: Default::default(),
688 }
689 }
690
691 pub(crate) fn new(
692 cx: &mut JSContext,
693 local_name: LocalName,
694 prefix: Option<Prefix>,
695 document: &Document,
696 proto: Option<HandleObject>,
697 ) -> DomRoot<HTMLIFrameElement> {
698 Node::reflect_node_with_proto(
699 cx,
700 Box::new(HTMLIFrameElement::new_inherited(
701 local_name, prefix, document,
702 )),
703 document,
704 proto,
705 )
706 }
707
708 #[inline]
709 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
710 self.pipeline_id.get()
711 }
712
713 #[inline]
714 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
715 self.browsing_context_id.get()
716 }
717
718 #[inline]
719 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
720 self.webview_id.get()
721 }
722
723 #[inline]
724 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
725 self.sandboxing_flag_set
726 .get()
727 .unwrap_or_else(SandboxingFlagSet::empty)
728 }
729
730 pub(crate) fn set_throttled(&self, throttled: bool) {
731 if self.throttled.get() != throttled {
732 self.throttled.set(throttled);
733 }
734 }
735
736 pub(crate) fn note_pending_navigation(&self) {
740 self.pending_navigation.set(true);
741 }
742
743 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, cx: &mut JSContext) {
745 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
748 return;
749 }
750
751 let should_fire_event = if self.is_initial_blank_document() {
780 !self.pending_navigation.get() &&
784 !self.upcast::<Element>().has_attribute(&local_name!("src"))
785 } else {
786 !self.pending_navigation.get()
789 };
790
791 let should_fire_event =
794 !self.already_fired_synchronous_load_event.replace(false) && should_fire_event;
795 if should_fire_event {
796 self.run_iframe_load_event_steps(cx);
797 } else {
798 debug!(
799 "suppressing load event for iframe, loaded {:?}",
800 loaded_pipeline
801 );
802 }
803 }
804
805 pub(crate) fn run_iframe_load_event_steps(&self, cx: &mut JSContext) {
807 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
815
816 let blocker = &self.load_blocker;
817 LoadBlocker::terminate(blocker, cx);
818
819 }
821
822 fn parse_sandbox_attribute(&self) {
826 let attribute = self
827 .upcast::<Element>()
828 .get_attribute(&local_name!("sandbox"));
829 self.sandboxing_flag_set
830 .set(attribute.map(|attribute_value| {
831 let tokens: Vec<_> = attribute_value
832 .value()
833 .as_tokens()
834 .iter()
835 .map(|atom| atom.to_string().to_ascii_lowercase())
836 .collect();
837 parse_a_sandboxing_directive(&tokens)
838 }));
839 }
840
841 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
843 let Some(pipeline_id) = self.pipeline_id.get() else {
844 return;
845 };
846 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
848 exited_document.destroy_document_and_its_descendants(cx);
849 }
850 self.destroy_nested_browsing_context();
851 }
852
853 fn destroy_child_navigable(&self, cx: &mut JSContext) {
855 let blocker = &self.load_blocker;
856 LoadBlocker::terminate(blocker, cx);
857
858 let Some(browsing_context_id) = self.browsing_context_id() else {
860 return;
862 };
863 let pipeline_id = self.pipeline_id.get();
866
867 self.destroy_nested_browsing_context();
875
876 let (sender, receiver) =
881 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
882 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
883 self.owner_window()
884 .as_global_scope()
885 .script_to_constellation_chan()
886 .send(msg)
887 .unwrap();
888 let _exited_pipeline_ids = receiver.recv().unwrap();
889 let Some(pipeline_id) = pipeline_id else {
890 return;
891 };
892 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
893 exited_document.destroy_document_and_its_descendants(cx);
894 }
895
896 }
911}
912
913impl LayoutDom<'_, HTMLIFrameElement> {
914 #[inline]
915 pub(crate) fn pipeline_id(self) -> Option<PipelineId> {
916 (self.unsafe_get()).pipeline_id.get()
917 }
918
919 #[inline]
920 pub(crate) fn browsing_context_id(self) -> Option<BrowsingContextId> {
921 (self.unsafe_get()).browsing_context_id.get()
922 }
923
924 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
925 self.upcast::<Element>()
926 .get_attr_for_layout(&ns!(), &local_name!("width"))
927 .map(AttrValue::as_dimension)
928 .cloned()
929 .unwrap_or(LengthOrPercentageOrAuto::Auto)
930 }
931
932 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
933 self.upcast::<Element>()
934 .get_attr_for_layout(&ns!(), &local_name!("height"))
935 .map(AttrValue::as_dimension)
936 .cloned()
937 .unwrap_or(LengthOrPercentageOrAuto::Auto)
938 }
939}
940
941impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
942 make_url_getter!(Src, "src");
944
945 make_url_setter!(cx, SetSrc, "src");
947
948 fn Srcdoc(&self) -> TrustedHTMLOrString {
950 let element = self.upcast::<Element>();
951 element.get_trusted_html_attribute(&local_name!("srcdoc"))
952 }
953
954 fn SetSrcdoc(&self, cx: &mut JSContext, value: TrustedHTMLOrString) -> Fallible<()> {
956 let element = self.upcast::<Element>();
960 let value = TrustedHTML::get_trusted_type_compliant_string(
961 cx,
962 &element.owner_global(),
963 value,
964 "HTMLIFrameElement srcdoc",
965 )?;
966 element.set_attribute(
968 &local_name!("srcdoc"),
969 AttrValue::String(value.str().to_owned()),
970 CanGc::from_cx(cx),
971 );
972 Ok(())
973 }
974
975 fn Sandbox(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
981 self.sandbox.or_init(|| {
982 DOMTokenList::new(
983 self.upcast::<Element>(),
984 &local_name!("sandbox"),
985 Some(vec![
986 Atom::from("allow-downloads"),
987 Atom::from("allow-forms"),
988 Atom::from("allow-modals"),
989 Atom::from("allow-orientation-lock"),
990 Atom::from("allow-pointer-lock"),
991 Atom::from("allow-popups"),
992 Atom::from("allow-popups-to-escape-sandbox"),
993 Atom::from("allow-presentation"),
994 Atom::from("allow-same-origin"),
995 Atom::from("allow-scripts"),
996 Atom::from("allow-top-navigation"),
997 Atom::from("allow-top-navigation-by-user-activation"),
998 Atom::from("allow-top-navigation-to-custom-protocols"),
999 ]),
1000 CanGc::from_cx(cx),
1001 )
1002 })
1003 }
1004
1005 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
1007 self.browsing_context_id
1008 .get()
1009 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
1010 }
1011
1012 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
1014 let pipeline_id = self.pipeline_id.get()?;
1016
1017 let document = ScriptThread::find_document(pipeline_id)?;
1021 if !self
1023 .owner_document()
1024 .origin()
1025 .same_origin_domain(&document.origin())
1026 {
1027 return None;
1028 }
1029 Some(document)
1031 }
1032
1033 fn ReferrerPolicy(&self) -> DOMString {
1035 reflect_referrer_policy_attribute(self.upcast::<Element>())
1036 }
1037
1038 make_setter!(cx, SetReferrerPolicy, "referrerpolicy");
1040
1041 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1043 make_bool_setter!(cx, SetAllowFullscreen, "allowfullscreen");
1045
1046 make_getter!(Width, "width");
1048 make_dimension_setter!(SetWidth, "width");
1050
1051 make_getter!(Height, "height");
1053 make_dimension_setter!(SetHeight, "height");
1055
1056 make_getter!(FrameBorder, "frameborder");
1058 make_setter!(cx, SetFrameBorder, "frameborder");
1060
1061 make_atomic_setter!(cx, SetName, "name");
1065
1066 make_getter!(Name, "name");
1070
1071 make_enumerated_getter!(
1074 Loading,
1075 "loading",
1076 "lazy" | "eager",
1077 missing => "eager",
1080 invalid => "eager"
1081 );
1082
1083 make_setter!(cx, SetLoading, "loading");
1085
1086 make_url_getter!(LongDesc, "longdesc");
1088
1089 make_url_setter!(cx, SetLongDesc, "longdesc");
1091}
1092
1093impl VirtualMethods for HTMLIFrameElement {
1094 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1095 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1096 }
1097
1098 fn attribute_mutated(&self, cx: &mut JSContext, attr: &Attr, mutation: AttributeMutation) {
1099 self.super_type()
1100 .unwrap()
1101 .attribute_mutated(cx, attr, mutation);
1102 match *attr.local_name() {
1103 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
1114 self.parse_sandbox_attribute();
1115 },
1116 local_name!("srcdoc") => {
1117 if self.upcast::<Node>().is_connected_with_browsing_context() {
1128 debug!("iframe srcdoc modified while in browsing context.");
1129 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1130 }
1131 },
1132 local_name!("src") => {
1133 if self.upcast::<Node>().is_connected_with_browsing_context() {
1142 debug!("iframe src set while in browsing context.");
1143 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1144 }
1145 },
1146 local_name!("loading") => {
1147 if !mutation.is_removal() && &**attr.value() == "lazy" {
1150 return;
1151 }
1152
1153 let previous_resumption_steps = self
1156 .lazy_load_resumption_steps
1157 .replace(LazyLoadResumptionSteps::None);
1158 match previous_resumption_steps {
1159 LazyLoadResumptionSteps::None => (),
1161 LazyLoadResumptionSteps::SrcDoc => {
1162 self.navigate_to_the_srcdoc_resource(ProcessingMode::NotFirstTime, cx);
1164 },
1165 }
1166 },
1167 _ => {},
1168 }
1169 }
1170
1171 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
1172 match attr.local_name() {
1173 &local_name!("width") | &local_name!("height") => true,
1174 _ => self
1175 .super_type()
1176 .unwrap()
1177 .attribute_affects_presentational_hints(attr),
1178 }
1179 }
1180
1181 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1182 match *name {
1183 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
1184 local_name!("width") => AttrValue::from_dimension(value.into()),
1185 local_name!("height") => AttrValue::from_dimension(value.into()),
1186 _ => self
1187 .super_type()
1188 .unwrap()
1189 .parse_plain_attribute(name, value),
1190 }
1191 }
1192
1193 fn post_connection_steps(&self, cx: &mut JSContext) {
1195 if let Some(s) = self.super_type() {
1196 s.post_connection_steps(cx);
1197 }
1198
1199 if !self.upcast::<Node>().is_connected_with_browsing_context() {
1203 return;
1204 }
1205
1206 debug!("<iframe> running post connection steps");
1207
1208 self.create_nested_browsing_context(cx);
1210
1211 self.parse_sandbox_attribute();
1214
1215 self.process_the_iframe_attributes(ProcessingMode::FirstTime, cx);
1217 }
1218
1219 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1220 if let Some(s) = self.super_type() {
1221 s.bind_to_tree(cx, context);
1222 }
1223 self.owner_document().invalidate_iframes_collection();
1224 }
1225
1226 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1228 self.super_type().unwrap().unbind_from_tree(cx, context);
1229
1230 self.destroy_child_navigable(cx);
1232
1233 self.owner_document().invalidate_iframes_collection();
1234 }
1235}
1236
1237pub(crate) struct IframeContext<'a> {
1240 element: &'a HTMLIFrameElement,
1242 url: ServoUrl,
1244}
1245
1246impl<'a> IframeContext<'a> {
1247 pub fn new(element: &'a HTMLIFrameElement) -> Self {
1249 Self {
1250 element,
1251 url: element
1252 .shared_attribute_processing_steps_for_iframe_and_frame_elements(
1253 ProcessingMode::NotFirstTime,
1254 )
1255 .expect("Must always have a URL when navigating"),
1256 }
1257 }
1258}
1259
1260impl<'a> ResourceTimingListener for IframeContext<'a> {
1261 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1262 (
1263 InitiatorType::LocalName("iframe".to_string()),
1264 self.url.clone(),
1265 )
1266 }
1267
1268 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1269 self.element.upcast::<Node>().owner_doc().global()
1270 }
1271}
1272
1273fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1274 let child_navigable = iframe.GetContentWindow();
1275 TargetSnapshotParams {
1276 sandboxing_flags: determine_creation_sandboxing_flags(
1277 child_navigable.as_deref(),
1278 Some(iframe.upcast()),
1279 ),
1280 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1281 iframe.upcast(),
1282 )),
1283 }
1284}