From a8971f87d3573ac247110e6afde0dc475fe21264 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Thu, 17 Sep 2020 01:10:28 +0900 Subject: [PATCH] url: support non-special URLs Fixes: https://github.com/nodejs/node/issues/34899 Refs: https://github.com/whatwg/url/pull/505 Refs: https://github.com/web-platform-tests/wpt/pull/25113 PR-URL: https://github.com/nodejs/node/pull/34925 Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: James M Snell --- lib/internal/url.js | 13 +- test/fixtures/wpt/README.md | 4 +- test/fixtures/wpt/interfaces/encoding.idl | 5 - test/fixtures/wpt/interfaces/html.idl | 2 +- test/fixtures/wpt/resources/test-only-api.js | 30 ++- .../wpt/url/resources/setters_tests.json | 55 ++++++ .../wpt/url/resources/urltestdata.json | 184 ++++++++++++++++++ test/fixtures/wpt/versions.json | 6 +- 8 files changed, 279 insertions(+), 20 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 6c376d1b32a00f..f56e006e90a63c 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -401,6 +401,7 @@ ObjectDefineProperties(URL.prototype, { ...options }; const ctx = this[context]; + // https://url.spec.whatwg.org/#url-serializing let ret = ctx.scheme; if (ctx.host !== null) { ret += '//'; @@ -420,8 +421,16 @@ ObjectDefineProperties(URL.prototype, { } else if (ctx.scheme === 'file:') { ret += '//'; } - if (this.pathname) - ret += this.pathname; + if (this[cannotBeBase]) { + ret += ctx.path[0]; + } else { + if (ctx.host === null && ctx.path.length > 1 && ctx.path[0] === '') { + ret += '/.'; + } + for (const segment of ctx.path) { + ret += '/' + segment; + } + } if (options.search && ctx.query !== null) ret += `?${ctx.query}`; if (options.fragment && ctx.fragment !== null) diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index b62220fd1b653c..94abe549018209 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -13,8 +13,8 @@ Last update: - console: https://github.com/web-platform-tests/wpt/tree/3b1f72e99a/console - encoding: https://github.com/web-platform-tests/wpt/tree/11e6941923/encoding - url: https://github.com/web-platform-tests/wpt/tree/551c9d604f/url -- resources: https://github.com/web-platform-tests/wpt/tree/55e9dc7f5e/resources -- interfaces: https://github.com/web-platform-tests/wpt/tree/4471cda31b/interfaces +- 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 - html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/264f12bc7b/html/webappapis/timers - hr-time: https://github.com/web-platform-tests/wpt/tree/a5d1774ecf/hr-time diff --git a/test/fixtures/wpt/interfaces/encoding.idl b/test/fixtures/wpt/interfaces/encoding.idl index bae48f11993e5f..7585cb3717c9aa 100644 --- a/test/fixtures/wpt/interfaces/encoding.idl +++ b/test/fixtures/wpt/interfaces/encoding.idl @@ -44,11 +44,6 @@ interface TextEncoder { }; TextEncoder includes TextEncoderCommon; -interface mixin GenericTransformStream { - readonly attribute ReadableStream readable; - readonly attribute WritableStream writable; -}; - [Exposed=(Window,Worker)] interface TextDecoderStream { constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options = {}); diff --git a/test/fixtures/wpt/interfaces/html.idl b/test/fixtures/wpt/interfaces/html.idl index a1f9ec99afe57e..e92bb8690dddd3 100644 --- a/test/fixtures/wpt/interfaces/html.idl +++ b/test/fixtures/wpt/interfaces/html.idl @@ -1552,7 +1552,7 @@ OffscreenCanvasRenderingContext2D includes CanvasPath; interface CustomElementRegistry { [CEReactions] undefined define(DOMString name, CustomElementConstructor constructor, optional ElementDefinitionOptions options = {}); (CustomElementConstructor or undefined) get(DOMString name); - Promise whenDefined(DOMString name); + Promise whenDefined(DOMString name); [CEReactions] undefined upgrade(Node root); }; diff --git a/test/fixtures/wpt/resources/test-only-api.js b/test/fixtures/wpt/resources/test-only-api.js index 3fdf1cea6eb137..ef66e0e733f9c6 100644 --- a/test/fixtures/wpt/resources/test-only-api.js +++ b/test/fixtures/wpt/resources/test-only-api.js @@ -47,11 +47,14 @@ function loadScript(path) { * Only call this function if isChromiumBased === true. * * @param {Array.} resources - A list of scripts to load: Mojo JS - * bindings should be of the form '/gen/../*.mojom.js', the ordering of which - * does not matter. Do not include mojo_bindings.js in this list. + * bindings should be of the form '/gen/../*.mojom.js' or + * '/gen/../*.mojom-lite.js' (requires `lite` to be true); the order does not + * matter. Do not include 'mojo_bindings.js' or 'mojo_bindings_lite.js'. + * @param {boolean=} lite - Whether the lite bindings (*.mojom-lite.js) are used + * (default is false). * @returns {Promise} */ -async function loadMojoResources(resources) { +async function loadMojoResources(resources, lite = false) { if (!isChromiumBased) { throw new Error('MojoJS not enabled; start Chrome with --enable-blink-features=MojoJS,MojoJSTest'); } @@ -70,13 +73,26 @@ async function loadMojoResources(resources) { if (path.endsWith('/mojo_bindings.js')) { throw new Error('Do not load mojo_bindings.js explicitly.'); } - if (! /^\/gen\/.*\.mojom\.js$/.test(path)) { - throw new Error(`Unrecognized resource path: ${path}`); + if (path.endsWith('/mojo_bindings_lite.js')) { + throw new Error('Do not load mojo_bindings_lite.js explicitly.'); + } + if (lite) { + if (! /^\/gen\/.*\.mojom-lite\.js$/.test(path)) { + throw new Error(`Unrecognized resource path: ${path}`); + } + } else { + if (! /^\/gen\/.*\.mojom\.js$/.test(path)) { + throw new Error(`Unrecognized resource path: ${path}`); + } } } - await loadScript(genPrefix + '/gen/layout_test_data/mojo/public/js/mojo_bindings.js'); - mojo.config.autoLoadMojomDeps = false; + if (lite) { + await loadScript(genPrefix + '/gen/layout_test_data/mojo/public/js/mojo_bindings_lite.js'); + } else { + await loadScript(genPrefix + '/gen/layout_test_data/mojo/public/js/mojo_bindings.js'); + mojo.config.autoLoadMojomDeps = false; + } for (const path of resources) { await loadScript(genPrefix + path); diff --git a/test/fixtures/wpt/url/resources/setters_tests.json b/test/fixtures/wpt/url/resources/setters_tests.json index db217da23d559b..d06fb2cfe629b8 100644 --- a/test/fixtures/wpt/url/resources/setters_tests.json +++ b/test/fixtures/wpt/url/resources/setters_tests.json @@ -1324,6 +1324,27 @@ "hostname": "test", "port": "12" } + }, + { + "comment": "Drop /. from path", + "href": "non-spec:/.//p", + "new_value": "h", + "expected": { + "href": "non-spec://h//p", + "host": "h", + "hostname": "h", + "pathname": "//p" + } + }, + { + "href": "non-spec:/.//p", + "new_value": "", + "expected": { + "href": "non-spec:////p", + "host": "", + "hostname": "", + "pathname": "//p" + } } ], "port": [ @@ -1672,6 +1693,40 @@ "href": "file:///", "pathname": "/" } + }, + { + "comment": "Serialize /. in path", + "href": "non-spec:/", + "new_value": "/.//p", + "expected": { + "href": "non-spec:/.//p", + "pathname": "//p" + } + }, + { + "href": "non-spec:/", + "new_value": "/..//p", + "expected": { + "href": "non-spec:/.//p", + "pathname": "//p" + } + }, + { + "href": "non-spec:/", + "new_value": "//p", + "expected": { + "href": "non-spec:/.//p", + "pathname": "//p" + } + }, + { + "comment": "Drop /. from path", + "href": "non-spec:/.//", + "new_value": "p", + "expected": { + "href": "non-spec:/p", + "pathname": "/p" + } } ], "search": [ diff --git a/test/fixtures/wpt/url/resources/urltestdata.json b/test/fixtures/wpt/url/resources/urltestdata.json index bc79426526c1a5..76a8b3a015dcbc 100644 --- a/test/fixtures/wpt/url/resources/urltestdata.json +++ b/test/fixtures/wpt/url/resources/urltestdata.json @@ -6445,6 +6445,190 @@ "search": "", "hash": "" }, + "Serialize /. in path", + { + "input": "non-spec:/.//", + "base": "about:blank", + "href": "non-spec:/.//", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/..//", + "base": "about:blank", + "href": "non-spec:/.//", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/a/..//", + "base": "about:blank", + "href": "non-spec:/.//", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/.//path", + "base": "about:blank", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/..//path", + "base": "about:blank", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/a/..//path", + "base": "about:blank", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "/.//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "/..//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "..//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "a/..//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "", + "base": "non-spec:/..//p", + "href": "non-spec:/.//p", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//p", + "search": "", + "hash": "" + }, + { + "input": "path", + "base": "non-spec:/..//p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + "Do not serialize /. in path", + { + "input": "../path", + "base": "non-spec:/.//p", + "href": "non-spec:/path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/path", + "search": "", + "hash": "" + }, "# percent encoded hosts in non-special-URLs", { "input": "non-special://%E2%80%A0/", diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 3b519be155123a..27aa340f4c45c9 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -8,15 +8,15 @@ "path": "encoding" }, "url": { - "commit": "6c74b7e43c9a1f6dc3dc529e427e2ef96152409e", + "commit": "551c9d604fb8b97d3f8c65793bb047d15baddbc2", "path": "url" }, "resources": { - "commit": "55e9dc7f5e74bb5bd189920ef348169f795ab71c", + "commit": "1d14e821b9586f250e6a31d550504e3d16a05ae7", "path": "resources" }, "interfaces": { - "commit": "4471cda31be9e299ba0546e115f84d5010ed8136", + "commit": "15e47f779cf61555669b0f67e2c49b9c830b9019", "path": "interfaces" }, "html/webappapis/microtask-queuing": {