1use std::borrow::{Borrow, ToOwned};
6use std::cell::Cell;
7use std::default::Default;
8use std::str::FromStr;
9
10use dom_struct::dom_struct;
11use html5ever::{LocalName, Prefix, local_name};
12use js::context::{JSContext, NoGC};
13use js::rust::HandleObject;
14use net_traits::image_cache::{
15 Image, ImageCache, ImageCacheResponseCallback, ImageCacheResult, ImageLoadListener,
16 ImageOrMetadataAvailable, ImageResponse, PendingImageId,
17};
18use net_traits::request::{Destination, Initiator, ParserMetadata, RequestBuilder, RequestId};
19use net_traits::{
20 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
21};
22use pixels::PixelFormat;
23use script_bindings::cell::DomRefCell;
24use script_bindings::root::Dom;
25use servo_arc::Arc;
26use servo_base::generic_channel::GenericSharedMemory;
27use servo_url::ServoUrl;
28use style::attr::AttrValue;
29use style::media_queries::MediaList as StyleMediaList;
30use style::stylesheets::Stylesheet;
31use stylo_atoms::Atom;
32use webrender_api::units::DeviceIntSize;
33
34use crate::css::stylesheet_loader::{
35 ElementStylesheetLoader, StylesheetContextSource, StylesheetOwner,
36};
37use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
38use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
39use crate::dom::bindings::inheritance::Castable;
40use crate::dom::bindings::refcounted::Trusted;
41use crate::dom::bindings::reflector::DomGlobal;
42use crate::dom::bindings::root::{DomRoot, MutNullableDom};
43use crate::dom::bindings::str::{DOMString, USVString};
44use crate::dom::csp::{GlobalCspReporting, Violation};
45use crate::dom::css::cssstylesheet::CSSStyleSheet;
46use crate::dom::css::stylesheet::StyleSheet as DOMStyleSheet;
47use crate::dom::document::Document;
48use crate::dom::documentorshadowroot::StylesheetSource;
49use crate::dom::domtokenlist::DOMTokenList;
50use crate::dom::element::attributes::storage::AttrRef;
51use crate::dom::element::{
52 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
53 cors_settings_attribute_credential_mode, referrer_policy_for_element,
54 reflect_cross_origin_attribute, reflect_referrer_policy_attribute, set_cross_origin_attribute,
55};
56use crate::dom::html::document_metadata::processingoptions::{
57 LinkFetchContext, LinkFetchContextType, LinkProcessingOptions,
58};
59use crate::dom::html::htmlelement::HTMLElement;
60use crate::dom::html::links::relations::LinkRelations;
61use crate::dom::medialist::MediaList;
62use crate::dom::node::virtualmethods::VirtualMethods;
63use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext};
64use crate::dom::performance::performanceresourcetiming::InitiatorType;
65use crate::dom::srcset::SourceSet;
66use crate::dom::types::{EventTarget, GlobalScope};
67use crate::fetch::network_listener::{
68 FetchResponseListener, ResourceTimingListener, submit_timing,
69};
70use crate::modules::script_module::{ScriptFetchOptions, fetch_a_modulepreload_module};
71use crate::url::ensure_blob_referenced_by_url_is_kept_alive;
72
73#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
74pub(crate) struct RequestGenerationId(u32);
75
76impl RequestGenerationId {
77 fn increment(self) -> RequestGenerationId {
78 RequestGenerationId(self.0 + 1)
79 }
80}
81
82#[dom_struct]
83pub(crate) struct HTMLLinkElement {
84 htmlelement: HTMLElement,
85 rel_list: MutNullableDom<DOMTokenList>,
87
88 #[no_trace]
94 relations: Cell<LinkRelations>,
95
96 #[conditional_malloc_size_of]
97 #[no_trace]
98 stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
99 cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
100
101 parser_inserted: Cell<bool>,
103 pending_loads: Cell<u32>,
106 any_failed_load: Cell<bool>,
108 request_generation_id: Cell<RequestGenerationId>,
110 is_explicitly_enabled: Cell<bool>,
112 previous_type_matched: Cell<bool>,
114 previous_media_environment_matched: Cell<bool>,
116 line_number: u64,
118 source_set: DomRefCell<SourceSet>,
120 blocking: MutNullableDom<DOMTokenList>,
122}
123
124impl HTMLLinkElement {
125 fn new_inherited(
126 local_name: LocalName,
127 prefix: Option<Prefix>,
128 document: &Document,
129 creator: ElementCreator,
130 ) -> HTMLLinkElement {
131 HTMLLinkElement {
132 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
133 rel_list: Default::default(),
134 relations: Cell::new(LinkRelations::empty()),
135 parser_inserted: Cell::new(creator.is_parser_created()),
136 stylesheet: DomRefCell::new(None),
137 cssom_stylesheet: MutNullableDom::new(None),
138 pending_loads: Cell::new(0),
139 any_failed_load: Cell::new(false),
140 request_generation_id: Cell::new(RequestGenerationId(0)),
141 is_explicitly_enabled: Cell::new(false),
142 previous_type_matched: Cell::new(true),
143 previous_media_environment_matched: Cell::new(true),
144 line_number: creator.return_line_number(),
145 source_set: DomRefCell::new(SourceSet::new()),
146 blocking: Default::default(),
147 }
148 }
149
150 pub(crate) fn new(
151 cx: &mut js::context::JSContext,
152 local_name: LocalName,
153 prefix: Option<Prefix>,
154 document: &Document,
155 proto: Option<HandleObject>,
156 creator: ElementCreator,
157 ) -> DomRoot<HTMLLinkElement> {
158 Node::reflect_node_with_proto(
159 cx,
160 Box::new(HTMLLinkElement::new_inherited(
161 local_name, prefix, document, creator,
162 )),
163 document,
164 proto,
165 )
166 }
167
168 pub(crate) fn get_request_generation_id(&self) -> RequestGenerationId {
169 self.request_generation_id.get()
170 }
171
172 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
173 fn remove_stylesheet(&self, no_gc: &NoGC) {
174 if let Some(stylesheet) = self.stylesheet.borrow_mut().take() {
175 let owner = self.stylesheet_list_owner();
176 owner.remove_stylesheet(
177 StylesheetSource::Element(Dom::from_ref(self.upcast())),
178 &stylesheet,
179 );
180 self.clean_stylesheet_ownership();
181 owner.invalidate_stylesheets(no_gc);
182 }
183 }
184
185 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
188 pub(crate) fn set_stylesheet(&self, new_stylesheet: Arc<Stylesheet>) {
189 let owner = self.stylesheet_list_owner();
190 if let Some(old_stylesheet) = self.stylesheet.borrow_mut().replace(new_stylesheet.clone()) {
191 owner.remove_stylesheet(
192 StylesheetSource::Element(Dom::from_ref(self.upcast())),
193 &old_stylesheet,
194 );
195 }
196 owner.add_owned_stylesheet(self.upcast(), new_stylesheet);
197 }
198
199 pub(crate) fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
200 self.stylesheet.borrow().clone()
201 }
202
203 pub(crate) fn get_cssom_stylesheet(
204 &self,
205 cx: &mut JSContext,
206 ) -> Option<DomRoot<CSSStyleSheet>> {
207 self.get_stylesheet().map(|sheet| {
208 self.cssom_stylesheet.or_init(|| {
209 CSSStyleSheet::new(
210 cx,
211 &self.owner_window(),
212 Some(self.upcast::<Element>()),
213 "text/css".into(),
214 Some(self.Href().into()),
215 None, sheet,
217 None, )
219 })
220 })
221 }
222
223 pub(crate) fn is_alternate(&self) -> bool {
224 self.relations.get().contains(LinkRelations::ALTERNATE) &&
225 !self
226 .upcast::<Element>()
227 .get_string_attribute(&local_name!("title"))
228 .is_empty()
229 }
230
231 pub(crate) fn is_effectively_disabled(&self) -> bool {
232 (self.is_alternate() && !self.is_explicitly_enabled.get()) ||
233 self.upcast::<Element>()
234 .has_attribute(&local_name!("disabled"))
235 }
236
237 fn clean_stylesheet_ownership(&self) {
238 if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() {
239 cssom_stylesheet.set_owner_node(None);
240 }
241 self.cssom_stylesheet.set(None);
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(
251 &self,
252 cx: &mut js::context::JSContext,
253 attr: AttrRef<'_>,
254 mutation: AttributeMutation,
255 ) {
256 self.super_type()
257 .unwrap()
258 .attribute_mutated(cx, attr, mutation);
259
260 let local_name = attr.local_name();
261 let is_removal = mutation.is_removal();
262 match *local_name {
263 local_name!("disabled") => {
264 self.handle_disabled_attribute_change(cx.no_gc(), is_removal);
265 return;
266 },
267 local_name!("rel") | local_name!("rev") => {
268 let previous_relations = self.relations.get();
269 self.relations
270 .set(LinkRelations::for_element(self.upcast()));
271
272 if previous_relations == self.relations.get() {
274 return;
275 }
276 },
277 _ => {},
278 }
279
280 let node = self.upcast::<Node>();
281 if !node.is_connected() {
282 return;
283 }
284
285 if self.relations.get().contains(LinkRelations::STYLESHEET) &&
288 let AttributeMutation::Set(Some(previous_value), _) = mutation &&
289 **previous_value == **attr.value()
290 {
291 return;
292 }
293
294 match *local_name {
295 local_name!("rel") | local_name!("rev") => {
296 if self.relations.get().contains(LinkRelations::STYLESHEET) {
299 self.handle_stylesheet_url(cx);
300 } else {
301 self.remove_stylesheet(cx.no_gc());
302 }
303
304 if self.relations.get().contains(LinkRelations::MODULE_PRELOAD) {
305 self.fetch_and_process_modulepreload(cx);
306 }
307 },
308 local_name!("href") => {
309 if is_removal {
312 if self.relations.get().contains(LinkRelations::STYLESHEET) {
313 self.remove_stylesheet(cx.no_gc());
314 }
315 return;
316 }
317 if self.relations.get().contains(LinkRelations::STYLESHEET) {
321 self.handle_stylesheet_url(cx);
322 }
323
324 if self.relations.get().contains(LinkRelations::ICON) {
325 self.handle_favicon_url(&attr.value());
326 }
327
328 if self.relations.get().contains(LinkRelations::PREFETCH) {
332 self.fetch_and_process_prefetch_link(&attr.value());
333 }
334
335 if self.relations.get().contains(LinkRelations::PRELOAD) {
339 self.handle_preload_url();
340 }
341
342 if self.relations.get().contains(LinkRelations::MODULE_PRELOAD) {
344 self.fetch_and_process_modulepreload(cx);
345 }
346 },
347 local_name!("imagesrcset") => {
348 self.source_set
349 .borrow_mut()
350 .update_source_set(self.upcast::<Element>());
351 },
352 local_name!("imagesizes") => {
353 if self
354 .upcast::<Element>()
355 .has_attribute(&local_name!("imagesrcset"))
356 {
357 self.source_set
358 .borrow_mut()
359 .update_source_set(self.upcast::<Element>());
360 }
361 },
362 local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
363 self.handle_favicon_url(&attr.value());
364 },
365 local_name!("crossorigin") => {
366 if self.relations.get().contains(LinkRelations::PREFETCH) {
370 self.fetch_and_process_prefetch_link(&attr.value());
371 }
372
373 if self.relations.get().contains(LinkRelations::STYLESHEET) {
377 self.handle_stylesheet_url(cx);
378 }
379 },
380 local_name!("as") => {
381 if self.relations.get().contains(LinkRelations::PRELOAD) &&
385 let AttributeMutation::Set(Some(_), _) = mutation
386 {
387 self.handle_preload_url();
388 }
389 },
390 local_name!("type") => {
391 if self.relations.get().contains(LinkRelations::STYLESHEET) {
399 self.handle_stylesheet_url(cx);
400 }
401
402 if self.relations.get().contains(LinkRelations::PRELOAD) &&
408 !self.previous_type_matched.get()
409 {
410 self.handle_preload_url();
411 }
412 },
413 local_name!("media") => {
414 if self.relations.get().contains(LinkRelations::PRELOAD) &&
419 !self.previous_media_environment_matched.get()
420 {
421 match mutation {
422 AttributeMutation::Removed | AttributeMutation::Set(Some(_), _) => {
423 self.handle_preload_url()
424 },
425 _ => {},
426 };
427 } else if self.relations.get().contains(LinkRelations::STYLESHEET) &&
428 let Some(ref stylesheet) = *self.stylesheet.borrow_mut()
429 {
430 let document = self.owner_document();
431 let shared_lock = document.style_shared_author_lock().clone();
432 let mut guard = shared_lock.write();
433 let media = stylesheet.media.write_with(&mut guard);
434 match mutation {
435 AttributeMutation::Set(..) => {
436 *media = MediaList::parse_media_list(&attr.value(), document.window())
437 },
438 AttributeMutation::Removed => *media = StyleMediaList::empty(),
439 };
440 self.owner_document().invalidate_stylesheets(cx.no_gc());
441 }
442
443 let matches_media_environment =
444 MediaList::matches_environment(&self.owner_document(), &attr.value());
445 self.previous_media_environment_matched
446 .set(matches_media_environment);
447 },
448 _ => {},
449 }
450 }
451
452 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
453 match name {
454 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
455 _ => self
456 .super_type()
457 .unwrap()
458 .parse_plain_attribute(name, value),
459 }
460 }
461
462 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
463 if let Some(s) = self.super_type() {
464 s.bind_to_tree(cx, context);
465 }
466
467 let element = self.upcast::<Element>();
468 let href = element.get_attribute_string_value(&local_name!("href"));
469
470 if context.tree_connected &&
471 (href.as_ref().is_some_and(|x| !x.is_empty()) ||
472 element.has_attribute(&local_name!("imagesrcset")))
473 {
474 let relations = self.relations.get();
475 if let Some(href) = href {
478 if relations.contains(LinkRelations::STYLESHEET) {
479 self.handle_stylesheet_url(cx);
480 }
481
482 if relations.contains(LinkRelations::ICON) {
483 self.handle_favicon_url(&href);
484 }
485
486 if relations.contains(LinkRelations::PREFETCH) {
487 self.fetch_and_process_prefetch_link(&href);
488 }
489
490 if relations.contains(LinkRelations::MODULE_PRELOAD) {
492 let link = DomRoot::from_ref(self);
493 self.owner_document().add_delayed_task(
494 task!(FetchModulePreload: |cx, link: DomRoot<HTMLLinkElement>| {
495 link.fetch_and_process_modulepreload(cx);
496 }),
497 );
498 }
499 }
500
501 if relations.contains(LinkRelations::PRELOAD) {
502 self.handle_preload_url();
503 }
504 }
505 }
506
507 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
508 if let Some(s) = self.super_type() {
509 s.unbind_from_tree(cx, context);
510 }
511
512 self.remove_stylesheet(cx.no_gc());
513 }
514}
515
516impl HTMLLinkElement {
517 fn compute_destination_for_attribute(&self) -> Option<Destination> {
518 let element = self.upcast::<Element>();
521 element
522 .get_attribute_string_value(&local_name!("as"))
523 .and_then(|attr| LinkProcessingOptions::translate_a_preload_destination(&attr))
524 }
525
526 fn processing_options(&self) -> LinkProcessingOptions {
528 let element = self.upcast::<Element>();
529
530 let document = self.upcast::<Node>().owner_doc();
532 let global = document.owner_global();
533
534 let mut options = LinkProcessingOptions {
536 href: String::new(),
537 destination: Destination::None,
538 integrity: String::new(),
539 link_type: String::new(),
540 cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
541 cross_origin: cors_setting_for_element(element),
542 referrer_policy: referrer_policy_for_element(element),
543 policy_container: document.policy_container().to_owned(),
544 source_set: Some(self.source_set.borrow().clone()),
545 origin: document.borrow().origin().immutable().to_owned(),
546 base_url: document.borrow().base_url(),
547 request_client: global.request_client(None),
548 referrer: global.get_referrer(),
549 };
550
551 if let Some(href_attribute) = element.get_attribute_string_value(&local_name!("href")) {
553 options.href = href_attribute;
554 }
555
556 if let Some(integrity_attribute) =
559 element.get_attribute_string_value(&local_name!("integrity"))
560 {
561 options.integrity = integrity_attribute;
562 }
563
564 if let Some(type_attribute) = element.get_attribute_string_value(&local_name!("type")) {
566 options.link_type = type_attribute;
567 }
568
569 assert!(!options.href.is_empty() || options.source_set.is_some());
571
572 options
574 }
575
576 fn default_fetch_and_process_the_linked_resource(&self) -> Option<RequestBuilder> {
581 let options = self.processing_options();
583
584 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
586 return None;
588 };
589 let mut request = request.synchronous(true);
591
592 if !self.linked_resource_fetch_setup(&mut request) {
594 return None;
595 }
596
597 Some(request)
603 }
604
605 fn linked_resource_fetch_setup(&self, request: &mut RequestBuilder) -> bool {
607 if self.relations.get().contains(LinkRelations::ICON) {
609 request.destination = Destination::Image;
611
612 }
616
617 if self.relations.get().contains(LinkRelations::STYLESHEET) {
619 if self
621 .upcast::<Element>()
622 .has_attribute(&local_name!("disabled"))
623 {
624 return false;
625 }
626 }
641
642 true
643 }
644
645 fn fetch_and_process_prefetch_link(&self, href: &str) {
647 if href.is_empty() {
649 return;
650 }
651
652 let mut options = self.processing_options();
654
655 options.destination = Destination::None;
657
658 let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
660 return;
662 };
663 let url = request.url.url();
664
665 let request = request.initiator(Initiator::Prefetch);
667
668 let document = self.upcast::<Node>().owner_doc();
672 let fetch_context = LinkFetchContext {
673 url,
674 link: Some(Trusted::new(self)),
675 global: Trusted::new(&document.global()),
676 type_: LinkFetchContextType::Prefetch,
677 response_body: vec![],
678 };
679
680 document.fetch_background(request, fetch_context);
681 }
682
683 fn handle_stylesheet_url(&self, cx: &mut js::context::JSContext) {
685 let document = self.owner_document();
686 if document.browsing_context().is_none() {
687 return;
688 }
689
690 let element = self.upcast::<Element>();
691
692 let type_ = element.get_string_attribute(&local_name!("type"));
699 if !type_.is_empty() && type_ != "text/css" {
700 return;
701 }
702
703 let href = element.get_string_attribute(&local_name!("href"));
705 if href.is_empty() {
706 return;
707 }
708
709 let link_url = match document.base_url().join(&href.str()) {
711 Ok(url) => url,
712 Err(e) => {
713 debug!("Parsing url {} failed: {}", href, e);
714 return;
715 },
716 };
717
718 let cors_setting = cors_setting_for_element(element);
720
721 let mq_str = element
722 .get_attribute_string_value(&local_name!("media"))
723 .unwrap_or_default();
724 let media = MediaList::parse_media_list(&mq_str, document.window());
725 let media = Arc::new(document.style_shared_author_lock().wrap(media));
726
727 let integrity_metadata = element
728 .get_attribute_string_value(&local_name!("integrity"))
729 .unwrap_or_default();
730
731 self.request_generation_id
732 .set(self.request_generation_id.get().increment());
733 self.pending_loads.set(0);
734
735 ElementStylesheetLoader::load_with_element(
736 cx,
737 self.upcast(),
738 StylesheetContextSource::LinkElement,
739 media,
740 link_url,
741 cors_setting,
742 integrity_metadata,
743 );
744 }
745
746 fn handle_disabled_attribute_change(&self, no_gc: &NoGC, is_removal: bool) {
748 if is_removal {
750 self.is_explicitly_enabled.set(true);
751 }
752 if let Some(stylesheet) = self.get_stylesheet() &&
753 stylesheet.set_disabled(!is_removal)
754 {
755 self.stylesheet_list_owner().invalidate_stylesheets(no_gc);
756 }
757 }
758
759 fn handle_favicon_url(&self, href: &str) {
760 if href.is_empty() {
762 return;
763 }
764
765 let window = self.owner_window();
768 if !window.is_top_level() {
769 return;
770 }
771 let Ok(href) = self.Href().parse() else {
772 return;
773 };
774
775 self.request_generation_id
777 .set(self.request_generation_id.get().increment());
778
779 let cache_result = window.image_cache().get_cached_image_status(
780 href,
781 window.origin().immutable().clone(),
782 cors_setting_for_element(self.upcast()),
783 );
784
785 match cache_result {
786 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
787 image, ..
788 }) => {
789 self.process_favicon_response(image);
790 },
791 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
792 ImageCacheResult::Pending(id) => {
793 let sender = self.register_image_cache_callback(id);
794 window.image_cache().add_listener(ImageLoadListener::new(
795 sender,
796 window.pipeline_id(),
797 id,
798 ));
799 },
800 ImageCacheResult::ReadyForRequest(id) => {
801 let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
802 return;
803 };
804
805 let sender = self.register_image_cache_callback(id);
806 window.image_cache().add_listener(ImageLoadListener::new(
807 sender,
808 window.pipeline_id(),
809 id,
810 ));
811
812 let document = self.upcast::<Node>().owner_doc();
813 let fetch_context = FaviconFetchContext {
814 url: self.owner_document().base_url(),
815 image_cache: window.image_cache(),
816 id,
817 link: Trusted::new(self),
818 };
819 document.fetch_background(request, fetch_context);
820 },
821 ImageCacheResult::FailedToLoadOrDecode => {},
822 };
823 }
824
825 fn register_image_cache_callback(&self, id: PendingImageId) -> ImageCacheResponseCallback {
826 let trusted_node = Trusted::new(self);
827 let window = self.owner_window();
828 let request_generation_id = self.get_request_generation_id();
829 window.register_image_cache_listener(id, move |response, _| {
830 let trusted_node = trusted_node.clone();
831 let link_element = trusted_node.root();
832 let window = link_element.owner_window();
833
834 let ImageResponse::Loaded(image, _) = response.response else {
835 return;
837 };
838
839 if request_generation_id != link_element.get_request_generation_id() {
840 return;
842 };
843
844 window
845 .as_global_scope()
846 .task_manager()
847 .networking_task_source()
848 .queue(task!(process_favicon_response: move || {
849 let element = trusted_node.root();
850
851 if request_generation_id != element.get_request_generation_id() {
852 return;
854 };
855
856 element.process_favicon_response(image);
857 }));
858 })
859 }
860
861 fn process_favicon_response(&self, image: Image) {
863 let window = self.owner_window();
865 let document = self.owner_document();
866
867 let send_rasterized_favicon_to_embedder = |raster_image: &pixels::RasterImage| {
868 let frame = raster_image.first_frame();
870
871 let format = match raster_image.format {
872 PixelFormat::K8 => embedder_traits::PixelFormat::K8,
873 PixelFormat::KA8 => embedder_traits::PixelFormat::KA8,
874 PixelFormat::RGB8 => embedder_traits::PixelFormat::RGB8,
875 PixelFormat::RGBA8 => embedder_traits::PixelFormat::RGBA8,
876 PixelFormat::BGRA8 => embedder_traits::PixelFormat::BGRA8,
877 };
878
879 let embedder_image = embedder_traits::Image::new(
880 frame.width,
881 frame.height,
882 std::sync::Arc::new(GenericSharedMemory::from_arc_vec(
883 raster_image.bytes.clone(),
884 )),
885 raster_image.frames[0].byte_range.clone(),
886 format,
887 );
888 document.set_favicon(embedder_image);
889 };
890
891 match image {
892 Image::Raster(raster_image) => send_rasterized_favicon_to_embedder(&raster_image),
893 Image::Vector(vector_image) => {
894 let size = DeviceIntSize::new(250, 250);
896
897 let image_cache = window.image_cache();
898 if let Some(raster_image) =
899 image_cache.rasterize_vector_image(vector_image.id, size, None)
900 {
901 send_rasterized_favicon_to_embedder(&raster_image);
902 } else {
903 let image_cache_sender = self.register_image_cache_callback(vector_image.id);
906 image_cache.add_rasterization_complete_listener(
907 window.pipeline_id(),
908 vector_image.id,
909 size,
910 image_cache_sender,
911 );
912 }
913 },
914 }
915 }
916
917 fn handle_preload_url(&self) {
920 self.source_set
922 .borrow_mut()
923 .update_source_set(self.upcast::<Element>());
924 let mut options = self.processing_options();
926 let Some(destination) = self.compute_destination_for_attribute() else {
929 return;
931 };
932 options.destination = destination;
934 {
936 let type_matches_destination = options.type_matches_destination();
938 self.previous_type_matched.set(type_matches_destination);
939 if !type_matches_destination {
940 return;
941 }
942 }
943 let document = self.upcast::<Node>().owner_doc();
945 options.preload(
946 self.owner_window().webview_id(),
947 Some(Trusted::new(self)),
948 &document,
949 );
950 }
951
952 pub(crate) fn fire_event_after_response(
954 &self,
955 cx: &mut JSContext,
956 response: Result<(), NetworkError>,
957 ) {
958 if response.is_err() {
961 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
962 } else {
963 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
964 }
965 }
966
967 fn fetch_and_process_modulepreload(&self, cx: &mut JSContext) {
969 let el = self.upcast::<Element>();
970 let href_attribute_value = el.get_string_attribute(&local_name!("href"));
971
972 if href_attribute_value.is_empty() {
974 return;
975 }
976
977 let destination = el
979 .get_attribute_string_value(&local_name!("as"))
980 .map(|value| value.to_ascii_lowercase())
981 .and_then(|value| match value.as_str() {
982 "" => None,
984 "fetch" => Some(Destination::None),
986 _ => Destination::from_str(&value).ok(),
987 })
988 .unwrap_or(Destination::Script);
989
990 let document = self.owner_document();
991 let global = document.global();
992
993 let is_a_modulepreload_destination = match destination {
995 Destination::Json | Destination::Style => true,
996 Destination::Xslt => false,
999 d => d.is_script_like(),
1000 };
1001
1002 if !is_a_modulepreload_destination {
1005 return global
1006 .task_manager()
1007 .networking_task_source()
1008 .queue_simple_event(self.upcast(), atom!("error"));
1009 }
1010
1011 let Ok(url) = document.encoding_parse_a_url(&href_attribute_value.str()) else {
1014 return;
1015 };
1016 let url = ensure_blob_referenced_by_url_is_kept_alive(&global, url);
1017
1018 let credentials_mode = cors_settings_attribute_credential_mode(el);
1022
1023 let cryptographic_nonce = el.nonce_value();
1025
1026 let integrity_metadata = el
1030 .get_attribute_string_value(&local_name!("integrity"))
1031 .unwrap_or_else(|| {
1032 global
1033 .import_map()
1034 .resolve_a_module_integrity_metadata(&url.url())
1035 });
1036
1037 let referrer_policy = referrer_policy_for_element(el);
1039
1040 let options = ScriptFetchOptions {
1046 cryptographic_nonce,
1047 integrity_metadata,
1048 parser_metadata: ParserMetadata::NotParserInserted,
1049 credentials_mode,
1050 referrer_policy,
1051 render_blocking: false,
1052 };
1053
1054 let link = DomRoot::from_ref(self);
1055
1056 fetch_a_modulepreload_module(
1059 cx,
1060 url,
1061 destination,
1062 &global,
1063 options,
1064 move |cx, fetch_failed| {
1065 let event = match fetch_failed {
1068 true => atom!("error"),
1069 false => atom!("load"),
1070 };
1071
1072 link.upcast::<EventTarget>().fire_event(cx, event);
1073 },
1074 );
1075 }
1076}
1077
1078impl StylesheetOwner for HTMLLinkElement {
1079 fn increment_pending_loads_count(&self) {
1080 self.pending_loads.set(self.pending_loads.get() + 1)
1081 }
1082
1083 fn load_finished(&self, succeeded: bool) -> Option<bool> {
1084 assert!(self.pending_loads.get() > 0, "What finished?");
1085 if !succeeded {
1086 self.any_failed_load.set(true);
1087 }
1088
1089 self.pending_loads.set(self.pending_loads.get() - 1);
1090 if self.pending_loads.get() != 0 {
1091 return None;
1092 }
1093
1094 let any_failed = self.any_failed_load.get();
1095 self.any_failed_load.set(false);
1096 Some(any_failed)
1097 }
1098
1099 fn parser_inserted(&self) -> bool {
1100 self.parser_inserted.get()
1101 }
1102
1103 fn potentially_render_blocking(&self) -> bool {
1105 self.parser_inserted() ||
1112 self.blocking
1113 .get()
1114 .is_some_and(|list| list.Contains("render".into()))
1115 }
1116
1117 fn referrer_policy(&self, cx: &mut js::context::JSContext) -> ReferrerPolicy {
1118 if self.RelList(cx).Contains("noreferrer".into()) {
1119 return ReferrerPolicy::NoReferrer;
1120 }
1121
1122 ReferrerPolicy::EmptyString
1123 }
1124
1125 fn set_origin_clean(&self, cx: &mut js::context::JSContext, origin_clean: bool) {
1126 if let Some(stylesheet) = self.get_cssom_stylesheet(cx) {
1127 stylesheet.set_origin_clean(origin_clean);
1128 }
1129 }
1130}
1131
1132impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
1133 make_url_getter!(Href, "href");
1135
1136 make_url_setter!(SetHref, "href");
1138
1139 make_getter!(Rel, "rel");
1141
1142 fn SetRel(&self, cx: &mut JSContext, rel: DOMString) {
1144 self.upcast::<Element>()
1145 .set_tokenlist_attribute(cx, &local_name!("rel"), rel);
1146 }
1147
1148 make_enumerated_getter!(
1150 As,
1151 "as",
1152 "fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
1153 | "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
1154 | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
1155 | "video" | "webidentity" | "worker" | "xslt",
1156 missing => "",
1157 invalid => ""
1158 );
1159
1160 make_setter!(SetAs, "as");
1162
1163 make_getter!(Media, "media");
1165
1166 make_setter!(SetMedia, "media");
1168
1169 make_getter!(Integrity, "integrity");
1171
1172 make_setter!(SetIntegrity, "integrity");
1174
1175 make_getter!(Hreflang, "hreflang");
1177
1178 make_setter!(SetHreflang, "hreflang");
1180
1181 make_getter!(Type, "type");
1183
1184 make_setter!(SetType, "type");
1186
1187 make_url_getter!(ImageSrcset, "imagesrcset");
1189
1190 make_url_setter!(SetImageSrcset, "imagesrcset");
1192
1193 make_getter!(ImageSizes, "imagesizes");
1195
1196 make_setter!(SetImageSizes, "imagesizes");
1198
1199 make_bool_getter!(Disabled, "disabled");
1201
1202 make_bool_setter!(SetDisabled, "disabled");
1204
1205 fn RelList(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMTokenList> {
1207 self.rel_list.or_init(|| {
1208 DOMTokenList::new(
1209 cx,
1210 self.upcast(),
1211 &local_name!("rel"),
1212 Some(vec![
1213 Atom::from("alternate"),
1214 Atom::from("apple-touch-icon"),
1215 Atom::from("apple-touch-icon-precomposed"),
1216 Atom::from("canonical"),
1217 Atom::from("dns-prefetch"),
1218 Atom::from("icon"),
1219 Atom::from("import"),
1220 Atom::from("manifest"),
1221 Atom::from("modulepreload"),
1222 Atom::from("next"),
1223 Atom::from("preconnect"),
1224 Atom::from("prefetch"),
1225 Atom::from("preload"),
1226 Atom::from("prerender"),
1227 Atom::from("stylesheet"),
1228 ]),
1229 )
1230 })
1231 }
1232
1233 make_getter!(Charset, "charset");
1235
1236 make_setter!(SetCharset, "charset");
1238
1239 make_getter!(Rev, "rev");
1241
1242 make_setter!(SetRev, "rev");
1244
1245 make_getter!(Target, "target");
1247
1248 make_setter!(SetTarget, "target");
1250
1251 fn Blocking(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMTokenList> {
1253 self.blocking.or_init(|| {
1254 DOMTokenList::new(
1255 cx,
1256 self.upcast(),
1257 &local_name!("blocking"),
1258 Some(vec![Atom::from("render")]),
1259 )
1260 })
1261 }
1262
1263 fn GetCrossOrigin(&self) -> Option<DOMString> {
1265 reflect_cross_origin_attribute(self.upcast::<Element>())
1266 }
1267
1268 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1270 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1271 }
1272
1273 fn ReferrerPolicy(&self) -> DOMString {
1275 reflect_referrer_policy_attribute(self.upcast::<Element>())
1276 }
1277
1278 make_setter!(SetReferrerPolicy, "referrerpolicy");
1280
1281 fn GetSheet(&self, cx: &mut JSContext) -> Option<DomRoot<DOMStyleSheet>> {
1283 self.get_cssom_stylesheet(cx).map(DomRoot::upcast)
1284 }
1285}
1286
1287struct FaviconFetchContext {
1288 link: Trusted<HTMLLinkElement>,
1290 image_cache: std::sync::Arc<dyn ImageCache>,
1291 id: PendingImageId,
1292
1293 url: ServoUrl,
1295}
1296
1297impl FetchResponseListener for FaviconFetchContext {
1298 fn process_request_body(&mut self, _: RequestId) {}
1299
1300 fn process_response(
1301 &mut self,
1302 _: &mut js::context::JSContext,
1303 request_id: RequestId,
1304 metadata: Result<FetchMetadata, NetworkError>,
1305 ) {
1306 self.image_cache.notify_pending_response(
1307 self.id,
1308 FetchResponseMsg::ProcessResponse(request_id, metadata),
1309 );
1310 }
1311
1312 fn process_response_chunk(
1313 &mut self,
1314 _: &mut js::context::JSContext,
1315 request_id: RequestId,
1316 chunk: Vec<u8>,
1317 ) {
1318 self.image_cache.notify_pending_response(
1319 self.id,
1320 FetchResponseMsg::ProcessResponseChunk(request_id, chunk.into()),
1321 );
1322 }
1323
1324 fn process_response_eof(
1325 self,
1326 cx: &mut js::context::JSContext,
1327 request_id: RequestId,
1328 response: Result<(), NetworkError>,
1329 timing: ResourceFetchTiming,
1330 ) {
1331 self.image_cache.notify_pending_response(
1332 self.id,
1333 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
1334 );
1335 submit_timing(cx, &self, &response, &timing);
1336 }
1337
1338 fn process_csp_violations(
1339 &mut self,
1340 cx: &mut js::context::JSContext,
1341 _request_id: RequestId,
1342 violations: Vec<Violation>,
1343 ) {
1344 let global = &self.resource_timing_global();
1345 global.report_csp_violations(cx, violations, None, None);
1346 }
1347
1348 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
1349 self.image_cache.notify_pending_response(
1350 self.id,
1351 FetchResponseMsg::ProcessContentLength(request_id, size),
1352 )
1353 }
1354}
1355
1356impl ResourceTimingListener for FaviconFetchContext {
1357 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1358 (
1359 InitiatorType::LocalName("link".to_string()),
1360 self.url.clone(),
1361 )
1362 }
1363
1364 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1365 self.link.root().upcast::<Node>().owner_doc().global()
1366 }
1367}