Skip to content

Commit ef62e5a

Browse files
KhafraDevRafaelGSS
authored andcommitted
url: implement URL.canParse
PR-URL: #47179 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Debadree Chatterjee <debadree333@gmail.com>
1 parent 337123d commit ef62e5a

File tree

7 files changed

+122
-2
lines changed

7 files changed

+122
-2
lines changed

benchmark/url/whatwgurl-canParse.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
4+
const bench = common.createBenchmark(main, {
5+
type: Object.keys(common.urls),
6+
n: [25e6],
7+
});
8+
9+
function main({ type, n }) {
10+
bench.start();
11+
for (let i = 0; i < n; i += 1)
12+
URL.canParse(common.urls[type]);
13+
bench.end(n);
14+
}

doc/api/url.md

+21
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,27 @@ added: v16.7.0
662662
Removes the stored {Blob} identified by the given ID. Attempting to revoke a
663663
ID that isn't registered will silently fail.
664664

665+
#### `URL.canParse(input[, base])`
666+
667+
<!-- YAML
668+
added: REPLACEME
669+
-->
670+
671+
* `input` {string} The absolute or relative input URL to parse. If `input`
672+
is relative, then `base` is required. If `input` is absolute, the `base`
673+
is ignored. If `input` is not a string, it is [converted to a string][] first.
674+
* `base` {string} The base URL to resolve against if the `input` is not
675+
absolute. If `base` is not a string, it is [converted to a string][] first.
676+
* Returns: {boolean}
677+
678+
Checks if an `input` relative to the `base` can be parsed to a `URL`.
679+
680+
```js
681+
const isValid = URL.canParse('/foo', 'https://example.org/'); // true
682+
683+
const isNotValid = URL.canParse('/foo'); // false
684+
```
685+
665686
### Class: `URLSearchParams`
666687

667688
<!-- YAML

lib/internal/url.js

+17
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const {
8888
domainToASCII: _domainToASCII,
8989
domainToUnicode: _domainToUnicode,
9090
parse,
91+
canParse: _canParse,
9192
updateUrl,
9293
} = internalBinding('url');
9394

@@ -789,6 +790,16 @@ class URL {
789790
// If there's an error, it's ignored.
790791
}
791792
}
793+
794+
static canParse(url, base = undefined) {
795+
url = `${url}`;
796+
797+
if (base !== undefined) {
798+
base = `${base}`;
799+
}
800+
801+
return _canParse(url, base);
802+
}
792803
}
793804

794805
ObjectDefineProperties(URL.prototype, {
@@ -812,6 +823,12 @@ ObjectDefineProperties(URL.prototype, {
812823
ObjectDefineProperties(URL, {
813824
createObjectURL: kEnumerableProperty,
814825
revokeObjectURL: kEnumerableProperty,
826+
canParse: {
827+
__proto__: null,
828+
configurable: true,
829+
writable: true,
830+
enumerable: true,
831+
},
815832
});
816833

817834
// application/x-www-form-urlencoded parser

src/node_url.cc

+26
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,30 @@ void Parse(const FunctionCallbackInfo<Value>& args) {
9393
args.GetReturnValue().Set(true);
9494
}
9595

96+
void CanParse(const FunctionCallbackInfo<Value>& args) {
97+
CHECK_GE(args.Length(), 2);
98+
CHECK(args[0]->IsString()); // input
99+
// args[1] // base url
100+
101+
Environment* env = Environment::GetCurrent(args);
102+
HandleScope handle_scope(env->isolate());
103+
Context::Scope context_scope(env->context());
104+
105+
Utf8Value input(env->isolate(), args[0]);
106+
ada::result base;
107+
ada::url* base_pointer = nullptr;
108+
if (args[1]->IsString()) {
109+
base = ada::parse(Utf8Value(env->isolate(), args[1]).ToString());
110+
if (!base) {
111+
return args.GetReturnValue().Set(false);
112+
}
113+
base_pointer = &base.value();
114+
}
115+
ada::result out = ada::parse(input.ToStringView(), base_pointer);
116+
117+
args.GetReturnValue().Set(out.has_value());
118+
}
119+
96120
void DomainToASCII(const FunctionCallbackInfo<Value>& args) {
97121
Environment* env = Environment::GetCurrent(args);
98122
CHECK_GE(args.Length(), 1);
@@ -285,6 +309,7 @@ void Initialize(Local<Object> target,
285309
void* priv) {
286310
SetMethod(context, target, "parse", Parse);
287311
SetMethod(context, target, "updateUrl", UpdateUrl);
312+
SetMethodNoSideEffect(context, target, "canParse", CanParse);
288313
SetMethodNoSideEffect(context, target, "formatUrl", FormatUrl);
289314

290315
SetMethodNoSideEffect(context, target, "domainToASCII", DomainToASCII);
@@ -294,6 +319,7 @@ void Initialize(Local<Object> target,
294319

295320
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
296321
registry->Register(Parse);
322+
registry->Register(CanParse);
297323
registry->Register(UpdateUrl);
298324
registry->Register(FormatUrl);
299325

test/fixtures/wpt/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Last update:
2727
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
2828
- resources: https://github.com/web-platform-tests/wpt/tree/919874f84f/resources
2929
- streams: https://github.com/web-platform-tests/wpt/tree/51750bc8d7/streams
30-
- url: https://github.com/web-platform-tests/wpt/tree/84caeb6fbd/url
30+
- url: https://github.com/web-platform-tests/wpt/tree/7c5c3cc125/url
3131
- user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing
3232
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/d8dbe6990b/wasm/jsapi
3333
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// This intentionally does not use resources/urltestdata.json to preserve resources.
2+
[
3+
{
4+
"url": undefined,
5+
"base": undefined,
6+
"expected": false
7+
},
8+
{
9+
"url": "a:b",
10+
"base": undefined,
11+
"expected": true
12+
},
13+
{
14+
"url": undefined,
15+
"base": "a:b",
16+
"expected": false
17+
},
18+
{
19+
"url": "a:/b",
20+
"base": undefined,
21+
"expected": true
22+
},
23+
{
24+
"url": undefined,
25+
"base": "a:/b",
26+
"expected": true
27+
},
28+
{
29+
"url": "https://test:test",
30+
"base": undefined,
31+
"expected": false
32+
},
33+
{
34+
"url": "a",
35+
"base": "https://b/",
36+
"expected": true
37+
}
38+
].forEach(({ url, base, expected }) => {
39+
test(() => {
40+
assert_equals(URL.canParse(url, base), expected);
41+
}, `URL.canParse(${url}, ${base})`);
42+
});

test/fixtures/wpt/versions.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"path": "streams"
6969
},
7070
"url": {
71-
"commit": "84caeb6fbdf45129f57c67448e6113ee1ced9fb3",
71+
"commit": "7c5c3cc125979b4768d414471e6ab655b473aae8",
7272
"path": "url"
7373
},
7474
"user-timing": {

0 commit comments

Comments
 (0)