Skip to content

Commit 11f48e0

Browse files
KhafraDevdanielleadams
authored andcommittedJul 12, 2023
url: implement URL.canParse
PR-URL: #47179 Backport-PR-URL: #48345 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Debadree Chatterjee <[email protected]>
1 parent 1f2c91f commit 11f48e0

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
@@ -91,6 +91,7 @@ const {
9191
domainToASCII: _domainToASCII,
9292
domainToUnicode: _domainToUnicode,
9393
parse,
94+
canParse: _canParse,
9495
updateUrl,
9596
} = internalBinding('url');
9697

@@ -793,6 +794,16 @@ class URL {
793794
return this[context].href;
794795
}
795796

797+
static canParse(url, base = undefined) {
798+
url = `${url}`;
799+
800+
if (base !== undefined) {
801+
base = `${base}`;
802+
}
803+
804+
return _canParse(url, base);
805+
}
806+
796807
static createObjectURL(obj) {
797808
const cryptoRandom = lazyCryptoRandom();
798809
if (cryptoRandom === undefined)
@@ -842,6 +853,12 @@ ObjectDefineProperties(URL.prototype, {
842853
});
843854

844855
ObjectDefineProperties(URL, {
856+
canParse: {
857+
__proto__: null,
858+
configurable: true,
859+
writable: true,
860+
enumerable: true,
861+
},
845862
createObjectURL: kEnumerableProperty,
846863
revokeObjectURL: kEnumerableProperty,
847864
});

‎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
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
2828
- resources: https://github.com/web-platform-tests/wpt/tree/fbf1e7d247/resources
2929
- streams: https://github.com/web-platform-tests/wpt/tree/9e5ef42bd3/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/cde25e7e3c/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)
Please sign in to comment.