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 attribute = self
826 .upcast::<Element>()
827 .get_attribute(&local_name!("sandbox"));
828 self.sandboxing_flag_set
829 .set(attribute.map(|attribute_value| {
830 let tokens: Vec<_> = attribute_value
831 .value()
832 .as_tokens()
833 .iter()
834 .map(|atom| atom.to_string().to_ascii_lowercase())
835 .collect();
836 parse_a_sandboxing_directive(&tokens)
837 }));
838 }
839
840 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
842 let Some(pipeline_id) = self.pipeline_id.get() else {
843 return;
844 };
845 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
847 exited_document.destroy_document_and_its_descendants(cx);
848 }
849 self.destroy_nested_browsing_context();
850 }
851
852 fn destroy_child_navigable(&self, cx: &mut JSContext) {
854 let blocker = &self.load_blocker;
855 LoadBlocker::terminate(blocker, cx);
856
857 let Some(browsing_context_id) = self.browsing_context_id() else {
859 return;
861 };
862 let pipeline_id = self.pipeline_id.get();
865
866 self.destroy_nested_browsing_context();
874
875 let (sender, receiver) =
880 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
881 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
882 self.owner_window()
883 .as_global_scope()
884 .script_to_constellation_chan()
885 .send(msg)
886 .unwrap();
887 let _exited_pipeline_ids = receiver.recv().unwrap();
888 let Some(pipeline_id) = pipeline_id else {
889 return;
890 };
891 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
892 exited_document.destroy_document_and_its_descendants(cx);
893 }
894
895 }
910}
911
912impl LayoutDom<'_, HTMLIFrameElement> {
913 #[inline]
914 pub(crate) fn pipeline_id(self) -> Option<PipelineId> {
915 (self.unsafe_get()).pipeline_id.get()
916 }
917
918 #[inline]
919 pub(crate) fn browsing_context_id(self) -> Option<BrowsingContextId> {
920 (self.unsafe_get()).browsing_context_id.get()
921 }
922
923 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
924 self.upcast::<Element>()
925 .get_attr_for_layout(&ns!(), &local_name!("width"))
926 .map(AttrValue::as_dimension)
927 .cloned()
928 .unwrap_or(LengthOrPercentageOrAuto::Auto)
929 }
930
931 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
932 self.upcast::<Element>()
933 .get_attr_for_layout(&ns!(), &local_name!("height"))
934 .map(AttrValue::as_dimension)
935 .cloned()
936 .unwrap_or(LengthOrPercentageOrAuto::Auto)
937 }
938}
939
940impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
941 make_url_getter!(Src, "src");
943
944 make_url_setter!(cx, SetSrc, "src");
946
947 fn Srcdoc(&self) -> TrustedHTMLOrString {
949 let element = self.upcast::<Element>();
950 element.get_trusted_html_attribute(&local_name!("srcdoc"))
951 }
952
953 fn SetSrcdoc(&self, cx: &mut JSContext, value: TrustedHTMLOrString) -> Fallible<()> {
955 let element = self.upcast::<Element>();
959 let value = TrustedHTML::get_trusted_type_compliant_string(
960 cx,
961 &element.owner_global(),
962 value,
963 "HTMLIFrameElement srcdoc",
964 )?;
965 element.set_attribute(
967 cx,
968 &local_name!("srcdoc"),
969 AttrValue::String(value.str().to_owned()),
970 );
971 Ok(())
972 }
973
974 fn Sandbox(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
980 self.sandbox.or_init(|| {
981 DOMTokenList::new(
982 cx,
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 )
1001 })
1002 }
1003
1004 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
1006 self.browsing_context_id
1007 .get()
1008 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
1009 }
1010
1011 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
1013 let pipeline_id = self.pipeline_id.get()?;
1015
1016 let document = ScriptThread::find_document(pipeline_id)?;
1020 if !self
1022 .owner_document()
1023 .origin()
1024 .same_origin_domain(&document.origin())
1025 {
1026 return None;
1027 }
1028 Some(document)
1030 }
1031
1032 fn ReferrerPolicy(&self) -> DOMString {
1034 reflect_referrer_policy_attribute(self.upcast::<Element>())
1035 }
1036
1037 make_setter!(cx, SetReferrerPolicy, "referrerpolicy");
1039
1040 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1042 make_bool_setter!(cx, SetAllowFullscreen, "allowfullscreen");
1044
1045 make_getter!(Width, "width");
1047 make_dimension_setter!(SetWidth, "width");
1049
1050 make_getter!(Height, "height");
1052 make_dimension_setter!(SetHeight, "height");
1054
1055 make_getter!(FrameBorder, "frameborder");
1057 make_setter!(cx, SetFrameBorder, "frameborder");
1059
1060 make_atomic_setter!(cx, SetName, "name");
1064
1065 make_getter!(Name, "name");
1069
1070 make_enumerated_getter!(
1073 Loading,
1074 "loading",
1075 "lazy" | "eager",
1076 missing => "eager",
1079 invalid => "eager"
1080 );
1081
1082 make_setter!(cx, SetLoading, "loading");
1084
1085 make_url_getter!(LongDesc, "longdesc");
1087
1088 make_url_setter!(cx, SetLongDesc, "longdesc");
1090}
1091
1092impl VirtualMethods for HTMLIFrameElement {
1093 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1094 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1095 }
1096
1097 fn attribute_mutated(
1098 &self,
1099 cx: &mut JSContext,
1100 attr: AttrRef<'_>,
1101 mutation: AttributeMutation,
1102 ) {
1103 self.super_type()
1104 .unwrap()
1105 .attribute_mutated(cx, attr, mutation);
1106 match *attr.local_name() {
1107 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
1118 self.parse_sandbox_attribute();
1119 },
1120 local_name!("srcdoc") => {
1121 if self.upcast::<Node>().is_connected_with_browsing_context() {
1132 debug!("iframe srcdoc modified while in browsing context.");
1133 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1134 }
1135 },
1136 local_name!("src") => {
1137 if self.upcast::<Node>().is_connected_with_browsing_context() {
1146 debug!("iframe src set while in browsing context.");
1147 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1148 }
1149 },
1150 local_name!("loading") => {
1151 if !mutation.is_removal() && &**attr.value() == "lazy" {
1154 return;
1155 }
1156
1157 let previous_resumption_steps = self
1160 .lazy_load_resumption_steps
1161 .replace(LazyLoadResumptionSteps::None);
1162 match previous_resumption_steps {
1163 LazyLoadResumptionSteps::None => (),
1165 LazyLoadResumptionSteps::SrcDoc => {
1166 self.navigate_to_the_srcdoc_resource(ProcessingMode::NotFirstTime, cx);
1168 },
1169 }
1170 },
1171 _ => {},
1172 }
1173 }
1174
1175 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1176 match attr.local_name() {
1177 &local_name!("width") | &local_name!("height") => true,
1178 _ => self
1179 .super_type()
1180 .unwrap()
1181 .attribute_affects_presentational_hints(attr),
1182 }
1183 }
1184
1185 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1186 match *name {
1187 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
1188 local_name!("width") => AttrValue::from_dimension(value.into()),
1189 local_name!("height") => AttrValue::from_dimension(value.into()),
1190 _ => self
1191 .super_type()
1192 .unwrap()
1193 .parse_plain_attribute(name, value),
1194 }
1195 }
1196
1197 fn post_connection_steps(&self, cx: &mut JSContext) {
1199 if let Some(s) = self.super_type() {
1200 s.post_connection_steps(cx);
1201 }
1202
1203 if !self.upcast::<Node>().is_connected_with_browsing_context() {
1207 return;
1208 }
1209
1210 debug!("<iframe> running post connection steps");
1211
1212 self.create_nested_browsing_context(cx);
1214
1215 self.parse_sandbox_attribute();
1218
1219 self.process_the_iframe_attributes(ProcessingMode::FirstTime, cx);
1221 }
1222
1223 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1224 if let Some(s) = self.super_type() {
1225 s.bind_to_tree(cx, context);
1226 }
1227 self.owner_document().invalidate_iframes_collection();
1228 }
1229
1230 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1232 self.super_type().unwrap().unbind_from_tree(cx, context);
1233
1234 self.destroy_child_navigable(cx);
1236
1237 self.owner_document().invalidate_iframes_collection();
1238 }
1239}
1240
1241pub(crate) struct IframeContext<'a> {
1244 element: &'a HTMLIFrameElement,
1246 url: ServoUrl,
1248}
1249
1250impl<'a> IframeContext<'a> {
1251 pub fn new(element: &'a HTMLIFrameElement) -> Self {
1253 Self {
1254 element,
1255 url: element
1256 .shared_attribute_processing_steps_for_iframe_and_frame_elements(
1257 ProcessingMode::NotFirstTime,
1258 )
1259 .expect("Must always have a URL when navigating"),
1260 }
1261 }
1262}
1263
1264impl<'a> ResourceTimingListener for IframeContext<'a> {
1265 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1266 (
1267 InitiatorType::LocalName("iframe".to_string()),
1268 self.url.clone(),
1269 )
1270 }
1271
1272 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1273 self.element.upcast::<Node>().owner_doc().global()
1274 }
1275}
1276
1277fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1278 let child_navigable = iframe.GetContentWindow();
1279 TargetSnapshotParams {
1280 sandboxing_flags: determine_creation_sandboxing_flags(
1281 child_navigable.as_deref(),
1282 Some(iframe.upcast()),
1283 ),
1284 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1285 iframe.upcast(),
1286 )),
1287 }
1288}