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,91 @@ 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
+ {
590
+ return true ;
591
+ }
592
+ // Step 7. Return false.
593
+ false
594
+ }
497
595
}
498
596
499
597
impl StylesheetOwner for HTMLLinkElement {
@@ -552,6 +650,18 @@ impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
552
650
. set_tokenlist_attribute ( & local_name ! ( "rel" ) , rel, can_gc) ;
553
651
}
554
652
653
+ // https://html.spec.whatwg.org/multipage/#dom-link-as
654
+ make_enumerated_getter ! (
655
+ As ,
656
+ "as" ,
657
+ "fetch" | "font" | "image" | "script" | "track" ,
658
+ missing => "" ,
659
+ invalid => ""
660
+ ) ;
661
+
662
+ // https://html.spec.whatwg.org/multipage/#dom-link-as
663
+ make_setter ! ( SetAs , "as" ) ;
664
+
555
665
// https://html.spec.whatwg.org/multipage/#dom-link-media
556
666
make_getter ! ( Media , "media" ) ;
557
667
@@ -689,6 +799,7 @@ impl LinkProcessingOptions {
689
799
self . has_trustworthy_ancestor_origin ,
690
800
self . policy_container ,
691
801
)
802
+ . origin ( self . origin )
692
803
. integrity_metadata ( self . integrity )
693
804
. cryptographic_nonce_metadata ( self . cryptographic_nonce_metadata )
694
805
. referrer_policy ( self . referrer_policy ) ;
@@ -795,3 +906,89 @@ impl PreInvoke for PrefetchContext {
795
906
true
796
907
}
797
908
}
909
+
910
+ struct PreloadContext {
911
+ /// The `<link>` element that caused this prefetch operation
912
+ link : Trusted < HTMLLinkElement > ,
913
+
914
+ resource_timing : ResourceFetchTiming ,
915
+
916
+ /// The url being prefetched
917
+ url : ServoUrl ,
918
+ }
919
+
920
+ impl FetchResponseListener for PreloadContext {
921
+ fn process_request_body ( & mut self , _: RequestId ) { }
922
+
923
+ fn process_request_eof ( & mut self , _: RequestId ) { }
924
+
925
+ fn process_response (
926
+ & mut self ,
927
+ _: RequestId ,
928
+ fetch_metadata : Result < FetchMetadata , NetworkError > ,
929
+ ) {
930
+ _ = fetch_metadata;
931
+ }
932
+
933
+ fn process_response_chunk ( & mut self , _: RequestId , chunk : Vec < u8 > ) {
934
+ _ = chunk;
935
+ }
936
+
937
+ // Step 3.1 of https://html.spec.whatwg.org/multipage/links.html#link-type-preload:fetch-and-process-the-linked-resource-2
938
+ fn process_response_eof (
939
+ & mut self ,
940
+ _: RequestId ,
941
+ response : Result < ResourceFetchTiming , NetworkError > ,
942
+ ) {
943
+ if response. is_err ( ) {
944
+ // Step 1. If response is a network error, fire an event named error at el.
945
+ self . link
946
+ . root ( )
947
+ . upcast :: < EventTarget > ( )
948
+ . fire_event ( atom ! ( "error" ) , CanGc :: note ( ) ) ;
949
+ } else {
950
+ // Step 2. Otherwise, fire an event named load at el.
951
+ self . link
952
+ . root ( )
953
+ . upcast :: < EventTarget > ( )
954
+ . fire_event ( atom ! ( "load" ) , CanGc :: note ( ) ) ;
955
+ }
956
+ }
957
+
958
+ fn resource_timing_mut ( & mut self ) -> & mut ResourceFetchTiming {
959
+ & mut self . resource_timing
960
+ }
961
+
962
+ fn resource_timing ( & self ) -> & ResourceFetchTiming {
963
+ & self . resource_timing
964
+ }
965
+
966
+ fn submit_resource_timing ( & mut self ) {
967
+ submit_timing ( self , CanGc :: note ( ) )
968
+ }
969
+
970
+ fn process_csp_violations ( & mut self , _request_id : RequestId , violations : Vec < csp:: Violation > ) {
971
+ let global = & self . resource_timing_global ( ) ;
972
+ global. report_csp_violations ( violations, None ) ;
973
+ }
974
+ }
975
+
976
+ impl ResourceTimingListener for PreloadContext {
977
+ fn resource_timing_information ( & self ) -> ( InitiatorType , ServoUrl ) {
978
+ (
979
+ InitiatorType :: LocalName ( "preload" . to_string ( ) ) ,
980
+ self . url . clone ( ) ,
981
+ )
982
+ }
983
+
984
+ fn resource_timing_global ( & self ) -> DomRoot < GlobalScope > {
985
+ self . link . root ( ) . upcast :: < Node > ( ) . owner_doc ( ) . global ( )
986
+ }
987
+ }
988
+
989
+ impl PreInvoke for PreloadContext {
990
+ fn should_invoke ( & self ) -> bool {
991
+ // Preload requests are never aborted.
992
+ true
993
+ }
994
+ }
0 commit comments