From bb62f4ad9e10007734c737c85df718eafa626503 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Mon, 5 Oct 2020 19:22:44 +0900 Subject: [PATCH] url: file URL path normalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/whatwg/url/pull/544 Refs: https://github.com/web-platform-tests/wpt/pull/25716 PR-URL: https://github.com/nodejs/node/pull/35477 Fixes: https://github.com/nodejs/node/issues/35429 Reviewed-By: Guy Bedford Reviewed-By: James M Snell Reviewed-By: David Carlier Reviewed-By: Bradley Farias Reviewed-By: Michaƫl Zasso --- lib/internal/bootstrap/pre_execution.js | 2 +- src/inspector_agent.cc | 2 +- src/node_url.cc | 52 ++-- test/cctest/test_url.cc | 6 +- test/fixtures/wpt/README.md | 2 +- .../wpt/url/resources/setters_tests.json | 12 +- .../wpt/url/resources/urltestdata.json | 237 ++++++++++++++---- test/fixtures/wpt/versions.json | 2 +- 8 files changed, 214 insertions(+), 101 deletions(-) diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 005b1b46c28227..e9700fc1f4639d 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -362,7 +362,7 @@ function initializePolicy() { // no bare specifiers for now let manifestURL; if (require('path').isAbsolute(experimentalPolicy)) { - manifestURL = new URL(`file:///${experimentalPolicy}`); + manifestURL = new URL(experimentalPolicy, 'file://'); } else { const cwdURL = pathToFileURL(process.cwd()); cwdURL.pathname += '/'; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 4b7997a02fdc30..c54b2ab17fcb6e 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -640,7 +640,7 @@ class NodeInspectorClient : public V8InspectorClient { node::url::URL url = node::url::URL::FromFilePath(resource_name); // TODO(ak239spb): replace this code with url.href(). // Refs: https://github.com/nodejs/node/issues/22610 - return Utf8ToStringView(url.protocol() + "//" + url.path()); + return Utf8ToStringView(url.protocol() + "/" + url.path()); } node::Environment* env_; diff --git a/src/node_url.cc b/src/node_url.cc index b7aeb8b006284f..466f3c689a51f8 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -1904,16 +1904,19 @@ void URL::Parse(const char* input, state = kFragment; break; default: + url->query.clear(); + if (base->flags & URL_FLAGS_HAS_HOST) { + url->flags |= URL_FLAGS_HAS_HOST; + url->host = base->host; + } + if (base->flags & URL_FLAGS_HAS_PATH) { + url->flags |= URL_FLAGS_HAS_PATH; + url->path = base->path; + } if (!StartsWithWindowsDriveLetter(p, end)) { - if (base->flags & URL_FLAGS_HAS_HOST) { - url->flags |= URL_FLAGS_HAS_HOST; - url->host = base->host; - } - if (base->flags & URL_FLAGS_HAS_PATH) { - url->flags |= URL_FLAGS_HAS_PATH; - url->path = base->path; - } ShortenUrlPath(url); + } else { + url->path.clear(); } state = kPath; continue; @@ -1927,20 +1930,13 @@ void URL::Parse(const char* input, if (ch == '/' || ch == '\\') { state = kFileHost; } else { - if (has_base && - base->scheme == "file:" && - !StartsWithWindowsDriveLetter(p, end)) { - if (IsNormalizedWindowsDriveLetter(base->path[0])) { + if (has_base && base->scheme == "file:") { + url->flags |= URL_FLAGS_HAS_HOST; + url->host = base->host; + if (!StartsWithWindowsDriveLetter(p, end) && + IsNormalizedWindowsDriveLetter(base->path[0])) { url->flags |= URL_FLAGS_HAS_PATH; url->path.push_back(base->path[0]); - } else { - if (base->flags & URL_FLAGS_HAS_HOST) { - url->flags |= URL_FLAGS_HAS_HOST; - url->host = base->host; - } else { - url->flags &= ~URL_FLAGS_HAS_HOST; - url->host.clear(); - } } } state = kPath; @@ -2024,29 +2020,19 @@ void URL::Parse(const char* input, url->path.empty() && buffer.size() == 2 && IsWindowsDriveLetter(buffer)) { - if ((url->flags & URL_FLAGS_HAS_HOST) && - !url->host.empty()) { - url->host.clear(); - url->flags |= URL_FLAGS_HAS_HOST; - } buffer[1] = ':'; } url->flags |= URL_FLAGS_HAS_PATH; url->path.emplace_back(std::move(buffer)); } buffer.clear(); - if (url->scheme == "file:" && - (ch == kEOL || - ch == '?' || - ch == '#')) { - while (url->path.size() > 1 && url->path[0].empty()) { - url->path.erase(url->path.begin()); - } - } if (ch == '?') { url->flags |= URL_FLAGS_HAS_QUERY; + url->query.clear(); state = kQuery; } else if (ch == '#') { + url->flags |= URL_FLAGS_HAS_FRAGMENT; + url->fragment.clear(); state = kFragment; } } else { diff --git a/test/cctest/test_url.cc b/test/cctest/test_url.cc index 2e78b24a5e3424..806fe1a4d2a600 100644 --- a/test/cctest/test_url.cc +++ b/test/cctest/test_url.cc @@ -148,14 +148,14 @@ TEST_F(URLTest, FromFilePath) { #else file_url = URL::FromFilePath("/"); EXPECT_EQ("file:", file_url.protocol()); - EXPECT_EQ("/", file_url.path()); + EXPECT_EQ("//", file_url.path()); file_url = URL::FromFilePath("/a/b/c"); EXPECT_EQ("file:", file_url.protocol()); - EXPECT_EQ("/a/b/c", file_url.path()); + EXPECT_EQ("//a/b/c", file_url.path()); file_url = URL::FromFilePath("/a/%%.js"); EXPECT_EQ("file:", file_url.protocol()); - EXPECT_EQ("/a/%25%25.js", file_url.path()); + EXPECT_EQ("//a/%25%25.js", file_url.path()); #endif } diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 9f6f20f5f47e89..d5ddd9b8c83f84 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -12,7 +12,7 @@ Last update: - console: https://github.com/web-platform-tests/wpt/tree/3b1f72e99a/console - encoding: https://github.com/web-platform-tests/wpt/tree/d7f9e16c9a/encoding -- url: https://github.com/web-platform-tests/wpt/tree/e2ddf48b78/url +- url: https://github.com/web-platform-tests/wpt/tree/050308a616/url - resources: https://github.com/web-platform-tests/wpt/tree/1d14e821b9/resources - interfaces: https://github.com/web-platform-tests/wpt/tree/15e47f779c/interfaces - html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing diff --git a/test/fixtures/wpt/url/resources/setters_tests.json b/test/fixtures/wpt/url/resources/setters_tests.json index d06fb2cfe629b8..8aa74d6b8a28d9 100644 --- a/test/fixtures/wpt/url/resources/setters_tests.json +++ b/test/fixtures/wpt/url/resources/setters_tests.json @@ -1672,8 +1672,8 @@ "href": "file://monkey/", "new_value": "\\\\", "expected": { - "href": "file://monkey/", - "pathname": "/" + "href": "file://monkey//", + "pathname": "//" } }, { @@ -1681,8 +1681,8 @@ "href": "file:///unicorn", "new_value": "//\\/", "expected": { - "href": "file:///", - "pathname": "/" + "href": "file://////", + "pathname": "////" } }, { @@ -1690,8 +1690,8 @@ "href": "file:///unicorn", "new_value": "//monkey/..//", "expected": { - "href": "file:///", - "pathname": "/" + "href": "file://///", + "pathname": "///" } }, { diff --git a/test/fixtures/wpt/url/resources/urltestdata.json b/test/fixtures/wpt/url/resources/urltestdata.json index 76a8b3a015dcbc..dba7237d88a682 100644 --- a/test/fixtures/wpt/url/resources/urltestdata.json +++ b/test/fixtures/wpt/url/resources/urltestdata.json @@ -5389,84 +5389,84 @@ { "input": "file:\\\\//", "base": "about:blank", - "href": "file:///", + "href": "file:////", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/", + "pathname": "//", "search": "", "hash": "" }, { "input": "file:\\\\\\\\", "base": "about:blank", - "href": "file:///", + "href": "file:////", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/", + "pathname": "//", "search": "", "hash": "" }, { "input": "file:\\\\\\\\?fox", "base": "about:blank", - "href": "file:///?fox", + "href": "file:////?fox", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/", + "pathname": "//", "search": "?fox", "hash": "" }, { "input": "file:\\\\\\\\#guppy", "base": "about:blank", - "href": "file:///#guppy", + "href": "file:////#guppy", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/", + "pathname": "//", "search": "", "hash": "#guppy" }, { "input": "file://spider///", "base": "about:blank", - "href": "file://spider/", + "href": "file://spider///", "protocol": "file:", "username": "", "password": "", "host": "spider", "hostname": "spider", "port": "", - "pathname": "/", + "pathname": "///", "search": "", "hash": "" }, { "input": "file:\\\\localhost//", "base": "about:blank", - "href": "file:///", + "href": "file:////", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/", + "pathname": "//", "search": "", "hash": "" }, @@ -5487,42 +5487,42 @@ { "input": "file://\\/localhost//cat", "base": "about:blank", - "href": "file:///localhost//cat", + "href": "file:////localhost//cat", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/localhost//cat", + "pathname": "//localhost//cat", "search": "", "hash": "" }, { "input": "file://localhost//a//../..//", "base": "about:blank", - "href": "file:///", + "href": "file://///", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/", + "pathname": "///", "search": "", "hash": "" }, { "input": "/////mouse", "base": "file:///elephant", - "href": "file:///mouse", + "href": "file://///mouse", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/mouse", + "pathname": "///mouse", "search": "", "hash": "" }, @@ -5543,42 +5543,42 @@ { "input": "\\/localhost//pig", "base": "file://lion/", - "href": "file:///pig", + "href": "file:////pig", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/pig", + "pathname": "//pig", "search": "", "hash": "" }, { "input": "//localhost//pig", "base": "file://lion/", - "href": "file:///pig", + "href": "file:////pig", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/pig", + "pathname": "//pig", "search": "", "hash": "" }, { "input": "/..//localhost//pig", "base": "file://lion/", - "href": "file://lion/localhost//pig", + "href": "file://lion//localhost//pig", "protocol": "file:", "username": "", "password": "", "host": "lion", "hostname": "lion", "port": "", - "pathname": "/localhost//pig", + "pathname": "//localhost//pig", "search": "", "hash": "" }, @@ -5629,12 +5629,12 @@ { "input": "C|", "base": "file://host/dir/file", - "href": "file:///C:", + "href": "file://host/C:", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/C:", "search": "", @@ -5643,12 +5643,12 @@ { "input": "C|#", "base": "file://host/dir/file", - "href": "file:///C:#", + "href": "file://host/C:#", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/C:", "search": "", @@ -5657,12 +5657,12 @@ { "input": "C|?", "base": "file://host/dir/file", - "href": "file:///C:?", + "href": "file://host/C:?", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/C:", "search": "", @@ -5671,12 +5671,12 @@ { "input": "C|/", "base": "file://host/dir/file", - "href": "file:///C:/", + "href": "file://host/C:/", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/C:/", "search": "", @@ -5685,12 +5685,12 @@ { "input": "C|\n/", "base": "file://host/dir/file", - "href": "file:///C:/", + "href": "file://host/C:/", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/C:/", "search": "", @@ -5699,12 +5699,12 @@ { "input": "C|\\", "base": "file://host/dir/file", - "href": "file:///C:/", + "href": "file://host/C:/", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/C:/", "search": "", @@ -5784,27 +5784,27 @@ { "input": "/c:/foo/bar", "base": "file://host/path", - "href": "file:///c:/foo/bar", + "href": "file://host/c:/foo/bar", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "host", + "hostname": "host", "port": "", "pathname": "/c:/foo/bar", "search": "", "hash": "" }, - "# Windows drive letter quirk with not empty host", + "# Do not drop the host in the presence of a drive letter", { "input": "file://example.net/C:/", "base": "about:blank", - "href": "file:///C:/", + "href": "file://example.net/C:/", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "example.net", + "hostname": "example.net", "port": "", "pathname": "/C:/", "search": "", @@ -5813,12 +5813,12 @@ { "input": "file://1.2.3.4/C:/", "base": "about:blank", - "href": "file:///C:/", + "href": "file://1.2.3.4/C:/", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "1.2.3.4", + "hostname": "1.2.3.4", "port": "", "pathname": "/C:/", "search": "", @@ -5827,12 +5827,12 @@ { "input": "file://[1::8]/C:/", "base": "about:blank", - "href": "file:///C:/", + "href": "file://[1::8]/C:/", "protocol": "file:", "username": "", "password": "", - "host": "", - "hostname": "", + "host": "[1::8]", + "hostname": "[1::8]", "port": "", "pathname": "/C:/", "search": "", @@ -6034,6 +6034,133 @@ "base": "about:blank", "failure": true }, + "# Additional file URL tetsts for (https://github.com/whatwg/url/issues/405)", + { + "input": "file://localhost//a//../..//foo", + "base": "about:blank", + "href": "file://///foo", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "///foo", + "search": "", + "hash": "" + }, + { + "input": "file://localhost////foo", + "base": "about:blank", + "href": "file://////foo", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "////foo", + "search": "", + "hash": "" + }, + { + "input": "file:////foo", + "base": "about:blank", + "href": "file:////foo", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//foo", + "search": "", + "hash": "" + }, + { + "input": "file:///one/two", + "base": "file:///", + "href": "file:///one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/one/two", + "search": "", + "hash": "" + }, + { + "input": "file:////one/two", + "base": "file:///", + "href": "file:////one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//one/two", + "search": "", + "hash": "" + }, + { + "input": "//one/two", + "base": "file:///", + "href": "file://one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "one", + "hostname": "one", + "port": "", + "pathname": "/two", + "search": "", + "hash": "" + }, + { + "input": "///one/two", + "base": "file:///", + "href": "file:///one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/one/two", + "search": "", + "hash": "" + }, + { + "input": "////one/two", + "base": "file:///", + "href": "file:////one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//one/two", + "search": "", + "hash": "" + }, + { + "input": "file:///.//", + "base": "file:////", + "href": "file:////", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, "# IPv6 tests", { "input": "http://[1:0::]", diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index d1d47d6d27caac..1977524052eea4 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -8,7 +8,7 @@ "path": "encoding" }, "url": { - "commit": "e2ddf48b78209d0aef4fa513b53a9f28243c9335", + "commit": "050308a616a8388f1ad5d6e87eac0270fd35023f", "path": "url" }, "resources": {