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::generic_channel::channel;
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::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
31use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
32use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
33use crate::dom::bindings::error::Fallible;
34use crate::dom::bindings::inheritance::Castable;
35use crate::dom::bindings::refcounted::Trusted;
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::attributes::storage::AttrRef;
42use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
43use crate::dom::eventtarget::EventTarget;
44use crate::dom::html::htmlelement::HTMLElement;
45use crate::dom::node::virtualmethods::VirtualMethods;
46use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
47use crate::dom::performance::performanceresourcetiming::InitiatorType;
48use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
49use crate::dom::windowproxy::WindowProxy;
50use crate::event_loop::document_loader::{LoadBlocker, LoadType};
51use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
52use crate::event_loop::script_window_proxies::ScriptWindowProxies;
53use crate::fetch::network_listener::ResourceTimingListener;
54use crate::navigation::{
55 determine_creation_sandboxing_flags, determine_iframe_element_referrer_policy,
56};
57
58#[derive(PartialEq)]
59enum PipelineType {
60 InitialAboutBlank,
61 Navigation,
62}
63
64#[derive(Clone, Copy, PartialEq)]
65pub(crate) enum ProcessingMode {
66 FirstTime,
67 NotFirstTime,
68}
69
70#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
72enum LazyLoadResumptionSteps {
73 #[default]
74 None,
75 SrcDoc,
76}
77
78#[dom_struct]
79pub(crate) struct HTMLIFrameElement {
80 htmlelement: HTMLElement,
81 #[no_trace]
82 webview_id: Cell<Option<WebViewId>>,
83 #[no_trace]
84 browsing_context_id: Cell<Option<BrowsingContextId>>,
85 #[no_trace]
86 pipeline_id: Cell<Option<PipelineId>>,
87 #[no_trace]
88 pending_pipeline_id: Cell<Option<PipelineId>>,
89 #[no_trace]
90 about_blank_pipeline_id: Cell<Option<PipelineId>>,
91 sandbox: MutNullableDom<DOMTokenList>,
92 #[no_trace]
93 sandboxing_flag_set: Cell<Option<SandboxingFlagSet>>,
94 load_blocker: DomRefCell<Option<LoadBlocker>>,
95 #[conditional_malloc_size_of]
96 script_window_proxies: Rc<ScriptWindowProxies>,
97 current_navigation_was_lazy_loaded: Cell<bool>,
99 #[no_trace]
101 lazy_load_resumption_steps: Cell<LazyLoadResumptionSteps>,
102 pending_navigation: Cell<bool>,
109 frozen_name: DomRefCell<Option<String>>,
114}
115
116impl HTMLIFrameElement {
117 fn shared_attribute_processing_steps_for_iframe_and_frame_elements(
119 &self,
120 _mode: ProcessingMode,
121 ) -> Option<ServoUrl> {
122 let element = self.upcast::<Element>();
123 let url = element
125 .get_attribute_string_value(&local_name!("src"))
126 .and_then(|url| {
127 if url.is_empty() {
128 None
129 } else {
130 self.owner_document().encoding_parse_a_url(&url).ok()
134 }
135 })
136 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
138 Some(url)
148 }
149
150 pub(crate) fn navigate_or_reload_child_browsing_context(
151 &self,
152 cx: &mut JSContext,
153 load_data: LoadData,
154 history_handling: NavigationHistoryBehavior,
155 mode: ProcessingMode,
156 target_snapshot_params: TargetSnapshotParams,
157 ) {
158 self.start_new_pipeline(
159 cx,
160 load_data,
161 PipelineType::Navigation,
162 history_handling,
163 mode,
164 target_snapshot_params,
165 );
166 }
167
168 fn start_new_pipeline(
169 &self,
170 cx: &mut JSContext,
171 mut load_data: LoadData,
172 pipeline_type: PipelineType,
173 history_handling: NavigationHistoryBehavior,
174 mode: ProcessingMode,
175 target_snapshot_params: TargetSnapshotParams,
176 ) {
177 let document = self.owner_document();
178
179 {
180 let load_blocker = &self.load_blocker;
181 LoadBlocker::terminate(cx, load_blocker);
184
185 *load_blocker.borrow_mut() = Some(LoadBlocker::new(
186 &document,
187 LoadType::Subframe(load_data.url.clone()),
188 ));
189 }
190
191 if load_data.url.scheme() != "javascript" {
192 self.continue_navigation(
193 cx,
194 load_data,
195 pipeline_type,
196 history_handling,
197 target_snapshot_params,
198 );
199 return;
200 }
201
202 let iframe = Trusted::new(self);
206 let doc = Trusted::new(&*document);
207 document
208 .global()
209 .task_manager()
210 .networking_task_source()
211 .queue(task!(navigate_to_javascript: move |cx| {
212 let this = iframe.root();
213 let window_proxy = this.GetContentWindow();
214 if let Some(window_proxy) = window_proxy {
215 if !ScriptThread::navigate_to_javascript_url(
218 cx,
219 &this.owner_global(),
220 &window_proxy.global(),
221 &mut load_data,
222 Some(this.upcast()),
223 Some(mode == ProcessingMode::FirstTime),
224 ) {
225 LoadBlocker::terminate(cx, &this.load_blocker);
226 return;
227 }
228 load_data.about_base_url = doc.root().about_base_url();
229 }
230 this.continue_navigation(cx, load_data, pipeline_type, history_handling, target_snapshot_params);
231 }));
232 }
233
234 fn continue_navigation(
235 &self,
236 cx: &mut JSContext,
237 load_data: LoadData,
238 pipeline_type: PipelineType,
239 history_handling: NavigationHistoryBehavior,
240 target_snapshot_params: TargetSnapshotParams,
241 ) {
242 let browsing_context_id = match self.browsing_context_id() {
243 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
244 Some(id) => id,
245 };
246
247 let webview_id = match self.webview_id() {
248 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
249 Some(id) => id,
250 };
251
252 let window = self.owner_window();
253 let old_pipeline_id = self.pipeline_id();
254 let new_pipeline_id = PipelineId::new();
255 self.pending_pipeline_id.set(Some(new_pipeline_id));
256
257 let load_info = IFrameLoadInfo {
258 parent_pipeline_id: window.pipeline_id(),
259 browsing_context_id,
260 webview_id,
261 new_pipeline_id,
262 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
264 history_handling,
265 target_snapshot_params,
266 name: self.frozen_name.borrow().clone(),
267 };
268
269 let viewport_details = window
270 .get_iframe_viewport_details_if_known(cx, browsing_context_id)
271 .unwrap_or_else(|| ViewportDetails {
272 hidpi_scale_factor: window.device_pixel_ratio(),
273 ..Default::default()
274 });
275
276 match pipeline_type {
277 PipelineType::InitialAboutBlank => {
278 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
279
280 let load_info = IFrameLoadInfoWithData {
281 info: load_info,
282 load_data: load_data.clone(),
283 old_pipeline_id,
284 viewport_details,
285 };
286 window
287 .as_global_scope()
288 .script_to_constellation_chan()
289 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
290 .unwrap();
291
292 let new_pipeline_info = NewPipelineInfo {
293 webview_state: (*window.webview_state()).clone(),
294 parent_info: Some(window.pipeline_id()),
295 new_pipeline_id,
296 browsing_context_id,
297 opener: None,
298 load_data,
299 viewport_details,
300 user_content_manager_id: None,
301 target_snapshot_params,
302 frame_name: self.frozen_name.borrow().clone(),
303 };
304
305 self.pipeline_id.set(Some(new_pipeline_id));
306 with_script_thread(|script_thread| {
307 script_thread.spawn_pipeline(cx, new_pipeline_info);
308 });
309 },
310 PipelineType::Navigation => {
311 let load_info = IFrameLoadInfoWithData {
312 info: load_info,
313 load_data,
314 old_pipeline_id,
315 viewport_details,
316 };
317 window
318 .as_global_scope()
319 .script_to_constellation_chan()
320 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
321 load_info,
322 ))
323 .unwrap();
324 },
325 }
326 }
327
328 pub(crate) fn is_initial_blank_document(&self) -> bool {
334 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
335 }
336
337 fn navigate_an_iframe_or_frame(
339 &self,
340 cx: &mut JSContext,
341 load_data: LoadData,
342 mode: ProcessingMode,
343 ) {
344 let history_handling = if !self
347 .GetContentDocument()
348 .is_some_and(|doc| doc.completely_loaded())
349 {
350 NavigationHistoryBehavior::Replace
351 } else {
352 NavigationHistoryBehavior::Auto
354 };
355 let target_snapshot_params = snapshot_self(self);
363 self.navigate_or_reload_child_browsing_context(
364 cx,
365 load_data,
366 history_handling,
367 mode,
368 target_snapshot_params,
369 );
370 }
371
372 fn will_lazy_load_element_steps(&self) -> bool {
374 if !self.owner_document().scripting_enabled() {
376 return false;
377 }
378 self.Loading() == "lazy"
381 }
382
383 fn navigate_to_the_srcdoc_resource(&self, cx: &mut JSContext, mode: ProcessingMode) {
385 let url = ServoUrl::parse("about:srcdoc").unwrap();
388 let document = self.owner_document();
389 let window = self.owner_window();
390 let pipeline_id = Some(window.pipeline_id());
391 let mut load_data = LoadData::new(
392 LoadOrigin::Script(document.origin().snapshot()),
393 url,
394 Some(document.base_url()),
395 pipeline_id,
396 window.as_global_scope().get_referrer(),
397 document.get_referrer_policy(),
398 Some(window.as_global_scope().is_secure_context()),
399 Some(document.insecure_requests_policy()),
400 document.has_trustworthy_ancestor_or_current_origin(),
401 self.sandboxing_flag_set(),
402 );
403 load_data.destination = Destination::IFrame;
404 load_data.policy_container = Some(window.as_global_scope().policy_container());
405 load_data.srcdoc = String::from(
406 self.upcast::<Element>()
407 .get_string_attribute(&local_name!("srcdoc")),
408 );
409
410 self.navigate_an_iframe_or_frame(cx, load_data, mode);
411 }
412
413 fn mark_navigation_as_lazy_loaded(&self, cx: &mut JSContext) {
415 self.current_navigation_was_lazy_loaded.set(true);
417 let blocker = &self.load_blocker;
418 LoadBlocker::terminate(cx, blocker);
419 }
420
421 fn process_the_iframe_attributes(&self, cx: &mut JSContext, mode: ProcessingMode) {
423 let element = self.upcast::<Element>();
424
425 if element.has_attribute(&local_name!("srcdoc")) {
429 self.current_navigation_was_lazy_loaded.set(false);
431 if self.will_lazy_load_element_steps() {
433 self.lazy_load_resumption_steps
436 .set(LazyLoadResumptionSteps::SrcDoc);
437 self.mark_navigation_as_lazy_loaded(cx);
439 return;
443 }
444 self.navigate_to_the_srcdoc_resource(cx, mode);
447 return;
448 }
449
450 let window = self.owner_window();
451
452 let Some(url) = self.shared_attribute_processing_steps_for_iframe_and_frame_elements(mode)
455 else {
456 return;
458 };
459
460 if url.matches_about_blank() && mode == ProcessingMode::FirstTime {
462 self.run_iframe_load_event_steps(cx);
464 return;
466 }
467
468 let document = self.owner_document();
471 let referrer_policy_token = self.ReferrerPolicy();
472
473 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
477 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
478 policy => policy,
479 };
480
481 let mut ancestor = window.GetParent();
490 while let Some(a) = ancestor {
491 if let Some(ancestor_url) = a.document().map(|d| d.url()) &&
492 ancestor_url.scheme() == url.scheme() &&
493 ancestor_url.username() == url.username() &&
494 ancestor_url.password() == url.password() &&
495 ancestor_url.host() == url.host() &&
496 ancestor_url.port() == url.port() &&
497 ancestor_url.path() == url.path() &&
498 ancestor_url.query() == url.query()
499 {
500 return;
501 }
502 ancestor = a.parent().map(DomRoot::from_ref);
503 }
504
505 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
506 (Some(window.pipeline_id()), Some(document.base_url()))
507 } else {
508 (None, document.about_base_url())
509 };
510
511 let propagate_encoding_to_child_document = url.origin().same_origin(&window.origin());
512 let mut load_data = LoadData::new(
513 LoadOrigin::Script(document.origin().snapshot()),
514 url,
515 about_base_url,
516 creator_pipeline_id,
517 window.as_global_scope().get_referrer(),
518 referrer_policy,
519 Some(window.as_global_scope().is_secure_context()),
520 Some(document.insecure_requests_policy()),
521 document.has_trustworthy_ancestor_or_current_origin(),
522 self.sandboxing_flag_set(),
523 );
524 load_data.destination = Destination::IFrame;
525 load_data.policy_container = Some(window.as_global_scope().policy_container());
526 if propagate_encoding_to_child_document {
527 load_data.container_document_encoding = Some(document.encoding());
528 }
529
530 let pipeline_id = self.pipeline_id();
531 let is_about_blank =
534 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
535
536 let history_handling = if is_about_blank {
537 NavigationHistoryBehavior::Replace
538 } else {
539 NavigationHistoryBehavior::Push
540 };
541
542 let target_snapshot_params = snapshot_self(self);
543 self.navigate_or_reload_child_browsing_context(
544 cx,
545 load_data,
546 history_handling,
547 mode,
548 target_snapshot_params,
549 );
550 }
551
552 fn create_nested_browsing_context(&self, cx: &mut JSContext) {
555 let document = self.owner_document();
557 let window = self.owner_window();
558 let pipeline_id = Some(window.pipeline_id());
559 *self.frozen_name.borrow_mut() = self
563 .upcast::<Element>()
564 .get_name()
565 .map(|name| name.to_string());
566 let mut load_data = LoadData::new(
568 LoadOrigin::Script(document.origin().snapshot()),
571 ServoUrl::parse("about:blank").unwrap(),
572 Some(document.base_url()),
575 pipeline_id,
576 window.as_global_scope().get_referrer(),
577 document.get_referrer_policy(),
578 Some(window.as_global_scope().is_secure_context()),
579 Some(document.insecure_requests_policy()),
580 document.has_trustworthy_ancestor_or_current_origin(),
581 self.sandboxing_flag_set(),
582 );
583 load_data.is_initial_about_blank = true;
584 load_data.destination = Destination::IFrame;
585 load_data.policy_container = Some(window.as_global_scope().policy_container());
586
587 let browsing_context_id = BrowsingContextId::new();
589 let webview_id = window.window_proxy().webview_id();
590 self.pipeline_id.set(None);
591 self.pending_pipeline_id.set(None);
592 self.webview_id.set(Some(webview_id));
593 self.browsing_context_id.set(Some(browsing_context_id));
594 self.start_new_pipeline(
596 cx,
597 load_data,
598 PipelineType::InitialAboutBlank,
599 NavigationHistoryBehavior::Push,
600 ProcessingMode::FirstTime,
601 snapshot_self(self),
602 );
603 if let Some(window) = self.GetContentWindow() &&
606 let Some(window_name) = &*self.frozen_name.borrow()
607 {
608 window.set_name(window_name.as_str().into());
609 }
610 }
611
612 fn destroy_nested_browsing_context(&self) {
613 self.pipeline_id.set(None);
614 self.pending_pipeline_id.set(None);
615 self.about_blank_pipeline_id.set(None);
616 self.webview_id.set(None);
617 if let Some(browsing_context_id) = self.browsing_context_id.take() {
618 self.script_window_proxies.remove(browsing_context_id)
619 }
620 }
621
622 pub(crate) fn update_pipeline_id(
626 &self,
627 cx: &mut JSContext,
628 new_pipeline_id: PipelineId,
629 reason: UpdatePipelineIdReason,
630 ) -> bool {
631 if !self.is_initial_blank_document() {
636 self.pending_navigation.set(false);
637 }
638 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
639 reason == UpdatePipelineIdReason::Navigation
640 {
641 return false;
642 }
643
644 self.pipeline_id.set(Some(new_pipeline_id));
645
646 if reason == UpdatePipelineIdReason::Traversal {
649 let blocker = &self.load_blocker;
650 LoadBlocker::terminate(cx, blocker);
651 }
652
653 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
654 true
655 }
656
657 fn new_inherited(
658 local_name: LocalName,
659 prefix: Option<Prefix>,
660 document: &Document,
661 ) -> HTMLIFrameElement {
662 HTMLIFrameElement {
663 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
664 browsing_context_id: Cell::new(None),
665 webview_id: Cell::new(None),
666 pipeline_id: Cell::new(None),
667 pending_pipeline_id: Cell::new(None),
668 about_blank_pipeline_id: Cell::new(None),
669 sandbox: Default::default(),
670 sandboxing_flag_set: Cell::new(None),
671 load_blocker: DomRefCell::new(None),
672 script_window_proxies: ScriptThread::window_proxies(),
673 current_navigation_was_lazy_loaded: Default::default(),
674 lazy_load_resumption_steps: Default::default(),
675 pending_navigation: Default::default(),
676 frozen_name: Default::default(),
677 }
678 }
679
680 pub(crate) fn new(
681 cx: &mut JSContext,
682 local_name: LocalName,
683 prefix: Option<Prefix>,
684 document: &Document,
685 proto: Option<HandleObject>,
686 ) -> DomRoot<HTMLIFrameElement> {
687 Node::reflect_node_with_proto(
688 cx,
689 Box::new(HTMLIFrameElement::new_inherited(
690 local_name, prefix, document,
691 )),
692 document,
693 proto,
694 )
695 }
696
697 #[inline]
698 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
699 self.pipeline_id.get()
700 }
701
702 #[inline]
703 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
704 self.browsing_context_id.get()
705 }
706
707 #[inline]
708 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
709 self.webview_id.get()
710 }
711
712 #[inline]
713 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
714 self.sandboxing_flag_set
715 .get()
716 .unwrap_or_else(SandboxingFlagSet::empty)
717 }
718
719 pub(crate) fn note_pending_navigation(&self) {
723 self.pending_navigation.set(true);
724 }
725
726 pub(crate) fn iframe_load_event_steps(&self, cx: &mut JSContext, loaded_pipeline: PipelineId) {
728 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
731 return;
732 }
733
734 let should_fire_event = if self.is_initial_blank_document() {
763 !self.pending_navigation.get() &&
767 !self.upcast::<Element>().has_attribute(&local_name!("src"))
768 } else {
769 !self.pending_navigation.get()
772 };
773
774 if should_fire_event {
775 self.run_iframe_load_event_steps(cx);
776 } else {
777 debug!(
778 "suppressing load event for iframe, loaded {:?}",
779 loaded_pipeline
780 );
781 }
782 }
783
784 pub(crate) fn run_iframe_load_event_steps(&self, cx: &mut JSContext) {
786 let child_document = self.GetContentDocument();
790
791 if let Some(document) = child_document {
794 if document.mute_iframe_load_flag() {
795 let blocker = &self.load_blocker;
796 LoadBlocker::terminate(cx, blocker);
797 return;
798 }
799 document.set_iframe_load_in_progress(true);
800 }
801
802 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
807
808 let blocker = &self.load_blocker;
809 LoadBlocker::terminate(cx, blocker);
810
811 if let Some(child_document) = self.GetContentDocument() {
813 child_document.set_iframe_load_in_progress(false);
814 }
815 }
816
817 fn parse_sandbox_attribute(&self) {
821 let sandbox_value =
822 self.upcast::<Element>()
823 .with_attribute(&ns!(), &local_name!("sandbox"), |attribute| {
824 let tokens: Vec<_> = attribute
825 .value()
826 .as_tokens()
827 .iter()
828 .map(|atom| atom.to_ascii_lowercase().to_string())
829 .collect();
830 parse_a_sandboxing_directive(&tokens)
831 });
832 self.sandboxing_flag_set.set(sandbox_value);
833 }
834
835 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
837 let Some(pipeline_id) = self.pipeline_id.get() else {
838 return;
839 };
840 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
842 exited_document.destroy_document_and_its_descendants(cx);
843 }
844 self.destroy_nested_browsing_context();
845 }
846
847 fn destroy_child_navigable(&self, cx: &mut JSContext) {
849 let blocker = &self.load_blocker;
850 LoadBlocker::terminate(cx, blocker);
851
852 let Some(browsing_context_id) = self.browsing_context_id() else {
854 return;
856 };
857 let pipeline_id = self.pipeline_id.get();
860
861 self.destroy_nested_browsing_context();
869
870 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
875 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
876 self.owner_window()
877 .as_global_scope()
878 .script_to_constellation_chan()
879 .send(msg)
880 .unwrap();
881 let _exited_pipeline_ids = receiver.recv().unwrap();
882 let Some(pipeline_id) = pipeline_id else {
883 return;
884 };
885 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
886 exited_document.destroy_document_and_its_descendants(cx);
887 }
888
889 }
904}
905
906impl LayoutDom<'_, HTMLIFrameElement> {
907 #[inline]
908 pub(crate) fn pipeline_id(self) -> Option<PipelineId> {
909 (self.unsafe_get()).pipeline_id.get()
910 }
911
912 #[inline]
913 pub(crate) fn browsing_context_id(self) -> Option<BrowsingContextId> {
914 (self.unsafe_get()).browsing_context_id.get()
915 }
916
917 pub(crate) fn width(self) -> LengthOrPercentageOrAuto {
918 self.upcast::<Element>()
919 .get_attr_for_layout(&ns!(), &local_name!("width"))
920 .map(AttrValue::as_dimension)
921 .cloned()
922 .unwrap_or(LengthOrPercentageOrAuto::Auto)
923 }
924
925 pub(crate) fn height(self) -> LengthOrPercentageOrAuto {
926 self.upcast::<Element>()
927 .get_attr_for_layout(&ns!(), &local_name!("height"))
928 .map(AttrValue::as_dimension)
929 .cloned()
930 .unwrap_or(LengthOrPercentageOrAuto::Auto)
931 }
932}
933
934impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
935 make_url_getter!(Src, "src");
937
938 make_url_setter!(SetSrc, "src");
940
941 fn Srcdoc(&self) -> TrustedHTMLOrString {
943 let element = self.upcast::<Element>();
944 element.get_trusted_html_attribute(&local_name!("srcdoc"))
945 }
946
947 fn SetSrcdoc(&self, cx: &mut JSContext, value: TrustedHTMLOrString) -> Fallible<()> {
949 let element = self.upcast::<Element>();
953 let value = TrustedHTML::get_trusted_type_compliant_string(
954 cx,
955 &element.owner_global(),
956 value,
957 "HTMLIFrameElement srcdoc",
958 )?;
959 element.set_attribute(
961 cx,
962 &local_name!("srcdoc"),
963 AttrValue::String(value.str().to_owned()),
964 );
965 Ok(())
966 }
967
968 fn Sandbox(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
974 self.sandbox.or_init(|| {
975 DOMTokenList::new(
976 cx,
977 self.upcast::<Element>(),
978 &local_name!("sandbox"),
979 Some(vec![
980 Atom::from("allow-downloads"),
981 Atom::from("allow-forms"),
982 Atom::from("allow-modals"),
983 Atom::from("allow-orientation-lock"),
984 Atom::from("allow-pointer-lock"),
985 Atom::from("allow-popups"),
986 Atom::from("allow-popups-to-escape-sandbox"),
987 Atom::from("allow-presentation"),
988 Atom::from("allow-same-origin"),
989 Atom::from("allow-scripts"),
990 Atom::from("allow-top-navigation"),
991 Atom::from("allow-top-navigation-by-user-activation"),
992 Atom::from("allow-top-navigation-to-custom-protocols"),
993 ]),
994 )
995 })
996 }
997
998 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
1000 self.browsing_context_id
1001 .get()
1002 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
1003 }
1004
1005 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
1007 let pipeline_id = self.pipeline_id.get()?;
1009
1010 let document = ScriptThread::find_document(pipeline_id)?;
1014 if !self
1016 .owner_document()
1017 .origin()
1018 .same_origin_domain(&document.origin())
1019 {
1020 return None;
1021 }
1022 Some(document)
1024 }
1025
1026 fn ReferrerPolicy(&self) -> DOMString {
1028 reflect_referrer_policy_attribute(self.upcast::<Element>())
1029 }
1030
1031 make_setter!(SetReferrerPolicy, "referrerpolicy");
1033
1034 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1036 make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
1038
1039 make_getter!(Width, "width");
1041 make_dimension_setter!(SetWidth, "width");
1043
1044 make_getter!(Height, "height");
1046 make_dimension_setter!(SetHeight, "height");
1048
1049 make_getter!(FrameBorder, "frameborder");
1051 make_setter!(SetFrameBorder, "frameborder");
1053
1054 make_atomic_setter!(SetName, "name");
1058
1059 make_getter!(Name, "name");
1063
1064 make_enumerated_getter!(
1067 Loading,
1068 "loading",
1069 "lazy" | "eager",
1070 missing => "eager",
1073 invalid => "eager"
1074 );
1075
1076 make_setter!(SetLoading, "loading");
1078
1079 make_url_getter!(LongDesc, "longdesc");
1081
1082 make_url_setter!(SetLongDesc, "longdesc");
1084}
1085
1086impl VirtualMethods for HTMLIFrameElement {
1087 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1088 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1089 }
1090
1091 fn attribute_mutated(
1092 &self,
1093 cx: &mut JSContext,
1094 attr: AttrRef<'_>,
1095 mutation: AttributeMutation,
1096 ) {
1097 self.super_type()
1098 .unwrap()
1099 .attribute_mutated(cx, attr, mutation);
1100 match *attr.local_name() {
1101 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
1112 self.parse_sandbox_attribute();
1113 },
1114 local_name!("srcdoc") => {
1115 if self
1126 .upcast::<Node>()
1127 .is_connected_with_browsing_context(cx.no_gc())
1128 {
1129 debug!("iframe srcdoc modified while in browsing context.");
1130 self.process_the_iframe_attributes(cx, ProcessingMode::NotFirstTime);
1131 }
1132 },
1133 local_name!("src") => {
1134 if self
1143 .upcast::<Node>()
1144 .is_connected_with_browsing_context(cx.no_gc())
1145 {
1146 debug!("iframe src set while in browsing context.");
1147 self.process_the_iframe_attributes(cx, ProcessingMode::NotFirstTime);
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(cx, ProcessingMode::NotFirstTime);
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
1207 .upcast::<Node>()
1208 .is_connected_with_browsing_context(cx.no_gc())
1209 {
1210 return;
1211 }
1212
1213 debug!("<iframe> running post connection steps");
1214
1215 self.parse_sandbox_attribute();
1218
1219 self.create_nested_browsing_context(cx);
1221
1222 self.process_the_iframe_attributes(cx, ProcessingMode::FirstTime);
1224 }
1225
1226 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1227 if let Some(super_type) = self.super_type() {
1228 super_type.bind_to_tree(cx, context);
1229 }
1230
1231 self.owner_document().iframes().add(cx.no_gc(), self);
1232 }
1233
1234 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1236 if let Some(super_type) = self.super_type() {
1237 super_type.unbind_from_tree(cx, context);
1238 }
1239
1240 self.destroy_child_navigable(cx);
1243
1244 self.owner_document().iframes().remove(cx.no_gc(), self);
1245 }
1246}
1247
1248pub(crate) struct IframeContext {
1251 url: ServoUrl,
1253}
1254
1255impl IframeContext {
1256 pub fn new(element: &HTMLIFrameElement) -> Self {
1258 Self {
1259 url: element
1260 .shared_attribute_processing_steps_for_iframe_and_frame_elements(
1261 ProcessingMode::NotFirstTime,
1262 )
1263 .expect("Must always have a URL when navigating"),
1264 }
1265 }
1266}
1267
1268impl ResourceTimingListener for IframeContext {
1269 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1270 (InitiatorType::LocalName("iframe".into()), self.url.clone())
1271 }
1272}
1273
1274fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1275 let child_navigable = iframe.GetContentWindow();
1276 TargetSnapshotParams {
1277 sandboxing_flags: determine_creation_sandboxing_flags(
1278 child_navigable.as_deref(),
1279 Some(iframe.upcast()),
1280 ),
1281 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1282 iframe.upcast(),
1283 )),
1284 }
1285}