@@ -440,7 +440,11 @@ impl<'a> Parser<'a> {
440
440
. collect :: < String > ( )
441
441
!= "//"
442
442
} ) ;
443
- self . after_double_slash ( remaining, scheme_type, scheme_end)
443
+ if let Some ( after_prefix) = input. split_prefix ( "//" ) {
444
+ return self . after_double_slash ( after_prefix, scheme_type, scheme_end) ;
445
+ } else {
446
+ self . after_double_slash ( remaining, scheme_type, scheme_end)
447
+ }
444
448
}
445
449
SchemeType :: NotSpecial => self . parse_non_special ( input, scheme_type, scheme_end) ,
446
450
}
@@ -634,7 +638,7 @@ impl<'a> Parser<'a> {
634
638
( Some ( i) , _) | ( None , Some ( i) ) => base_url. slice ( ..i) ,
635
639
} ;
636
640
self . serialization . push_str ( before_query) ;
637
- self . pop_path ( SchemeType :: File , base_url. path_start as usize ) ;
641
+ self . shorten_path ( SchemeType :: File , base_url. path_start as usize ) ;
638
642
let remaining = self . parse_path (
639
643
SchemeType :: File ,
640
644
& mut true ,
@@ -753,6 +757,9 @@ impl<'a> Parser<'a> {
753
757
debug_assert ! ( base_url. byte_at( scheme_end) == b':' ) ;
754
758
self . serialization
755
759
. push_str ( base_url. slice ( ..scheme_end + 1 ) ) ;
760
+ if let Some ( after_prefix) = input. split_prefix ( "//" ) {
761
+ return self . after_double_slash ( after_prefix, scheme_type, scheme_end) ;
762
+ }
756
763
return self . after_double_slash ( remaining, scheme_type, scheme_end) ;
757
764
}
758
765
let path_start = base_url. path_start ;
@@ -960,7 +967,7 @@ impl<'a> Parser<'a> {
960
967
host_str = & input_str[ ..bytes]
961
968
}
962
969
}
963
- if scheme_type. is_special ( ) && host_str. is_empty ( ) {
970
+ if scheme_type == SchemeType :: SpecialNotFile && host_str. is_empty ( ) {
964
971
return Err ( ParseError :: EmptyHost ) ;
965
972
}
966
973
if !scheme_type. is_special ( ) {
@@ -1150,8 +1157,15 @@ impl<'a> Parser<'a> {
1150
1157
".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e"
1151
1158
| ".%2E" => {
1152
1159
debug_assert ! ( self . serialization. as_bytes( ) [ segment_start - 1 ] == b'/' ) ;
1153
- self . serialization . truncate ( segment_start - 1 ) ; // Truncate "/../"
1154
- self . pop_path ( scheme_type, path_start) ;
1160
+ self . serialization . truncate ( segment_start) ;
1161
+ // Do not remove the root slash
1162
+ if self . serialization . ends_with ( "/" ) && path_start + 1 < segment_start {
1163
+ self . serialization . pop ( ) ;
1164
+ self . shorten_path ( scheme_type, path_start) ;
1165
+ } else {
1166
+ self . shorten_path ( scheme_type, path_start) ;
1167
+ }
1168
+
1155
1169
// and then if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
1156
1170
if ends_with_slash && !self . serialization . ends_with ( "/" ) {
1157
1171
self . serialization . push ( '/' ) ;
@@ -1195,16 +1209,36 @@ impl<'a> Parser<'a> {
1195
1209
input
1196
1210
}
1197
1211
1212
+ /// https://url.spec.whatwg.org/#shorten-a-urls-path
1213
+ fn shorten_path ( & mut self , scheme_type : SchemeType , path_start : usize ) {
1214
+ // If path is empty, then return.
1215
+ if self . serialization . len ( ) <= path_start {
1216
+ return ;
1217
+ }
1218
+ // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then return.
1219
+ let segments: Vec < & str > = self . serialization [ path_start..]
1220
+ . split ( '/' )
1221
+ . filter ( |s| !s. is_empty ( ) )
1222
+ . collect ( ) ;
1223
+ if scheme_type. is_file ( )
1224
+ && segments. len ( ) == 1
1225
+ && is_normalized_windows_drive_letter ( segments[ 0 ] )
1226
+ {
1227
+ return ;
1228
+ }
1229
+ // Remove path’s last item.
1230
+ self . pop_path ( scheme_type, path_start) ;
1231
+ }
1232
+
1198
1233
/// https://url.spec.whatwg.org/#pop-a-urls-path
1199
1234
fn pop_path ( & mut self , scheme_type : SchemeType , path_start : usize ) {
1200
1235
if self . serialization . len ( ) > path_start {
1201
1236
let slash_position = self . serialization [ path_start..] . rfind ( '/' ) . unwrap ( ) ;
1202
1237
// + 1 since rfind returns the position before the slash.
1203
1238
let segment_start = path_start + slash_position + 1 ;
1204
1239
// Don’t pop a Windows drive letter
1205
- // FIXME: *normalized* Windows drive letter
1206
1240
if !( scheme_type. is_file ( )
1207
- && is_windows_drive_letter ( & self . serialization [ segment_start..] ) )
1241
+ && is_normalized_windows_drive_letter ( & self . serialization [ segment_start..] ) )
1208
1242
{
1209
1243
self . serialization . truncate ( segment_start) ;
1210
1244
}
0 commit comments