1use std::borrow::{Borrow, ToOwned};
6use std::cell::Cell;
7use std::default::Default;
8use std::str::FromStr;
9
10use base::id::WebViewId;
11use dom_struct::dom_struct;
12use embedder_traits::EmbedderMsg;
13use html5ever::{LocalName, Prefix, local_name, ns};
14use ipc_channel::ipc::IpcSender;
15use js::rust::HandleObject;
16use mime::Mime;
17use net_traits::image_cache::{
18 Image, ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener,
19 ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder,
20};
21use net_traits::mime_classifier::{MediaType, MimeClassifier};
22use net_traits::policy_container::PolicyContainer;
23use net_traits::request::{
24 CorsSettings, Destination, Initiator, InsecureRequestsPolicy, Referrer, RequestBuilder,
25 RequestId,
26};
27use net_traits::{
28 FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ReferrerPolicy,
29 ResourceFetchTiming, ResourceTimingType,
30};
31use pixels::PixelFormat;
32use script_bindings::root::Dom;
33use servo_arc::Arc;
34use servo_url::{ImmutableOrigin, ServoUrl};
35use style::attr::AttrValue;
36use style::stylesheets::Stylesheet;
37use stylo_atoms::Atom;
38use webrender_api::units::DeviceIntSize;
39
40use crate::dom::attr::Attr;
41use crate::dom::bindings::cell::DomRefCell;
42use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
43use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
44use crate::dom::bindings::inheritance::Castable;
45use crate::dom::bindings::refcounted::Trusted;
46use crate::dom::bindings::reflector::DomGlobal;
47use crate::dom::bindings::root::{DomRoot, MutNullableDom};
48use crate::dom::bindings::str::{DOMString, USVString};
49use crate::dom::csp::{GlobalCspReporting, Violation};
50use crate::dom::cssstylesheet::CSSStyleSheet;
51use crate::dom::document::Document;
52use crate::dom::documentorshadowroot::StylesheetSource;
53use crate::dom::domtokenlist::DOMTokenList;
54use crate::dom::element::{
55 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
56 referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
57 set_cross_origin_attribute,
58};
59use crate::dom::html::htmlelement::HTMLElement;
60use crate::dom::medialist::MediaList;
61use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext};
62use crate::dom::performanceresourcetiming::InitiatorType;
63use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
64use crate::dom::types::{EventTarget, GlobalScope};
65use crate::dom::virtualmethods::VirtualMethods;
66use crate::fetch::create_a_potential_cors_request;
67use crate::links::LinkRelations;
68use crate::network_listener::{PreInvoke, ResourceTimingListener, submit_timing};
69use crate::script_runtime::CanGc;
70use crate::stylesheet_loader::{StylesheetContextSource, StylesheetLoader, StylesheetOwner};
71
72#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
73pub(crate) struct RequestGenerationId(u32);
74
75impl RequestGenerationId {
76 fn increment(self) -> RequestGenerationId {
77 RequestGenerationId(self.0 + 1)
78 }
79}
80
81struct LinkProcessingOptions {
83 href: String,
84 destination: Option<Destination>,
85 integrity: String,
86 link_type: String,
87 cryptographic_nonce_metadata: String,
88 cross_origin: Option<CorsSettings>,
89 referrer_policy: ReferrerPolicy,
90 policy_container: PolicyContainer,
91 source_set: Option<()>,
92 base_url: ServoUrl,
93 origin: ImmutableOrigin,
94 insecure_requests_policy: InsecureRequestsPolicy,
95 has_trustworthy_ancestor_origin: bool,
96 }
98
99#[dom_struct]
100pub(crate) struct HTMLLinkElement {
101 htmlelement: HTMLElement,
102 rel_list: MutNullableDom<DOMTokenList>,
104
105 #[no_trace]
111 relations: Cell<LinkRelations>,
112
113 #[conditional_malloc_size_of]
114 #[no_trace]
115 stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
116 cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
117
118 parser_inserted: Cell<bool>,
120 pending_loads: Cell<u32>,
123 any_failed_load: Cell<bool>,
125 request_generation_id: Cell<RequestGenerationId>,
127 is_explicitly_enabled: Cell<bool>,
129 previous_type_matched: Cell<bool>,
131 previous_media_environment_matched: Cell<bool>,
133 line_number: u64,
135}
136
137impl HTMLLinkElement {
138 fn new_inherited(
139 local_name: LocalName,
140 prefix: Option<Prefix>,
141 document: &Document,
142 creator: ElementCreator,
143 ) -> HTMLLinkElement {
144 HTMLLinkElement {
145 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
146 rel_list: Default::default(),
147 relations: Cell::new(LinkRelations::empty()),
148 parser_inserted: Cell::new(creator.is_parser_created()),
149 stylesheet: DomRefCell::new(None),
150 cssom_stylesheet: MutNullableDom::new(None),
151 pending_loads: Cell::new(0),
152 any_failed_load: Cell::new(false),
153 request_generation_id: Cell::new(RequestGenerationId(0)),
154 is_explicitly_enabled: Cell::new(false),
155 previous_type_matched: Cell::new(true),
156 previous_media_environment_matched: Cell::new(true),
157 line_number: creator.return_line_number(),
158 }
159 }
160
161 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
162 pub(crate) fn new(
163 local_name: LocalName,
164 prefix: Option<Prefix>,
165 document: &Document,
166 proto: Option<HandleObject>,
167 creator: ElementCreator,
168 can_gc: CanGc,
169 ) -> DomRoot<HTMLLinkElement> {
170 Node::reflect_node_with_proto(
171 Box::new(HTMLLinkElement::new_inherited(
172 local_name, prefix, document, creator,
173 )),
174 document,
175 proto,
176 can_gc,
177 )
178 }
179
180 pub(crate) fn get_request_generation_id(&self) -> RequestGenerationId {
181 self.request_generation_id.get()
182 }
183
184 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
187 pub(crate) fn set_stylesheet(&self, s: Arc<Stylesheet>) {
188 let stylesheets_owner = self.stylesheet_list_owner();
189 if let Some(ref s) = *self.stylesheet.borrow() {
190 stylesheets_owner
191 .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), s)
192 }
193 *self.stylesheet.borrow_mut() = Some(s.clone());
194 self.clean_stylesheet_ownership();
195 stylesheets_owner.add_owned_stylesheet(self.upcast(), s);
196 }
197
198 pub(crate) fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
199 self.stylesheet.borrow().clone()
200 }
201
202 pub(crate) fn get_cssom_stylesheet(&self, can_gc: CanGc) -> Option<DomRoot<CSSStyleSheet>> {
203 self.get_stylesheet().map(|sheet| {
204 self.cssom_stylesheet.or_init(|| {
205 CSSStyleSheet::new(
206 &self.owner_window(),
207 Some(self.upcast::<Element>()),
208 "text/css".into(),
209 None, None, sheet,
212 None, can_gc,
214 )
215 })
216 })
217 }
218
219 pub(crate) fn is_alternate(&self) -> bool {
220 self.relations.get().contains(LinkRelations::ALTERNATE)
221 }
222
223 pub(crate) fn is_effectively_disabled(&self) -> bool {
224 (self.is_alternate() && !self.is_explicitly_enabled.get()) ||
225 self.upcast::<Element>()
226 .has_attribute(&local_name!("disabled"))
227 }
228
229 fn clean_stylesheet_ownership(&self) {
230 if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() {
231 cssom_stylesheet.set_owner_node(None);
232 }
233 self.cssom_stylesheet.set(None);
234 }
235}
236
237fn get_attr(element: &Element, local_name: &LocalName) -> Option<String> {
238 let elem = element.get_attribute(&ns!(), local_name);
239 elem.map(|e| {
240 let value = e.value();
241 (**value).to_owned()
242 })
243}
244
245impl VirtualMethods for HTMLLinkElement {
246 fn super_type(&self) -> Option<&dyn VirtualMethods> {
247 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
248 }
249
250 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
251 self.super_type()
252 .unwrap()
253 .attribute_mutated(attr, mutation, can_gc);
254
255 let local_name = attr.local_name();
256 let is_removal = mutation.is_removal();
257 if *local_name == local_name!("disabled") {
258 self.handle_disabled_attribute_change(!is_removal);
259 return;
260 }
261
262 if !self.upcast::<Node>().is_connected() {
263 return;
264 }
265 match *local_name {
266 local_name!("rel") | local_name!("rev") => {
267 self.relations
268 .set(LinkRelations::for_element(self.upcast()));
269 },
270 local_name!("href") => {
271 if is_removal {
272 return;
273 }
274 if self.relations.get().contains(LinkRelations::STYLESHEET) {
278 self.handle_stylesheet_url(&attr.value());
279 }
280
281 if self.relations.get().contains(LinkRelations::ICON) {
282 self.handle_favicon_url();
283 }
284
285 if self.relations.get().contains(LinkRelations::PREFETCH) {
289 self.fetch_and_process_prefetch_link(&attr.value());
290 }
291
292 if self.relations.get().contains(LinkRelations::PRELOAD) {
296 self.handle_preload_url();
297 }
298 },
299 local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
300 self.handle_favicon_url();
301 },
302 local_name!("crossorigin") => {
303 if self.relations.get().contains(LinkRelations::PREFETCH) {
307 self.fetch_and_process_prefetch_link(&attr.value());
308 }
309
310 if self.relations.get().contains(LinkRelations::STYLESHEET) {
314 self.handle_stylesheet_url(&attr.value());
315 }
316 },
317 local_name!("as") => {
318 if self.relations.get().contains(LinkRelations::PRELOAD) {
322 if let AttributeMutation::Set(Some(_)) = mutation {
323 self.handle_preload_url();
324 }
325 }
326 },
327 local_name!("type") => {
328 if self.relations.get().contains(LinkRelations::STYLESHEET) {
336 self.handle_stylesheet_url(&attr.value());
337 }
338
339 if self.relations.get().contains(LinkRelations::PRELOAD) &&
345 !self.previous_type_matched.get()
346 {
347 self.handle_preload_url();
348 }
349 },
350 local_name!("media") => {
351 if self.relations.get().contains(LinkRelations::PRELOAD) &&
356 !self.previous_media_environment_matched.get()
357 {
358 match mutation {
359 AttributeMutation::Removed | AttributeMutation::Set(Some(_)) => {
360 self.handle_preload_url()
361 },
362 _ => {},
363 };
364 }
365
366 let matches_media_environment =
367 self.upcast::<Element>().matches_environment(&attr.value());
368 self.previous_media_environment_matched
369 .set(matches_media_environment);
370 },
371 _ => {},
372 }
373 }
374
375 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
376 match name {
377 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
378 _ => self
379 .super_type()
380 .unwrap()
381 .parse_plain_attribute(name, value),
382 }
383 }
384
385 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
386 if let Some(s) = self.super_type() {
387 s.bind_to_tree(context, can_gc);
388 }
389
390 self.relations
391 .set(LinkRelations::for_element(self.upcast()));
392
393 if context.tree_connected {
394 let element = self.upcast();
395
396 if let Some(href) = get_attr(element, &local_name!("href")) {
397 let relations = self.relations.get();
398 if relations.contains(LinkRelations::STYLESHEET) {
399 self.handle_stylesheet_url(&href);
400 }
401
402 if relations.contains(LinkRelations::ICON) {
403 self.handle_favicon_url();
404 }
405
406 if relations.contains(LinkRelations::PREFETCH) {
407 self.fetch_and_process_prefetch_link(&href);
408 }
409
410 if relations.contains(LinkRelations::PRELOAD) {
411 self.handle_preload_url();
412 }
413 }
414 }
415 }
416
417 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
418 if let Some(s) = self.super_type() {
419 s.unbind_from_tree(context, can_gc);
420 }
421
422 if let Some(s) = self.stylesheet.borrow_mut().take() {
423 self.clean_stylesheet_ownership();
424 self.stylesheet_list_owner()
425 .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), &s);
426 }
427 }
428}
429
430impl HTMLLinkElement {
431 fn compute_destination_for_attribute(&self) -> Destination {
432 let element = self.upcast::<Element>();
433 element
434 .get_attribute(&ns!(), &local_name!("as"))
435 .map(|attr| translate_a_preload_destination(&attr.value()))
436 .unwrap_or(Destination::None)
437 }
438
439 fn processing_options(&self) -> LinkProcessingOptions {
441 let element = self.upcast::<Element>();
442
443 let document = self.upcast::<Node>().owner_doc();
445
446 let destination = self.compute_destination_for_attribute();
448
449 let mut options = LinkProcessingOptions {
450 href: String::new(),
451 destination: Some(destination),
452 integrity: String::new(),
453 link_type: String::new(),
454 cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
455 cross_origin: cors_setting_for_element(element),
456 referrer_policy: referrer_policy_for_element(element),
457 policy_container: document.policy_container().to_owned(),
458 source_set: None, origin: document.borrow().origin().immutable().to_owned(),
460 base_url: document.borrow().base_url(),
461 insecure_requests_policy: document.insecure_requests_policy(),
462 has_trustworthy_ancestor_origin: document.has_trustworthy_ancestor_or_current_origin(),
463 };
464
465 if let Some(href_attribute) = element.get_attribute(&ns!(), &local_name!("href")) {
467 options.href = (**href_attribute.value()).to_owned();
468 }
469
470 if let Some(integrity_attribute) = element.get_attribute(&ns!(), &local_name!("integrity"))
473 {
474 options.integrity = (**integrity_attribute.value()).to_owned();
475 }
476
477 if let Some(type_attribute) = element.get_attribute(&ns!(), &local_name!("type")) {
479 options.link_type = (**type_attribute.value()).to_owned();
480 }
481
482 assert!(!options.href.is_empty() || options.source_set.is_some());
484
485 options
487 }
488
489 fn default_fetch_and_process_the_linked_resource(&self) -> Option<RequestBuilder> {
494 let options = self.processing_options();
496
497 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
499 return None;
501 };
502 let mut request = request.synchronous(true);
504
505 if !self.linked_resource_fetch_setup(&mut request) {
507 return None;
508 }
509
510 Some(request)
516 }
517
518 fn linked_resource_fetch_setup(&self, request: &mut RequestBuilder) -> bool {
520 if self.relations.get().contains(LinkRelations::ICON) {
521 request.destination = Destination::Image;
523
524 return true;
526 }
527
528 true
529 }
530
531 fn fetch_and_process_prefetch_link(&self, href: &str) {
533 if href.is_empty() {
535 return;
536 }
537
538 let mut options = self.processing_options();
540
541 options.destination = Some(Destination::None);
543
544 let url = options.base_url.clone();
546 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
547 return;
549 };
550
551 let request = request.initiator(Initiator::Prefetch);
553
554 let document = self.upcast::<Node>().owner_doc();
558 let fetch_context = PrefetchContext {
559 url,
560 link: Trusted::new(self),
561 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
562 };
563
564 document.fetch_background(request, fetch_context);
565 }
566
567 fn handle_stylesheet_url(&self, href: &str) {
569 let document = self.owner_document();
570 if document.browsing_context().is_none() {
571 return;
572 }
573
574 if href.is_empty() {
576 return;
577 }
578
579 let link_url = match document.base_url().join(href) {
581 Ok(url) => url,
582 Err(e) => {
583 debug!("Parsing url {} failed: {}", href, e);
584 return;
585 },
586 };
587
588 let element = self.upcast::<Element>();
589
590 let cors_setting = cors_setting_for_element(element);
592
593 let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
594 let value = mq_attribute.as_ref().map(|a| a.value());
595 let mq_str = match value {
596 Some(ref value) => &***value,
597 None => "",
598 };
599
600 if !element.matches_environment(mq_str) {
601 return;
602 }
603
604 let media = MediaList::parse_media_list(mq_str, document.window());
605
606 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
607 let integrity_val = im_attribute.as_ref().map(|a| a.value());
608 let integrity_metadata = match integrity_val {
609 Some(ref value) => &***value,
610 None => "",
611 };
612
613 self.request_generation_id
614 .set(self.request_generation_id.get().increment());
615
616 let loader = StylesheetLoader::for_element(self.upcast());
617 loader.load(
618 StylesheetContextSource::LinkElement { media: Some(media) },
619 link_url,
620 cors_setting,
621 integrity_metadata.to_owned(),
622 );
623 }
624
625 fn handle_disabled_attribute_change(&self, disabled: bool) {
627 if !disabled {
628 self.is_explicitly_enabled.set(true);
629 }
630 if let Some(stylesheet) = self.get_stylesheet() {
631 if stylesheet.set_disabled(disabled) {
632 self.stylesheet_list_owner().invalidate_stylesheets();
633 }
634 }
635 }
636
637 fn handle_favicon_url(&self) {
638 let window = self.owner_window();
641 if !window.is_top_level() {
642 return;
643 }
644 let Ok(href) = self.Href().parse() else {
645 return;
646 };
647
648 self.request_generation_id
650 .set(self.request_generation_id.get().increment());
651
652 let cache_result = window.image_cache().get_cached_image_status(
653 href,
654 window.origin().immutable().clone(),
655 cors_setting_for_element(self.upcast()),
656 UsePlaceholder::No,
657 );
658
659 match cache_result {
660 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
661 image,
662 is_placeholder,
663 ..
664 }) => {
665 debug_assert!(!is_placeholder);
666
667 self.process_favicon_response(image);
668 },
669 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
670 ImageCacheResult::Pending(id) => {
671 let sender = self.register_image_cache_callback(id);
672 window.image_cache().add_listener(ImageLoadListener::new(
673 sender,
674 window.pipeline_id(),
675 id,
676 ));
677 },
678 ImageCacheResult::ReadyForRequest(id) => {
679 let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
680 return;
681 };
682
683 let sender = self.register_image_cache_callback(id);
684 window.image_cache().add_listener(ImageLoadListener::new(
685 sender,
686 window.pipeline_id(),
687 id,
688 ));
689
690 let document = self.upcast::<Node>().owner_doc();
691 let fetch_context = FaviconFetchContext {
692 url: self.owner_document().base_url(),
693 image_cache: window.image_cache(),
694 id,
695 link: Trusted::new(self),
696 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
697 };
698 document.fetch_background(request, fetch_context);
699 },
700 ImageCacheResult::LoadError => {},
701 };
702 }
703
704 fn register_image_cache_callback(
705 &self,
706 id: PendingImageId,
707 ) -> IpcSender<ImageCacheResponseMessage> {
708 let trusted_node = Trusted::new(self);
709 let window = self.owner_window();
710 let request_generation_id = self.get_request_generation_id();
711 window.register_image_cache_listener(id, move |response| {
712 let trusted_node = trusted_node.clone();
713 let link_element = trusted_node.root();
714 let window = link_element.owner_window();
715
716 let ImageResponse::Loaded(image, _) = response.response else {
717 return;
719 };
720
721 if request_generation_id != link_element.get_request_generation_id() {
722 return;
724 };
725
726 window
727 .as_global_scope()
728 .task_manager()
729 .networking_task_source()
730 .queue(task!(process_favicon_response: move || {
731 let element = trusted_node.root();
732
733 if request_generation_id != element.get_request_generation_id() {
734 return;
736 };
737
738 element.process_favicon_response(image);
739 }));
740 })
741 }
742
743 fn process_favicon_response(&self, image: Image) {
745 let window = self.owner_window();
747
748 let send_rasterized_favicon_to_embedder = |raster_image: &pixels::RasterImage| {
749 let frame = raster_image.first_frame();
751
752 let format = match raster_image.format {
753 PixelFormat::K8 => embedder_traits::PixelFormat::K8,
754 PixelFormat::KA8 => embedder_traits::PixelFormat::KA8,
755 PixelFormat::RGB8 => embedder_traits::PixelFormat::RGB8,
756 PixelFormat::RGBA8 => embedder_traits::PixelFormat::RGBA8,
757 PixelFormat::BGRA8 => embedder_traits::PixelFormat::BGRA8,
758 };
759
760 let embedder_image = embedder_traits::Image::new(
761 frame.width,
762 frame.height,
763 raster_image.bytes.clone(),
764 raster_image.frames[0].byte_range.clone(),
765 format,
766 );
767 window.send_to_embedder(EmbedderMsg::NewFavicon(window.webview_id(), embedder_image));
768 };
769
770 match image {
771 Image::Raster(raster_image) => send_rasterized_favicon_to_embedder(&raster_image),
772 Image::Vector(vector_image) => {
773 let size = DeviceIntSize::new(250, 250);
775
776 let image_cache = window.image_cache();
777 if let Some(raster_image) =
778 image_cache.rasterize_vector_image(vector_image.id, size)
779 {
780 send_rasterized_favicon_to_embedder(&raster_image);
781 } else {
782 let image_cache_sender = self.register_image_cache_callback(vector_image.id);
785 image_cache.add_rasterization_complete_listener(
786 window.pipeline_id(),
787 vector_image.id,
788 size,
789 image_cache_sender,
790 );
791 }
792 },
793 }
794 }
795
796 fn handle_preload_url(&self) {
798 let options = self.processing_options();
802 self.preload(options);
806 }
807
808 fn preload(&self, options: LinkProcessingOptions) {
810 let type_matches_destination: bool =
812 HTMLLinkElement::type_matches_destination(&options.link_type, options.destination);
813 self.previous_type_matched.set(type_matches_destination);
814 if !type_matches_destination {
815 return;
816 }
817 let url = options.base_url.clone();
822 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
823 return;
825 };
826 let document = self.upcast::<Node>().owner_doc();
827 let fetch_context = PreloadContext {
841 url,
842 link: Trusted::new(self),
843 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
844 };
845 document.fetch_background(request.clone(), fetch_context);
846 }
847
848 fn type_matches_destination(mime_type: &str, destination: Option<Destination>) -> bool {
850 if mime_type.is_empty() {
852 return true;
853 }
854 let Some(destination) = destination else {
859 return false;
860 };
861 if destination == Destination::None {
862 return true;
863 }
864 let Ok(mime_type_record) = Mime::from_str(mime_type) else {
866 return false;
868 };
869 let Some(mime_type) = MimeClassifier::get_media_type(&mime_type_record) else {
874 return false;
875 };
876 if
878 ((destination == Destination::Audio || destination == Destination::Video) &&
880 mime_type == MediaType::AudioVideo)
881 || (destination.is_script_like() && mime_type == MediaType::JavaScript)
883 || (destination == Destination::Image && mime_type == MediaType::Image)
885 || (destination == Destination::Font && mime_type == MediaType::Font)
887 || (destination == Destination::Json && mime_type == MediaType::Json)
889 || (destination == Destination::Style && mime_type_record == mime::TEXT_CSS)
891 || (destination == Destination::Track && mime_type_record.essence_str() == "text/vtt")
893 {
894 return true;
896 }
897 false
899 }
900
901 fn fire_event_after_response(&self, response: Result<ResourceFetchTiming, NetworkError>) {
902 if response.is_err() {
903 self.upcast::<EventTarget>()
904 .fire_event(atom!("error"), CanGc::note());
905 } else {
906 let this = Trusted::new(self);
911 self.owner_global()
912 .task_manager()
913 .performance_timeline_task_source()
914 .queue(task!(preload_load_event: move || {
915 let this = this.root();
916 this
917 .upcast::<EventTarget>()
918 .fire_event(atom!("load"), CanGc::note());
919 }));
920 }
921 }
922}
923
924impl StylesheetOwner for HTMLLinkElement {
925 fn increment_pending_loads_count(&self) {
926 self.pending_loads.set(self.pending_loads.get() + 1)
927 }
928
929 fn load_finished(&self, succeeded: bool) -> Option<bool> {
930 assert!(self.pending_loads.get() > 0, "What finished?");
931 if !succeeded {
932 self.any_failed_load.set(true);
933 }
934
935 self.pending_loads.set(self.pending_loads.get() - 1);
936 if self.pending_loads.get() != 0 {
937 return None;
938 }
939
940 let any_failed = self.any_failed_load.get();
941 self.any_failed_load.set(false);
942 Some(any_failed)
943 }
944
945 fn parser_inserted(&self) -> bool {
946 self.parser_inserted.get()
947 }
948
949 fn referrer_policy(&self) -> ReferrerPolicy {
950 if self.RelList(CanGc::note()).Contains("noreferrer".into()) {
951 return ReferrerPolicy::NoReferrer;
952 }
953
954 ReferrerPolicy::EmptyString
955 }
956
957 fn set_origin_clean(&self, origin_clean: bool) {
958 if let Some(stylesheet) = self.get_cssom_stylesheet(CanGc::note()) {
959 stylesheet.set_origin_clean(origin_clean);
960 }
961 }
962}
963
964impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
965 make_url_getter!(Href, "href");
967
968 make_url_setter!(SetHref, "href");
970
971 make_getter!(Rel, "rel");
973
974 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
976 self.upcast::<Element>()
977 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
978 }
979
980 make_enumerated_getter!(
982 As,
983 "as",
984 "fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
985 | "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
986 | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
987 | "video" | "webidentity" | "worker" | "xslt",
988 missing => "",
989 invalid => ""
990 );
991
992 make_setter!(SetAs, "as");
994
995 make_getter!(Media, "media");
997
998 make_setter!(SetMedia, "media");
1000
1001 make_getter!(Integrity, "integrity");
1003
1004 make_setter!(SetIntegrity, "integrity");
1006
1007 make_getter!(Hreflang, "hreflang");
1009
1010 make_setter!(SetHreflang, "hreflang");
1012
1013 make_getter!(Type, "type");
1015
1016 make_setter!(SetType, "type");
1018
1019 make_bool_getter!(Disabled, "disabled");
1021
1022 make_bool_setter!(SetDisabled, "disabled");
1024
1025 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
1027 self.rel_list.or_init(|| {
1028 DOMTokenList::new(
1029 self.upcast(),
1030 &local_name!("rel"),
1031 Some(vec![
1032 Atom::from("alternate"),
1033 Atom::from("apple-touch-icon"),
1034 Atom::from("apple-touch-icon-precomposed"),
1035 Atom::from("canonical"),
1036 Atom::from("dns-prefetch"),
1037 Atom::from("icon"),
1038 Atom::from("import"),
1039 Atom::from("manifest"),
1040 Atom::from("modulepreload"),
1041 Atom::from("next"),
1042 Atom::from("preconnect"),
1043 Atom::from("prefetch"),
1044 Atom::from("preload"),
1045 Atom::from("prerender"),
1046 Atom::from("stylesheet"),
1047 ]),
1048 can_gc,
1049 )
1050 })
1051 }
1052
1053 make_getter!(Charset, "charset");
1055
1056 make_setter!(SetCharset, "charset");
1058
1059 make_getter!(Rev, "rev");
1061
1062 make_setter!(SetRev, "rev");
1064
1065 make_getter!(Target, "target");
1067
1068 make_setter!(SetTarget, "target");
1070
1071 fn GetCrossOrigin(&self) -> Option<DOMString> {
1073 reflect_cross_origin_attribute(self.upcast::<Element>())
1074 }
1075
1076 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1078 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1079 }
1080
1081 fn ReferrerPolicy(&self) -> DOMString {
1083 reflect_referrer_policy_attribute(self.upcast::<Element>())
1084 }
1085
1086 make_setter!(SetReferrerPolicy, "referrerpolicy");
1088
1089 fn GetSheet(&self, can_gc: CanGc) -> Option<DomRoot<DOMStyleSheet>> {
1091 self.get_cssom_stylesheet(can_gc).map(DomRoot::upcast)
1092 }
1093}
1094
1095impl LinkProcessingOptions {
1096 fn create_link_request(self, webview_id: WebViewId) -> Option<RequestBuilder> {
1098 assert!(!self.href.is_empty());
1100
1101 let destination = self.destination?;
1103
1104 let Ok(url) = self.base_url.join(&self.href) else {
1108 return None;
1110 };
1111
1112 let builder = create_a_potential_cors_request(
1122 Some(webview_id),
1123 url,
1124 destination,
1125 self.cross_origin,
1126 None,
1127 Referrer::NoReferrer,
1128 self.insecure_requests_policy,
1129 self.has_trustworthy_ancestor_origin,
1130 self.policy_container,
1131 )
1132 .initiator(Initiator::Link)
1133 .origin(self.origin)
1134 .integrity_metadata(self.integrity)
1135 .cryptographic_nonce_metadata(self.cryptographic_nonce_metadata)
1136 .referrer_policy(self.referrer_policy);
1137
1138 Some(builder)
1140 }
1141}
1142
1143fn translate_a_preload_destination(potential_destination: &str) -> Destination {
1145 match potential_destination {
1146 "fetch" => Destination::None,
1147 "font" => Destination::Font,
1148 "image" => Destination::Image,
1149 "script" => Destination::Script,
1150 "track" => Destination::Track,
1151 _ => Destination::None,
1152 }
1153}
1154
1155struct FaviconFetchContext {
1156 link: Trusted<HTMLLinkElement>,
1158 image_cache: std::sync::Arc<dyn ImageCache>,
1159 id: PendingImageId,
1160
1161 url: ServoUrl,
1163
1164 resource_timing: ResourceFetchTiming,
1165}
1166
1167impl FetchResponseListener for FaviconFetchContext {
1168 fn process_request_body(&mut self, _: RequestId) {}
1169
1170 fn process_request_eof(&mut self, _: RequestId) {}
1171
1172 fn process_response(
1173 &mut self,
1174 request_id: RequestId,
1175 metadata: Result<FetchMetadata, NetworkError>,
1176 ) {
1177 self.image_cache.notify_pending_response(
1178 self.id,
1179 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
1180 );
1181 }
1182
1183 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>) {
1184 self.image_cache.notify_pending_response(
1185 self.id,
1186 FetchResponseMsg::ProcessResponseChunk(request_id, chunk),
1187 );
1188 }
1189
1190 fn process_response_eof(
1191 &mut self,
1192 request_id: RequestId,
1193 response: Result<ResourceFetchTiming, NetworkError>,
1194 ) {
1195 self.image_cache.notify_pending_response(
1196 self.id,
1197 FetchResponseMsg::ProcessResponseEOF(request_id, response),
1198 );
1199 }
1200
1201 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1202 &mut self.resource_timing
1203 }
1204
1205 fn resource_timing(&self) -> &ResourceFetchTiming {
1206 &self.resource_timing
1207 }
1208
1209 fn submit_resource_timing(&mut self) {
1210 submit_timing(self, CanGc::note())
1211 }
1212
1213 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1214 let global = &self.resource_timing_global();
1215 let link = self.link.root();
1216 let source_position = link
1217 .upcast::<Element>()
1218 .compute_source_position(link.line_number as u32);
1219 global.report_csp_violations(violations, None, Some(source_position));
1220 }
1221}
1222
1223impl ResourceTimingListener for FaviconFetchContext {
1224 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1225 (
1226 InitiatorType::LocalName("link".to_string()),
1227 self.url.clone(),
1228 )
1229 }
1230
1231 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1232 self.link.root().upcast::<Node>().owner_doc().global()
1233 }
1234}
1235
1236impl PreInvoke for FaviconFetchContext {
1237 fn should_invoke(&self) -> bool {
1238 true
1239 }
1240}
1241
1242struct PrefetchContext {
1243 link: Trusted<HTMLLinkElement>,
1245
1246 resource_timing: ResourceFetchTiming,
1247
1248 url: ServoUrl,
1250}
1251
1252impl FetchResponseListener for PrefetchContext {
1253 fn process_request_body(&mut self, _: RequestId) {}
1254
1255 fn process_request_eof(&mut self, _: RequestId) {}
1256
1257 fn process_response(
1258 &mut self,
1259 _: RequestId,
1260 fetch_metadata: Result<FetchMetadata, NetworkError>,
1261 ) {
1262 _ = fetch_metadata;
1263 }
1264
1265 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
1266 _ = chunk;
1267 }
1268
1269 fn process_response_eof(
1271 &mut self,
1272 _: RequestId,
1273 response: Result<ResourceFetchTiming, NetworkError>,
1274 ) {
1275 if response.is_err() {
1276 self.link
1278 .root()
1279 .upcast::<EventTarget>()
1280 .fire_event(atom!("error"), CanGc::note());
1281 } else {
1282 self.link
1284 .root()
1285 .upcast::<EventTarget>()
1286 .fire_event(atom!("load"), CanGc::note());
1287 }
1288 }
1289
1290 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1291 &mut self.resource_timing
1292 }
1293
1294 fn resource_timing(&self) -> &ResourceFetchTiming {
1295 &self.resource_timing
1296 }
1297
1298 fn submit_resource_timing(&mut self) {
1299 submit_timing(self, CanGc::note())
1300 }
1301
1302 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1303 let global = &self.resource_timing_global();
1304 let link = self.link.root();
1305 let source_position = link
1306 .upcast::<Element>()
1307 .compute_source_position(link.line_number as u32);
1308 global.report_csp_violations(violations, None, Some(source_position));
1309 }
1310}
1311
1312impl ResourceTimingListener for PrefetchContext {
1313 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1314 (
1315 InitiatorType::LocalName("prefetch".to_string()),
1316 self.url.clone(),
1317 )
1318 }
1319
1320 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1321 self.link.root().upcast::<Node>().owner_doc().global()
1322 }
1323}
1324
1325impl PreInvoke for PrefetchContext {
1326 fn should_invoke(&self) -> bool {
1327 true
1329 }
1330}
1331
1332struct PreloadContext {
1333 link: Trusted<HTMLLinkElement>,
1335
1336 resource_timing: ResourceFetchTiming,
1337
1338 url: ServoUrl,
1340}
1341
1342impl FetchResponseListener for PreloadContext {
1343 fn process_request_body(&mut self, _: RequestId) {}
1344
1345 fn process_request_eof(&mut self, _: RequestId) {}
1346
1347 fn process_response(
1348 &mut self,
1349 _: RequestId,
1350 fetch_metadata: Result<FetchMetadata, NetworkError>,
1351 ) {
1352 _ = fetch_metadata;
1353 }
1354
1355 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
1356 _ = chunk;
1357 }
1358
1359 fn process_response_eof(
1361 &mut self,
1362 _: RequestId,
1363 response: Result<ResourceFetchTiming, NetworkError>,
1364 ) {
1365 self.link.root().fire_event_after_response(response);
1366 }
1367
1368 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1369 &mut self.resource_timing
1370 }
1371
1372 fn resource_timing(&self) -> &ResourceFetchTiming {
1373 &self.resource_timing
1374 }
1375
1376 fn submit_resource_timing(&mut self) {
1377 submit_timing(self, CanGc::note())
1378 }
1379
1380 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1381 let global = &self.resource_timing_global();
1382 let link = self.link.root();
1383 let source_position = link
1384 .upcast::<Element>()
1385 .compute_source_position(link.line_number as u32);
1386 global.report_csp_violations(violations, None, Some(source_position));
1387 }
1388}
1389
1390impl ResourceTimingListener for PreloadContext {
1391 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1392 (
1393 InitiatorType::LocalName(self.url.clone().into_string()),
1394 self.url.clone(),
1395 )
1396 }
1397
1398 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1399 self.link.root().upcast::<Node>().owner_doc().global()
1400 }
1401}
1402
1403impl PreInvoke for PreloadContext {
1404 fn should_invoke(&self) -> bool {
1405 true
1407 }
1408}