1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::Cell;
8use std::rc::Rc;
9
10use content_security_policy::sandboxing_directive::{
11 SandboxingFlagSet, parse_a_sandboxing_directive,
12};
13use dom_struct::dom_struct;
14use embedder_traits::ViewportDetails;
15use html5ever::{LocalName, Prefix, local_name, ns};
16use js::context::JSContext;
17use js::rust::HandleObject;
18use net_traits::ReferrerPolicy;
19use net_traits::request::Destination;
20use profile_traits::generic_channel::channel;
21use script_bindings::cell::DomRefCell;
22use script_traits::{NewPipelineInfo, UpdatePipelineIdReason};
23use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
24use servo_constellation_traits::{
25 IFrameLoadInfo, IFrameLoadInfoWithData, LoadData, LoadOrigin, NavigationHistoryBehavior,
26 ScriptToConstellationMessage, TargetSnapshotParams,
27};
28use servo_url::ServoUrl;
29use style::attr::{AttrValue, LengthOrPercentageOrAuto};
30use stylo_atoms::Atom;
31
32use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
33use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
34use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
35use crate::dom::bindings::error::Fallible;
36use crate::dom::bindings::inheritance::Castable;
37use crate::dom::bindings::refcounted::Trusted;
38use crate::dom::bindings::reflector::DomGlobal;
39use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
40use crate::dom::bindings::str::{DOMString, USVString};
41use crate::dom::document::Document;
42use crate::dom::domtokenlist::DOMTokenList;
43use crate::dom::element::attributes::storage::AttrRef;
44use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
45use crate::dom::eventtarget::EventTarget;
46use crate::dom::globalscope::GlobalScope;
47use crate::dom::html::htmlelement::HTMLElement;
48use crate::dom::node::virtualmethods::VirtualMethods;
49use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext};
50use crate::dom::performance::performanceresourcetiming::InitiatorType;
51use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
52use crate::dom::windowproxy::WindowProxy;
53use crate::event_loop::document_loader::{LoadBlocker, LoadType};
54use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
55use crate::event_loop::script_window_proxies::ScriptWindowProxies;
56use crate::fetch::network_listener::ResourceTimingListener;
57use crate::navigation::{
58 determine_creation_sandboxing_flags, determine_iframe_element_referrer_policy,
59};
60
61#[derive(PartialEq)]
62enum PipelineType {
63 InitialAboutBlank,
64 Navigation,
65}
66
67#[derive(Clone, Copy, PartialEq)]
68pub(crate) enum ProcessingMode {
69 FirstTime,
70 NotFirstTime,
71}
72
73#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
75enum LazyLoadResumptionSteps {
76 #[default]
77 None,
78 SrcDoc,
79}
80
81#[dom_struct]
82pub(crate) struct HTMLIFrameElement {
83 htmlelement: HTMLElement,
84 #[no_trace]
85 webview_id: Cell<Option<WebViewId>>,
86 #[no_trace]
87 browsing_context_id: Cell<Option<BrowsingContextId>>,
88 #[no_trace]
89 pipeline_id: Cell<Option<PipelineId>>,
90 #[no_trace]
91 pending_pipeline_id: Cell<Option<PipelineId>>,
92 #[no_trace]
93 about_blank_pipeline_id: Cell<Option<PipelineId>>,
94 sandbox: MutNullableDom<DOMTokenList>,
95 #[no_trace]
96 sandboxing_flag_set: Cell<Option<SandboxingFlagSet>>,
97 load_blocker: DomRefCell<Option<LoadBlocker>>,
98 #[conditional_malloc_size_of]
99 script_window_proxies: Rc<ScriptWindowProxies>,
100 current_navigation_was_lazy_loaded: Cell<bool>,
102 #[no_trace]
104 lazy_load_resumption_steps: Cell<LazyLoadResumptionSteps>,
105 pending_navigation: Cell<bool>,
112 frozen_name: DomRefCell<Option<String>>,
117}
118
119impl HTMLIFrameElement {
120 fn shared_attribute_processing_steps_for_iframe_and_frame_elements(
122 &self,
123 _mode: ProcessingMode,
124 ) -> Option<ServoUrl> {
125 let element = self.upcast::<Element>();
126 let url = element
128 .get_attribute_string_value(&local_name!("src"))
129 .and_then(|url| {
130 if url.is_empty() {
131 None
132 } else {
133 self.owner_document().encoding_parse_a_url(&url).ok()
137 }
138 })
139 .unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
141 Some(url)
151 }
152
153 pub(crate) fn navigate_or_reload_child_browsing_context(
154 &self,
155 load_data: LoadData,
156 history_handling: NavigationHistoryBehavior,
157 mode: ProcessingMode,
158 target_snapshot_params: TargetSnapshotParams,
159 cx: &mut JSContext,
160 ) {
161 self.start_new_pipeline(
162 cx,
163 load_data,
164 PipelineType::Navigation,
165 history_handling,
166 mode,
167 target_snapshot_params,
168 );
169 }
170
171 fn start_new_pipeline(
172 &self,
173 cx: &mut JSContext,
174 mut load_data: LoadData,
175 pipeline_type: PipelineType,
176 history_handling: NavigationHistoryBehavior,
177 mode: ProcessingMode,
178 target_snapshot_params: TargetSnapshotParams,
179 ) {
180 let document = self.owner_document();
181
182 {
183 let load_blocker = &self.load_blocker;
184 LoadBlocker::terminate(load_blocker, cx);
187
188 *load_blocker.borrow_mut() = Some(LoadBlocker::new(
189 &document,
190 LoadType::Subframe(load_data.url.clone()),
191 ));
192 }
193
194 if load_data.url.scheme() != "javascript" {
195 self.continue_navigation(
196 cx,
197 load_data,
198 pipeline_type,
199 history_handling,
200 target_snapshot_params,
201 );
202 return;
203 }
204
205 let iframe = Trusted::new(self);
209 let doc = Trusted::new(&*document);
210 document
211 .global()
212 .task_manager()
213 .networking_task_source()
214 .queue(task!(navigate_to_javascript: move |cx| {
215 let this = iframe.root();
216 let window_proxy = this.GetContentWindow();
217 if let Some(window_proxy) = window_proxy {
218 if !ScriptThread::navigate_to_javascript_url(
221 cx,
222 &this.owner_global(),
223 &window_proxy.global(),
224 &mut load_data,
225 Some(this.upcast()),
226 Some(mode == ProcessingMode::FirstTime),
227 ) {
228 LoadBlocker::terminate(&this.load_blocker, cx);
229 return;
230 }
231 load_data.about_base_url = doc.root().about_base_url();
232 }
233 this.continue_navigation(cx, load_data, pipeline_type, history_handling, target_snapshot_params);
234 }));
235 }
236
237 fn continue_navigation(
238 &self,
239 cx: &mut JSContext,
240 load_data: LoadData,
241 pipeline_type: PipelineType,
242 history_handling: NavigationHistoryBehavior,
243 target_snapshot_params: TargetSnapshotParams,
244 ) {
245 let browsing_context_id = match self.browsing_context_id() {
246 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
247 Some(id) => id,
248 };
249
250 let webview_id = match self.webview_id() {
251 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
252 Some(id) => id,
253 };
254
255 let window = self.owner_window();
256 let old_pipeline_id = self.pipeline_id();
257 let new_pipeline_id = PipelineId::new();
258 self.pending_pipeline_id.set(Some(new_pipeline_id));
259
260 let load_info = IFrameLoadInfo {
261 parent_pipeline_id: window.pipeline_id(),
262 browsing_context_id,
263 webview_id,
264 new_pipeline_id,
265 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
267 history_handling,
268 target_snapshot_params,
269 name: self.frozen_name.borrow().clone(),
270 };
271
272 let viewport_details = window
273 .get_iframe_viewport_details_if_known(browsing_context_id)
274 .unwrap_or_else(|| ViewportDetails {
275 hidpi_scale_factor: window.device_pixel_ratio(),
276 ..Default::default()
277 });
278
279 match pipeline_type {
280 PipelineType::InitialAboutBlank => {
281 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
282
283 let load_info = IFrameLoadInfoWithData {
284 info: load_info,
285 load_data: load_data.clone(),
286 old_pipeline_id,
287 viewport_details,
288 embedder_theme: window.embedder_theme(),
289 };
290 window
291 .as_global_scope()
292 .script_to_constellation_chan()
293 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
294 .unwrap();
295
296 let new_pipeline_info = NewPipelineInfo {
297 parent_info: Some(window.pipeline_id()),
298 new_pipeline_id,
299 browsing_context_id,
300 webview_id,
301 opener: None,
302 load_data,
303 viewport_details,
304 user_content_manager_id: None,
305 embedder_theme: window.embedder_theme(),
306 target_snapshot_params,
307 frame_name: self.frozen_name.borrow().clone(),
308 };
309
310 self.pipeline_id.set(Some(new_pipeline_id));
311 with_script_thread(|script_thread| {
312 script_thread.spawn_pipeline(cx, new_pipeline_info);
313 });
314 },
315 PipelineType::Navigation => {
316 let load_info = IFrameLoadInfoWithData {
317 info: load_info,
318 load_data,
319 old_pipeline_id,
320 viewport_details,
321 embedder_theme: window.embedder_theme(),
322 };
323 window
324 .as_global_scope()
325 .script_to_constellation_chan()
326 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
327 load_info,
328 ))
329 .unwrap();
330 },
331 }
332 }
333
334 pub(crate) fn is_initial_blank_document(&self) -> bool {
340 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
341 }
342
343 fn navigate_an_iframe_or_frame(
345 &self,
346 cx: &mut JSContext,
347 load_data: LoadData,
348 mode: ProcessingMode,
349 ) {
350 let history_handling = if !self
353 .GetContentDocument()
354 .is_some_and(|doc| doc.completely_loaded())
355 {
356 NavigationHistoryBehavior::Replace
357 } else {
358 NavigationHistoryBehavior::Auto
360 };
361 let target_snapshot_params = snapshot_self(self);
369 self.navigate_or_reload_child_browsing_context(
370 load_data,
371 history_handling,
372 mode,
373 target_snapshot_params,
374 cx,
375 );
376 }
377
378 fn will_lazy_load_element_steps(&self) -> bool {
380 if !self.owner_document().scripting_enabled() {
382 return false;
383 }
384 self.Loading() == "lazy"
387 }
388
389 fn navigate_to_the_srcdoc_resource(&self, mode: ProcessingMode, cx: &mut JSContext) {
391 let url = ServoUrl::parse("about:srcdoc").unwrap();
394 let document = self.owner_document();
395 let window = self.owner_window();
396 let pipeline_id = Some(window.pipeline_id());
397 let mut load_data = LoadData::new(
398 LoadOrigin::Script(document.origin().snapshot()),
399 url,
400 Some(document.base_url()),
401 pipeline_id,
402 window.as_global_scope().get_referrer(),
403 document.get_referrer_policy(),
404 Some(window.as_global_scope().is_secure_context()),
405 Some(document.insecure_requests_policy()),
406 document.has_trustworthy_ancestor_or_current_origin(),
407 self.sandboxing_flag_set(),
408 );
409 load_data.destination = Destination::IFrame;
410 load_data.policy_container = Some(window.as_global_scope().policy_container());
411 load_data.srcdoc = String::from(
412 self.upcast::<Element>()
413 .get_string_attribute(&local_name!("srcdoc")),
414 );
415
416 self.navigate_an_iframe_or_frame(cx, load_data, mode);
417 }
418
419 fn mark_navigation_as_lazy_loaded(&self, cx: &mut JSContext) {
421 self.current_navigation_was_lazy_loaded.set(true);
423 let blocker = &self.load_blocker;
424 LoadBlocker::terminate(blocker, cx);
425 }
426
427 fn process_the_iframe_attributes(&self, mode: ProcessingMode, cx: &mut JSContext) {
429 let element = self.upcast::<Element>();
430
431 if element.has_attribute(&local_name!("srcdoc")) {
435 self.current_navigation_was_lazy_loaded.set(false);
437 if self.will_lazy_load_element_steps() {
439 self.lazy_load_resumption_steps
442 .set(LazyLoadResumptionSteps::SrcDoc);
443 self.mark_navigation_as_lazy_loaded(cx);
445 return;
449 }
450 self.navigate_to_the_srcdoc_resource(mode, cx);
453 return;
454 }
455
456 let window = self.owner_window();
457
458 let Some(url) = self.shared_attribute_processing_steps_for_iframe_and_frame_elements(mode)
461 else {
462 return;
464 };
465
466 if url.matches_about_blank() && mode == ProcessingMode::FirstTime {
468 self.run_iframe_load_event_steps(cx);
470 return;
472 }
473
474 let document = self.owner_document();
477 let referrer_policy_token = self.ReferrerPolicy();
478
479 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
483 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
484 policy => policy,
485 };
486
487 let mut ancestor = window.GetParent();
496 while let Some(a) = ancestor {
497 if let Some(ancestor_url) = a.document().map(|d| d.url()) &&
498 ancestor_url.scheme() == url.scheme() &&
499 ancestor_url.username() == url.username() &&
500 ancestor_url.password() == url.password() &&
501 ancestor_url.host() == url.host() &&
502 ancestor_url.port() == url.port() &&
503 ancestor_url.path() == url.path() &&
504 ancestor_url.query() == url.query()
505 {
506 return;
507 }
508 ancestor = a.parent().map(DomRoot::from_ref);
509 }
510
511 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
512 (Some(window.pipeline_id()), Some(document.base_url()))
513 } else {
514 (None, document.about_base_url())
515 };
516
517 let propagate_encoding_to_child_document = url.origin().same_origin(&window.origin());
518 let mut load_data = LoadData::new(
519 LoadOrigin::Script(document.origin().snapshot()),
520 url,
521 about_base_url,
522 creator_pipeline_id,
523 window.as_global_scope().get_referrer(),
524 referrer_policy,
525 Some(window.as_global_scope().is_secure_context()),
526 Some(document.insecure_requests_policy()),
527 document.has_trustworthy_ancestor_or_current_origin(),
528 self.sandboxing_flag_set(),
529 );
530 load_data.destination = Destination::IFrame;
531 load_data.policy_container = Some(window.as_global_scope().policy_container());
532 if propagate_encoding_to_child_document {
533 load_data.container_document_encoding = Some(document.encoding());
534 }
535
536 let pipeline_id = self.pipeline_id();
537 let is_about_blank =
540 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
541
542 let history_handling = if is_about_blank {
543 NavigationHistoryBehavior::Replace
544 } else {
545 NavigationHistoryBehavior::Push
546 };
547
548 let target_snapshot_params = snapshot_self(self);
549 self.navigate_or_reload_child_browsing_context(
550 load_data,
551 history_handling,
552 mode,
553 target_snapshot_params,
554 cx,
555 );
556 }
557
558 fn create_nested_browsing_context(&self, cx: &mut JSContext) {
561 let document = self.owner_document();
563 let window = self.owner_window();
564 let pipeline_id = Some(window.pipeline_id());
565 *self.frozen_name.borrow_mut() = self
569 .upcast::<Element>()
570 .get_name()
571 .map(|name| name.to_string());
572 let mut load_data = LoadData::new(
574 LoadOrigin::Script(document.origin().snapshot()),
577 ServoUrl::parse("about:blank").unwrap(),
578 Some(document.base_url()),
581 pipeline_id,
582 window.as_global_scope().get_referrer(),
583 document.get_referrer_policy(),
584 Some(window.as_global_scope().is_secure_context()),
585 Some(document.insecure_requests_policy()),
586 document.has_trustworthy_ancestor_or_current_origin(),
587 self.sandboxing_flag_set(),
588 );
589 load_data.is_initial_about_blank = true;
590 load_data.destination = Destination::IFrame;
591 load_data.policy_container = Some(window.as_global_scope().policy_container());
592
593 let browsing_context_id = BrowsingContextId::new();
595 let webview_id = window.window_proxy().webview_id();
596 self.pipeline_id.set(None);
597 self.pending_pipeline_id.set(None);
598 self.webview_id.set(Some(webview_id));
599 self.browsing_context_id.set(Some(browsing_context_id));
600 self.start_new_pipeline(
602 cx,
603 load_data,
604 PipelineType::InitialAboutBlank,
605 NavigationHistoryBehavior::Push,
606 ProcessingMode::FirstTime,
607 snapshot_self(self),
608 );
609 if let Some(window) = self.GetContentWindow() &&
612 let Some(window_name) = &*self.frozen_name.borrow()
613 {
614 window.set_name(window_name.as_str().into());
615 }
616 }
617
618 fn destroy_nested_browsing_context(&self) {
619 self.pipeline_id.set(None);
620 self.pending_pipeline_id.set(None);
621 self.about_blank_pipeline_id.set(None);
622 self.webview_id.set(None);
623 if let Some(browsing_context_id) = self.browsing_context_id.take() {
624 self.script_window_proxies.remove(browsing_context_id)
625 }
626 }
627
628 pub(crate) fn update_pipeline_id(
632 &self,
633 new_pipeline_id: PipelineId,
634 reason: UpdatePipelineIdReason,
635 cx: &mut JSContext,
636 ) -> bool {
637 if !self.is_initial_blank_document() {
642 self.pending_navigation.set(false);
643 }
644 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
645 reason == UpdatePipelineIdReason::Navigation
646 {
647 return false;
648 }
649
650 self.pipeline_id.set(Some(new_pipeline_id));
651
652 if reason == UpdatePipelineIdReason::Traversal {
655 let blocker = &self.load_blocker;
656 LoadBlocker::terminate(blocker, cx);
657 }
658
659 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
660 true
661 }
662
663 fn new_inherited(
664 local_name: LocalName,
665 prefix: Option<Prefix>,
666 document: &Document,
667 ) -> HTMLIFrameElement {
668 HTMLIFrameElement {
669 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
670 browsing_context_id: Cell::new(None),
671 webview_id: Cell::new(None),
672 pipeline_id: Cell::new(None),
673 pending_pipeline_id: Cell::new(None),
674 about_blank_pipeline_id: Cell::new(None),
675 sandbox: Default::default(),
676 sandboxing_flag_set: Cell::new(None),
677 load_blocker: DomRefCell::new(None),
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 note_pending_navigation(&self) {
729 self.pending_navigation.set(true);
730 }
731
732 pub(crate) fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId, cx: &mut JSContext) {
734 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
737 return;
738 }
739
740 let should_fire_event = if self.is_initial_blank_document() {
769 !self.pending_navigation.get() &&
773 !self.upcast::<Element>().has_attribute(&local_name!("src"))
774 } else {
775 !self.pending_navigation.get()
778 };
779
780 if should_fire_event {
781 self.run_iframe_load_event_steps(cx);
782 } else {
783 debug!(
784 "suppressing load event for iframe, loaded {:?}",
785 loaded_pipeline
786 );
787 }
788 }
789
790 pub(crate) fn run_iframe_load_event_steps(&self, cx: &mut JSContext) {
792 let child_document = self.GetContentDocument();
796
797 if let Some(document) = child_document {
800 if document.mute_iframe_load_flag() {
801 let blocker = &self.load_blocker;
802 LoadBlocker::terminate(blocker, cx);
803 return;
804 }
805 document.set_iframe_load_in_progress(true);
806 }
807
808 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
813
814 let blocker = &self.load_blocker;
815 LoadBlocker::terminate(blocker, cx);
816
817 if let Some(child_document) = self.GetContentDocument() {
819 child_document.set_iframe_load_in_progress(false);
820 }
821 }
822
823 fn parse_sandbox_attribute(&self) {
827 let sandbox_value =
828 self.upcast::<Element>()
829 .with_attribute(&ns!(), &local_name!("sandbox"), |attribute| {
830 let tokens: Vec<_> = attribute
831 .value()
832 .as_tokens()
833 .iter()
834 .map(|atom| atom.to_ascii_lowercase().to_string())
835 .collect();
836 parse_a_sandboxing_directive(&tokens)
837 });
838 self.sandboxing_flag_set.set(sandbox_value);
839 }
840
841 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
843 let Some(pipeline_id) = self.pipeline_id.get() else {
844 return;
845 };
846 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
848 exited_document.destroy_document_and_its_descendants(cx);
849 }
850 self.destroy_nested_browsing_context();
851 }
852
853 fn destroy_child_navigable(&self, cx: &mut JSContext) {
855 let blocker = &self.load_blocker;
856 LoadBlocker::terminate(blocker, cx);
857
858 let Some(browsing_context_id) = self.browsing_context_id() else {
860 return;
862 };
863 let pipeline_id = self.pipeline_id.get();
866
867 self.destroy_nested_browsing_context();
875
876 let (sender, receiver) = 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!(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!(SetReferrerPolicy, "referrerpolicy");
1039
1040 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1042 make_bool_setter!(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!(SetFrameBorder, "frameborder");
1059
1060 make_atomic_setter!(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!(SetLoading, "loading");
1084
1085 make_url_getter!(LongDesc, "longdesc");
1087
1088 make_url_setter!(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.parse_sandbox_attribute();
1215
1216 self.create_nested_browsing_context(cx);
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(super_type) = self.super_type() {
1225 super_type.bind_to_tree(cx, context);
1226 }
1227
1228 self.owner_document().iframes_mut().add(self);
1229 }
1230
1231 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1233 if let Some(super_type) = self.super_type() {
1234 super_type.unbind_from_tree(cx, context);
1235 }
1236
1237 self.destroy_child_navigable(cx);
1240
1241 self.owner_document().iframes_mut().remove(self);
1242 }
1243}
1244
1245pub(crate) struct IframeContext<'a> {
1248 element: &'a HTMLIFrameElement,
1250 url: ServoUrl,
1252}
1253
1254impl<'a> IframeContext<'a> {
1255 pub fn new(element: &'a HTMLIFrameElement) -> Self {
1257 Self {
1258 element,
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<'a> ResourceTimingListener for IframeContext<'a> {
1269 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1270 (
1271 InitiatorType::LocalName("iframe".to_string()),
1272 self.url.clone(),
1273 )
1274 }
1275
1276 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1277 self.element.upcast::<Node>().owner_doc().global()
1278 }
1279}
1280
1281fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1282 let child_navigable = iframe.GetContentWindow();
1283 TargetSnapshotParams {
1284 sandboxing_flags: determine_creation_sandboxing_flags(
1285 child_navigable.as_deref(),
1286 Some(iframe.upcast()),
1287 ),
1288 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1289 iframe.upcast(),
1290 )),
1291 }
1292}