Skip to content

Commit ed718fb

Browse files
committed
Add a shorten_path helper.
1 parent 0d961c4 commit ed718fb

File tree

1 file changed

+29
-9
lines changed

1 file changed

+29
-9
lines changed

src/parser.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ impl<'a> Parser<'a> {
620620
(Some(i), _) | (None, Some(i)) => base_url.slice(..i),
621621
};
622622
self.serialization.push_str(before_query);
623-
self.pop_path(SchemeType::File, base_url.path_start as usize);
623+
self.shorten_path(SchemeType::File, base_url.path_start as usize);
624624
let remaining = self.parse_path(
625625
SchemeType::File,
626626
&mut true,
@@ -1139,15 +1139,15 @@ impl<'a> Parser<'a> {
11391139
".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e"
11401140
| ".%2E" => {
11411141
debug_assert!(self.serialization.as_bytes()[segment_start - 1] == b'/');
1142-
// We dont want to truncate beyond the path start:
1143-
if segment_start - 1 > path_start {
1144-
self.serialization.truncate(segment_start - 1); // Truncate "/.."
1142+
self.serialization.truncate(segment_start);
1143+
// Do not remove the root slash
1144+
if self.serialization.ends_with("/") && path_start + 1 < segment_start {
1145+
self.serialization.pop();
1146+
self.shorten_path(scheme_type, path_start);
11451147
} else {
1146-
self.serialization.truncate(segment_start); // Truncate ".."
1148+
self.shorten_path(scheme_type, path_start);
11471149
}
11481150

1149-
self.pop_path(scheme_type, path_start);
1150-
11511151
// 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.
11521152
if ends_with_slash && !self.serialization.ends_with("/") {
11531153
self.serialization.push('/');
@@ -1191,16 +1191,36 @@ impl<'a> Parser<'a> {
11911191
input
11921192
}
11931193

1194+
/// https://url.spec.whatwg.org/#shorten-a-urls-path
1195+
fn shorten_path(&mut self, scheme_type: SchemeType, path_start: usize) {
1196+
// If path is empty, then return.
1197+
if self.serialization.len() <= path_start {
1198+
return;
1199+
}
1200+
// If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then return.
1201+
let segments: Vec<&str> = self.serialization[path_start..]
1202+
.split('/')
1203+
.filter(|s| !s.is_empty())
1204+
.collect();
1205+
if scheme_type.is_file()
1206+
&& segments.len() == 1
1207+
&& is_normalized_windows_drive_letter(segments[0])
1208+
{
1209+
return;
1210+
}
1211+
// Remove path’s last item.
1212+
self.pop_path(scheme_type, path_start);
1213+
}
1214+
11941215
/// https://url.spec.whatwg.org/#pop-a-urls-path
11951216
fn pop_path(&mut self, scheme_type: SchemeType, path_start: usize) {
11961217
if self.serialization.len() > path_start {
11971218
let slash_position = self.serialization[path_start..].rfind('/').unwrap();
11981219
// + 1 since rfind returns the position before the slash.
11991220
let segment_start = path_start + slash_position + 1;
12001221
// Don’t pop a Windows drive letter
1201-
// FIXME: *normalized* Windows drive letter
12021222
if !(scheme_type.is_file()
1203-
&& is_windows_drive_letter(&self.serialization[segment_start..]))
1223+
&& is_normalized_windows_drive_letter(&self.serialization[segment_start..]))
12041224
{
12051225
self.serialization.truncate(segment_start);
12061226
}

0 commit comments

Comments
 (0)