1use std::borrow::{Borrow, ToOwned};
6use std::cell::Cell;
7use std::default::Default;
8
9use dom_struct::dom_struct;
10use embedder_traits::EmbedderMsg;
11use html5ever::{LocalName, Prefix, local_name, ns};
12use ipc_channel::ipc::IpcSender;
13use js::rust::HandleObject;
14use net_traits::image_cache::{
15 Image, ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener,
16 ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder,
17};
18use net_traits::request::{Destination, Initiator, RequestBuilder, RequestId};
19use net_traits::{
20 FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ReferrerPolicy,
21 ResourceFetchTiming, ResourceTimingType,
22};
23use pixels::PixelFormat;
24use script_bindings::root::Dom;
25use servo_arc::Arc;
26use servo_url::ServoUrl;
27use strum_macros::IntoStaticStr;
28use style::attr::AttrValue;
29use style::stylesheets::Stylesheet;
30use stylo_atoms::Atom;
31use webrender_api::units::DeviceIntSize;
32
33use crate::dom::attr::Attr;
34use crate::dom::bindings::cell::DomRefCell;
35use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
37use crate::dom::bindings::inheritance::Castable;
38use crate::dom::bindings::refcounted::Trusted;
39use crate::dom::bindings::reflector::DomGlobal;
40use crate::dom::bindings::root::{DomRoot, MutNullableDom};
41use crate::dom::bindings::str::{DOMString, USVString};
42use crate::dom::csp::{GlobalCspReporting, Violation};
43use crate::dom::cssstylesheet::CSSStyleSheet;
44use crate::dom::document::Document;
45use crate::dom::documentorshadowroot::StylesheetSource;
46use crate::dom::domtokenlist::DOMTokenList;
47use crate::dom::element::{
48 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
49 referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
50 set_cross_origin_attribute,
51};
52use crate::dom::html::htmlelement::HTMLElement;
53use crate::dom::linkprocessingoptions::LinkProcessingOptions;
54use crate::dom::medialist::MediaList;
55use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext};
56use crate::dom::performanceresourcetiming::InitiatorType;
57use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
58use crate::dom::types::{EventTarget, GlobalScope};
59use crate::dom::virtualmethods::VirtualMethods;
60use crate::links::LinkRelations;
61use crate::network_listener::{PreInvoke, ResourceTimingListener, submit_timing};
62use crate::script_runtime::CanGc;
63use crate::stylesheet_loader::{StylesheetContextSource, StylesheetLoader, StylesheetOwner};
64
65#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
66pub(crate) struct RequestGenerationId(u32);
67
68impl RequestGenerationId {
69 fn increment(self) -> RequestGenerationId {
70 RequestGenerationId(self.0 + 1)
71 }
72}
73
74#[dom_struct]
75pub(crate) struct HTMLLinkElement {
76 htmlelement: HTMLElement,
77 rel_list: MutNullableDom<DOMTokenList>,
79
80 #[no_trace]
86 relations: Cell<LinkRelations>,
87
88 #[conditional_malloc_size_of]
89 #[no_trace]
90 stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
91 cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
92
93 parser_inserted: Cell<bool>,
95 pending_loads: Cell<u32>,
98 any_failed_load: Cell<bool>,
100 request_generation_id: Cell<RequestGenerationId>,
102 is_explicitly_enabled: Cell<bool>,
104 previous_type_matched: Cell<bool>,
106 previous_media_environment_matched: Cell<bool>,
108 line_number: u64,
110}
111
112impl HTMLLinkElement {
113 fn new_inherited(
114 local_name: LocalName,
115 prefix: Option<Prefix>,
116 document: &Document,
117 creator: ElementCreator,
118 ) -> HTMLLinkElement {
119 HTMLLinkElement {
120 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
121 rel_list: Default::default(),
122 relations: Cell::new(LinkRelations::empty()),
123 parser_inserted: Cell::new(creator.is_parser_created()),
124 stylesheet: DomRefCell::new(None),
125 cssom_stylesheet: MutNullableDom::new(None),
126 pending_loads: Cell::new(0),
127 any_failed_load: Cell::new(false),
128 request_generation_id: Cell::new(RequestGenerationId(0)),
129 is_explicitly_enabled: Cell::new(false),
130 previous_type_matched: Cell::new(true),
131 previous_media_environment_matched: Cell::new(true),
132 line_number: creator.return_line_number(),
133 }
134 }
135
136 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
137 pub(crate) fn new(
138 local_name: LocalName,
139 prefix: Option<Prefix>,
140 document: &Document,
141 proto: Option<HandleObject>,
142 creator: ElementCreator,
143 can_gc: CanGc,
144 ) -> DomRoot<HTMLLinkElement> {
145 Node::reflect_node_with_proto(
146 Box::new(HTMLLinkElement::new_inherited(
147 local_name, prefix, document, creator,
148 )),
149 document,
150 proto,
151 can_gc,
152 )
153 }
154
155 pub(crate) fn get_request_generation_id(&self) -> RequestGenerationId {
156 self.request_generation_id.get()
157 }
158
159 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
162 pub(crate) fn set_stylesheet(&self, s: Arc<Stylesheet>) {
163 let stylesheets_owner = self.stylesheet_list_owner();
164 if let Some(ref s) = *self.stylesheet.borrow() {
165 stylesheets_owner
166 .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), s)
167 }
168 *self.stylesheet.borrow_mut() = Some(s.clone());
169 self.clean_stylesheet_ownership();
170 stylesheets_owner.add_owned_stylesheet(self.upcast(), s);
171 }
172
173 pub(crate) fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
174 self.stylesheet.borrow().clone()
175 }
176
177 pub(crate) fn get_cssom_stylesheet(&self, can_gc: CanGc) -> Option<DomRoot<CSSStyleSheet>> {
178 self.get_stylesheet().map(|sheet| {
179 self.cssom_stylesheet.or_init(|| {
180 CSSStyleSheet::new(
181 &self.owner_window(),
182 Some(self.upcast::<Element>()),
183 "text/css".into(),
184 None, None, sheet,
187 None, can_gc,
189 )
190 })
191 })
192 }
193
194 pub(crate) fn is_alternate(&self) -> bool {
195 self.relations.get().contains(LinkRelations::ALTERNATE)
196 }
197
198 pub(crate) fn is_effectively_disabled(&self) -> bool {
199 (self.is_alternate() && !self.is_explicitly_enabled.get()) ||
200 self.upcast::<Element>()
201 .has_attribute(&local_name!("disabled"))
202 }
203
204 fn clean_stylesheet_ownership(&self) {
205 if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() {
206 cssom_stylesheet.set_owner_node(None);
207 }
208 self.cssom_stylesheet.set(None);
209 }
210}
211
212fn get_attr(element: &Element, local_name: &LocalName) -> Option<String> {
213 let elem = element.get_attribute(&ns!(), local_name);
214 elem.map(|e| {
215 let value = e.value();
216 (**value).to_owned()
217 })
218}
219
220impl VirtualMethods for HTMLLinkElement {
221 fn super_type(&self) -> Option<&dyn VirtualMethods> {
222 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
223 }
224
225 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
226 self.super_type()
227 .unwrap()
228 .attribute_mutated(attr, mutation, can_gc);
229
230 let local_name = attr.local_name();
231 let is_removal = mutation.is_removal();
232 if *local_name == local_name!("disabled") {
233 self.handle_disabled_attribute_change(!is_removal);
234 return;
235 }
236
237 if !self.upcast::<Node>().is_connected() {
238 return;
239 }
240 match *local_name {
241 local_name!("rel") | local_name!("rev") => {
242 self.relations
243 .set(LinkRelations::for_element(self.upcast()));
244 },
245 local_name!("href") => {
246 if is_removal {
247 return;
248 }
249 if self.relations.get().contains(LinkRelations::STYLESHEET) {
253 self.handle_stylesheet_url(&attr.value());
254 }
255
256 if self.relations.get().contains(LinkRelations::ICON) {
257 self.handle_favicon_url();
258 }
259
260 if self.relations.get().contains(LinkRelations::PREFETCH) {
264 self.fetch_and_process_prefetch_link(&attr.value());
265 }
266
267 if self.relations.get().contains(LinkRelations::PRELOAD) {
271 self.handle_preload_url();
272 }
273 },
274 local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
275 self.handle_favicon_url();
276 },
277 local_name!("crossorigin") => {
278 if self.relations.get().contains(LinkRelations::PREFETCH) {
282 self.fetch_and_process_prefetch_link(&attr.value());
283 }
284
285 if self.relations.get().contains(LinkRelations::STYLESHEET) {
289 self.handle_stylesheet_url(&attr.value());
290 }
291 },
292 local_name!("as") => {
293 if self.relations.get().contains(LinkRelations::PRELOAD) {
297 if let AttributeMutation::Set(Some(_)) = mutation {
298 self.handle_preload_url();
299 }
300 }
301 },
302 local_name!("type") => {
303 if self.relations.get().contains(LinkRelations::STYLESHEET) {
311 self.handle_stylesheet_url(&attr.value());
312 }
313
314 if self.relations.get().contains(LinkRelations::PRELOAD) &&
320 !self.previous_type_matched.get()
321 {
322 self.handle_preload_url();
323 }
324 },
325 local_name!("media") => {
326 if self.relations.get().contains(LinkRelations::PRELOAD) &&
331 !self.previous_media_environment_matched.get()
332 {
333 match mutation {
334 AttributeMutation::Removed | AttributeMutation::Set(Some(_)) => {
335 self.handle_preload_url()
336 },
337 _ => {},
338 };
339 }
340
341 let matches_media_environment =
342 MediaList::matches_environment(&self.owner_document(), &attr.value());
343 self.previous_media_environment_matched
344 .set(matches_media_environment);
345 },
346 _ => {},
347 }
348 }
349
350 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
351 match name {
352 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
353 _ => self
354 .super_type()
355 .unwrap()
356 .parse_plain_attribute(name, value),
357 }
358 }
359
360 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
361 if let Some(s) = self.super_type() {
362 s.bind_to_tree(context, can_gc);
363 }
364
365 self.relations
366 .set(LinkRelations::for_element(self.upcast()));
367
368 if context.tree_connected {
369 let element = self.upcast();
370
371 if let Some(href) = get_attr(element, &local_name!("href")) {
372 let relations = self.relations.get();
373 if relations.contains(LinkRelations::STYLESHEET) {
374 self.handle_stylesheet_url(&href);
375 }
376
377 if relations.contains(LinkRelations::ICON) {
378 self.handle_favicon_url();
379 }
380
381 if relations.contains(LinkRelations::PREFETCH) {
382 self.fetch_and_process_prefetch_link(&href);
383 }
384
385 if relations.contains(LinkRelations::PRELOAD) {
386 self.handle_preload_url();
387 }
388 }
389 }
390 }
391
392 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
393 if let Some(s) = self.super_type() {
394 s.unbind_from_tree(context, can_gc);
395 }
396
397 if let Some(s) = self.stylesheet.borrow_mut().take() {
398 self.clean_stylesheet_ownership();
399 self.stylesheet_list_owner()
400 .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), &s);
401 }
402 }
403}
404
405impl HTMLLinkElement {
406 fn compute_destination_for_attribute(&self) -> Destination {
407 let element = self.upcast::<Element>();
408 element
409 .get_attribute(&ns!(), &local_name!("as"))
410 .map(|attr| translate_a_preload_destination(&attr.value()))
411 .unwrap_or(Destination::None)
412 }
413
414 fn processing_options(&self) -> LinkProcessingOptions {
416 let element = self.upcast::<Element>();
417
418 let document = self.upcast::<Node>().owner_doc();
420
421 let destination = self.compute_destination_for_attribute();
423
424 let mut options = LinkProcessingOptions {
425 href: String::new(),
426 destination: Some(destination),
427 integrity: String::new(),
428 link_type: String::new(),
429 cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
430 cross_origin: cors_setting_for_element(element),
431 referrer_policy: referrer_policy_for_element(element),
432 policy_container: document.policy_container().to_owned(),
433 source_set: None, origin: document.borrow().origin().immutable().to_owned(),
435 base_url: document.borrow().base_url(),
436 insecure_requests_policy: document.insecure_requests_policy(),
437 has_trustworthy_ancestor_origin: document.has_trustworthy_ancestor_or_current_origin(),
438 };
439
440 if let Some(href_attribute) = element.get_attribute(&ns!(), &local_name!("href")) {
442 options.href = (**href_attribute.value()).to_owned();
443 }
444
445 if let Some(integrity_attribute) = element.get_attribute(&ns!(), &local_name!("integrity"))
448 {
449 options.integrity = (**integrity_attribute.value()).to_owned();
450 }
451
452 if let Some(type_attribute) = element.get_attribute(&ns!(), &local_name!("type")) {
454 options.link_type = (**type_attribute.value()).to_owned();
455 }
456
457 assert!(!options.href.is_empty() || options.source_set.is_some());
459
460 options
462 }
463
464 fn default_fetch_and_process_the_linked_resource(&self) -> Option<RequestBuilder> {
469 let options = self.processing_options();
471
472 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
474 return None;
476 };
477 let mut request = request.synchronous(true);
479
480 if !self.linked_resource_fetch_setup(&mut request) {
482 return None;
483 }
484
485 Some(request)
491 }
492
493 fn linked_resource_fetch_setup(&self, request: &mut RequestBuilder) -> bool {
495 if self.relations.get().contains(LinkRelations::ICON) {
496 request.destination = Destination::Image;
498
499 return true;
501 }
502
503 true
504 }
505
506 fn fetch_and_process_prefetch_link(&self, href: &str) {
508 if href.is_empty() {
510 return;
511 }
512
513 let mut options = self.processing_options();
515
516 options.destination = Some(Destination::None);
518
519 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
521 return;
523 };
524 let url = request.url.clone();
525
526 let request = request.initiator(Initiator::Prefetch);
528
529 let document = self.upcast::<Node>().owner_doc();
533 let fetch_context = LinkFetchContext {
534 url,
535 link: Trusted::new(self),
536 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
537 type_: LinkFetchContextType::Prefetch,
538 };
539
540 document.fetch_background(request, fetch_context);
541 }
542
543 fn handle_stylesheet_url(&self, href: &str) {
545 let document = self.owner_document();
546 if document.browsing_context().is_none() {
547 return;
548 }
549
550 if href.is_empty() {
552 return;
553 }
554
555 let link_url = match document.base_url().join(href) {
557 Ok(url) => url,
558 Err(e) => {
559 debug!("Parsing url {} failed: {}", href, e);
560 return;
561 },
562 };
563
564 let element = self.upcast::<Element>();
565
566 let cors_setting = cors_setting_for_element(element);
568
569 let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
570 let value = mq_attribute.as_ref().map(|a| a.value());
571 let mq_str = match value {
572 Some(ref value) => &***value,
573 None => "",
574 };
575
576 if !MediaList::matches_environment(&document, mq_str) {
577 return;
578 }
579
580 let media = MediaList::parse_media_list(mq_str, document.window());
581
582 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
583 let integrity_val = im_attribute.as_ref().map(|a| a.value());
584 let integrity_metadata = match integrity_val {
585 Some(ref value) => &***value,
586 None => "",
587 };
588
589 self.request_generation_id
590 .set(self.request_generation_id.get().increment());
591
592 let loader = StylesheetLoader::for_element(self.upcast());
593 loader.load(
594 StylesheetContextSource::LinkElement { media: Some(media) },
595 link_url,
596 cors_setting,
597 integrity_metadata.to_owned(),
598 );
599 }
600
601 fn handle_disabled_attribute_change(&self, disabled: bool) {
603 if !disabled {
604 self.is_explicitly_enabled.set(true);
605 }
606 if let Some(stylesheet) = self.get_stylesheet() {
607 if stylesheet.set_disabled(disabled) {
608 self.stylesheet_list_owner().invalidate_stylesheets();
609 }
610 }
611 }
612
613 fn handle_favicon_url(&self) {
614 let window = self.owner_window();
617 if !window.is_top_level() {
618 return;
619 }
620 let Ok(href) = self.Href().parse() else {
621 return;
622 };
623
624 self.request_generation_id
626 .set(self.request_generation_id.get().increment());
627
628 let cache_result = window.image_cache().get_cached_image_status(
629 href,
630 window.origin().immutable().clone(),
631 cors_setting_for_element(self.upcast()),
632 UsePlaceholder::No,
633 );
634
635 match cache_result {
636 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
637 image,
638 is_placeholder,
639 ..
640 }) => {
641 debug_assert!(!is_placeholder);
642
643 self.process_favicon_response(image);
644 },
645 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
646 ImageCacheResult::Pending(id) => {
647 let sender = self.register_image_cache_callback(id);
648 window.image_cache().add_listener(ImageLoadListener::new(
649 sender,
650 window.pipeline_id(),
651 id,
652 ));
653 },
654 ImageCacheResult::ReadyForRequest(id) => {
655 let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
656 return;
657 };
658
659 let sender = self.register_image_cache_callback(id);
660 window.image_cache().add_listener(ImageLoadListener::new(
661 sender,
662 window.pipeline_id(),
663 id,
664 ));
665
666 let document = self.upcast::<Node>().owner_doc();
667 let fetch_context = FaviconFetchContext {
668 url: self.owner_document().base_url(),
669 image_cache: window.image_cache(),
670 id,
671 link: Trusted::new(self),
672 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
673 };
674 document.fetch_background(request, fetch_context);
675 },
676 ImageCacheResult::LoadError => {},
677 };
678 }
679
680 fn register_image_cache_callback(
681 &self,
682 id: PendingImageId,
683 ) -> IpcSender<ImageCacheResponseMessage> {
684 let trusted_node = Trusted::new(self);
685 let window = self.owner_window();
686 let request_generation_id = self.get_request_generation_id();
687 window.register_image_cache_listener(id, move |response| {
688 let trusted_node = trusted_node.clone();
689 let link_element = trusted_node.root();
690 let window = link_element.owner_window();
691
692 let ImageResponse::Loaded(image, _) = response.response else {
693 return;
695 };
696
697 if request_generation_id != link_element.get_request_generation_id() {
698 return;
700 };
701
702 window
703 .as_global_scope()
704 .task_manager()
705 .networking_task_source()
706 .queue(task!(process_favicon_response: move || {
707 let element = trusted_node.root();
708
709 if request_generation_id != element.get_request_generation_id() {
710 return;
712 };
713
714 element.process_favicon_response(image);
715 }));
716 })
717 }
718
719 fn process_favicon_response(&self, image: Image) {
721 let window = self.owner_window();
723
724 let send_rasterized_favicon_to_embedder = |raster_image: &pixels::RasterImage| {
725 let frame = raster_image.first_frame();
727
728 let format = match raster_image.format {
729 PixelFormat::K8 => embedder_traits::PixelFormat::K8,
730 PixelFormat::KA8 => embedder_traits::PixelFormat::KA8,
731 PixelFormat::RGB8 => embedder_traits::PixelFormat::RGB8,
732 PixelFormat::RGBA8 => embedder_traits::PixelFormat::RGBA8,
733 PixelFormat::BGRA8 => embedder_traits::PixelFormat::BGRA8,
734 };
735
736 let embedder_image = embedder_traits::Image::new(
737 frame.width,
738 frame.height,
739 raster_image.bytes.clone(),
740 raster_image.frames[0].byte_range.clone(),
741 format,
742 );
743 window.send_to_embedder(EmbedderMsg::NewFavicon(window.webview_id(), embedder_image));
744 };
745
746 match image {
747 Image::Raster(raster_image) => send_rasterized_favicon_to_embedder(&raster_image),
748 Image::Vector(vector_image) => {
749 let size = DeviceIntSize::new(250, 250);
751
752 let image_cache = window.image_cache();
753 if let Some(raster_image) =
754 image_cache.rasterize_vector_image(vector_image.id, size)
755 {
756 send_rasterized_favicon_to_embedder(&raster_image);
757 } else {
758 let image_cache_sender = self.register_image_cache_callback(vector_image.id);
761 image_cache.add_rasterization_complete_listener(
762 window.pipeline_id(),
763 vector_image.id,
764 size,
765 image_cache_sender,
766 );
767 }
768 },
769 }
770 }
771
772 fn handle_preload_url(&self) {
775 let options = self.processing_options();
779 {
781 let type_matches_destination = options.type_matches_destination();
783 self.previous_type_matched.set(type_matches_destination);
784 if !type_matches_destination {
785 return;
786 }
787 }
788 let Some(request) = options.preload(self.owner_window().webview_id()) else {
790 return;
791 };
792 let url = request.url.clone();
793 let fetch_context = LinkFetchContext {
794 url,
795 link: Trusted::new(self),
796 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
797 type_: LinkFetchContextType::Preload,
798 };
799 self.upcast::<Node>()
800 .owner_doc()
801 .fetch_background(request, fetch_context);
802 }
803
804 fn fire_event_after_response(&self, response: Result<ResourceFetchTiming, NetworkError>) {
806 if response.is_err() {
809 self.upcast::<EventTarget>()
810 .fire_event(atom!("error"), CanGc::note());
811 } else {
812 self.upcast::<EventTarget>()
813 .fire_event(atom!("load"), CanGc::note());
814 }
815 }
816}
817
818impl StylesheetOwner for HTMLLinkElement {
819 fn increment_pending_loads_count(&self) {
820 self.pending_loads.set(self.pending_loads.get() + 1)
821 }
822
823 fn load_finished(&self, succeeded: bool) -> Option<bool> {
824 assert!(self.pending_loads.get() > 0, "What finished?");
825 if !succeeded {
826 self.any_failed_load.set(true);
827 }
828
829 self.pending_loads.set(self.pending_loads.get() - 1);
830 if self.pending_loads.get() != 0 {
831 return None;
832 }
833
834 let any_failed = self.any_failed_load.get();
835 self.any_failed_load.set(false);
836 Some(any_failed)
837 }
838
839 fn parser_inserted(&self) -> bool {
840 self.parser_inserted.get()
841 }
842
843 fn referrer_policy(&self) -> ReferrerPolicy {
844 if self.RelList(CanGc::note()).Contains("noreferrer".into()) {
845 return ReferrerPolicy::NoReferrer;
846 }
847
848 ReferrerPolicy::EmptyString
849 }
850
851 fn set_origin_clean(&self, origin_clean: bool) {
852 if let Some(stylesheet) = self.get_cssom_stylesheet(CanGc::note()) {
853 stylesheet.set_origin_clean(origin_clean);
854 }
855 }
856}
857
858impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
859 make_url_getter!(Href, "href");
861
862 make_url_setter!(SetHref, "href");
864
865 make_getter!(Rel, "rel");
867
868 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
870 self.upcast::<Element>()
871 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
872 }
873
874 make_enumerated_getter!(
876 As,
877 "as",
878 "fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
879 | "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
880 | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
881 | "video" | "webidentity" | "worker" | "xslt",
882 missing => "",
883 invalid => ""
884 );
885
886 make_setter!(SetAs, "as");
888
889 make_getter!(Media, "media");
891
892 make_setter!(SetMedia, "media");
894
895 make_getter!(Integrity, "integrity");
897
898 make_setter!(SetIntegrity, "integrity");
900
901 make_getter!(Hreflang, "hreflang");
903
904 make_setter!(SetHreflang, "hreflang");
906
907 make_getter!(Type, "type");
909
910 make_setter!(SetType, "type");
912
913 make_bool_getter!(Disabled, "disabled");
915
916 make_bool_setter!(SetDisabled, "disabled");
918
919 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
921 self.rel_list.or_init(|| {
922 DOMTokenList::new(
923 self.upcast(),
924 &local_name!("rel"),
925 Some(vec![
926 Atom::from("alternate"),
927 Atom::from("apple-touch-icon"),
928 Atom::from("apple-touch-icon-precomposed"),
929 Atom::from("canonical"),
930 Atom::from("dns-prefetch"),
931 Atom::from("icon"),
932 Atom::from("import"),
933 Atom::from("manifest"),
934 Atom::from("modulepreload"),
935 Atom::from("next"),
936 Atom::from("preconnect"),
937 Atom::from("prefetch"),
938 Atom::from("preload"),
939 Atom::from("prerender"),
940 Atom::from("stylesheet"),
941 ]),
942 can_gc,
943 )
944 })
945 }
946
947 make_getter!(Charset, "charset");
949
950 make_setter!(SetCharset, "charset");
952
953 make_getter!(Rev, "rev");
955
956 make_setter!(SetRev, "rev");
958
959 make_getter!(Target, "target");
961
962 make_setter!(SetTarget, "target");
964
965 fn GetCrossOrigin(&self) -> Option<DOMString> {
967 reflect_cross_origin_attribute(self.upcast::<Element>())
968 }
969
970 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
972 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
973 }
974
975 fn ReferrerPolicy(&self) -> DOMString {
977 reflect_referrer_policy_attribute(self.upcast::<Element>())
978 }
979
980 make_setter!(SetReferrerPolicy, "referrerpolicy");
982
983 fn GetSheet(&self, can_gc: CanGc) -> Option<DomRoot<DOMStyleSheet>> {
985 self.get_cssom_stylesheet(can_gc).map(DomRoot::upcast)
986 }
987}
988
989fn translate_a_preload_destination(potential_destination: &str) -> Destination {
991 match potential_destination {
992 "fetch" => Destination::None,
993 "font" => Destination::Font,
994 "image" => Destination::Image,
995 "script" => Destination::Script,
996 "track" => Destination::Track,
997 _ => Destination::None,
998 }
999}
1000
1001struct FaviconFetchContext {
1002 link: Trusted<HTMLLinkElement>,
1004 image_cache: std::sync::Arc<dyn ImageCache>,
1005 id: PendingImageId,
1006
1007 url: ServoUrl,
1009
1010 resource_timing: ResourceFetchTiming,
1011}
1012
1013impl FetchResponseListener for FaviconFetchContext {
1014 fn process_request_body(&mut self, _: RequestId) {}
1015
1016 fn process_request_eof(&mut self, _: RequestId) {}
1017
1018 fn process_response(
1019 &mut self,
1020 request_id: RequestId,
1021 metadata: Result<FetchMetadata, NetworkError>,
1022 ) {
1023 self.image_cache.notify_pending_response(
1024 self.id,
1025 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
1026 );
1027 }
1028
1029 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>) {
1030 self.image_cache.notify_pending_response(
1031 self.id,
1032 FetchResponseMsg::ProcessResponseChunk(request_id, chunk),
1033 );
1034 }
1035
1036 fn process_response_eof(
1037 &mut self,
1038 request_id: RequestId,
1039 response: Result<ResourceFetchTiming, NetworkError>,
1040 ) {
1041 self.image_cache.notify_pending_response(
1042 self.id,
1043 FetchResponseMsg::ProcessResponseEOF(request_id, response),
1044 );
1045 }
1046
1047 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1048 &mut self.resource_timing
1049 }
1050
1051 fn resource_timing(&self) -> &ResourceFetchTiming {
1052 &self.resource_timing
1053 }
1054
1055 fn submit_resource_timing(&mut self) {
1056 submit_timing(self, CanGc::note())
1057 }
1058
1059 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1060 let global = &self.resource_timing_global();
1061 let link = self.link.root();
1062 let source_position = link
1063 .upcast::<Element>()
1064 .compute_source_position(link.line_number as u32);
1065 global.report_csp_violations(violations, None, Some(source_position));
1066 }
1067}
1068
1069impl ResourceTimingListener for FaviconFetchContext {
1070 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1071 (
1072 InitiatorType::LocalName("link".to_string()),
1073 self.url.clone(),
1074 )
1075 }
1076
1077 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1078 self.link.root().upcast::<Node>().owner_doc().global()
1079 }
1080}
1081
1082impl PreInvoke for FaviconFetchContext {
1083 fn should_invoke(&self) -> bool {
1084 true
1085 }
1086}
1087
1088#[derive(Clone, IntoStaticStr)]
1089#[strum(serialize_all = "lowercase")]
1090enum LinkFetchContextType {
1091 Prefetch,
1092 Preload,
1093}
1094
1095impl From<LinkFetchContextType> for InitiatorType {
1096 fn from(other: LinkFetchContextType) -> Self {
1097 let name: &'static str = other.into();
1098 InitiatorType::LocalName(name.to_owned())
1099 }
1100}
1101
1102struct LinkFetchContext {
1103 link: Trusted<HTMLLinkElement>,
1105
1106 resource_timing: ResourceFetchTiming,
1107
1108 url: ServoUrl,
1110
1111 type_: LinkFetchContextType,
1113}
1114
1115impl FetchResponseListener for LinkFetchContext {
1116 fn process_request_body(&mut self, _: RequestId) {}
1117
1118 fn process_request_eof(&mut self, _: RequestId) {}
1119
1120 fn process_response(
1121 &mut self,
1122 _: RequestId,
1123 fetch_metadata: Result<FetchMetadata, NetworkError>,
1124 ) {
1125 _ = fetch_metadata;
1126 }
1127
1128 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
1129 _ = chunk;
1130 }
1131
1132 fn process_response_eof(
1135 &mut self,
1136 _: RequestId,
1137 response: Result<ResourceFetchTiming, NetworkError>,
1138 ) {
1139 self.link.root().fire_event_after_response(response);
1140 }
1141
1142 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1143 &mut self.resource_timing
1144 }
1145
1146 fn resource_timing(&self) -> &ResourceFetchTiming {
1147 &self.resource_timing
1148 }
1149
1150 fn submit_resource_timing(&mut self) {
1151 submit_timing(self, CanGc::note())
1152 }
1153
1154 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1155 let global = &self.resource_timing_global();
1156 let link = self.link.root();
1157 let source_position = link
1158 .upcast::<Element>()
1159 .compute_source_position(link.line_number as u32);
1160 global.report_csp_violations(violations, None, Some(source_position));
1161 }
1162}
1163
1164impl ResourceTimingListener for LinkFetchContext {
1165 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1166 (self.type_.clone().into(), self.url.clone())
1167 }
1168
1169 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1170 self.link.root().upcast::<Node>().owner_doc().global()
1171 }
1172}
1173
1174impl PreInvoke for LinkFetchContext {
1175 fn should_invoke(&self) -> bool {
1176 true
1178 }
1179}