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::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::globalscope::GlobalScope;
45use crate::dom::html::htmlelement::HTMLElement;
46use crate::dom::node::virtualmethods::VirtualMethods;
47use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
48use crate::dom::performance::performanceresourcetiming::InitiatorType;
49use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
50use crate::dom::windowproxy::WindowProxy;
51use crate::event_loop::document_loader::{LoadBlocker, LoadType};
52use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
53use crate::event_loop::script_window_proxies::ScriptWindowProxies;
54use crate::fetch::network_listener::ResourceTimingListener;
55use crate::navigation::{
56 determine_creation_sandboxing_flags, determine_iframe_element_referrer_policy,
57};
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 frozen_name: DomRefCell<Option<String>>,
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_string_value(&local_name!("src"))
128 .and_then(|url| {
129 if url.is_empty() {
130 None
131 } else {
132 self.owner_document().encoding_parse_a_url(&url).ok()
136 }
137 })
138 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
140 Some(url)
150 }
151
152 pub(crate) fn navigate_or_reload_child_browsing_context(
153 &self,
154 load_data: LoadData,
155 history_handling: NavigationHistoryBehavior,
156 mode: ProcessingMode,
157 target_snapshot_params: TargetSnapshotParams,
158 cx: &mut JSContext,
159 ) {
160 self.start_new_pipeline(
161 cx,
162 load_data,
163 PipelineType::Navigation,
164 history_handling,
165 mode,
166 target_snapshot_params,
167 );
168 }
169
170 fn start_new_pipeline(
171 &self,
172 cx: &mut JSContext,
173 mut load_data: LoadData,
174 pipeline_type: PipelineType,
175 history_handling: NavigationHistoryBehavior,
176 mode: ProcessingMode,
177 target_snapshot_params: TargetSnapshotParams,
178 ) {
179 let document = self.owner_document();
180
181 {
182 let load_blocker = &self.load_blocker;
183 LoadBlocker::terminate(load_blocker, cx);
186
187 *load_blocker.borrow_mut() = Some(LoadBlocker::new(
188 &document,
189 LoadType::Subframe(load_data.url.clone()),
190 ));
191 }
192
193 if load_data.url.scheme() != "javascript" {
194 self.continue_navigation(
195 cx,
196 load_data,
197 pipeline_type,
198 history_handling,
199 target_snapshot_params,
200 );
201 return;
202 }
203
204 let iframe = Trusted::new(self);
208 let doc = Trusted::new(&*document);
209 document
210 .global()
211 .task_manager()
212 .networking_task_source()
213 .queue(task!(navigate_to_javascript: move |cx| {
214 let this = iframe.root();
215 let window_proxy = this.GetContentWindow();
216 if let Some(window_proxy) = window_proxy {
217 if !ScriptThread::navigate_to_javascript_url(
220 cx,
221 &this.owner_global(),
222 &window_proxy.global(),
223 &mut load_data,
224 Some(this.upcast()),
225 Some(mode == ProcessingMode::FirstTime),
226 ) {
227 LoadBlocker::terminate(&this.load_blocker, cx);
228 return;
229 }
230 load_data.about_base_url = doc.root().about_base_url();
231 }
232 this.continue_navigation(cx, load_data, pipeline_type, history_handling, target_snapshot_params);
233 }));
234 }
235
236 fn continue_navigation(
237 &self,
238 cx: &mut JSContext,
239 load_data: LoadData,
240 pipeline_type: PipelineType,
241 history_handling: NavigationHistoryBehavior,
242 target_snapshot_params: TargetSnapshotParams,
243 ) {
244 let browsing_context_id = match self.browsing_context_id() {
245 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
246 Some(id) => id,
247 };
248
249 let webview_id = match self.webview_id() {
250 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
251 Some(id) => id,
252 };
253
254 let window = self.owner_window();
255 let old_pipeline_id = self.pipeline_id();
256 let new_pipeline_id = PipelineId::new();
257 self.pending_pipeline_id.set(Some(new_pipeline_id));
258
259 let load_info = IFrameLoadInfo {
260 parent_pipeline_id: window.pipeline_id(),
261 browsing_context_id,
262 webview_id,
263 new_pipeline_id,
264 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
266 history_handling,
267 target_snapshot_params,
268 name: self.frozen_name.borrow().clone(),
269 };
270
271 let viewport_details = window
272 .get_iframe_viewport_details_if_known(browsing_context_id)
273 .unwrap_or_else(|| ViewportDetails {
274 hidpi_scale_factor: window.device_pixel_ratio(),
275 ..Default::default()
276 });
277
278 match pipeline_type {
279 PipelineType::InitialAboutBlank => {
280 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
281
282 let load_info = IFrameLoadInfoWithData {
283 info: load_info,
284 load_data: load_data.clone(),
285 old_pipeline_id,
286 viewport_details,
287 embedder_theme: window.embedder_theme(),
288 };
289 window
290 .as_global_scope()
291 .script_to_constellation_chan()
292 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
293 .unwrap();
294
295 let new_pipeline_info = NewPipelineInfo {
296 parent_info: Some(window.pipeline_id()),
297 new_pipeline_id,
298 browsing_context_id,
299 webview_id,
300 opener: None,
301 load_data,
302 viewport_details,
303 user_content_manager_id: None,
304 embedder_theme: window.embedder_theme(),
305 target_snapshot_params,
306 frame_name: self.frozen_name.borrow().clone(),
307 };
308
309 self.pipeline_id.set(Some(new_pipeline_id));
310 with_script_thread(|script_thread| {
311 script_thread.spawn_pipeline(cx, new_pipeline_info);
312 });
313 },
314 PipelineType::Navigation => {
315 let load_info = IFrameLoadInfoWithData {
316 info: load_info,
317 load_data,
318 old_pipeline_id,
319 viewport_details,
320 embedder_theme: window.embedder_theme(),
321 };
322 window
323 .as_global_scope()
324 .script_to_constellation_chan()
325 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
326 load_info,
327 ))
328 .unwrap();
329 },
330 }
331 }
332
333 pub(crate) fn is_initial_blank_document(&self) -> bool {
339 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
340 }
341
342 fn navigate_an_iframe_or_frame(
344 &self,
345 cx: &mut JSContext,
346 load_data: LoadData,
347 mode: ProcessingMode,
348 ) {
349 let history_handling = if !self
352 .GetContentDocument()
353 .is_some_and(|doc| doc.completely_loaded())
354 {
355 NavigationHistoryBehavior::Replace
356 } else {
357 NavigationHistoryBehavior::Auto
359 };
360 let target_snapshot_params = snapshot_self(self);
368 self.navigate_or_reload_child_browsing_context(
369 load_data,
370 history_handling,
371 mode,
372 target_snapshot_params,
373 cx,
374 );
375 }
376
377 fn will_lazy_load_element_steps(&self) -> bool {
379 if !self.owner_document().scripting_enabled() {
381 return false;
382 }
383 self.Loading() == "lazy"
386 }
387
388 fn navigate_to_the_srcdoc_resource(&self, mode: ProcessingMode, cx: &mut JSContext) {
390 let url = ServoUrl::parse("about:srcdoc").unwrap();
393 let document = self.owner_document();
394 let window = self.owner_window();
395 let pipeline_id = Some(window.pipeline_id());
396 let mut load_data = LoadData::new(
397 LoadOrigin::Script(document.origin().snapshot()),
398 url,
399 Some(document.base_url()),
400 pipeline_id,
401 window.as_global_scope().get_referrer(),
402 document.get_referrer_policy(),
403 Some(window.as_global_scope().is_secure_context()),
404 Some(document.insecure_requests_policy()),
405 document.has_trustworthy_ancestor_or_current_origin(),
406 self.sandboxing_flag_set(),
407 );
408 load_data.destination = Destination::IFrame;
409 load_data.policy_container = Some(window.as_global_scope().policy_container());
410 load_data.srcdoc = String::from(
411 self.upcast::<Element>()
412 .get_string_attribute(&local_name!("srcdoc")),
413 );
414
415 self.navigate_an_iframe_or_frame(cx, load_data, mode);
416 }
417
418 fn mark_navigation_as_lazy_loaded(&self, cx: &mut JSContext) {
420 self.current_navigation_was_lazy_loaded.set(true);
422 let blocker = &self.load_blocker;
423 LoadBlocker::terminate(blocker, cx);
424 }
425
426 fn process_the_iframe_attributes(&self, mode: ProcessingMode, cx: &mut JSContext) {
428 let element = self.upcast::<Element>();
429
430 if element.has_attribute(&local_name!("srcdoc")) {
434 self.current_navigation_was_lazy_loaded.set(false);
436 if self.will_lazy_load_element_steps() {
438 self.lazy_load_resumption_steps
441 .set(LazyLoadResumptionSteps::SrcDoc);
442 self.mark_navigation_as_lazy_loaded(cx);
444 return;
448 }
449 self.navigate_to_the_srcdoc_resource(mode, cx);
452 return;
453 }
454
455 let window = self.owner_window();
456
457 let Some(url) = self.shared_attribute_processing_steps_for_iframe_and_frame_elements(mode)
460 else {
461 return;
463 };
464
465 if url.matches_about_blank() && mode == ProcessingMode::FirstTime {
467 self.run_iframe_load_event_steps(cx);
469 return;
471 }
472
473 let document = self.owner_document();
476 let referrer_policy_token = self.ReferrerPolicy();
477
478 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
482 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
483 policy => policy,
484 };
485
486 let mut ancestor = window.GetParent();
495 while let Some(a) = ancestor {
496 if let Some(ancestor_url) = a.document().map(|d| d.url()) &&
497 ancestor_url.scheme() == url.scheme() &&
498 ancestor_url.username() == url.username() &&
499 ancestor_url.password() == url.password() &&
500 ancestor_url.host() == url.host() &&
501 ancestor_url.port() == url.port() &&
502 ancestor_url.path() == url.path() &&
503 ancestor_url.query() == url.query()
504 {
505 return;
506 }
507 ancestor = a.parent().map(DomRoot::from_ref);
508 }
509
510 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
511 (Some(window.pipeline_id()), Some(document.base_url()))
512 } else {
513 (None, document.about_base_url())
514 };
515
516 let propagate_encoding_to_child_document = url.origin().same_origin(&window.origin());
517 let mut load_data = LoadData::new(
518 LoadOrigin::Script(document.origin().snapshot()),
519 url,
520 about_base_url,
521 creator_pipeline_id,
522 window.as_global_scope().get_referrer(),
523 referrer_policy,
524 Some(window.as_global_scope().is_secure_context()),
525 Some(document.insecure_requests_policy()),
526 document.has_trustworthy_ancestor_or_current_origin(),
527 self.sandboxing_flag_set(),
528 );
529 load_data.destination = Destination::IFrame;
530 load_data.policy_container = Some(window.as_global_scope().policy_container());
531 if propagate_encoding_to_child_document {
532 load_data.container_document_encoding = Some(document.encoding());
533 }
534
535 let pipeline_id = self.pipeline_id();
536 let is_about_blank =
539 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
540
541 let history_handling = if is_about_blank {
542 NavigationHistoryBehavior::Replace
543 } else {
544 NavigationHistoryBehavior::Push
545 };
546
547 let target_snapshot_params = snapshot_self(self);
548 self.navigate_or_reload_child_browsing_context(
549 load_data,
550 history_handling,
551 mode,
552 target_snapshot_params,
553 cx,
554 );
555 }
556
557 fn create_nested_browsing_context(&self, cx: &mut JSContext) {
560 let document = self.owner_document();
562 let window = self.owner_window();
563 let pipeline_id = Some(window.pipeline_id());
564 *self.frozen_name.borrow_mut() = self
568 .upcast::<Element>()
569 .get_name()
570 .map(|name| name.to_string());
571 let mut load_data = LoadData::new(
573 LoadOrigin::Script(document.origin().snapshot()),
576 ServoUrl::parse("about:blank").unwrap(),
577 Some(document.base_url()),
580 pipeline_id,
581 window.as_global_scope().get_referrer(),
582 document.get_referrer_policy(),
583 Some(window.as_global_scope().is_secure_context()),
584 Some(document.insecure_requests_policy()),
585 document.has_trustworthy_ancestor_or_current_origin(),
586 self.sandboxing_flag_set(),
587 );
588 load_data.is_initial_about_blank = true;
589 load_data.destination = Destination::IFrame;
590 load_data.policy_container = Some(window.as_global_scope().policy_container());
591
592 let browsing_context_id = BrowsingContextId::new();
594 let webview_id = window.window_proxy().webview_id();
595 self.pipeline_id.set(None);
596 self.pending_pipeline_id.set(None);
597 self.webview_id.set(Some(webview_id));
598 self.browsing_context_id.set(Some(browsing_context_id));
599 self.start_new_pipeline(
601 cx,
602 load_data,
603 PipelineType::InitialAboutBlank,
604 NavigationHistoryBehavior::Push,
605 ProcessingMode::FirstTime,
606 snapshot_self(self),
607 );
608 if let Some(window) = self.GetContentWindow() &&
611 let Some(window_name) = &*self.frozen_name.borrow()
612 {
613 window.set_name(window_name.as_str().into());
614 }
615 }
616
617 fn destroy_nested_browsing_context(&self) {
618 self.pipeline_id.set(None);
619 self.pending_pipeline_id.set(None);
620 self.about_blank_pipeline_id.set(None);
621 self.webview_id.set(None);
622 if let Some(browsing_context_id) = self.browsing_context_id.take() {
623 self.script_window_proxies.remove(browsing_context_id)
624 }
625 }
626
627 pub(crate) fn update_pipeline_id(
631 &self,
632 new_pipeline_id: PipelineId,
633 reason: UpdatePipelineIdReason,
634 cx: &mut JSContext,
635 ) -> bool {
636 if !self.is_initial_blank_document() {
641 self.pending_navigation.set(false);
642 }
643 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
644 reason == UpdatePipelineIdReason::Navigation
645 {
646 return false;
647 }
648
649 self.pipeline_id.set(Some(new_pipeline_id));
650
651 if reason == UpdatePipelineIdReason::Traversal {
654 let blocker = &self.load_blocker;
655 LoadBlocker::terminate(blocker, cx);
656 }
657
658 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
659 true
660 }
661
662 fn new_inherited(
663 local_name: LocalName,
664 prefix: Option<Prefix>,
665 document: &Document,
666 ) -> HTMLIFrameElement {
667 HTMLIFrameElement {
668 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
669 browsing_context_id: Cell::new(None),
670 webview_id: Cell::new(None),
671 pipeline_id: Cell::new(None),
672 pending_pipeline_id: Cell::new(None),
673 about_blank_pipeline_id: Cell::new(None),
674 sandbox: Default::default(),
675 sandboxing_flag_set: Cell::new(None),
676 load_blocker: DomRefCell::new(None),
677 throttled: Cell::new(false),
678 script_window_proxies: ScriptThread::window_proxies(),
679 current_navigation_was_lazy_loaded: Default::default(),
680 lazy_load_resumption_steps: Default::default(),
681 pending_navigation: Default::default(),
682 frozen_name: Default::default(),
683 }
684 }
685
686 pub(crate) fn new(
687 cx: &mut JSContext,
688 local_name: LocalName,
689 prefix: Option<Prefix>,
690 document: &Document,
691 proto: Option<HandleObject>,
692 ) -> DomRoot<HTMLIFrameElement> {
693 Node::reflect_node_with_proto(
694 cx,
695 Box::new(HTMLIFrameElement::new_inherited(
696 local_name, prefix, document,
697 )),
698 document,
699 proto,
700 )
701 }
702
703 #[inline]
704 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
705 self.pipeline_id.get()
706 }
707
708 #[inline]
709 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
710 self.browsing_context_id.get()
711 }
712
713 #[inline]
714 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
715 self.webview_id.get()
716 }
717
718 #[inline]
719 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
720 self.sandboxing_flag_set
721 .get()
722 .unwrap_or_else(SandboxingFlagSet::empty)
723 }
724
725 pub(crate) fn set_throttled(&self, throttled: bool) {
726 if self.throttled.get() != throttled {
727 self.throttled.set(throttled);
728 }
729 }
730
731 pub(crate) fn note_pending_navigation(&self) {
735 self.pending_navigation.set(true);
736 }
737
738 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, cx: &mut JSContext) {
740 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
743 return;
744 }
745
746 let should_fire_event = if self.is_initial_blank_document() {
775 !self.pending_navigation.get() &&
779 !self.upcast::<Element>().has_attribute(&local_name!("src"))
780 } else {
781 !self.pending_navigation.get()
784 };
785
786 if should_fire_event {
787 self.run_iframe_load_event_steps(cx);
788 } else {
789 debug!(
790 "suppressing load event for iframe, loaded {:?}",
791 loaded_pipeline
792 );
793 }
794 }
795
796 pub(crate) fn run_iframe_load_event_steps(&self, cx: &mut JSContext) {
798 let child_document = self.GetContentDocument();
802
803 if let Some(document) = child_document {
806 if document.mute_iframe_load_flag() {
807 let blocker = &self.load_blocker;
808 LoadBlocker::terminate(blocker, cx);
809 return;
810 }
811 document.set_iframe_load_in_progress(true);
812 }
813
814 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
819
820 let blocker = &self.load_blocker;
821 LoadBlocker::terminate(blocker, cx);
822
823 if let Some(child_document) = self.GetContentDocument() {
825 child_document.set_iframe_load_in_progress(false);
826 }
827 }
828
829 fn parse_sandbox_attribute(&self) {
833 let sandbox_value =
834 self.upcast::<Element>()
835 .with_attribute(&ns!(), &local_name!("sandbox"), |attribute| {
836 let tokens: Vec<_> = attribute
837 .value()
838 .as_tokens()
839 .iter()
840 .map(|atom| atom.to_string().to_ascii_lowercase())
841 .collect();
842 parse_a_sandboxing_directive(&tokens)
843 });
844 self.sandboxing_flag_set.set(sandbox_value);
845 }
846
847 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
849 let Some(pipeline_id) = self.pipeline_id.get() else {
850 return;
851 };
852 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
854 exited_document.destroy_document_and_its_descendants(cx);
855 }
856 self.destroy_nested_browsing_context();
857 }
858
859 fn destroy_child_navigable(&self, cx: &mut JSContext) {
861 let blocker = &self.load_blocker;
862 LoadBlocker::terminate(blocker, cx);
863
864 let Some(browsing_context_id) = self.browsing_context_id() else {
866 return;
868 };
869 let pipeline_id = self.pipeline_id.get();
872
873 self.destroy_nested_browsing_context();
881
882 let (sender, receiver) =
887 ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
888 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
889 self.owner_window()
890 .as_global_scope()
891 .script_to_constellation_chan()
892 .send(msg)
893 .unwrap();
894 let _exited_pipeline_ids = receiver.recv().unwrap();
895 let Some(pipeline_id) = pipeline_id else {
896 return;
897 };
898 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
899 exited_document.destroy_document_and_its_descendants(cx);
900 }
901
902 }
917}
918
919impl LayoutDom<'_, HTMLIFrameElement> {
920 #[inline]
921 pub(crate) fn pipeline_id(self) -> Option<PipelineId> {
922 (self.unsafe_get()).pipeline_id.get()
923 }
924
925 #[inline]
926 pub(crate) fn browsing_context_id(self) -> Option<BrowsingContextId> {
927 (self.unsafe_get()).browsing_context_id.get()
928 }
929
930 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
931 self.upcast::<Element>()
932 .get_attr_for_layout(&ns!(), &local_name!("width"))
933 .map(AttrValue::as_dimension)
934 .cloned()
935 .unwrap_or(LengthOrPercentageOrAuto::Auto)
936 }
937
938 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
939 self.upcast::<Element>()
940 .get_attr_for_layout(&ns!(), &local_name!("height"))
941 .map(AttrValue::as_dimension)
942 .cloned()
943 .unwrap_or(LengthOrPercentageOrAuto::Auto)
944 }
945}
946
947impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
948 make_url_getter!(Src, "src");
950
951 make_url_setter!(SetSrc, "src");
953
954 fn Srcdoc(&self) -> TrustedHTMLOrString {
956 let element = self.upcast::<Element>();
957 element.get_trusted_html_attribute(&local_name!("srcdoc"))
958 }
959
960 fn SetSrcdoc(&self, cx: &mut JSContext, value: TrustedHTMLOrString) -> Fallible<()> {
962 let element = self.upcast::<Element>();
966 let value = TrustedHTML::get_trusted_type_compliant_string(
967 cx,
968 &element.owner_global(),
969 value,
970 "HTMLIFrameElement srcdoc",
971 )?;
972 element.set_attribute(
974 cx,
975 &local_name!("srcdoc"),
976 AttrValue::String(value.str().to_owned()),
977 );
978 Ok(())
979 }
980
981 fn Sandbox(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
987 self.sandbox.or_init(|| {
988 DOMTokenList::new(
989 cx,
990 self.upcast::<Element>(),
991 &local_name!("sandbox"),
992 Some(vec![
993 Atom::from("allow-downloads"),
994 Atom::from("allow-forms"),
995 Atom::from("allow-modals"),
996 Atom::from("allow-orientation-lock"),
997 Atom::from("allow-pointer-lock"),
998 Atom::from("allow-popups"),
999 Atom::from("allow-popups-to-escape-sandbox"),
1000 Atom::from("allow-presentation"),
1001 Atom::from("allow-same-origin"),
1002 Atom::from("allow-scripts"),
1003 Atom::from("allow-top-navigation"),
1004 Atom::from("allow-top-navigation-by-user-activation"),
1005 Atom::from("allow-top-navigation-to-custom-protocols"),
1006 ]),
1007 )
1008 })
1009 }
1010
1011 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
1013 self.browsing_context_id
1014 .get()
1015 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
1016 }
1017
1018 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
1020 let pipeline_id = self.pipeline_id.get()?;
1022
1023 let document = ScriptThread::find_document(pipeline_id)?;
1027 if !self
1029 .owner_document()
1030 .origin()
1031 .same_origin_domain(&document.origin())
1032 {
1033 return None;
1034 }
1035 Some(document)
1037 }
1038
1039 fn ReferrerPolicy(&self) -> DOMString {
1041 reflect_referrer_policy_attribute(self.upcast::<Element>())
1042 }
1043
1044 make_setter!(SetReferrerPolicy, "referrerpolicy");
1046
1047 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1049 make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
1051
1052 make_getter!(Width, "width");
1054 make_dimension_setter!(SetWidth, "width");
1056
1057 make_getter!(Height, "height");
1059 make_dimension_setter!(SetHeight, "height");
1061
1062 make_getter!(FrameBorder, "frameborder");
1064 make_setter!(SetFrameBorder, "frameborder");
1066
1067 make_atomic_setter!(SetName, "name");
1071
1072 make_getter!(Name, "name");
1076
1077 make_enumerated_getter!(
1080 Loading,
1081 "loading",
1082 "lazy" | "eager",
1083 missing => "eager",
1086 invalid => "eager"
1087 );
1088
1089 make_setter!(SetLoading, "loading");
1091
1092 make_url_getter!(LongDesc, "longdesc");
1094
1095 make_url_setter!(SetLongDesc, "longdesc");
1097}
1098
1099impl VirtualMethods for HTMLIFrameElement {
1100 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1101 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1102 }
1103
1104 fn attribute_mutated(
1105 &self,
1106 cx: &mut JSContext,
1107 attr: AttrRef<'_>,
1108 mutation: AttributeMutation,
1109 ) {
1110 self.super_type()
1111 .unwrap()
1112 .attribute_mutated(cx, attr, mutation);
1113 match *attr.local_name() {
1114 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
1125 self.parse_sandbox_attribute();
1126 },
1127 local_name!("srcdoc") => {
1128 if self.upcast::<Node>().is_connected_with_browsing_context() {
1139 debug!("iframe srcdoc modified while in browsing context.");
1140 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1141 }
1142 },
1143 local_name!("src") => {
1144 if self.upcast::<Node>().is_connected_with_browsing_context() {
1153 debug!("iframe src set while in browsing context.");
1154 self.process_the_iframe_attributes(ProcessingMode::NotFirstTime, cx);
1155 }
1156 },
1157 local_name!("loading") => {
1158 if !mutation.is_removal() && &**attr.value() == "lazy" {
1161 return;
1162 }
1163
1164 let previous_resumption_steps = self
1167 .lazy_load_resumption_steps
1168 .replace(LazyLoadResumptionSteps::None);
1169 match previous_resumption_steps {
1170 LazyLoadResumptionSteps::None => (),
1172 LazyLoadResumptionSteps::SrcDoc => {
1173 self.navigate_to_the_srcdoc_resource(ProcessingMode::NotFirstTime, cx);
1175 },
1176 }
1177 },
1178 _ => {},
1179 }
1180 }
1181
1182 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1183 match attr.local_name() {
1184 &local_name!("width") | &local_name!("height") => true,
1185 _ => self
1186 .super_type()
1187 .unwrap()
1188 .attribute_affects_presentational_hints(attr),
1189 }
1190 }
1191
1192 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1193 match *name {
1194 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
1195 local_name!("width") => AttrValue::from_dimension(value.into()),
1196 local_name!("height") => AttrValue::from_dimension(value.into()),
1197 _ => self
1198 .super_type()
1199 .unwrap()
1200 .parse_plain_attribute(name, value),
1201 }
1202 }
1203
1204 fn post_connection_steps(&self, cx: &mut JSContext) {
1206 if let Some(s) = self.super_type() {
1207 s.post_connection_steps(cx);
1208 }
1209
1210 if !self.upcast::<Node>().is_connected_with_browsing_context() {
1214 return;
1215 }
1216
1217 debug!("<iframe> running post connection steps");
1218
1219 self.parse_sandbox_attribute();
1222
1223 self.create_nested_browsing_context(cx);
1225
1226 self.process_the_iframe_attributes(ProcessingMode::FirstTime, cx);
1228 }
1229
1230 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1231 if let Some(super_type) = self.super_type() {
1232 super_type.bind_to_tree(cx, context);
1233 }
1234
1235 self.owner_document().iframes_mut().add(self);
1236 }
1237
1238 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1240 if let Some(super_type) = self.super_type() {
1241 super_type.unbind_from_tree(cx, context);
1242 }
1243
1244 self.destroy_child_navigable(cx);
1247
1248 self.owner_document().iframes_mut().remove(self);
1249 }
1250}
1251
1252pub(crate) struct IframeContext<'a> {
1255 element: &'a HTMLIFrameElement,
1257 url: ServoUrl,
1259}
1260
1261impl<'a> IframeContext<'a> {
1262 pub fn new(element: &'a HTMLIFrameElement) -> Self {
1264 Self {
1265 element,
1266 url: element
1267 .shared_attribute_processing_steps_for_iframe_and_frame_elements(
1268 ProcessingMode::NotFirstTime,
1269 )
1270 .expect("Must always have a URL when navigating"),
1271 }
1272 }
1273}
1274
1275impl<'a> ResourceTimingListener for IframeContext<'a> {
1276 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1277 (
1278 InitiatorType::LocalName("iframe".to_string()),
1279 self.url.clone(),
1280 )
1281 }
1282
1283 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1284 self.element.upcast::<Node>().owner_doc().global()
1285 }
1286}
1287
1288fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1289 let child_navigable = iframe.GetContentWindow();
1290 TargetSnapshotParams {
1291 sandboxing_flags: determine_creation_sandboxing_flags(
1292 child_navigable.as_deref(),
1293 Some(iframe.upcast()),
1294 ),
1295 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1296 iframe.upcast(),
1297 )),
1298 }
1299}