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_bindings::cell::DomRefCell;
20use script_traits::{NewPipelineInfo, UpdatePipelineIdReason};
21use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
22use servo_constellation_traits::{
23 IFrameLoadInfo, IFrameLoadInfoWithData, LoadData, LoadOrigin, NavigationHistoryBehavior,
24 ScriptToConstellationMessage, TargetSnapshotParams,
25};
26use servo_url::ServoUrl;
27use style::attr::{AttrValue, LengthOrPercentageOrAuto};
28use stylo_atoms::Atom;
29
30use crate::document_loader::{LoadBlocker, LoadType};
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::refcounted::Trusted;
37use crate::dom::bindings::reflector::DomGlobal;
38use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
39use crate::dom::bindings::str::{DOMString, USVString};
40use crate::dom::document::Document;
41use crate::dom::domtokenlist::DOMTokenList;
42use crate::dom::element::attributes::storage::AttrRef;
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_thread::{ScriptThread, with_script_thread};
57use crate::script_window_proxies::ScriptWindowProxies;
58
59#[derive(PartialEq)]
60enum PipelineType {
61 InitialAboutBlank,
62 Navigation,
63}
64
65#[derive(Clone, Copy, PartialEq)]
66pub(crate) enum ProcessingMode {
67 FirstTime,
68 NotFirstTime,
69}
70
71#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
73enum LazyLoadResumptionSteps {
74 #[default]
75 None,
76 SrcDoc,
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 #[no_trace]
94 sandboxing_flag_set: Cell<Option<SandboxingFlagSet>>,
95 load_blocker: DomRefCell<Option<LoadBlocker>>,
96 throttled: Cell<bool>,
97 #[conditional_malloc_size_of]
98 script_window_proxies: Rc<ScriptWindowProxies>,
99 current_navigation_was_lazy_loaded: Cell<bool>,
101 #[no_trace]
103 lazy_load_resumption_steps: Cell<LazyLoadResumptionSteps>,
104 pending_navigation: Cell<bool>,
111 already_fired_synchronous_load_event: Cell<bool>,
115}
116
117impl HTMLIFrameElement {
118 fn shared_attribute_processing_steps_for_iframe_and_frame_elements(
120 &self,
121 _mode: ProcessingMode,
122 ) -> Option<ServoUrl> {
123 let element = self.upcast::<Element>();
124 let url = element
126 .get_attribute_string_value(&local_name!("src"))
127 .and_then(|url| {
128 if url.is_empty() {
129 None
130 } else {
131 self.owner_document().encoding_parse_a_url(&url).ok()
135 }
136 })
137 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
139 Some(url)
149 }
150
151 pub(crate) fn navigate_or_reload_child_browsing_context(
152 &self,
153 load_data: LoadData,
154 history_handling: NavigationHistoryBehavior,
155 mode: ProcessingMode,
156 target_snapshot_params: TargetSnapshotParams,
157 cx: &mut JSContext,
158 ) {
159 self.already_fired_synchronous_load_event.set(false);
166
167 self.start_new_pipeline(
168 cx,
169 load_data,
170 PipelineType::Navigation,
171 history_handling,
172 mode,
173 target_snapshot_params,
174 );
175 }
176
177 fn start_new_pipeline(
178 &self,
179 cx: &mut JSContext,
180 mut load_data: LoadData,
181 pipeline_type: PipelineType,
182 history_handling: NavigationHistoryBehavior,
183 mode: ProcessingMode,
184 target_snapshot_params: TargetSnapshotParams,
185 ) {
186 let document = self.owner_document();
187
188 {
189 let load_blocker = &self.load_blocker;
190 LoadBlocker::terminate(load_blocker, cx);
193
194 *load_blocker.borrow_mut() = Some(LoadBlocker::new(
195 &document,
196 LoadType::Subframe(load_data.url.clone()),
197 ));
198 }
199
200 if load_data.url.scheme() != "javascript" {
201 self.continue_navigation(
202 cx,
203 load_data,
204 pipeline_type,
205 history_handling,
206 target_snapshot_params,
207 );
208 return;
209 }
210
211 let iframe = Trusted::new(self);
215 let doc = Trusted::new(&*document);
216 document
217 .global()
218 .task_manager()
219 .networking_task_source()
220 .queue(task!(navigate_to_javascript: move |cx| {
221 let this = iframe.root();
222 let window_proxy = this.GetContentWindow();
223 if let Some(window_proxy) = window_proxy {
224 if !ScriptThread::navigate_to_javascript_url(
227 cx,
228 &this.owner_global(),
229 &window_proxy.global(),
230 &mut load_data,
231 Some(this.upcast()),
232 Some(mode == ProcessingMode::FirstTime),
233 ) {
234 LoadBlocker::terminate(&this.load_blocker, cx);
235 return;
236 }
237 load_data.about_base_url = doc.root().about_base_url();
238 }
239 this.continue_navigation(cx, load_data, pipeline_type, history_handling, target_snapshot_params);
240 }));
241 }
242
243 fn continue_navigation(
244 &self,
245 cx: &mut JSContext,
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(cx, 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 let Some(window) = self.GetContentWindow()
468 {
469 window.set_name(
470 element
471 .get_name()
472 .map_or(DOMString::from(""), |n| DOMString::from(&*n)),
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 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 ancestor = a.parent().map(DomRoot::from_ref);
529 }
530
531 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
532 (Some(window.pipeline_id()), Some(document.base_url()))
533 } else {
534 (None, document.about_base_url())
535 };
536
537 let propagate_encoding_to_child_document = url.origin().same_origin(window.origin());
538 let mut load_data = LoadData::new(
539 LoadOrigin::Script(document.origin().snapshot()),
540 url,
541 about_base_url,
542 creator_pipeline_id,
543 window.as_global_scope().get_referrer(),
544 referrer_policy,
545 Some(window.as_global_scope().is_secure_context()),
546 Some(document.insecure_requests_policy()),
547 document.has_trustworthy_ancestor_or_current_origin(),
548 self.sandboxing_flag_set(),
549 );
550 load_data.destination = Destination::IFrame;
551 load_data.policy_container = Some(window.as_global_scope().policy_container());
552 if propagate_encoding_to_child_document {
553 load_data.container_document_encoding = Some(document.encoding());
554 }
555
556 let pipeline_id = self.pipeline_id();
557 let is_about_blank =
560 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
561
562 let history_handling = if is_about_blank {
563 NavigationHistoryBehavior::Replace
564 } else {
565 NavigationHistoryBehavior::Push
566 };
567
568 let target_snapshot_params = snapshot_self(self);
569 self.navigate_or_reload_child_browsing_context(
570 load_data,
571 history_handling,
572 mode,
573 target_snapshot_params,
574 cx,
575 );
576 }
577
578 fn create_nested_browsing_context(&self, cx: &mut JSContext) {
586 let url = ServoUrl::parse("about:blank").unwrap();
587 let document = self.owner_document();
588 let window = self.owner_window();
589 let pipeline_id = Some(window.pipeline_id());
590 let mut load_data = LoadData::new(
591 LoadOrigin::Script(document.origin().snapshot()),
592 url,
593 Some(document.base_url()),
594 pipeline_id,
595 window.as_global_scope().get_referrer(),
596 document.get_referrer_policy(),
597 Some(window.as_global_scope().is_secure_context()),
598 Some(document.insecure_requests_policy()),
599 document.has_trustworthy_ancestor_or_current_origin(),
600 self.sandboxing_flag_set(),
601 );
602 load_data.destination = Destination::IFrame;
603 load_data.policy_container = Some(window.as_global_scope().policy_container());
604
605 let browsing_context_id = BrowsingContextId::new();
606 let webview_id = window.window_proxy().webview_id();
607 self.pipeline_id.set(None);
608 self.pending_pipeline_id.set(None);
609 self.webview_id.set(Some(webview_id));
610 self.browsing_context_id.set(Some(browsing_context_id));
611 self.start_new_pipeline(
612 cx,
613 load_data,
614 PipelineType::InitialAboutBlank,
615 NavigationHistoryBehavior::Push,
616 ProcessingMode::FirstTime,
617 snapshot_self(self),
618 );
619 }
620
621 fn destroy_nested_browsing_context(&self) {
622 self.pipeline_id.set(None);
623 self.pending_pipeline_id.set(None);
624 self.about_blank_pipeline_id.set(None);
625 self.webview_id.set(None);
626 if let Some(browsing_context_id) = self.browsing_context_id.take() {
627 self.script_window_proxies.remove(browsing_context_id)
628 }
629 }
630
631 pub(crate) fn update_pipeline_id(
635 &self,
636 new_pipeline_id: PipelineId,
637 reason: UpdatePipelineIdReason,
638 cx: &mut JSContext,
639 ) -> bool {
640 if !self.is_initial_blank_document() {
645 self.pending_navigation.set(false);
646 }
647 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
648 reason == UpdatePipelineIdReason::Navigation
649 {
650 return false;
651 }
652
653 self.pipeline_id.set(Some(new_pipeline_id));
654
655 if reason == UpdatePipelineIdReason::Traversal {
658 let blocker = &self.load_blocker;
659 LoadBlocker::terminate(blocker, cx);
660 }
661
662 self.upcast::<Node>().dirty(NodeDamage::Other);
663 true
664 }
665
666 fn new_inherited(
667 local_name: LocalName,
668 prefix: Option<Prefix>,
669 document: &Document,
670 ) -> HTMLIFrameElement {
671 HTMLIFrameElement {
672 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
673 browsing_context_id: Cell::new(None),
674 webview_id: Cell::new(None),
675 pipeline_id: Cell::new(None),
676 pending_pipeline_id: Cell::new(None),
677 about_blank_pipeline_id: Cell::new(None),
678 sandbox: Default::default(),
679 sandboxing_flag_set: Cell::new(None),
680 load_blocker: DomRefCell::new(None),
681 throttled: Cell::new(false),
682 script_window_proxies: ScriptThread::window_proxies(),
683 current_navigation_was_lazy_loaded: Default::default(),
684 lazy_load_resumption_steps: Default::default(),
685 pending_navigation: Default::default(),
686 already_fired_synchronous_load_event: Default::default(),
687 }
688 }
689
690 pub(crate) fn new(
691 cx: &mut JSContext,
692 local_name: LocalName,
693 prefix: Option<Prefix>,
694 document: &Document,
695 proto: Option<HandleObject>,
696 ) -> DomRoot<HTMLIFrameElement> {
697 Node::reflect_node_with_proto(
698 cx,
699 Box::new(HTMLIFrameElement::new_inherited(
700 local_name, prefix, document,
701 )),
702 document,
703 proto,
704 )
705 }
706
707 #[inline]
708 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
709 self.pipeline_id.get()
710 }
711
712 #[inline]
713 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
714 self.browsing_context_id.get()
715 }
716
717 #[inline]
718 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
719 self.webview_id.get()
720 }
721
722 #[inline]
723 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
724 self.sandboxing_flag_set
725 .get()
726 .unwrap_or_else(SandboxingFlagSet::empty)
727 }
728
729 pub(crate) fn set_throttled(&self, throttled: bool) {
730 if self.throttled.get() != throttled {
731 self.throttled.set(throttled);
732 }
733 }
734
735 pub(crate) fn note_pending_navigation(&self) {
739 self.pending_navigation.set(true);
740 }
741
742 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, cx: &mut JSContext) {
744 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
747 return;
748 }
749
750 let should_fire_event = if self.is_initial_blank_document() {
779 !self.pending_navigation.get() &&
783 !self.upcast::<Element>().has_attribute(&local_name!("src"))
784 } else {
785 !self.pending_navigation.get()
788 };
789
790 let should_fire_event =
793 !self.already_fired_synchronous_load_event.replace(false) && should_fire_event;
794 if should_fire_event {
795 self.run_iframe_load_event_steps(cx);
796 } else {
797 debug!(
798 "suppressing load event for iframe, loaded {:?}",
799 loaded_pipeline
800 );
801 }
802 }
803
804 pub(crate) fn run_iframe_load_event_steps(&self, cx: &mut JSContext) {
806 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
814
815 let blocker = &self.load_blocker;
816 LoadBlocker::terminate(blocker, cx);
817
818 }
820
821 fn parse_sandbox_attribute(&self) {
825 let sandbox_value =
826 self.upcast::<Element>()
827 .with_attribute(&ns!(), &local_name!("sandbox"), |attribute| {
828 let tokens: Vec<_> = attribute
829 .value()
830 .as_tokens()
831 .iter()
832 .map(|atom| atom.to_string().to_ascii_lowercase())
833 .collect();
834 parse_a_sandboxing_directive(&tokens)
835 });
836 self.sandboxing_flag_set.set(sandbox_value);
837 }
838
839 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
841 let Some(pipeline_id) = self.pipeline_id.get() else {
842 return;
843 };
844 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
846 exited_document.destroy_document_and_its_descendants(cx);
847 }
848 self.destroy_nested_browsing_context();
849 }
850
851 fn destroy_child_navigable(&self, cx: &mut JSContext) {
853 let blocker = &self.load_blocker;
854 LoadBlocker::terminate(blocker, cx);
855
856 let Some(browsing_context_id) = self.browsing_context_id() else {
858 return;
860 };
861 let pipeline_id = self.pipeline_id.get();
864
865 self.destroy_nested_browsing_context();
873
874 let (sender, receiver) =
879 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
880 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
881 self.owner_window()
882 .as_global_scope()
883 .script_to_constellation_chan()
884 .send(msg)
885 .unwrap();
886 let _exited_pipeline_ids = receiver.recv().unwrap();
887 let Some(pipeline_id) = pipeline_id else {
888 return;
889 };
890 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
891 exited_document.destroy_document_and_its_descendants(cx);
892 }
893
894 }
909}
910
911impl LayoutDom<'_, HTMLIFrameElement> {
912 #[inline]
913 pub(crate) fn pipeline_id(self) -> Option<PipelineId> {
914 (self.unsafe_get()).pipeline_id.get()
915 }
916
917 #[inline]
918 pub(crate) fn browsing_context_id(self) -> Option<BrowsingContextId> {
919 (self.unsafe_get()).browsing_context_id.get()
920 }
921
922 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
923 self.upcast::<Element>()
924 .get_attr_for_layout(&ns!(), &local_name!("width"))
925 .map(AttrValue::as_dimension)
926 .cloned()
927 .unwrap_or(LengthOrPercentageOrAuto::Auto)
928 }
929
930 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
931 self.upcast::<Element>()
932 .get_attr_for_layout(&ns!(), &local_name!("height"))
933 .map(AttrValue::as_dimension)
934 .cloned()
935 .unwrap_or(LengthOrPercentageOrAuto::Auto)
936 }
937}
938
939impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
940 make_url_getter!(Src, "src");
942
943 make_url_setter!(cx, SetSrc, "src");
945
946 fn Srcdoc(&self) -> TrustedHTMLOrString {
948 let element = self.upcast::<Element>();
949 element.get_trusted_html_attribute(&local_name!("srcdoc"))
950 }
951
952 fn SetSrcdoc(&self, cx: &mut JSContext, value: TrustedHTMLOrString) -> Fallible<()> {
954 let element = self.upcast::<Element>();
958 let value = TrustedHTML::get_trusted_type_compliant_string(
959 cx,
960 &element.owner_global(),
961 value,
962 "HTMLIFrameElement srcdoc",
963 )?;
964 element.set_attribute(
966 cx,
967 &local_name!("srcdoc"),
968 AttrValue::String(value.str().to_owned()),
969 );
970 Ok(())
971 }
972
973 fn Sandbox(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
979 self.sandbox.or_init(|| {
980 DOMTokenList::new(
981 cx,
982 self.upcast::<Element>(),
983 &local_name!("sandbox"),
984 Some(vec![
985 Atom::from("allow-downloads"),
986 Atom::from("allow-forms"),
987 Atom::from("allow-modals"),
988 Atom::from("allow-orientation-lock"),
989 Atom::from("allow-pointer-lock"),
990 Atom::from("allow-popups"),
991 Atom::from("allow-popups-to-escape-sandbox"),
992 Atom::from("allow-presentation"),
993 Atom::from("allow-same-origin"),
994 Atom::from("allow-scripts"),
995 Atom::from("allow-top-navigation"),
996 Atom::from("allow-top-navigation-by-user-activation"),
997 Atom::from("allow-top-navigation-to-custom-protocols"),
998 ]),
999 )
1000 })
1001 }
1002
1003 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
1005 self.browsing_context_id
1006 .get()
1007 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
1008 }
1009
1010 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
1012 let pipeline_id = self.pipeline_id.get()?;
1014
1015 let document = ScriptThread::find_document(pipeline_id)?;
1019 if !self
1021 .owner_document()
1022 .origin()
1023 .same_origin_domain(&document.origin())
1024 {
1025 return None;
1026 }
1027 Some(document)
1029 }
1030
1031 fn ReferrerPolicy(&self) -> DOMString {
1033 reflect_referrer_policy_attribute(self.upcast::<Element>())
1034 }
1035
1036 make_setter!(cx, SetReferrerPolicy, "referrerpolicy");
1038
1039 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1041 make_bool_setter!(cx, SetAllowFullscreen, "allowfullscreen");
1043
1044 make_getter!(Width, "width");
1046 make_dimension_setter!(SetWidth, "width");
1048
1049 make_getter!(Height, "height");
1051 make_dimension_setter!(SetHeight, "height");
1053
1054 make_getter!(FrameBorder, "frameborder");
1056 make_setter!(cx, SetFrameBorder, "frameborder");
1058
1059 make_atomic_setter!(cx, SetName, "name");
1063
1064 make_getter!(Name, "name");
1068
1069 make_enumerated_getter!(
1072 Loading,
1073 "loading",
1074 "lazy" | "eager",
1075 missing => "eager",
1078 invalid => "eager"
1079 );
1080
1081 make_setter!(cx, SetLoading, "loading");
1083
1084 make_url_getter!(LongDesc, "longdesc");
1086
1087 make_url_setter!(cx, SetLongDesc, "longdesc");
1089}
1090
1091impl VirtualMethods for HTMLIFrameElement {
1092 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1093 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1094 }
1095
1096 fn attribute_mutated(
1097 &self,
1098 cx: &mut JSContext,
1099 attr: AttrRef<'_>,
1100 mutation: AttributeMutation,
1101 ) {
1102 self.super_type()
1103 .unwrap()
1104 .attribute_mutated(cx, attr, mutation);
1105 match *attr.local_name() {
1106 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
1117 self.parse_sandbox_attribute();
1118 },
1119 local_name!("srcdoc") => {
1120 if self.upcast::<Node>().is_connected_with_browsing_context() {
1131 debug!("iframe srcdoc modified while in browsing context.");
1132 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1133 }
1134 },
1135 local_name!("src") => {
1136 if self.upcast::<Node>().is_connected_with_browsing_context() {
1145 debug!("iframe src set while in browsing context.");
1146 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1147 }
1148 },
1149 local_name!("loading") => {
1150 if !mutation.is_removal() && &**attr.value() == "lazy" {
1153 return;
1154 }
1155
1156 let previous_resumption_steps = self
1159 .lazy_load_resumption_steps
1160 .replace(LazyLoadResumptionSteps::None);
1161 match previous_resumption_steps {
1162 LazyLoadResumptionSteps::None => (),
1164 LazyLoadResumptionSteps::SrcDoc => {
1165 self.navigate_to_the_srcdoc_resource(ProcessingMode::NotFirstTime, cx);
1167 },
1168 }
1169 },
1170 _ => {},
1171 }
1172 }
1173
1174 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1175 match attr.local_name() {
1176 &local_name!("width") | &local_name!("height") => true,
1177 _ => self
1178 .super_type()
1179 .unwrap()
1180 .attribute_affects_presentational_hints(attr),
1181 }
1182 }
1183
1184 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1185 match *name {
1186 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
1187 local_name!("width") => AttrValue::from_dimension(value.into()),
1188 local_name!("height") => AttrValue::from_dimension(value.into()),
1189 _ => self
1190 .super_type()
1191 .unwrap()
1192 .parse_plain_attribute(name, value),
1193 }
1194 }
1195
1196 fn post_connection_steps(&self, cx: &mut JSContext) {
1198 if let Some(s) = self.super_type() {
1199 s.post_connection_steps(cx);
1200 }
1201
1202 if !self.upcast::<Node>().is_connected_with_browsing_context() {
1206 return;
1207 }
1208
1209 debug!("<iframe> running post connection steps");
1210
1211 self.create_nested_browsing_context(cx);
1213
1214 self.parse_sandbox_attribute();
1217
1218 self.process_the_iframe_attributes(ProcessingMode::FirstTime, cx);
1220 }
1221
1222 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1223 if let Some(super_type) = self.super_type() {
1224 super_type.bind_to_tree(cx, context);
1225 }
1226
1227 self.owner_document().iframes_mut().add(self);
1228 }
1229
1230 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1232 if let Some(super_type) = self.super_type() {
1233 super_type.unbind_from_tree(cx, context);
1234 }
1235
1236 self.destroy_child_navigable(cx);
1239
1240 self.owner_document().iframes_mut().remove(self);
1241 }
1242}
1243
1244pub(crate) struct IframeContext<'a> {
1247 element: &'a HTMLIFrameElement,
1249 url: ServoUrl,
1251}
1252
1253impl<'a> IframeContext<'a> {
1254 pub fn new(element: &'a HTMLIFrameElement) -> Self {
1256 Self {
1257 element,
1258 url: element
1259 .shared_attribute_processing_steps_for_iframe_and_frame_elements(
1260 ProcessingMode::NotFirstTime,
1261 )
1262 .expect("Must always have a URL when navigating"),
1263 }
1264 }
1265}
1266
1267impl<'a> ResourceTimingListener for IframeContext<'a> {
1268 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1269 (
1270 InitiatorType::LocalName("iframe".to_string()),
1271 self.url.clone(),
1272 )
1273 }
1274
1275 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1276 self.element.upcast::<Node>().owner_doc().global()
1277 }
1278}
1279
1280fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1281 let child_navigable = iframe.GetContentWindow();
1282 TargetSnapshotParams {
1283 sandboxing_flags: determine_creation_sandboxing_flags(
1284 child_navigable.as_deref(),
1285 Some(iframe.upcast()),
1286 ),
1287 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1288 iframe.upcast(),
1289 )),
1290 }
1291}