5
5
use std:: borrow:: { Borrow , ToOwned } ;
6
6
use std:: cell:: Cell ;
7
7
use std:: default:: Default ;
8
+ use std:: str:: FromStr ;
8
9
9
10
use base:: id:: WebViewId ;
10
11
use content_security_policy as csp;
11
12
use dom_struct:: dom_struct;
12
13
use embedder_traits:: EmbedderMsg ;
13
14
use html5ever:: { LocalName , Prefix , local_name, ns} ;
14
15
use js:: rust:: HandleObject ;
16
+ use mime:: Mime ;
17
+ use net_traits:: mime_classifier:: { MediaType , MimeClassifier } ;
15
18
use net_traits:: policy_container:: PolicyContainer ;
16
19
use net_traits:: request:: {
17
20
CorsSettings , Destination , Initiator , InsecureRequestsPolicy , Referrer , RequestBuilder ,
@@ -22,7 +25,7 @@ use net_traits::{
22
25
ResourceTimingType ,
23
26
} ;
24
27
use servo_arc:: Arc ;
25
- use servo_url:: ServoUrl ;
28
+ use servo_url:: { ImmutableOrigin , ServoUrl } ;
26
29
use style:: attr:: AttrValue ;
27
30
use style:: stylesheets:: Stylesheet ;
28
31
use stylo_atoms:: Atom ;
@@ -78,6 +81,7 @@ struct LinkProcessingOptions {
78
81
policy_container : PolicyContainer ,
79
82
source_set : Option < ( ) > ,
80
83
base_url : ServoUrl ,
84
+ origin : ImmutableOrigin ,
81
85
insecure_requests_policy : InsecureRequestsPolicy ,
82
86
has_trustworthy_ancestor_origin : bool ,
83
87
// Some fields that we don't need yet are missing
@@ -257,6 +261,10 @@ impl VirtualMethods for HTMLLinkElement {
257
261
if self . relations . get ( ) . contains ( LinkRelations :: PREFETCH ) {
258
262
self . fetch_and_process_prefetch_link ( & attr. value ( ) ) ;
259
263
}
264
+
265
+ if self . relations . get ( ) . contains ( LinkRelations :: PRELOAD ) {
266
+ self . handle_preload_url ( ) ;
267
+ }
260
268
} ,
261
269
local_name ! ( "sizes" ) if self . relations . get ( ) . contains ( LinkRelations :: ICON ) => {
262
270
if let Some ( ref href) = get_attr ( self . upcast ( ) , & local_name ! ( "href" ) ) {
@@ -307,6 +315,10 @@ impl VirtualMethods for HTMLLinkElement {
307
315
if relations. contains ( LinkRelations :: PREFETCH ) {
308
316
self . fetch_and_process_prefetch_link ( & href) ;
309
317
}
318
+
319
+ if relations. contains ( LinkRelations :: PRELOAD ) {
320
+ self . handle_preload_url ( ) ;
321
+ }
310
322
}
311
323
}
312
324
}
@@ -348,6 +360,7 @@ impl HTMLLinkElement {
348
360
referrer_policy : referrer_policy_for_element ( element) ,
349
361
policy_container : document. policy_container ( ) . to_owned ( ) ,
350
362
source_set : None , // FIXME
363
+ origin : document. borrow ( ) . origin ( ) . immutable ( ) . to_owned ( ) ,
351
364
base_url : document. borrow ( ) . base_url ( ) ,
352
365
insecure_requests_policy : document. insecure_requests_policy ( ) ,
353
366
has_trustworthy_ancestor_origin : document. has_trustworthy_ancestor_or_current_origin ( ) ,
@@ -494,6 +507,99 @@ impl HTMLLinkElement {
494
507
Err ( e) => debug ! ( "Parsing url {} failed: {}" , href, e) ,
495
508
}
496
509
}
510
+
511
+ /// <https://html.spec.whatwg.org/multipage/links.html#link-type-preload:fetch-and-process-the-linked-resource-2>
512
+ fn handle_preload_url ( & self ) {
513
+ // Step 1. Update the source set for el.
514
+ // TODO
515
+ // Step 2. Let options be the result of creating link options from el.
516
+ let options = self . processing_options ( ) ;
517
+ // Step 3. Preload options, with the following steps given a response response:
518
+ // Step 3.1 If response is a network error, fire an event named error at el. Otherwise, fire an event named load at el.
519
+ self . preload ( options) ;
520
+ }
521
+
522
+ /// <https://html.spec.whatwg.org/multipage/links.html#preload>
523
+ fn preload ( & self , options : LinkProcessingOptions ) {
524
+ // Step 1. If options's type doesn't match options's destination, then return.
525
+ if !HTMLLinkElement :: type_matches_destination ( & options. link_type , options. destination ) {
526
+ return ;
527
+ }
528
+ // Step 2. If options's destination is "image" and options's source set is not null,
529
+ // then set options's href to the result of selecting an image source from options's source set.
530
+ // TODO
531
+ // Step 3. Let request be the result of creating a link request given options.
532
+ let url = options. base_url . clone ( ) ;
533
+ let Some ( mut request) = options. create_link_request ( self . owner_window ( ) . webview_id ( ) )
534
+ else {
535
+ // Step 4. If request is null, then return.
536
+ return ;
537
+ } ;
538
+ let document = self . upcast :: < Node > ( ) . owner_doc ( ) ;
539
+ // Step 5. Let unsafeEndTime be 0.
540
+ // TODO
541
+ // Step 6. Let entry be a new preload entry whose integrity metadata is options's integrity.
542
+ // TODO
543
+ // Step 7. Let key be the result of creating a preload key given request.
544
+ // TODO
545
+ // Step 8. If options's document is "pending", then set request's initiator type to "early hint".
546
+ if document. has_pending_parsing_blocking_script ( ) {
547
+ request = request. initiator ( Initiator :: EarlyHint ) ;
548
+ }
549
+ // Step 9. Let controller be null.
550
+ // Step 10. Let reportTiming given a Document document be to report timing for controller given document's relevant global object.
551
+ // Step 11. Set controller to the result of fetching request, with processResponseConsumeBody
552
+ // set to the following steps given a response response and null, failure, or a byte sequence bodyBytes:
553
+ let fetch_context = PreloadContext {
554
+ url,
555
+ link : Trusted :: new ( self ) ,
556
+ resource_timing : ResourceFetchTiming :: new ( ResourceTimingType :: Resource ) ,
557
+ } ;
558
+ document. fetch_background ( request. clone ( ) , fetch_context) ;
559
+ }
560
+
561
+ fn type_matches_destination ( mime_type : & str , destination : Option < Destination > ) -> bool {
562
+ // Step 1. If type is an empty string, then return true.
563
+ if mime_type. is_empty ( ) {
564
+ return true ;
565
+ }
566
+ let Some ( destination) = destination else {
567
+ return false ;
568
+ } ;
569
+ // Step 2. If destination is "fetch", then return true.
570
+ // TODO
571
+ // Step 3. Let mimeTypeRecord be the result of parsing type.
572
+ let Ok ( mime_type_record) = Mime :: from_str ( mime_type) else {
573
+ // Step 4. If mimeTypeRecord is failure, then return false.
574
+ return false ;
575
+ } ;
576
+ // Step 5. If mimeTypeRecord is not supported by the user agent, then return false.
577
+ let Some ( mime_type) = MimeClassifier :: get_media_type ( & mime_type_record) else {
578
+ return false ;
579
+ } ;
580
+ // Step 6. If any of the following are true:
581
+ if
582
+ // destination is "audio" or "video", and mimeTypeRecord is an audio or video MIME type;
583
+ ( ( destination == Destination :: Audio || destination == Destination :: Video ) &&
584
+ mime_type == MediaType :: AudioVideo )
585
+ // destination is a script-like destination and mimeTypeRecord is a JavaScript MIME type;
586
+ || ( destination. is_script_like ( ) && mime_type == MediaType :: Script )
587
+ // destination is "image" and mimeTypeRecord is an image MIME type;
588
+ || ( destination == Destination :: Image && mime_type == MediaType :: Image )
589
+ // destination is "font" and mimeTypeRecord is a font MIME type;
590
+ // TODO
591
+ // destination is "json" and mimeTypeRecord is a JSON MIME type;
592
+ // TODO
593
+ // destination is "style" and mimeTypeRecord's essence is text/css; or
594
+ // TODO
595
+ // destination is "track" and mimeTypeRecord's essence is text/vtt,
596
+ // TODO
597
+ {
598
+ return true ;
599
+ }
600
+ // Step 7. Return false.
601
+ false
602
+ }
497
603
}
498
604
499
605
impl StylesheetOwner for HTMLLinkElement {
@@ -552,6 +658,18 @@ impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
552
658
. set_tokenlist_attribute ( & local_name ! ( "rel" ) , rel, can_gc) ;
553
659
}
554
660
661
+ // https://html.spec.whatwg.org/multipage/#dom-link-as
662
+ make_enumerated_getter ! (
663
+ As ,
664
+ "as" ,
665
+ "fetch" | "font" | "image" | "script" | "track" ,
666
+ missing => "" ,
667
+ invalid => ""
668
+ ) ;
669
+
670
+ // https://html.spec.whatwg.org/multipage/#dom-link-as
671
+ make_setter ! ( SetAs , "as" ) ;
672
+
555
673
// https://html.spec.whatwg.org/multipage/#dom-link-media
556
674
make_getter ! ( Media , "media" ) ;
557
675
@@ -689,6 +807,7 @@ impl LinkProcessingOptions {
689
807
self . has_trustworthy_ancestor_origin ,
690
808
self . policy_container ,
691
809
)
810
+ . origin ( self . origin )
692
811
. integrity_metadata ( self . integrity )
693
812
. cryptographic_nonce_metadata ( self . cryptographic_nonce_metadata )
694
813
. referrer_policy ( self . referrer_policy ) ;
@@ -795,3 +914,89 @@ impl PreInvoke for PrefetchContext {
795
914
true
796
915
}
797
916
}
917
+
918
+ struct PreloadContext {
919
+ /// The `<link>` element that caused this prefetch operation
920
+ link : Trusted < HTMLLinkElement > ,
921
+
922
+ resource_timing : ResourceFetchTiming ,
923
+
924
+ /// The url being prefetched
925
+ url : ServoUrl ,
926
+ }
927
+
928
+ impl FetchResponseListener for PreloadContext {
929
+ fn process_request_body ( & mut self , _: RequestId ) { }
930
+
931
+ fn process_request_eof ( & mut self , _: RequestId ) { }
932
+
933
+ fn process_response (
934
+ & mut self ,
935
+ _: RequestId ,
936
+ fetch_metadata : Result < FetchMetadata , NetworkError > ,
937
+ ) {
938
+ _ = fetch_metadata;
939
+ }
940
+
941
+ fn process_response_chunk ( & mut self , _: RequestId , chunk : Vec < u8 > ) {
942
+ _ = chunk;
943
+ }
944
+
945
+ // Step 3.1 of https://html.spec.whatwg.org/multipage/links.html#link-type-preload:fetch-and-process-the-linked-resource-2
946
+ fn process_response_eof (
947
+ & mut self ,
948
+ _: RequestId ,
949
+ response : Result < ResourceFetchTiming , NetworkError > ,
950
+ ) {
951
+ if response. is_err ( ) {
952
+ // Step 1. If response is a network error, fire an event named error at el.
953
+ self . link
954
+ . root ( )
955
+ . upcast :: < EventTarget > ( )
956
+ . fire_event ( atom ! ( "error" ) , CanGc :: note ( ) ) ;
957
+ } else {
958
+ // Step 2. Otherwise, fire an event named load at el.
959
+ self . link
960
+ . root ( )
961
+ . upcast :: < EventTarget > ( )
962
+ . fire_event ( atom ! ( "load" ) , CanGc :: note ( ) ) ;
963
+ }
964
+ }
965
+
966
+ fn resource_timing_mut ( & mut self ) -> & mut ResourceFetchTiming {
967
+ & mut self . resource_timing
968
+ }
969
+
970
+ fn resource_timing ( & self ) -> & ResourceFetchTiming {
971
+ & self . resource_timing
972
+ }
973
+
974
+ fn submit_resource_timing ( & mut self ) {
975
+ submit_timing ( self , CanGc :: note ( ) )
976
+ }
977
+
978
+ fn process_csp_violations ( & mut self , _request_id : RequestId , violations : Vec < csp:: Violation > ) {
979
+ let global = & self . resource_timing_global ( ) ;
980
+ global. report_csp_violations ( violations, None ) ;
981
+ }
982
+ }
983
+
984
+ impl ResourceTimingListener for PreloadContext {
985
+ fn resource_timing_information ( & self ) -> ( InitiatorType , ServoUrl ) {
986
+ (
987
+ InitiatorType :: LocalName ( "preload" . to_string ( ) ) ,
988
+ self . url . clone ( ) ,
989
+ )
990
+ }
991
+
992
+ fn resource_timing_global ( & self ) -> DomRoot < GlobalScope > {
993
+ self . link . root ( ) . upcast :: < Node > ( ) . owner_doc ( ) . global ( )
994
+ }
995
+ }
996
+
997
+ impl PreInvoke for PreloadContext {
998
+ fn should_invoke ( & self ) -> bool {
999
+ // Preload requests are never aborted.
1000
+ true
1001
+ }
1002
+ }
0 commit comments