1use std::borrow::{Borrow, ToOwned};
6use std::cell::Cell;
7use std::default::Default;
8
9use dom_struct::dom_struct;
10use html5ever::{LocalName, Prefix, local_name, ns};
11use ipc_channel::ipc::IpcSender;
12use js::rust::HandleObject;
13use net_traits::image_cache::{
14 Image, ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener,
15 ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder,
16};
17use net_traits::request::{Destination, Initiator, RequestBuilder, RequestId};
18use net_traits::{
19 FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ReferrerPolicy,
20 ResourceFetchTiming, ResourceTimingType,
21};
22use pixels::PixelFormat;
23use script_bindings::root::Dom;
24use servo_arc::Arc;
25use servo_url::ServoUrl;
26use style::attr::AttrValue;
27use style::stylesheets::Stylesheet;
28use stylo_atoms::Atom;
29use webrender_api::units::DeviceIntSize;
30
31use crate::dom::attr::Attr;
32use crate::dom::bindings::cell::DomRefCell;
33use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
34use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
35use crate::dom::bindings::inheritance::Castable;
36use crate::dom::bindings::refcounted::Trusted;
37use crate::dom::bindings::reflector::DomGlobal;
38use crate::dom::bindings::root::{DomRoot, MutNullableDom};
39use crate::dom::bindings::str::{DOMString, USVString};
40use crate::dom::csp::{GlobalCspReporting, Violation};
41use crate::dom::cssstylesheet::CSSStyleSheet;
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::performanceresourcetiming::InitiatorType;
54use crate::dom::processingoptions::{
55 LinkFetchContext, LinkFetchContextType, LinkProcessingOptions,
56};
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::{ElementStylesheetLoader, StylesheetContextSource, 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 pub(crate) fn line_number(&self) -> u32 {
212 self.line_number as u32
213 }
214}
215
216fn get_attr(element: &Element, local_name: &LocalName) -> Option<String> {
217 let elem = element.get_attribute(&ns!(), local_name);
218 elem.map(|e| {
219 let value = e.value();
220 (**value).to_owned()
221 })
222}
223
224impl VirtualMethods for HTMLLinkElement {
225 fn super_type(&self) -> Option<&dyn VirtualMethods> {
226 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
227 }
228
229 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
230 self.super_type()
231 .unwrap()
232 .attribute_mutated(attr, mutation, can_gc);
233
234 let local_name = attr.local_name();
235 let is_removal = mutation.is_removal();
236 if *local_name == local_name!("disabled") {
237 self.handle_disabled_attribute_change(!is_removal);
238 return;
239 }
240
241 if !self.upcast::<Node>().is_connected() {
242 return;
243 }
244 match *local_name {
245 local_name!("rel") | local_name!("rev") => {
246 self.relations
247 .set(LinkRelations::for_element(self.upcast()));
248 },
249 local_name!("href") => {
250 if is_removal {
251 return;
252 }
253 if self.relations.get().contains(LinkRelations::STYLESHEET) {
257 self.handle_stylesheet_url(&attr.value());
258 }
259
260 if self.relations.get().contains(LinkRelations::ICON) {
261 self.handle_favicon_url();
262 }
263
264 if self.relations.get().contains(LinkRelations::PREFETCH) {
268 self.fetch_and_process_prefetch_link(&attr.value());
269 }
270
271 if self.relations.get().contains(LinkRelations::PRELOAD) {
275 self.handle_preload_url();
276 }
277 },
278 local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
279 self.handle_favicon_url();
280 },
281 local_name!("crossorigin") => {
282 if self.relations.get().contains(LinkRelations::PREFETCH) {
286 self.fetch_and_process_prefetch_link(&attr.value());
287 }
288
289 if self.relations.get().contains(LinkRelations::STYLESHEET) {
293 self.handle_stylesheet_url(&attr.value());
294 }
295 },
296 local_name!("as") => {
297 if self.relations.get().contains(LinkRelations::PRELOAD) {
301 if let AttributeMutation::Set(Some(_)) = mutation {
302 self.handle_preload_url();
303 }
304 }
305 },
306 local_name!("type") => {
307 if self.relations.get().contains(LinkRelations::STYLESHEET) {
315 self.handle_stylesheet_url(&attr.value());
316 }
317
318 if self.relations.get().contains(LinkRelations::PRELOAD) &&
324 !self.previous_type_matched.get()
325 {
326 self.handle_preload_url();
327 }
328 },
329 local_name!("media") => {
330 if self.relations.get().contains(LinkRelations::PRELOAD) &&
335 !self.previous_media_environment_matched.get()
336 {
337 match mutation {
338 AttributeMutation::Removed | AttributeMutation::Set(Some(_)) => {
339 self.handle_preload_url()
340 },
341 _ => {},
342 };
343 }
344
345 let matches_media_environment =
346 MediaList::matches_environment(&self.owner_document(), &attr.value());
347 self.previous_media_environment_matched
348 .set(matches_media_environment);
349 },
350 _ => {},
351 }
352 }
353
354 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
355 match name {
356 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
357 _ => self
358 .super_type()
359 .unwrap()
360 .parse_plain_attribute(name, value),
361 }
362 }
363
364 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
365 if let Some(s) = self.super_type() {
366 s.bind_to_tree(context, can_gc);
367 }
368
369 self.relations
370 .set(LinkRelations::for_element(self.upcast()));
371
372 if context.tree_connected {
373 let element = self.upcast();
374
375 if let Some(href) = get_attr(element, &local_name!("href")) {
376 let relations = self.relations.get();
377 if relations.contains(LinkRelations::STYLESHEET) {
378 self.handle_stylesheet_url(&href);
379 }
380
381 if relations.contains(LinkRelations::ICON) {
382 self.handle_favicon_url();
383 }
384
385 if relations.contains(LinkRelations::PREFETCH) {
386 self.fetch_and_process_prefetch_link(&href);
387 }
388
389 if relations.contains(LinkRelations::PRELOAD) {
390 self.handle_preload_url();
391 }
392 }
393 }
394 }
395
396 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
397 if let Some(s) = self.super_type() {
398 s.unbind_from_tree(context, can_gc);
399 }
400
401 if let Some(s) = self.stylesheet.borrow_mut().take() {
402 self.clean_stylesheet_ownership();
403 self.stylesheet_list_owner()
404 .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), &s);
405 }
406 }
407}
408
409impl HTMLLinkElement {
410 fn compute_destination_for_attribute(&self) -> Option<Destination> {
411 let element = self.upcast::<Element>();
414 element
415 .get_attribute(&ns!(), &local_name!("as"))
416 .and_then(|attr| LinkProcessingOptions::translate_a_preload_destination(&attr.value()))
417 }
418
419 fn processing_options(&self) -> LinkProcessingOptions {
421 let element = self.upcast::<Element>();
422
423 let document = self.upcast::<Node>().owner_doc();
425
426 let mut options = LinkProcessingOptions {
428 href: String::new(),
429 destination: Destination::None,
430 integrity: String::new(),
431 link_type: String::new(),
432 cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
433 cross_origin: cors_setting_for_element(element),
434 referrer_policy: referrer_policy_for_element(element),
435 policy_container: document.policy_container().to_owned(),
436 source_set: None, origin: document.borrow().origin().immutable().to_owned(),
438 base_url: document.borrow().base_url(),
439 insecure_requests_policy: document.insecure_requests_policy(),
440 has_trustworthy_ancestor_origin: document.has_trustworthy_ancestor_or_current_origin(),
441 };
442
443 if let Some(href_attribute) = element.get_attribute(&ns!(), &local_name!("href")) {
445 options.href = (**href_attribute.value()).to_owned();
446 }
447
448 if let Some(integrity_attribute) = element.get_attribute(&ns!(), &local_name!("integrity"))
451 {
452 options.integrity = (**integrity_attribute.value()).to_owned();
453 }
454
455 if let Some(type_attribute) = element.get_attribute(&ns!(), &local_name!("type")) {
457 options.link_type = (**type_attribute.value()).to_owned();
458 }
459
460 assert!(!options.href.is_empty() || options.source_set.is_some());
462
463 options
465 }
466
467 fn default_fetch_and_process_the_linked_resource(&self) -> Option<RequestBuilder> {
472 let options = self.processing_options();
474
475 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
477 return None;
479 };
480 let mut request = request.synchronous(true);
482
483 if !self.linked_resource_fetch_setup(&mut request) {
485 return None;
486 }
487
488 Some(request)
494 }
495
496 fn linked_resource_fetch_setup(&self, request: &mut RequestBuilder) -> bool {
498 if self.relations.get().contains(LinkRelations::ICON) {
499 request.destination = Destination::Image;
501
502 return true;
504 }
505
506 true
507 }
508
509 fn fetch_and_process_prefetch_link(&self, href: &str) {
511 if href.is_empty() {
513 return;
514 }
515
516 let mut options = self.processing_options();
518
519 options.destination = Destination::None;
521
522 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
524 return;
526 };
527 let url = request.url.clone();
528
529 let request = request.initiator(Initiator::Prefetch);
531
532 let document = self.upcast::<Node>().owner_doc();
536 let fetch_context = LinkFetchContext {
537 url,
538 link: Some(Trusted::new(self)),
539 global: Trusted::new(&document.global()),
540 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
541 type_: LinkFetchContextType::Prefetch,
542 };
543
544 document.fetch_background(request, fetch_context);
545 }
546
547 fn handle_stylesheet_url(&self, href: &str) {
549 let document = self.owner_document();
550 if document.browsing_context().is_none() {
551 return;
552 }
553
554 if href.is_empty() {
556 return;
557 }
558
559 let link_url = match document.base_url().join(href) {
561 Ok(url) => url,
562 Err(e) => {
563 debug!("Parsing url {} failed: {}", href, e);
564 return;
565 },
566 };
567
568 let element = self.upcast::<Element>();
569
570 let cors_setting = cors_setting_for_element(element);
572
573 let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
574 let value = mq_attribute.as_ref().map(|a| a.value());
575 let mq_str = match value {
576 Some(ref value) => &***value,
577 None => "",
578 };
579
580 if !MediaList::matches_environment(&document, mq_str) {
581 return;
582 }
583
584 let media = MediaList::parse_media_list(mq_str, document.window());
585 let media = Arc::new(document.style_shared_lock().wrap(media));
586
587 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
588 let integrity_val = im_attribute.as_ref().map(|a| a.value());
589 let integrity_metadata = match integrity_val {
590 Some(ref value) => &***value,
591 None => "",
592 };
593
594 self.request_generation_id
595 .set(self.request_generation_id.get().increment());
596
597 let loader = ElementStylesheetLoader::new(self.upcast());
598 loader.load(
599 StylesheetContextSource::LinkElement { media },
600 link_url,
601 cors_setting,
602 integrity_metadata.to_owned(),
603 );
604 }
605
606 fn handle_disabled_attribute_change(&self, disabled: bool) {
608 if !disabled {
609 self.is_explicitly_enabled.set(true);
610 }
611 if let Some(stylesheet) = self.get_stylesheet() {
612 if stylesheet.set_disabled(disabled) {
613 self.stylesheet_list_owner().invalidate_stylesheets();
614 }
615 }
616 }
617
618 fn handle_favicon_url(&self) {
619 let window = self.owner_window();
622 if !window.is_top_level() {
623 return;
624 }
625 let Ok(href) = self.Href().parse() else {
626 return;
627 };
628
629 self.request_generation_id
631 .set(self.request_generation_id.get().increment());
632
633 let cache_result = window.image_cache().get_cached_image_status(
634 href,
635 window.origin().immutable().clone(),
636 cors_setting_for_element(self.upcast()),
637 UsePlaceholder::No,
638 );
639
640 match cache_result {
641 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
642 image,
643 is_placeholder,
644 ..
645 }) => {
646 debug_assert!(!is_placeholder);
647
648 self.process_favicon_response(image);
649 },
650 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
651 ImageCacheResult::Pending(id) => {
652 let sender = self.register_image_cache_callback(id);
653 window.image_cache().add_listener(ImageLoadListener::new(
654 sender,
655 window.pipeline_id(),
656 id,
657 ));
658 },
659 ImageCacheResult::ReadyForRequest(id) => {
660 let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
661 return;
662 };
663
664 let sender = self.register_image_cache_callback(id);
665 window.image_cache().add_listener(ImageLoadListener::new(
666 sender,
667 window.pipeline_id(),
668 id,
669 ));
670
671 let document = self.upcast::<Node>().owner_doc();
672 let fetch_context = FaviconFetchContext {
673 url: self.owner_document().base_url(),
674 image_cache: window.image_cache(),
675 id,
676 link: Trusted::new(self),
677 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
678 };
679 document.fetch_background(request, fetch_context);
680 },
681 ImageCacheResult::LoadError => {},
682 };
683 }
684
685 fn register_image_cache_callback(
686 &self,
687 id: PendingImageId,
688 ) -> IpcSender<ImageCacheResponseMessage> {
689 let trusted_node = Trusted::new(self);
690 let window = self.owner_window();
691 let request_generation_id = self.get_request_generation_id();
692 window.register_image_cache_listener(id, move |response| {
693 let trusted_node = trusted_node.clone();
694 let link_element = trusted_node.root();
695 let window = link_element.owner_window();
696
697 let ImageResponse::Loaded(image, _) = response.response else {
698 return;
700 };
701
702 if request_generation_id != link_element.get_request_generation_id() {
703 return;
705 };
706
707 window
708 .as_global_scope()
709 .task_manager()
710 .networking_task_source()
711 .queue(task!(process_favicon_response: move || {
712 let element = trusted_node.root();
713
714 if request_generation_id != element.get_request_generation_id() {
715 return;
717 };
718
719 element.process_favicon_response(image);
720 }));
721 })
722 }
723
724 fn process_favicon_response(&self, image: Image) {
726 let window = self.owner_window();
728 let document = self.owner_document();
729
730 let send_rasterized_favicon_to_embedder = |raster_image: &pixels::RasterImage| {
731 let frame = raster_image.first_frame();
733
734 let format = match raster_image.format {
735 PixelFormat::K8 => embedder_traits::PixelFormat::K8,
736 PixelFormat::KA8 => embedder_traits::PixelFormat::KA8,
737 PixelFormat::RGB8 => embedder_traits::PixelFormat::RGB8,
738 PixelFormat::RGBA8 => embedder_traits::PixelFormat::RGBA8,
739 PixelFormat::BGRA8 => embedder_traits::PixelFormat::BGRA8,
740 };
741
742 let embedder_image = embedder_traits::Image::new(
743 frame.width,
744 frame.height,
745 raster_image.bytes.clone(),
746 raster_image.frames[0].byte_range.clone(),
747 format,
748 );
749 document.set_favicon(embedder_image);
750 };
751
752 match image {
753 Image::Raster(raster_image) => send_rasterized_favicon_to_embedder(&raster_image),
754 Image::Vector(vector_image) => {
755 let size = DeviceIntSize::new(250, 250);
757
758 let image_cache = window.image_cache();
759 if let Some(raster_image) =
760 image_cache.rasterize_vector_image(vector_image.id, size)
761 {
762 send_rasterized_favicon_to_embedder(&raster_image);
763 } else {
764 let image_cache_sender = self.register_image_cache_callback(vector_image.id);
767 image_cache.add_rasterization_complete_listener(
768 window.pipeline_id(),
769 vector_image.id,
770 size,
771 image_cache_sender,
772 );
773 }
774 },
775 }
776 }
777
778 fn handle_preload_url(&self) {
781 let mut options = self.processing_options();
785 let Some(destination) = self.compute_destination_for_attribute() else {
788 return;
790 };
791 options.destination = destination;
793 {
795 let type_matches_destination = options.type_matches_destination();
797 self.previous_type_matched.set(type_matches_destination);
798 if !type_matches_destination {
799 return;
800 }
801 }
802 let Some(request) = options.preload(self.owner_window().webview_id()) else {
804 return;
805 };
806 let url = request.url.clone();
807 let document = self.upcast::<Node>().owner_doc();
808 let fetch_context = LinkFetchContext {
809 url,
810 link: Some(Trusted::new(self)),
811 global: Trusted::new(&document.global()),
812 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
813 type_: LinkFetchContextType::Preload,
814 };
815 document.fetch_background(request, fetch_context);
816 }
817
818 pub(crate) fn fire_event_after_response(
820 &self,
821 response: Result<ResourceFetchTiming, NetworkError>,
822 ) {
823 if response.is_err() {
826 self.upcast::<EventTarget>()
827 .fire_event(atom!("error"), CanGc::note());
828 } else {
829 self.upcast::<EventTarget>()
830 .fire_event(atom!("load"), CanGc::note());
831 }
832 }
833}
834
835impl StylesheetOwner for HTMLLinkElement {
836 fn increment_pending_loads_count(&self) {
837 self.pending_loads.set(self.pending_loads.get() + 1)
838 }
839
840 fn load_finished(&self, succeeded: bool) -> Option<bool> {
841 assert!(self.pending_loads.get() > 0, "What finished?");
842 if !succeeded {
843 self.any_failed_load.set(true);
844 }
845
846 self.pending_loads.set(self.pending_loads.get() - 1);
847 if self.pending_loads.get() != 0 {
848 return None;
849 }
850
851 let any_failed = self.any_failed_load.get();
852 self.any_failed_load.set(false);
853 Some(any_failed)
854 }
855
856 fn parser_inserted(&self) -> bool {
857 self.parser_inserted.get()
858 }
859
860 fn referrer_policy(&self) -> ReferrerPolicy {
861 if self.RelList(CanGc::note()).Contains("noreferrer".into()) {
862 return ReferrerPolicy::NoReferrer;
863 }
864
865 ReferrerPolicy::EmptyString
866 }
867
868 fn set_origin_clean(&self, origin_clean: bool) {
869 if let Some(stylesheet) = self.get_cssom_stylesheet(CanGc::note()) {
870 stylesheet.set_origin_clean(origin_clean);
871 }
872 }
873}
874
875impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
876 make_url_getter!(Href, "href");
878
879 make_url_setter!(SetHref, "href");
881
882 make_getter!(Rel, "rel");
884
885 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
887 self.upcast::<Element>()
888 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
889 }
890
891 make_enumerated_getter!(
893 As,
894 "as",
895 "fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
896 | "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
897 | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
898 | "video" | "webidentity" | "worker" | "xslt",
899 missing => "",
900 invalid => ""
901 );
902
903 make_setter!(SetAs, "as");
905
906 make_getter!(Media, "media");
908
909 make_setter!(SetMedia, "media");
911
912 make_getter!(Integrity, "integrity");
914
915 make_setter!(SetIntegrity, "integrity");
917
918 make_getter!(Hreflang, "hreflang");
920
921 make_setter!(SetHreflang, "hreflang");
923
924 make_getter!(Type, "type");
926
927 make_setter!(SetType, "type");
929
930 make_bool_getter!(Disabled, "disabled");
932
933 make_bool_setter!(SetDisabled, "disabled");
935
936 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
938 self.rel_list.or_init(|| {
939 DOMTokenList::new(
940 self.upcast(),
941 &local_name!("rel"),
942 Some(vec![
943 Atom::from("alternate"),
944 Atom::from("apple-touch-icon"),
945 Atom::from("apple-touch-icon-precomposed"),
946 Atom::from("canonical"),
947 Atom::from("dns-prefetch"),
948 Atom::from("icon"),
949 Atom::from("import"),
950 Atom::from("manifest"),
951 Atom::from("modulepreload"),
952 Atom::from("next"),
953 Atom::from("preconnect"),
954 Atom::from("prefetch"),
955 Atom::from("preload"),
956 Atom::from("prerender"),
957 Atom::from("stylesheet"),
958 ]),
959 can_gc,
960 )
961 })
962 }
963
964 make_getter!(Charset, "charset");
966
967 make_setter!(SetCharset, "charset");
969
970 make_getter!(Rev, "rev");
972
973 make_setter!(SetRev, "rev");
975
976 make_getter!(Target, "target");
978
979 make_setter!(SetTarget, "target");
981
982 fn GetCrossOrigin(&self) -> Option<DOMString> {
984 reflect_cross_origin_attribute(self.upcast::<Element>())
985 }
986
987 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
989 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
990 }
991
992 fn ReferrerPolicy(&self) -> DOMString {
994 reflect_referrer_policy_attribute(self.upcast::<Element>())
995 }
996
997 make_setter!(SetReferrerPolicy, "referrerpolicy");
999
1000 fn GetSheet(&self, can_gc: CanGc) -> Option<DomRoot<DOMStyleSheet>> {
1002 self.get_cssom_stylesheet(can_gc).map(DomRoot::upcast)
1003 }
1004}
1005
1006struct FaviconFetchContext {
1007 link: Trusted<HTMLLinkElement>,
1009 image_cache: std::sync::Arc<dyn ImageCache>,
1010 id: PendingImageId,
1011
1012 url: ServoUrl,
1014
1015 resource_timing: ResourceFetchTiming,
1016}
1017
1018impl FetchResponseListener for FaviconFetchContext {
1019 fn process_request_body(&mut self, _: RequestId) {}
1020
1021 fn process_request_eof(&mut self, _: RequestId) {}
1022
1023 fn process_response(
1024 &mut self,
1025 request_id: RequestId,
1026 metadata: Result<FetchMetadata, NetworkError>,
1027 ) {
1028 self.image_cache.notify_pending_response(
1029 self.id,
1030 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
1031 );
1032 }
1033
1034 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>) {
1035 self.image_cache.notify_pending_response(
1036 self.id,
1037 FetchResponseMsg::ProcessResponseChunk(request_id, chunk),
1038 );
1039 }
1040
1041 fn process_response_eof(
1042 &mut self,
1043 request_id: RequestId,
1044 response: Result<ResourceFetchTiming, NetworkError>,
1045 ) {
1046 self.image_cache.notify_pending_response(
1047 self.id,
1048 FetchResponseMsg::ProcessResponseEOF(request_id, response),
1049 );
1050 }
1051
1052 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1053 &mut self.resource_timing
1054 }
1055
1056 fn resource_timing(&self) -> &ResourceFetchTiming {
1057 &self.resource_timing
1058 }
1059
1060 fn submit_resource_timing(&mut self) {
1061 submit_timing(self, CanGc::note())
1062 }
1063
1064 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1065 let global = &self.resource_timing_global();
1066 let link = self.link.root();
1067 let source_position = link
1068 .upcast::<Element>()
1069 .compute_source_position(link.line_number as u32);
1070 global.report_csp_violations(violations, None, Some(source_position));
1071 }
1072}
1073
1074impl ResourceTimingListener for FaviconFetchContext {
1075 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1076 (
1077 InitiatorType::LocalName("link".to_string()),
1078 self.url.clone(),
1079 )
1080 }
1081
1082 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1083 self.link.root().upcast::<Node>().owner_doc().global()
1084 }
1085}
1086
1087impl PreInvoke for FaviconFetchContext {
1088 fn should_invoke(&self) -> bool {
1089 true
1090 }
1091}