1use std::borrow::{Borrow, ToOwned};
6use std::cell::Cell;
7use std::default::Default;
8
9use base::generic_channel::GenericSharedMemory;
10use dom_struct::dom_struct;
11use html5ever::{LocalName, Prefix, local_name, ns};
12use js::rust::HandleObject;
13use net_traits::image_cache::{
14 Image, ImageCache, ImageCacheResponseCallback, ImageCacheResult, ImageLoadListener,
15 ImageOrMetadataAvailable, ImageResponse, PendingImageId,
16};
17use net_traits::request::{Destination, Initiator, RequestBuilder, RequestId};
18use net_traits::{
19 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
20};
21use pixels::PixelFormat;
22use script_bindings::root::Dom;
23use servo_arc::Arc;
24use servo_url::ServoUrl;
25use style::attr::AttrValue;
26use style::stylesheets::Stylesheet;
27use stylo_atoms::Atom;
28use webrender_api::units::DeviceIntSize;
29
30use crate::dom::attr::Attr;
31use crate::dom::bindings::cell::DomRefCell;
32use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
33use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
34use crate::dom::bindings::inheritance::Castable;
35use crate::dom::bindings::refcounted::Trusted;
36use crate::dom::bindings::reflector::DomGlobal;
37use crate::dom::bindings::root::{DomRoot, MutNullableDom};
38use crate::dom::bindings::str::{DOMString, USVString};
39use crate::dom::csp::{GlobalCspReporting, Violation};
40use crate::dom::css::cssstylesheet::CSSStyleSheet;
41use crate::dom::css::stylesheet::StyleSheet as DOMStyleSheet;
42use crate::dom::document::Document;
43use crate::dom::documentorshadowroot::StylesheetSource;
44use crate::dom::domtokenlist::DOMTokenList;
45use crate::dom::element::{
46 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
47 referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
48 set_cross_origin_attribute,
49};
50use crate::dom::html::htmlelement::HTMLElement;
51use crate::dom::medialist::MediaList;
52use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext};
53use crate::dom::performance::performanceresourcetiming::InitiatorType;
54use crate::dom::processingoptions::{
55 LinkFetchContext, LinkFetchContextType, LinkProcessingOptions,
56};
57use crate::dom::types::{EventTarget, GlobalScope};
58use crate::dom::virtualmethods::VirtualMethods;
59use crate::links::LinkRelations;
60use crate::network_listener::{FetchResponseListener, ResourceTimingListener, submit_timing};
61use crate::script_runtime::CanGc;
62use crate::stylesheet_loader::{ElementStylesheetLoader, StylesheetContextSource, StylesheetOwner};
63
64#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
65pub(crate) struct RequestGenerationId(u32);
66
67impl RequestGenerationId {
68 fn increment(self) -> RequestGenerationId {
69 RequestGenerationId(self.0 + 1)
70 }
71}
72
73#[dom_struct]
74pub(crate) struct HTMLLinkElement {
75 htmlelement: HTMLElement,
76 rel_list: MutNullableDom<DOMTokenList>,
78
79 #[no_trace]
85 relations: Cell<LinkRelations>,
86
87 #[conditional_malloc_size_of]
88 #[no_trace]
89 stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
90 cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
91
92 parser_inserted: Cell<bool>,
94 pending_loads: Cell<u32>,
97 any_failed_load: Cell<bool>,
99 request_generation_id: Cell<RequestGenerationId>,
101 is_explicitly_enabled: Cell<bool>,
103 previous_type_matched: Cell<bool>,
105 previous_media_environment_matched: Cell<bool>,
107 line_number: u64,
109 blocking: MutNullableDom<DOMTokenList>,
111}
112
113impl HTMLLinkElement {
114 fn new_inherited(
115 local_name: LocalName,
116 prefix: Option<Prefix>,
117 document: &Document,
118 creator: ElementCreator,
119 ) -> HTMLLinkElement {
120 HTMLLinkElement {
121 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
122 rel_list: Default::default(),
123 relations: Cell::new(LinkRelations::empty()),
124 parser_inserted: Cell::new(creator.is_parser_created()),
125 stylesheet: DomRefCell::new(None),
126 cssom_stylesheet: MutNullableDom::new(None),
127 pending_loads: Cell::new(0),
128 any_failed_load: Cell::new(false),
129 request_generation_id: Cell::new(RequestGenerationId(0)),
130 is_explicitly_enabled: Cell::new(false),
131 previous_type_matched: Cell::new(true),
132 previous_media_environment_matched: Cell::new(true),
133 line_number: creator.return_line_number(),
134 blocking: Default::default(),
135 }
136 }
137
138 pub(crate) fn new(
139 local_name: LocalName,
140 prefix: Option<Prefix>,
141 document: &Document,
142 proto: Option<HandleObject>,
143 creator: ElementCreator,
144 can_gc: CanGc,
145 ) -> DomRoot<HTMLLinkElement> {
146 Node::reflect_node_with_proto(
147 Box::new(HTMLLinkElement::new_inherited(
148 local_name, prefix, document, creator,
149 )),
150 document,
151 proto,
152 can_gc,
153 )
154 }
155
156 pub(crate) fn get_request_generation_id(&self) -> RequestGenerationId {
157 self.request_generation_id.get()
158 }
159
160 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
161 fn remove_stylesheet(&self) {
162 if let Some(stylesheet) = self.stylesheet.borrow_mut().take() {
163 let owner = self.stylesheet_list_owner();
164 owner.remove_stylesheet(
165 StylesheetSource::Element(Dom::from_ref(self.upcast())),
166 &stylesheet,
167 );
168 self.clean_stylesheet_ownership();
169 owner.invalidate_stylesheets();
170 }
171 }
172
173 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
176 pub(crate) fn set_stylesheet(&self, new_stylesheet: Arc<Stylesheet>) {
177 let owner = self.stylesheet_list_owner();
178 if let Some(old_stylesheet) = self.stylesheet.borrow_mut().replace(new_stylesheet.clone()) {
179 owner.remove_stylesheet(
180 StylesheetSource::Element(Dom::from_ref(self.upcast())),
181 &old_stylesheet,
182 );
183 }
184 owner.add_owned_stylesheet(self.upcast(), new_stylesheet);
185 }
186
187 pub(crate) fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
188 self.stylesheet.borrow().clone()
189 }
190
191 pub(crate) fn get_cssom_stylesheet(&self, can_gc: CanGc) -> Option<DomRoot<CSSStyleSheet>> {
192 self.get_stylesheet().map(|sheet| {
193 self.cssom_stylesheet.or_init(|| {
194 CSSStyleSheet::new(
195 &self.owner_window(),
196 Some(self.upcast::<Element>()),
197 "text/css".into(),
198 Some(self.Href().into()),
199 None, sheet,
201 None, can_gc,
203 )
204 })
205 })
206 }
207
208 pub(crate) fn is_alternate(&self) -> bool {
209 self.relations.get().contains(LinkRelations::ALTERNATE) &&
210 !self
211 .upcast::<Element>()
212 .get_string_attribute(&local_name!("title"))
213 .is_empty()
214 }
215
216 pub(crate) fn is_effectively_disabled(&self) -> bool {
217 (self.is_alternate() && !self.is_explicitly_enabled.get()) ||
218 self.upcast::<Element>()
219 .has_attribute(&local_name!("disabled"))
220 }
221
222 fn clean_stylesheet_ownership(&self) {
223 if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() {
224 cssom_stylesheet.set_owner_node(None);
225 }
226 self.cssom_stylesheet.set(None);
227 }
228
229 pub(crate) fn line_number(&self) -> u32 {
230 self.line_number as u32
231 }
232}
233
234fn get_attr(element: &Element, local_name: &LocalName) -> Option<String> {
235 let elem = element.get_attribute(&ns!(), local_name);
236 elem.map(|e| {
237 let value = e.value();
238 (**value).to_owned()
239 })
240}
241
242impl VirtualMethods for HTMLLinkElement {
243 fn super_type(&self) -> Option<&dyn VirtualMethods> {
244 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
245 }
246
247 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
248 self.super_type()
249 .unwrap()
250 .attribute_mutated(attr, mutation, can_gc);
251
252 let local_name = attr.local_name();
253 let is_removal = mutation.is_removal();
254 if *local_name == local_name!("disabled") {
255 self.handle_disabled_attribute_change(is_removal);
256 return;
257 }
258
259 if !self.upcast::<Node>().is_connected() {
260 return;
261 }
262 match *local_name {
263 local_name!("rel") | local_name!("rev") => {
264 let previous_relations = self.relations.get();
265 self.relations
266 .set(LinkRelations::for_element(self.upcast()));
267
268 if previous_relations == self.relations.get() {
270 return;
271 }
272
273 if self.relations.get().contains(LinkRelations::STYLESHEET) {
276 self.handle_stylesheet_url();
277 } else {
278 self.remove_stylesheet();
279 }
280 },
281 local_name!("href") => {
282 if is_removal {
285 if self.relations.get().contains(LinkRelations::STYLESHEET) {
286 self.remove_stylesheet();
287 }
288 return;
289 }
290 if self.relations.get().contains(LinkRelations::STYLESHEET) {
294 self.handle_stylesheet_url();
295 }
296
297 if self.relations.get().contains(LinkRelations::ICON) {
298 self.handle_favicon_url(&attr.value());
299 }
300
301 if self.relations.get().contains(LinkRelations::PREFETCH) {
305 self.fetch_and_process_prefetch_link(&attr.value());
306 }
307
308 if self.relations.get().contains(LinkRelations::PRELOAD) {
312 self.handle_preload_url();
313 }
314 },
315 local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
316 self.handle_favicon_url(&attr.value());
317 },
318 local_name!("crossorigin") => {
319 if self.relations.get().contains(LinkRelations::PREFETCH) {
323 self.fetch_and_process_prefetch_link(&attr.value());
324 }
325
326 if self.relations.get().contains(LinkRelations::STYLESHEET) {
330 self.handle_stylesheet_url();
331 }
332 },
333 local_name!("as") => {
334 if self.relations.get().contains(LinkRelations::PRELOAD) {
338 if let AttributeMutation::Set(Some(_), _) = mutation {
339 self.handle_preload_url();
340 }
341 }
342 },
343 local_name!("type") => {
344 if self.relations.get().contains(LinkRelations::STYLESHEET) {
352 self.handle_stylesheet_url();
353 }
354
355 if self.relations.get().contains(LinkRelations::PRELOAD) &&
361 !self.previous_type_matched.get()
362 {
363 self.handle_preload_url();
364 }
365 },
366 local_name!("media") => {
367 if self.relations.get().contains(LinkRelations::PRELOAD) &&
372 !self.previous_media_environment_matched.get()
373 {
374 match mutation {
375 AttributeMutation::Removed | AttributeMutation::Set(Some(_), _) => {
376 self.handle_preload_url()
377 },
378 _ => {},
379 };
380 }
381
382 let matches_media_environment =
383 MediaList::matches_environment(&self.owner_document(), &attr.value());
384 self.previous_media_environment_matched
385 .set(matches_media_environment);
386 },
387 _ => {},
388 }
389 }
390
391 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
392 match name {
393 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
394 _ => self
395 .super_type()
396 .unwrap()
397 .parse_plain_attribute(name, value),
398 }
399 }
400
401 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
402 if let Some(s) = self.super_type() {
403 s.bind_to_tree(context, can_gc);
404 }
405
406 self.relations
407 .set(LinkRelations::for_element(self.upcast()));
408
409 if context.tree_connected {
410 let element = self.upcast();
411
412 if let Some(href) = get_attr(element, &local_name!("href")) {
413 let relations = self.relations.get();
414 if relations.contains(LinkRelations::STYLESHEET) {
417 self.handle_stylesheet_url();
418 }
419
420 if relations.contains(LinkRelations::ICON) {
421 self.handle_favicon_url(&href);
422 }
423
424 if relations.contains(LinkRelations::PREFETCH) {
425 self.fetch_and_process_prefetch_link(&href);
426 }
427
428 if relations.contains(LinkRelations::PRELOAD) {
429 self.handle_preload_url();
430 }
431 }
432 }
433 }
434
435 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
436 if let Some(s) = self.super_type() {
437 s.unbind_from_tree(context, can_gc);
438 }
439
440 self.remove_stylesheet();
441 }
442}
443
444impl HTMLLinkElement {
445 fn compute_destination_for_attribute(&self) -> Option<Destination> {
446 let element = self.upcast::<Element>();
449 element
450 .get_attribute(&ns!(), &local_name!("as"))
451 .and_then(|attr| LinkProcessingOptions::translate_a_preload_destination(&attr.value()))
452 }
453
454 fn processing_options(&self) -> LinkProcessingOptions {
456 let element = self.upcast::<Element>();
457
458 let document = self.upcast::<Node>().owner_doc();
460 let global = document.owner_global();
461
462 let mut options = LinkProcessingOptions {
464 href: String::new(),
465 destination: Destination::None,
466 integrity: String::new(),
467 link_type: String::new(),
468 cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
469 cross_origin: cors_setting_for_element(element),
470 referrer_policy: referrer_policy_for_element(element),
471 policy_container: document.policy_container().to_owned(),
472 source_set: None, origin: document.borrow().origin().immutable().to_owned(),
474 base_url: document.borrow().base_url(),
475 insecure_requests_policy: document.insecure_requests_policy(),
476 has_trustworthy_ancestor_origin: document.has_trustworthy_ancestor_or_current_origin(),
477 request_client: global.request_client(),
478 referrer: global.get_referrer(),
479 };
480
481 if let Some(href_attribute) = element.get_attribute(&ns!(), &local_name!("href")) {
483 options.href = (**href_attribute.value()).to_owned();
484 }
485
486 if let Some(integrity_attribute) = element.get_attribute(&ns!(), &local_name!("integrity"))
489 {
490 options.integrity = (**integrity_attribute.value()).to_owned();
491 }
492
493 if let Some(type_attribute) = element.get_attribute(&ns!(), &local_name!("type")) {
495 options.link_type = (**type_attribute.value()).to_owned();
496 }
497
498 assert!(!options.href.is_empty() || options.source_set.is_some());
500
501 options
503 }
504
505 fn default_fetch_and_process_the_linked_resource(&self) -> Option<RequestBuilder> {
510 let options = self.processing_options();
512
513 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
515 return None;
517 };
518 let mut request = request.synchronous(true);
520
521 if !self.linked_resource_fetch_setup(&mut request) {
523 return None;
524 }
525
526 Some(request)
532 }
533
534 fn linked_resource_fetch_setup(&self, request: &mut RequestBuilder) -> bool {
536 if self.relations.get().contains(LinkRelations::ICON) {
538 request.destination = Destination::Image;
540
541 }
545
546 if self.relations.get().contains(LinkRelations::STYLESHEET) {
548 if self
550 .upcast::<Element>()
551 .has_attribute(&local_name!("disabled"))
552 {
553 return false;
554 }
555 }
570
571 true
572 }
573
574 fn fetch_and_process_prefetch_link(&self, href: &str) {
576 if href.is_empty() {
578 return;
579 }
580
581 let mut options = self.processing_options();
583
584 options.destination = Destination::None;
586
587 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
589 return;
591 };
592 let url = request.url.clone();
593
594 let request = request.initiator(Initiator::Prefetch);
596
597 let document = self.upcast::<Node>().owner_doc();
601 let fetch_context = LinkFetchContext {
602 url,
603 link: Some(Trusted::new(self)),
604 document: Trusted::new(&document),
605 global: Trusted::new(&document.global()),
606 type_: LinkFetchContextType::Prefetch,
607 response_body: vec![],
608 };
609
610 document.fetch_background(request, fetch_context);
611 }
612
613 fn handle_stylesheet_url(&self) {
615 let document = self.owner_document();
616 if document.browsing_context().is_none() {
617 return;
618 }
619
620 let element = self.upcast::<Element>();
621
622 let href = element.get_string_attribute(&local_name!("href"));
624 if href.is_empty() {
625 return;
626 }
627
628 let link_url = match document.base_url().join(&href.str()) {
630 Ok(url) => url,
631 Err(e) => {
632 debug!("Parsing url {} failed: {}", href, e);
633 return;
634 },
635 };
636
637 let cors_setting = cors_setting_for_element(element);
639
640 let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
641 let value = mq_attribute.as_ref().map(|a| a.value());
642 let mq_str = match value {
643 Some(ref value) => &***value,
644 None => "",
645 };
646
647 if !MediaList::matches_environment(&document, mq_str) {
648 return;
649 }
650
651 let media = MediaList::parse_media_list(mq_str, document.window());
652 let media = Arc::new(document.style_shared_lock().wrap(media));
653
654 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
655 let integrity_val = im_attribute.as_ref().map(|a| a.value());
656 let integrity_metadata = match integrity_val {
657 Some(ref value) => &***value,
658 None => "",
659 };
660
661 self.request_generation_id
662 .set(self.request_generation_id.get().increment());
663 self.pending_loads.set(0);
664
665 ElementStylesheetLoader::load_with_element(
666 self.upcast(),
667 StylesheetContextSource::LinkElement,
668 media,
669 link_url,
670 cors_setting,
671 integrity_metadata.to_owned(),
672 );
673 }
674
675 fn handle_disabled_attribute_change(&self, is_removal: bool) {
677 if is_removal {
679 self.is_explicitly_enabled.set(true);
680 }
681 if let Some(stylesheet) = self.get_stylesheet() {
682 if stylesheet.set_disabled(!is_removal) {
683 self.stylesheet_list_owner().invalidate_stylesheets();
684 }
685 }
686 }
687
688 fn handle_favicon_url(&self, href: &str) {
689 if href.is_empty() {
691 return;
692 }
693
694 let window = self.owner_window();
697 if !window.is_top_level() {
698 return;
699 }
700 let Ok(href) = self.Href().parse() else {
701 return;
702 };
703
704 self.request_generation_id
706 .set(self.request_generation_id.get().increment());
707
708 let cache_result = window.image_cache().get_cached_image_status(
709 href,
710 window.origin().immutable().clone(),
711 cors_setting_for_element(self.upcast()),
712 );
713
714 match cache_result {
715 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
716 image, ..
717 }) => {
718 self.process_favicon_response(image);
719 },
720 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
721 ImageCacheResult::Pending(id) => {
722 let sender = self.register_image_cache_callback(id);
723 window.image_cache().add_listener(ImageLoadListener::new(
724 sender,
725 window.pipeline_id(),
726 id,
727 ));
728 },
729 ImageCacheResult::ReadyForRequest(id) => {
730 let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
731 return;
732 };
733
734 let sender = self.register_image_cache_callback(id);
735 window.image_cache().add_listener(ImageLoadListener::new(
736 sender,
737 window.pipeline_id(),
738 id,
739 ));
740
741 let document = self.upcast::<Node>().owner_doc();
742 let fetch_context = FaviconFetchContext {
743 url: self.owner_document().base_url(),
744 image_cache: window.image_cache(),
745 id,
746 link: Trusted::new(self),
747 };
748 document.fetch_background(request, fetch_context);
749 },
750 ImageCacheResult::FailedToLoadOrDecode => {},
751 };
752 }
753
754 fn register_image_cache_callback(&self, id: PendingImageId) -> ImageCacheResponseCallback {
755 let trusted_node = Trusted::new(self);
756 let window = self.owner_window();
757 let request_generation_id = self.get_request_generation_id();
758 window.register_image_cache_listener(id, move |response| {
759 let trusted_node = trusted_node.clone();
760 let link_element = trusted_node.root();
761 let window = link_element.owner_window();
762
763 let ImageResponse::Loaded(image, _) = response.response else {
764 return;
766 };
767
768 if request_generation_id != link_element.get_request_generation_id() {
769 return;
771 };
772
773 window
774 .as_global_scope()
775 .task_manager()
776 .networking_task_source()
777 .queue(task!(process_favicon_response: move || {
778 let element = trusted_node.root();
779
780 if request_generation_id != element.get_request_generation_id() {
781 return;
783 };
784
785 element.process_favicon_response(image);
786 }));
787 })
788 }
789
790 fn process_favicon_response(&self, image: Image) {
792 let window = self.owner_window();
794 let document = self.owner_document();
795
796 let send_rasterized_favicon_to_embedder = |raster_image: &pixels::RasterImage| {
797 let frame = raster_image.first_frame();
799
800 let format = match raster_image.format {
801 PixelFormat::K8 => embedder_traits::PixelFormat::K8,
802 PixelFormat::KA8 => embedder_traits::PixelFormat::KA8,
803 PixelFormat::RGB8 => embedder_traits::PixelFormat::RGB8,
804 PixelFormat::RGBA8 => embedder_traits::PixelFormat::RGBA8,
805 PixelFormat::BGRA8 => embedder_traits::PixelFormat::BGRA8,
806 };
807
808 let embedder_image = embedder_traits::Image::new(
809 frame.width,
810 frame.height,
811 std::sync::Arc::new(GenericSharedMemory::from_bytes(&raster_image.bytes)),
812 raster_image.frames[0].byte_range.clone(),
813 format,
814 );
815 document.set_favicon(embedder_image);
816 };
817
818 match image {
819 Image::Raster(raster_image) => send_rasterized_favicon_to_embedder(&raster_image),
820 Image::Vector(vector_image) => {
821 let size = DeviceIntSize::new(250, 250);
823
824 let image_cache = window.image_cache();
825 if let Some(raster_image) =
826 image_cache.rasterize_vector_image(vector_image.id, size, None)
827 {
828 send_rasterized_favicon_to_embedder(&raster_image);
829 } else {
830 let image_cache_sender = self.register_image_cache_callback(vector_image.id);
833 image_cache.add_rasterization_complete_listener(
834 window.pipeline_id(),
835 vector_image.id,
836 size,
837 image_cache_sender,
838 );
839 }
840 },
841 }
842 }
843
844 fn handle_preload_url(&self) {
847 let mut options = self.processing_options();
851 let Some(destination) = self.compute_destination_for_attribute() else {
854 return;
856 };
857 options.destination = destination;
859 {
861 let type_matches_destination = options.type_matches_destination();
863 self.previous_type_matched.set(type_matches_destination);
864 if !type_matches_destination {
865 return;
866 }
867 }
868 let document = self.upcast::<Node>().owner_doc();
870 options.preload(
871 self.owner_window().webview_id(),
872 Some(Trusted::new(self)),
873 &document,
874 );
875 }
876
877 pub(crate) fn fire_event_after_response(
879 &self,
880 response: Result<(), NetworkError>,
881 can_gc: CanGc,
882 ) {
883 if response.is_err() {
886 self.upcast::<EventTarget>()
887 .fire_event(atom!("error"), can_gc);
888 } else {
889 self.upcast::<EventTarget>()
890 .fire_event(atom!("load"), can_gc);
891 }
892 }
893}
894
895impl StylesheetOwner for HTMLLinkElement {
896 fn increment_pending_loads_count(&self) {
897 self.pending_loads.set(self.pending_loads.get() + 1)
898 }
899
900 fn load_finished(&self, succeeded: bool) -> Option<bool> {
901 assert!(self.pending_loads.get() > 0, "What finished?");
902 if !succeeded {
903 self.any_failed_load.set(true);
904 }
905
906 self.pending_loads.set(self.pending_loads.get() - 1);
907 if self.pending_loads.get() != 0 {
908 return None;
909 }
910
911 let any_failed = self.any_failed_load.get();
912 self.any_failed_load.set(false);
913 Some(any_failed)
914 }
915
916 fn parser_inserted(&self) -> bool {
917 self.parser_inserted.get()
918 }
919
920 fn potentially_render_blocking(&self) -> bool {
922 self.parser_inserted() ||
929 self.blocking
930 .get()
931 .is_some_and(|list| list.Contains("render".into()))
932 }
933
934 fn referrer_policy(&self) -> ReferrerPolicy {
935 if self.RelList(CanGc::note()).Contains("noreferrer".into()) {
936 return ReferrerPolicy::NoReferrer;
937 }
938
939 ReferrerPolicy::EmptyString
940 }
941
942 fn set_origin_clean(&self, origin_clean: bool) {
943 if let Some(stylesheet) = self.get_cssom_stylesheet(CanGc::note()) {
944 stylesheet.set_origin_clean(origin_clean);
945 }
946 }
947}
948
949impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
950 make_url_getter!(Href, "href");
952
953 make_url_setter!(SetHref, "href");
955
956 make_getter!(Rel, "rel");
958
959 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
961 self.upcast::<Element>()
962 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
963 }
964
965 make_enumerated_getter!(
967 As,
968 "as",
969 "fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
970 | "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
971 | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
972 | "video" | "webidentity" | "worker" | "xslt",
973 missing => "",
974 invalid => ""
975 );
976
977 make_setter!(SetAs, "as");
979
980 make_getter!(Media, "media");
982
983 make_setter!(SetMedia, "media");
985
986 make_getter!(Integrity, "integrity");
988
989 make_setter!(SetIntegrity, "integrity");
991
992 make_getter!(Hreflang, "hreflang");
994
995 make_setter!(SetHreflang, "hreflang");
997
998 make_getter!(Type, "type");
1000
1001 make_setter!(SetType, "type");
1003
1004 make_bool_getter!(Disabled, "disabled");
1006
1007 make_bool_setter!(SetDisabled, "disabled");
1009
1010 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
1012 self.rel_list.or_init(|| {
1013 DOMTokenList::new(
1014 self.upcast(),
1015 &local_name!("rel"),
1016 Some(vec![
1017 Atom::from("alternate"),
1018 Atom::from("apple-touch-icon"),
1019 Atom::from("apple-touch-icon-precomposed"),
1020 Atom::from("canonical"),
1021 Atom::from("dns-prefetch"),
1022 Atom::from("icon"),
1023 Atom::from("import"),
1024 Atom::from("manifest"),
1025 Atom::from("modulepreload"),
1026 Atom::from("next"),
1027 Atom::from("preconnect"),
1028 Atom::from("prefetch"),
1029 Atom::from("preload"),
1030 Atom::from("prerender"),
1031 Atom::from("stylesheet"),
1032 ]),
1033 can_gc,
1034 )
1035 })
1036 }
1037
1038 make_getter!(Charset, "charset");
1040
1041 make_setter!(SetCharset, "charset");
1043
1044 make_getter!(Rev, "rev");
1046
1047 make_setter!(SetRev, "rev");
1049
1050 make_getter!(Target, "target");
1052
1053 make_setter!(SetTarget, "target");
1055
1056 fn Blocking(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
1058 self.blocking.or_init(|| {
1059 DOMTokenList::new(
1060 self.upcast(),
1061 &local_name!("blocking"),
1062 Some(vec![Atom::from("render")]),
1063 can_gc,
1064 )
1065 })
1066 }
1067
1068 fn GetCrossOrigin(&self) -> Option<DOMString> {
1070 reflect_cross_origin_attribute(self.upcast::<Element>())
1071 }
1072
1073 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1075 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1076 }
1077
1078 fn ReferrerPolicy(&self) -> DOMString {
1080 reflect_referrer_policy_attribute(self.upcast::<Element>())
1081 }
1082
1083 make_setter!(SetReferrerPolicy, "referrerpolicy");
1085
1086 fn GetSheet(&self, can_gc: CanGc) -> Option<DomRoot<DOMStyleSheet>> {
1088 self.get_cssom_stylesheet(can_gc).map(DomRoot::upcast)
1089 }
1090}
1091
1092struct FaviconFetchContext {
1093 link: Trusted<HTMLLinkElement>,
1095 image_cache: std::sync::Arc<dyn ImageCache>,
1096 id: PendingImageId,
1097
1098 url: ServoUrl,
1100}
1101
1102impl FetchResponseListener for FaviconFetchContext {
1103 fn process_request_body(&mut self, _: RequestId) {}
1104
1105 fn process_request_eof(&mut self, _: RequestId) {}
1106
1107 fn process_response(
1108 &mut self,
1109 request_id: RequestId,
1110 metadata: Result<FetchMetadata, NetworkError>,
1111 ) {
1112 self.image_cache.notify_pending_response(
1113 self.id,
1114 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
1115 );
1116 }
1117
1118 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>) {
1119 self.image_cache.notify_pending_response(
1120 self.id,
1121 FetchResponseMsg::ProcessResponseChunk(request_id, chunk.into()),
1122 );
1123 }
1124
1125 fn process_response_eof(
1126 self,
1127 request_id: RequestId,
1128 response: Result<(), NetworkError>,
1129 timing: ResourceFetchTiming,
1130 ) {
1131 self.image_cache.notify_pending_response(
1132 self.id,
1133 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
1134 );
1135 submit_timing(&self, &response, &timing, CanGc::note());
1136 }
1137
1138 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1139 let global = &self.resource_timing_global();
1140 let link = self.link.root();
1141 let source_position = link
1142 .upcast::<Element>()
1143 .compute_source_position(link.line_number as u32);
1144 global.report_csp_violations(violations, None, Some(source_position));
1145 }
1146}
1147
1148impl ResourceTimingListener for FaviconFetchContext {
1149 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1150 (
1151 InitiatorType::LocalName("link".to_string()),
1152 self.url.clone(),
1153 )
1154 }
1155
1156 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1157 self.link.root().upcast::<Node>().owner_doc().global()
1158 }
1159}