diff --git a/dom/url/URL.cpp b/dom/url/URL.cpp index 0681fd8ee6..90f4594e1e 100644 --- a/dom/url/URL.cpp +++ b/dom/url/URL.cpp @@ -1789,17 +1789,26 @@ URL::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL, return URLWorker::IsValidURL(aGlobal, aURL, aRv); } -bool -URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL, - const Optional& aBase) { +already_AddRefed +URL::Parse(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase) +{ ErrorResult rv; RefPtr parsed = URL::Constructor(aGlobal, aURL, aBase, rv); if (rv.Failed() || !parsed) { rv.SuppressException(); - return false; + return nullptr; } - return true; + return parsed.forget(); +} + +bool +URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase) +{ + RefPtr parsed = URL::Parse(aGlobal, aURL, aBase); + return !!parsed; } already_AddRefed diff --git a/dom/url/URL.h b/dom/url/URL.h index 8db4cd5154..aa11dbbe72 100644 --- a/dom/url/URL.h +++ b/dom/url/URL.h @@ -88,6 +88,10 @@ public: IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL, ErrorResult& aRv); + static already_AddRefed + Parse(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase); + static bool CanParse(const GlobalObject& aGlobal, const nsAString& aURL, const Optional& aBase); diff --git a/dom/url/tests/test_url.html b/dom/url/tests/test_url.html index a637efff25..31a0c31ee0 100644 --- a/dom/url/tests/test_url.html +++ b/dom/url/tests/test_url.html @@ -448,6 +448,34 @@ !URL.canParse("/static/client/runtime.js", "mailto:test@example.com"), "URL.canParse rejects root-relative input against opaque mailto: base", ); + ok("parse" in URL, "URL.parse exists"); + is(URL.parse.length, 1, "URL.parse has one required argument"); + var parsed = URL.parse("/static/client/runtime.js", "x:/"); + ok(parsed instanceof URL, "URL.parse returns a URL object on success"); + is(parsed.href, "x:/static/client/runtime.js", "URL.parse resolves against a base"); + is(URL.parse("/static/client/runtime.js"), null, "URL.parse returns null for invalid input"); + is( + URL.parse("/static/client/runtime.js", "mailto:test@example.com"), + null, + "URL.parse returns null for an invalid base/input combination", + ); + + var conversionOrder = []; + parsed = URL.parse( + { toString: function() { conversionOrder.push("url"); return "child"; } }, + { toString: function() { conversionOrder.push("base"); return "https://example.com/root/"; } }, + ); + is(conversionOrder.join(","), "url,base", "URL.parse converts its arguments in order"); + is(parsed.href, "https://example.com/root/child", "URL.parse uses converted arguments"); + + var sentinel = {}; + var thrown; + try { + URL.parse({ toString: function() { throw sentinel; } }); + } catch (e) { + thrown = e; + } + is(thrown, sentinel, "URL.parse propagates argument conversion exceptions"); SimpleTest.doesThrow(() => { new URL("/static/client/runtime.js", "mailto:test@example.com"); }, "constructor rejects root-relative input against opaque mailto: base"); diff --git a/dom/url/tests/urlApi_worker.js b/dom/url/tests/urlApi_worker.js index bc26d8a0a9..e5247aed8c 100644 --- a/dom/url/tests/urlApi_worker.js +++ b/dom/url/tests/urlApi_worker.js @@ -301,6 +301,64 @@ onmessage = function () { !URL.canParse("/static/client/runtime.js", "mailto:test@example.com"), "URL.canParse rejects root-relative input against opaque mailto: base", ); + ok("parse" in URL, "URL.parse exists"); + is(URL.parse.length, 1, "URL.parse has one required argument"); + let parsed = URL.parse("/static/client/runtime.js", "x:/"); + ok(parsed instanceof URL, "URL.parse returns a URL object on success"); + is( + parsed.href, + "x:/static/client/runtime.js", + "URL.parse resolves against a base", + ); + is( + URL.parse("/static/client/runtime.js"), + null, + "URL.parse returns null for invalid input", + ); + is( + URL.parse("/static/client/runtime.js", "mailto:test@example.com"), + null, + "URL.parse returns null for an invalid base/input combination", + ); + + const conversionOrder = []; + parsed = URL.parse( + { + toString() { + conversionOrder.push("url"); + return "child"; + }, + }, + { + toString() { + conversionOrder.push("base"); + return "https://example.com/root/"; + }, + }, + ); + is( + conversionOrder.join(","), + "url,base", + "URL.parse converts its arguments in order", + ); + is( + parsed.href, + "https://example.com/root/child", + "URL.parse uses converted arguments", + ); + + const sentinel = {}; + let thrown; + try { + URL.parse({ + toString() { + throw sentinel; + }, + }); + } catch (e) { + thrown = e; + } + is(thrown, sentinel, "URL.parse propagates argument conversion exceptions"); let mailtoRelativeFailed = false; try { new URL("/static/client/runtime.js", "mailto:test@example.com"); diff --git a/dom/webidl/URL.webidl b/dom/webidl/URL.webidl index 11e3c42772..5589e2ad17 100644 --- a/dom/webidl/URL.webidl +++ b/dom/webidl/URL.webidl @@ -23,6 +23,7 @@ interface URL { [Throws] stringifier; + static URL? parse(USVString url, optional USVString base); static boolean canParse(USVString url, optional USVString base); [Throws]