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::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 #[conditional_malloc_size_of]
97 script_window_proxies: Rc<ScriptWindowProxies>,
98 current_navigation_was_lazy_loaded: Cell<bool>,
100 #[no_trace]
102 lazy_load_resumption_steps: Cell<LazyLoadResumptionSteps>,
103 pending_navigation: Cell<bool>,
110 frozen_name: DomRefCell<Option<String>>,
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 cx: &mut JSContext,
154 load_data: LoadData,
155 history_handling: NavigationHistoryBehavior,
156 mode: ProcessingMode,
157 target_snapshot_params: TargetSnapshotParams,
158 ) {
159 self.start_new_pipeline(
160 cx,
161 load_data,
162 PipelineType::Navigation,
163 history_handling,
164 mode,
165 target_snapshot_params,
166 );
167 }
168
169 fn start_new_pipeline(
170 &self,
171 cx: &mut JSContext,
172 mut load_data: LoadData,
173 pipeline_type: PipelineType,
174 history_handling: NavigationHistoryBehavior,
175 mode: ProcessingMode,
176 target_snapshot_params: TargetSnapshotParams,
177 ) {
178 let document = self.owner_document();
179
180 {
181 let load_blocker = &self.load_blocker;
182 LoadBlocker::terminate(load_blocker, cx);
185
186 *load_blocker.borrow_mut() = Some(LoadBlocker::new(
187 &document,
188 LoadType::Subframe(load_data.url.clone()),
189 ));
190 }
191
192 if load_data.url.scheme() != "javascript" {
193 self.continue_navigation(
194 cx,
195 load_data,
196 pipeline_type,
197 history_handling,
198 target_snapshot_params,
199 );
200 return;
201 }
202
203 let iframe = Trusted::new(self);
207 let doc = Trusted::new(&*document);
208 document
209 .global()
210 .task_manager()
211 .networking_task_source()
212 .queue(task!(navigate_to_javascript: move |cx| {
213 let this = iframe.root();
214 let window_proxy = this.GetContentWindow();
215 if let Some(window_proxy) = window_proxy {
216 if !ScriptThread::navigate_to_javascript_url(
219 cx,
220 &this.owner_global(),
221 &window_proxy.global(),
222 &mut load_data,
223 Some(this.upcast()),
224 Some(mode == ProcessingMode::FirstTime),
225 ) {
226 LoadBlocker::terminate(&this.load_blocker, cx);
227 return;
228 }
229 load_data.about_base_url = doc.root().about_base_url();
230 }
231 this.continue_navigation(cx, load_data, pipeline_type, history_handling, target_snapshot_params);
232 }));
233 }
234
235 fn continue_navigation(
236 &self,
237 cx: &mut JSContext,
238 load_data: LoadData,
239 pipeline_type: PipelineType,
240 history_handling: NavigationHistoryBehavior,
241 target_snapshot_params: TargetSnapshotParams,
242 ) {
243 let browsing_context_id = match self.browsing_context_id() {
244 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
245 Some(id) => id,
246 };
247
248 let webview_id = match self.webview_id() {
249 None => return warn!("Attempted to start a new pipeline on an unattached iframe."),
250 Some(id) => id,
251 };
252
253 let window = self.owner_window();
254 let old_pipeline_id = self.pipeline_id();
255 let new_pipeline_id = PipelineId::new();
256 self.pending_pipeline_id.set(Some(new_pipeline_id));
257
258 let load_info = IFrameLoadInfo {
259 parent_pipeline_id: window.pipeline_id(),
260 browsing_context_id,
261 webview_id,
262 new_pipeline_id,
263 is_private: false, inherited_secure_context: load_data.inherited_secure_context,
265 history_handling,
266 target_snapshot_params,
267 name: self.frozen_name.borrow().clone(),
268 };
269
270 let viewport_details = window
271 .get_iframe_viewport_details_if_known(browsing_context_id)
272 .unwrap_or_else(|| ViewportDetails {
273 hidpi_scale_factor: window.device_pixel_ratio(),
274 ..Default::default()
275 });
276
277 match pipeline_type {
278 PipelineType::InitialAboutBlank => {
279 self.about_blank_pipeline_id.set(Some(new_pipeline_id));
280
281 let load_info = IFrameLoadInfoWithData {
282 info: load_info,
283 load_data: load_data.clone(),
284 old_pipeline_id,
285 viewport_details,
286 };
287 window
288 .as_global_scope()
289 .script_to_constellation_chan()
290 .send(ScriptToConstellationMessage::ScriptNewIFrame(load_info))
291 .unwrap();
292
293 let new_pipeline_info = NewPipelineInfo {
294 webview_state: (*window.webview_state()).clone(),
295 parent_info: Some(window.pipeline_id()),
296 new_pipeline_id,
297 browsing_context_id,
298 opener: None,
299 load_data,
300 viewport_details,
301 user_content_manager_id: None,
302 target_snapshot_params,
303 frame_name: self.frozen_name.borrow().clone(),
304 };
305
306 self.pipeline_id.set(Some(new_pipeline_id));
307 with_script_thread(|script_thread| {
308 script_thread.spawn_pipeline(cx, new_pipeline_info);
309 });
310 },
311 PipelineType::Navigation => {
312 let load_info = IFrameLoadInfoWithData {
313 info: load_info,
314 load_data,
315 old_pipeline_id,
316 viewport_details,
317 };
318 window
319 .as_global_scope()
320 .script_to_constellation_chan()
321 .send(ScriptToConstellationMessage::ScriptLoadedURLInIFrame(
322 load_info,
323 ))
324 .unwrap();
325 },
326 }
327 }
328
329 pub(crate) fn is_initial_blank_document(&self) -> bool {
335 self.pending_pipeline_id.get() == self.about_blank_pipeline_id.get()
336 }
337
338 fn navigate_an_iframe_or_frame(
340 &self,
341 cx: &mut JSContext,
342 load_data: LoadData,
343 mode: ProcessingMode,
344 ) {
345 let history_handling = if !self
348 .GetContentDocument()
349 .is_some_and(|doc| doc.completely_loaded())
350 {
351 NavigationHistoryBehavior::Replace
352 } else {
353 NavigationHistoryBehavior::Auto
355 };
356 let target_snapshot_params = snapshot_self(self);
364 self.navigate_or_reload_child_browsing_context(
365 cx,
366 load_data,
367 history_handling,
368 mode,
369 target_snapshot_params,
370 );
371 }
372
373 fn will_lazy_load_element_steps(&self) -> bool {
375 if !self.owner_document().scripting_enabled() {
377 return false;
378 }
379 self.Loading() == "lazy"
382 }
383
384 fn navigate_to_the_srcdoc_resource(&self, cx: &mut JSContext, mode: ProcessingMode) {
386 let url = ServoUrl::parse("about:srcdoc").unwrap();
389 let document = self.owner_document();
390 let window = self.owner_window();
391 let pipeline_id = Some(window.pipeline_id());
392 let mut load_data = LoadData::new(
393 LoadOrigin::Script(document.origin().snapshot()),
394 url,
395 Some(document.base_url()),
396 pipeline_id,
397 window.as_global_scope().get_referrer(),
398 document.get_referrer_policy(),
399 Some(window.as_global_scope().is_secure_context()),
400 Some(document.insecure_requests_policy()),
401 document.has_trustworthy_ancestor_or_current_origin(),
402 self.sandboxing_flag_set(),
403 );
404 load_data.destination = Destination::IFrame;
405 load_data.policy_container = Some(window.as_global_scope().policy_container());
406 load_data.srcdoc = String::from(
407 self.upcast::<Element>()
408 .get_string_attribute(&local_name!("srcdoc")),
409 );
410
411 self.navigate_an_iframe_or_frame(cx, load_data, mode);
412 }
413
414 fn mark_navigation_as_lazy_loaded(&self, cx: &mut JSContext) {
416 self.current_navigation_was_lazy_loaded.set(true);
418 let blocker = &self.load_blocker;
419 LoadBlocker::terminate(blocker, cx);
420 }
421
422 fn process_the_iframe_attributes(&self, cx: &mut JSContext, mode: ProcessingMode) {
424 let element = self.upcast::<Element>();
425
426 if element.has_attribute(&local_name!("srcdoc")) {
430 self.current_navigation_was_lazy_loaded.set(false);
432 if self.will_lazy_load_element_steps() {
434 self.lazy_load_resumption_steps
437 .set(LazyLoadResumptionSteps::SrcDoc);
438 self.mark_navigation_as_lazy_loaded(cx);
440 return;
444 }
445 self.navigate_to_the_srcdoc_resource(cx, mode);
448 return;
449 }
450
451 let window = self.owner_window();
452
453 let Some(url) = self.shared_attribute_processing_steps_for_iframe_and_frame_elements(mode)
456 else {
457 return;
459 };
460
461 if url.matches_about_blank() && mode == ProcessingMode::FirstTime {
463 self.run_iframe_load_event_steps(cx);
465 return;
467 }
468
469 let document = self.owner_document();
472 let referrer_policy_token = self.ReferrerPolicy();
473
474 let referrer_policy = match ReferrerPolicy::from(&*referrer_policy_token.str()) {
478 ReferrerPolicy::EmptyString => document.get_referrer_policy(),
479 policy => policy,
480 };
481
482 let mut ancestor = window.GetParent();
491 while let Some(a) = ancestor {
492 if let Some(ancestor_url) = a.document().map(|d| d.url()) &&
493 ancestor_url.scheme() == url.scheme() &&
494 ancestor_url.username() == url.username() &&
495 ancestor_url.password() == url.password() &&
496 ancestor_url.host() == url.host() &&
497 ancestor_url.port() == url.port() &&
498 ancestor_url.path() == url.path() &&
499 ancestor_url.query() == url.query()
500 {
501 return;
502 }
503 ancestor = a.parent().map(DomRoot::from_ref);
504 }
505
506 let (creator_pipeline_id, about_base_url) = if url.matches_about_blank() {
507 (Some(window.pipeline_id()), Some(document.base_url()))
508 } else {
509 (None, document.about_base_url())
510 };
511
512 let propagate_encoding_to_child_document = url.origin().same_origin(&window.origin());
513 let mut load_data = LoadData::new(
514 LoadOrigin::Script(document.origin().snapshot()),
515 url,
516 about_base_url,
517 creator_pipeline_id,
518 window.as_global_scope().get_referrer(),
519 referrer_policy,
520 Some(window.as_global_scope().is_secure_context()),
521 Some(document.insecure_requests_policy()),
522 document.has_trustworthy_ancestor_or_current_origin(),
523 self.sandboxing_flag_set(),
524 );
525 load_data.destination = Destination::IFrame;
526 load_data.policy_container = Some(window.as_global_scope().policy_container());
527 if propagate_encoding_to_child_document {
528 load_data.container_document_encoding = Some(document.encoding());
529 }
530
531 let pipeline_id = self.pipeline_id();
532 let is_about_blank =
535 pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
536
537 let history_handling = if is_about_blank {
538 NavigationHistoryBehavior::Replace
539 } else {
540 NavigationHistoryBehavior::Push
541 };
542
543 let target_snapshot_params = snapshot_self(self);
544 self.navigate_or_reload_child_browsing_context(
545 cx,
546 load_data,
547 history_handling,
548 mode,
549 target_snapshot_params,
550 );
551 }
552
553 fn create_nested_browsing_context(&self, cx: &mut JSContext) {
556 let document = self.owner_document();
558 let window = self.owner_window();
559 let pipeline_id = Some(window.pipeline_id());
560 *self.frozen_name.borrow_mut() = self
564 .upcast::<Element>()
565 .get_name()
566 .map(|name| name.to_string());
567 let mut load_data = LoadData::new(
569 LoadOrigin::Script(document.origin().snapshot()),
572 ServoUrl::parse("about:blank").unwrap(),
573 Some(document.base_url()),
576 pipeline_id,
577 window.as_global_scope().get_referrer(),
578 document.get_referrer_policy(),
579 Some(window.as_global_scope().is_secure_context()),
580 Some(document.insecure_requests_policy()),
581 document.has_trustworthy_ancestor_or_current_origin(),
582 self.sandboxing_flag_set(),
583 );
584 load_data.is_initial_about_blank = true;
585 load_data.destination = Destination::IFrame;
586 load_data.policy_container = Some(window.as_global_scope().policy_container());
587
588 let browsing_context_id = BrowsingContextId::new();
590 let webview_id = window.window_proxy().webview_id();
591 self.pipeline_id.set(None);
592 self.pending_pipeline_id.set(None);
593 self.webview_id.set(Some(webview_id));
594 self.browsing_context_id.set(Some(browsing_context_id));
595 self.start_new_pipeline(
597 cx,
598 load_data,
599 PipelineType::InitialAboutBlank,
600 NavigationHistoryBehavior::Push,
601 ProcessingMode::FirstTime,
602 snapshot_self(self),
603 );
604 if let Some(window) = self.GetContentWindow() &&
607 let Some(window_name) = &*self.frozen_name.borrow()
608 {
609 window.set_name(window_name.as_str().into());
610 }
611 }
612
613 fn destroy_nested_browsing_context(&self) {
614 self.pipeline_id.set(None);
615 self.pending_pipeline_id.set(None);
616 self.about_blank_pipeline_id.set(None);
617 self.webview_id.set(None);
618 if let Some(browsing_context_id) = self.browsing_context_id.take() {
619 self.script_window_proxies.remove(browsing_context_id)
620 }
621 }
622
623 pub(crate) fn update_pipeline_id(
627 &self,
628 cx: &mut JSContext,
629 new_pipeline_id: PipelineId,
630 reason: UpdatePipelineIdReason,
631 ) -> bool {
632 if !self.is_initial_blank_document() {
637 self.pending_navigation.set(false);
638 }
639 if self.pending_pipeline_id.get() != Some(new_pipeline_id) &&
640 reason == UpdatePipelineIdReason::Navigation
641 {
642 return false;
643 }
644
645 self.pipeline_id.set(Some(new_pipeline_id));
646
647 if reason == UpdatePipelineIdReason::Traversal {
650 let blocker = &self.load_blocker;
651 LoadBlocker::terminate(blocker, cx);
652 }
653
654 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
655 true
656 }
657
658 fn new_inherited(
659 local_name: LocalName,
660 prefix: Option<Prefix>,
661 document: &Document,
662 ) -> HTMLIFrameElement {
663 HTMLIFrameElement {
664 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
665 browsing_context_id: Cell::new(None),
666 webview_id: Cell::new(None),
667 pipeline_id: Cell::new(None),
668 pending_pipeline_id: Cell::new(None),
669 about_blank_pipeline_id: Cell::new(None),
670 sandbox: Default::default(),
671 sandboxing_flag_set: Cell::new(None),
672 load_blocker: DomRefCell::new(None),
673 script_window_proxies: ScriptThread::window_proxies(),
674 current_navigation_was_lazy_loaded: Default::default(),
675 lazy_load_resumption_steps: Default::default(),
676 pending_navigation: Default::default(),
677 frozen_name: Default::default(),
678 }
679 }
680
681 pub(crate) fn new(
682 cx: &mut JSContext,
683 local_name: LocalName,
684 prefix: Option<Prefix>,
685 document: &Document,
686 proto: Option<HandleObject>,
687 ) -> DomRoot<HTMLIFrameElement> {
688 Node::reflect_node_with_proto(
689 cx,
690 Box::new(HTMLIFrameElement::new_inherited(
691 local_name, prefix, document,
692 )),
693 document,
694 proto,
695 )
696 }
697
698 #[inline]
699 pub(crate) fn pipeline_id(&self) -> Option<PipelineId> {
700 self.pipeline_id.get()
701 }
702
703 #[inline]
704 pub(crate) fn browsing_context_id(&self) -> Option<BrowsingContextId> {
705 self.browsing_context_id.get()
706 }
707
708 #[inline]
709 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
710 self.webview_id.get()
711 }
712
713 #[inline]
714 pub(crate) fn sandboxing_flag_set(&self) -> SandboxingFlagSet {
715 self.sandboxing_flag_set
716 .get()
717 .unwrap_or_else(SandboxingFlagSet::empty)
718 }
719
720 pub(crate) fn note_pending_navigation(&self) {
724 self.pending_navigation.set(true);
725 }
726
727 pub(crate) fn iframe_load_event_steps(&self, cx: &mut JSContext, loaded_pipeline: PipelineId) {
729 if Some(loaded_pipeline) != self.pending_pipeline_id.get() {
732 return;
733 }
734
735 let should_fire_event = if self.is_initial_blank_document() {
764 !self.pending_navigation.get() &&
768 !self.upcast::<Element>().has_attribute(&local_name!("src"))
769 } else {
770 !self.pending_navigation.get()
773 };
774
775 if should_fire_event {
776 self.run_iframe_load_event_steps(cx);
777 } else {
778 debug!(
779 "suppressing load event for iframe, loaded {:?}",
780 loaded_pipeline
781 );
782 }
783 }
784
785 pub(crate) fn run_iframe_load_event_steps(&self, cx: &mut JSContext) {
787 let child_document = self.GetContentDocument();
791
792 if let Some(document) = child_document {
795 if document.mute_iframe_load_flag() {
796 let blocker = &self.load_blocker;
797 LoadBlocker::terminate(blocker, cx);
798 return;
799 }
800 document.set_iframe_load_in_progress(true);
801 }
802
803 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
808
809 let blocker = &self.load_blocker;
810 LoadBlocker::terminate(blocker, cx);
811
812 if let Some(child_document) = self.GetContentDocument() {
814 child_document.set_iframe_load_in_progress(false);
815 }
816 }
817
818 fn parse_sandbox_attribute(&self) {
822 let sandbox_value =
823 self.upcast::<Element>()
824 .with_attribute(&ns!(), &local_name!("sandbox"), |attribute| {
825 let tokens: Vec<_> = attribute
826 .value()
827 .as_tokens()
828 .iter()
829 .map(|atom| atom.to_ascii_lowercase().to_string())
830 .collect();
831 parse_a_sandboxing_directive(&tokens)
832 });
833 self.sandboxing_flag_set.set(sandbox_value);
834 }
835
836 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
838 let Some(pipeline_id) = self.pipeline_id.get() else {
839 return;
840 };
841 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
843 exited_document.destroy_document_and_its_descendants(cx);
844 }
845 self.destroy_nested_browsing_context();
846 }
847
848 fn destroy_child_navigable(&self, cx: &mut JSContext) {
850 let blocker = &self.load_blocker;
851 LoadBlocker::terminate(blocker, cx);
852
853 let Some(browsing_context_id) = self.browsing_context_id() else {
855 return;
857 };
858 let pipeline_id = self.pipeline_id.get();
861
862 self.destroy_nested_browsing_context();
870
871 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
876 let msg = ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, sender);
877 self.owner_window()
878 .as_global_scope()
879 .script_to_constellation_chan()
880 .send(msg)
881 .unwrap();
882 let _exited_pipeline_ids = receiver.recv().unwrap();
883 let Some(pipeline_id) = pipeline_id else {
884 return;
885 };
886 if let Some(exited_document) = ScriptThread::find_document(pipeline_id) {
887 exited_document.destroy_document_and_its_descendants(cx);
888 }
889
890 }
905}
906
907impl LayoutDom<'_, HTMLIFrameElement> {
908 #[inline]
909 pub(crate) fn pipeline_id(self) -> Option<PipelineId> {
910 (self.unsafe_get()).pipeline_id.get()
911 }
912
913 #[inline]
914 pub(crate) fn browsing_context_id(self) -> Option<BrowsingContextId> {
915 (self.unsafe_get()).browsing_context_id.get()
916 }
917
918 pub(crate) fn width(self) -> LengthOrPercentageOrAuto {
919 self.upcast::<Element>()
920 .get_attr_for_layout(&ns!(), &local_name!("width"))
921 .map(AttrValue::as_dimension)
922 .cloned()
923 .unwrap_or(LengthOrPercentageOrAuto::Auto)
924 }
925
926 pub(crate) fn height(self) -> LengthOrPercentageOrAuto {
927 self.upcast::<Element>()
928 .get_attr_for_layout(&ns!(), &local_name!("height"))
929 .map(AttrValue::as_dimension)
930 .cloned()
931 .unwrap_or(LengthOrPercentageOrAuto::Auto)
932 }
933}
934
935impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
936 make_url_getter!(Src, "src");
938
939 make_url_setter!(SetSrc, "src");
941
942 fn Srcdoc(&self) -> TrustedHTMLOrString {
944 let element = self.upcast::<Element>();
945 element.get_trusted_html_attribute(&local_name!("srcdoc"))
946 }
947
948 fn SetSrcdoc(&self, cx: &mut JSContext, value: TrustedHTMLOrString) -> Fallible<()> {
950 let element = self.upcast::<Element>();
954 let value = TrustedHTML::get_trusted_type_compliant_string(
955 cx,
956 &element.owner_global(),
957 value,
958 "HTMLIFrameElement srcdoc",
959 )?;
960 element.set_attribute(
962 cx,
963 &local_name!("srcdoc"),
964 AttrValue::String(value.str().to_owned()),
965 );
966 Ok(())
967 }
968
969 fn Sandbox(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
975 self.sandbox.or_init(|| {
976 DOMTokenList::new(
977 cx,
978 self.upcast::<Element>(),
979 &local_name!("sandbox"),
980 Some(vec![
981 Atom::from("allow-downloads"),
982 Atom::from("allow-forms"),
983 Atom::from("allow-modals"),
984 Atom::from("allow-orientation-lock"),
985 Atom::from("allow-pointer-lock"),
986 Atom::from("allow-popups"),
987 Atom::from("allow-popups-to-escape-sandbox"),
988 Atom::from("allow-presentation"),
989 Atom::from("allow-same-origin"),
990 Atom::from("allow-scripts"),
991 Atom::from("allow-top-navigation"),
992 Atom::from("allow-top-navigation-by-user-activation"),
993 Atom::from("allow-top-navigation-to-custom-protocols"),
994 ]),
995 )
996 })
997 }
998
999 fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
1001 self.browsing_context_id
1002 .get()
1003 .and_then(|id| self.script_window_proxies.find_window_proxy(id))
1004 }
1005
1006 fn GetContentDocument(&self) -> Option<DomRoot<Document>> {
1008 let pipeline_id = self.pipeline_id.get()?;
1010
1011 let document = ScriptThread::find_document(pipeline_id)?;
1015 if !self
1017 .owner_document()
1018 .origin()
1019 .same_origin_domain(&document.origin())
1020 {
1021 return None;
1022 }
1023 Some(document)
1025 }
1026
1027 fn ReferrerPolicy(&self) -> DOMString {
1029 reflect_referrer_policy_attribute(self.upcast::<Element>())
1030 }
1031
1032 make_setter!(SetReferrerPolicy, "referrerpolicy");
1034
1035 make_bool_getter!(AllowFullscreen, "allowfullscreen");
1037 make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
1039
1040 make_getter!(Width, "width");
1042 make_dimension_setter!(SetWidth, "width");
1044
1045 make_getter!(Height, "height");
1047 make_dimension_setter!(SetHeight, "height");
1049
1050 make_getter!(FrameBorder, "frameborder");
1052 make_setter!(SetFrameBorder, "frameborder");
1054
1055 make_atomic_setter!(SetName, "name");
1059
1060 make_getter!(Name, "name");
1064
1065 make_enumerated_getter!(
1068 Loading,
1069 "loading",
1070 "lazy" | "eager",
1071 missing => "eager",
1074 invalid => "eager"
1075 );
1076
1077 make_setter!(SetLoading, "loading");
1079
1080 make_url_getter!(LongDesc, "longdesc");
1082
1083 make_url_setter!(SetLongDesc, "longdesc");
1085}
1086
1087impl VirtualMethods for HTMLIFrameElement {
1088 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1089 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1090 }
1091
1092 fn attribute_mutated(
1093 &self,
1094 cx: &mut JSContext,
1095 attr: AttrRef<'_>,
1096 mutation: AttributeMutation,
1097 ) {
1098 self.super_type()
1099 .unwrap()
1100 .attribute_mutated(cx, attr, mutation);
1101 match *attr.local_name() {
1102 local_name!("sandbox") if self.browsing_context_id.get().is_some() => {
1113 self.parse_sandbox_attribute();
1114 },
1115 local_name!("srcdoc") => {
1116 if self.upcast::<Node>().is_connected_with_browsing_context() {
1127 debug!("iframe srcdoc modified while in browsing context.");
1128 self.process_the_iframe_attributes(cx, ProcessingMode::NotFirstTime);
1129 }
1130 },
1131 local_name!("src") => {
1132 if self.upcast::<Node>().is_connected_with_browsing_context() {
1141 debug!("iframe src set while in browsing context.");
1142 self.process_the_iframe_attributes(cx, ProcessingMode::NotFirstTime);
1143 }
1144 },
1145 local_name!("loading") => {
1146 if !mutation.is_removal() && &**attr.value() == "lazy" {
1149 return;
1150 }
1151
1152 let previous_resumption_steps = self
1155 .lazy_load_resumption_steps
1156 .replace(LazyLoadResumptionSteps::None);
1157 match previous_resumption_steps {
1158 LazyLoadResumptionSteps::None => (),
1160 LazyLoadResumptionSteps::SrcDoc => {
1161 self.navigate_to_the_srcdoc_resource(cx, ProcessingMode::NotFirstTime);
1163 },
1164 }
1165 },
1166 _ => {},
1167 }
1168 }
1169
1170 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1171 match attr.local_name() {
1172 &local_name!("width") | &local_name!("height") => true,
1173 _ => self
1174 .super_type()
1175 .unwrap()
1176 .attribute_affects_presentational_hints(attr),
1177 }
1178 }
1179
1180 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1181 match *name {
1182 local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
1183 local_name!("width") => AttrValue::from_dimension(value.into()),
1184 local_name!("height") => AttrValue::from_dimension(value.into()),
1185 _ => self
1186 .super_type()
1187 .unwrap()
1188 .parse_plain_attribute(name, value),
1189 }
1190 }
1191
1192 fn post_connection_steps(&self, cx: &mut JSContext) {
1194 if let Some(s) = self.super_type() {
1195 s.post_connection_steps(cx);
1196 }
1197
1198 if !self.upcast::<Node>().is_connected_with_browsing_context() {
1202 return;
1203 }
1204
1205 debug!("<iframe> running post connection steps");
1206
1207 self.parse_sandbox_attribute();
1210
1211 self.create_nested_browsing_context(cx);
1213
1214 self.process_the_iframe_attributes(cx, ProcessingMode::FirstTime);
1216 }
1217
1218 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1219 if let Some(super_type) = self.super_type() {
1220 super_type.bind_to_tree(cx, context);
1221 }
1222
1223 self.owner_document().iframes().add(cx.no_gc(), self);
1224 }
1225
1226 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1228 if let Some(super_type) = self.super_type() {
1229 super_type.unbind_from_tree(cx, context);
1230 }
1231
1232 self.destroy_child_navigable(cx);
1235
1236 self.owner_document().iframes().remove(cx.no_gc(), self);
1237 }
1238}
1239
1240pub(crate) struct IframeContext<'a> {
1243 element: &'a HTMLIFrameElement,
1245 url: ServoUrl,
1247}
1248
1249impl<'a> IframeContext<'a> {
1250 pub fn new(element: &'a HTMLIFrameElement) -> Self {
1252 Self {
1253 element,
1254 url: element
1255 .shared_attribute_processing_steps_for_iframe_and_frame_elements(
1256 ProcessingMode::NotFirstTime,
1257 )
1258 .expect("Must always have a URL when navigating"),
1259 }
1260 }
1261}
1262
1263impl<'a> ResourceTimingListener for IframeContext<'a> {
1264 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1265 (
1266 InitiatorType::LocalName("iframe".to_string()),
1267 self.url.clone(),
1268 )
1269 }
1270
1271 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1272 self.element.upcast::<Node>().owner_doc().global()
1273 }
1274}
1275
1276fn snapshot_self(iframe: &HTMLIFrameElement) -> TargetSnapshotParams {
1277 let child_navigable = iframe.GetContentWindow();
1278 TargetSnapshotParams {
1279 sandboxing_flags: determine_creation_sandboxing_flags(
1280 child_navigable.as_deref(),
1281 Some(iframe.upcast()),
1282 ),
1283 iframe_element_referrer_policy: determine_iframe_element_referrer_policy(Some(
1284 iframe.upcast(),
1285 )),
1286 }
1287}