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::IpcSharedMemory;
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 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::css::cssstylesheet::CSSStyleSheet;
42use crate::dom::css::stylesheet::StyleSheet as DOMStyleSheet;
43use crate::dom::document::Document;
44use crate::dom::documentorshadowroot::StylesheetSource;
45use crate::dom::domtokenlist::DOMTokenList;
46use crate::dom::element::{
47 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
48 referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
49 set_cross_origin_attribute,
50};
51use crate::dom::html::htmlelement::HTMLElement;
52use crate::dom::medialist::MediaList;
53use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext};
54use crate::dom::performance::performanceresourcetiming::InitiatorType;
55use crate::dom::processingoptions::{
56 LinkFetchContext, LinkFetchContextType, LinkProcessingOptions,
57};
58use crate::dom::types::{EventTarget, GlobalScope};
59use crate::dom::virtualmethods::VirtualMethods;
60use crate::links::LinkRelations;
61use crate::network_listener::{FetchResponseListener, 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 document: Trusted::new(&document),
540 global: Trusted::new(&document.global()),
541 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
542 type_: LinkFetchContextType::Prefetch,
543 response_body: vec![],
544 };
545
546 document.fetch_background(request, fetch_context);
547 }
548
549 fn handle_stylesheet_url(&self, href: &str) {
551 let document = self.owner_document();
552 if document.browsing_context().is_none() {
553 return;
554 }
555
556 if href.is_empty() {
558 return;
559 }
560
561 let link_url = match document.base_url().join(href) {
563 Ok(url) => url,
564 Err(e) => {
565 debug!("Parsing url {} failed: {}", href, e);
566 return;
567 },
568 };
569
570 let element = self.upcast::<Element>();
571
572 let cors_setting = cors_setting_for_element(element);
574
575 let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
576 let value = mq_attribute.as_ref().map(|a| a.value());
577 let mq_str = match value {
578 Some(ref value) => &***value,
579 None => "",
580 };
581
582 if !MediaList::matches_environment(&document, mq_str) {
583 return;
584 }
585
586 let media = MediaList::parse_media_list(mq_str, document.window());
587 let media = Arc::new(document.style_shared_lock().wrap(media));
588
589 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
590 let integrity_val = im_attribute.as_ref().map(|a| a.value());
591 let integrity_metadata = match integrity_val {
592 Some(ref value) => &***value,
593 None => "",
594 };
595
596 self.request_generation_id
597 .set(self.request_generation_id.get().increment());
598
599 let loader = ElementStylesheetLoader::new(self.upcast());
600 loader.load(
601 StylesheetContextSource::LinkElement { media },
602 link_url,
603 cors_setting,
604 integrity_metadata.to_owned(),
605 );
606 }
607
608 fn handle_disabled_attribute_change(&self, disabled: bool) {
610 if !disabled {
611 self.is_explicitly_enabled.set(true);
612 }
613 if let Some(stylesheet) = self.get_stylesheet() {
614 if stylesheet.set_disabled(disabled) {
615 self.stylesheet_list_owner().invalidate_stylesheets();
616 }
617 }
618 }
619
620 fn handle_favicon_url(&self) {
621 let window = self.owner_window();
624 if !window.is_top_level() {
625 return;
626 }
627 let Ok(href) = self.Href().parse() else {
628 return;
629 };
630
631 self.request_generation_id
633 .set(self.request_generation_id.get().increment());
634
635 let cache_result = window.image_cache().get_cached_image_status(
636 href,
637 window.origin().immutable().clone(),
638 cors_setting_for_element(self.upcast()),
639 );
640
641 match cache_result {
642 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
643 image, ..
644 }) => {
645 self.process_favicon_response(image);
646 },
647 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
648 ImageCacheResult::Pending(id) => {
649 let sender = self.register_image_cache_callback(id);
650 window.image_cache().add_listener(ImageLoadListener::new(
651 sender,
652 window.pipeline_id(),
653 id,
654 ));
655 },
656 ImageCacheResult::ReadyForRequest(id) => {
657 let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
658 return;
659 };
660
661 let sender = self.register_image_cache_callback(id);
662 window.image_cache().add_listener(ImageLoadListener::new(
663 sender,
664 window.pipeline_id(),
665 id,
666 ));
667
668 let document = self.upcast::<Node>().owner_doc();
669 let fetch_context = FaviconFetchContext {
670 url: self.owner_document().base_url(),
671 image_cache: window.image_cache(),
672 id,
673 link: Trusted::new(self),
674 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
675 };
676 document.fetch_background(request, fetch_context);
677 },
678 ImageCacheResult::FailedToLoadOrDecode => {},
679 };
680 }
681
682 fn register_image_cache_callback(&self, id: PendingImageId) -> ImageCacheResponseCallback {
683 let trusted_node = Trusted::new(self);
684 let window = self.owner_window();
685 let request_generation_id = self.get_request_generation_id();
686 window.register_image_cache_listener(id, move |response| {
687 let trusted_node = trusted_node.clone();
688 let link_element = trusted_node.root();
689 let window = link_element.owner_window();
690
691 let ImageResponse::Loaded(image, _) = response.response else {
692 return;
694 };
695
696 if request_generation_id != link_element.get_request_generation_id() {
697 return;
699 };
700
701 window
702 .as_global_scope()
703 .task_manager()
704 .networking_task_source()
705 .queue(task!(process_favicon_response: move || {
706 let element = trusted_node.root();
707
708 if request_generation_id != element.get_request_generation_id() {
709 return;
711 };
712
713 element.process_favicon_response(image);
714 }));
715 })
716 }
717
718 fn process_favicon_response(&self, image: Image) {
720 let window = self.owner_window();
722 let document = self.owner_document();
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 std::sync::Arc::new(IpcSharedMemory::from_bytes(&raster_image.bytes)),
740 raster_image.frames[0].byte_range.clone(),
741 format,
742 );
743 document.set_favicon(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 mut options = self.processing_options();
779 let Some(destination) = self.compute_destination_for_attribute() else {
782 return;
784 };
785 options.destination = destination;
787 {
789 let type_matches_destination = options.type_matches_destination();
791 self.previous_type_matched.set(type_matches_destination);
792 if !type_matches_destination {
793 return;
794 }
795 }
796 let document = self.upcast::<Node>().owner_doc();
798 options.preload(
799 self.owner_window().webview_id(),
800 Some(Trusted::new(self)),
801 &document,
802 );
803 }
804
805 pub(crate) fn fire_event_after_response(
807 &self,
808 response: Result<ResourceFetchTiming, NetworkError>,
809 can_gc: CanGc,
810 ) {
811 if response.is_err() {
814 self.upcast::<EventTarget>()
815 .fire_event(atom!("error"), can_gc);
816 } else {
817 self.upcast::<EventTarget>()
818 .fire_event(atom!("load"), can_gc);
819 }
820 }
821}
822
823impl StylesheetOwner for HTMLLinkElement {
824 fn increment_pending_loads_count(&self) {
825 self.pending_loads.set(self.pending_loads.get() + 1)
826 }
827
828 fn load_finished(&self, succeeded: bool) -> Option<bool> {
829 assert!(self.pending_loads.get() > 0, "What finished?");
830 if !succeeded {
831 self.any_failed_load.set(true);
832 }
833
834 self.pending_loads.set(self.pending_loads.get() - 1);
835 if self.pending_loads.get() != 0 {
836 return None;
837 }
838
839 let any_failed = self.any_failed_load.get();
840 self.any_failed_load.set(false);
841 Some(any_failed)
842 }
843
844 fn parser_inserted(&self) -> bool {
845 self.parser_inserted.get()
846 }
847
848 fn referrer_policy(&self) -> ReferrerPolicy {
849 if self.RelList(CanGc::note()).Contains("noreferrer".into()) {
850 return ReferrerPolicy::NoReferrer;
851 }
852
853 ReferrerPolicy::EmptyString
854 }
855
856 fn set_origin_clean(&self, origin_clean: bool) {
857 if let Some(stylesheet) = self.get_cssom_stylesheet(CanGc::note()) {
858 stylesheet.set_origin_clean(origin_clean);
859 }
860 }
861}
862
863impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
864 make_url_getter!(Href, "href");
866
867 make_url_setter!(SetHref, "href");
869
870 make_getter!(Rel, "rel");
872
873 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
875 self.upcast::<Element>()
876 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
877 }
878
879 make_enumerated_getter!(
881 As,
882 "as",
883 "fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
884 | "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
885 | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
886 | "video" | "webidentity" | "worker" | "xslt",
887 missing => "",
888 invalid => ""
889 );
890
891 make_setter!(SetAs, "as");
893
894 make_getter!(Media, "media");
896
897 make_setter!(SetMedia, "media");
899
900 make_getter!(Integrity, "integrity");
902
903 make_setter!(SetIntegrity, "integrity");
905
906 make_getter!(Hreflang, "hreflang");
908
909 make_setter!(SetHreflang, "hreflang");
911
912 make_getter!(Type, "type");
914
915 make_setter!(SetType, "type");
917
918 make_bool_getter!(Disabled, "disabled");
920
921 make_bool_setter!(SetDisabled, "disabled");
923
924 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
926 self.rel_list.or_init(|| {
927 DOMTokenList::new(
928 self.upcast(),
929 &local_name!("rel"),
930 Some(vec![
931 Atom::from("alternate"),
932 Atom::from("apple-touch-icon"),
933 Atom::from("apple-touch-icon-precomposed"),
934 Atom::from("canonical"),
935 Atom::from("dns-prefetch"),
936 Atom::from("icon"),
937 Atom::from("import"),
938 Atom::from("manifest"),
939 Atom::from("modulepreload"),
940 Atom::from("next"),
941 Atom::from("preconnect"),
942 Atom::from("prefetch"),
943 Atom::from("preload"),
944 Atom::from("prerender"),
945 Atom::from("stylesheet"),
946 ]),
947 can_gc,
948 )
949 })
950 }
951
952 make_getter!(Charset, "charset");
954
955 make_setter!(SetCharset, "charset");
957
958 make_getter!(Rev, "rev");
960
961 make_setter!(SetRev, "rev");
963
964 make_getter!(Target, "target");
966
967 make_setter!(SetTarget, "target");
969
970 fn GetCrossOrigin(&self) -> Option<DOMString> {
972 reflect_cross_origin_attribute(self.upcast::<Element>())
973 }
974
975 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
977 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
978 }
979
980 fn ReferrerPolicy(&self) -> DOMString {
982 reflect_referrer_policy_attribute(self.upcast::<Element>())
983 }
984
985 make_setter!(SetReferrerPolicy, "referrerpolicy");
987
988 fn GetSheet(&self, can_gc: CanGc) -> Option<DomRoot<DOMStyleSheet>> {
990 self.get_cssom_stylesheet(can_gc).map(DomRoot::upcast)
991 }
992}
993
994struct FaviconFetchContext {
995 link: Trusted<HTMLLinkElement>,
997 image_cache: std::sync::Arc<dyn ImageCache>,
998 id: PendingImageId,
999
1000 url: ServoUrl,
1002
1003 resource_timing: ResourceFetchTiming,
1004}
1005
1006impl FetchResponseListener for FaviconFetchContext {
1007 fn process_request_body(&mut self, _: RequestId) {}
1008
1009 fn process_request_eof(&mut self, _: RequestId) {}
1010
1011 fn process_response(
1012 &mut self,
1013 request_id: RequestId,
1014 metadata: Result<FetchMetadata, NetworkError>,
1015 ) {
1016 self.image_cache.notify_pending_response(
1017 self.id,
1018 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
1019 );
1020 }
1021
1022 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>) {
1023 self.image_cache.notify_pending_response(
1024 self.id,
1025 FetchResponseMsg::ProcessResponseChunk(request_id, chunk.into()),
1026 );
1027 }
1028
1029 fn process_response_eof(
1030 &mut self,
1031 request_id: RequestId,
1032 response: Result<ResourceFetchTiming, NetworkError>,
1033 ) {
1034 self.image_cache.notify_pending_response(
1035 self.id,
1036 FetchResponseMsg::ProcessResponseEOF(request_id, response),
1037 );
1038 }
1039
1040 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1041 &mut self.resource_timing
1042 }
1043
1044 fn resource_timing(&self) -> &ResourceFetchTiming {
1045 &self.resource_timing
1046 }
1047
1048 fn submit_resource_timing(&mut self) {
1049 submit_timing(self, CanGc::note())
1050 }
1051
1052 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1053 let global = &self.resource_timing_global();
1054 let link = self.link.root();
1055 let source_position = link
1056 .upcast::<Element>()
1057 .compute_source_position(link.line_number as u32);
1058 global.report_csp_violations(violations, None, Some(source_position));
1059 }
1060}
1061
1062impl ResourceTimingListener for FaviconFetchContext {
1063 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1064 (
1065 InitiatorType::LocalName("link".to_string()),
1066 self.url.clone(),
1067 )
1068 }
1069
1070 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1071 self.link.root().upcast::<Node>().owner_doc().global()
1072 }
1073}