Skip to content

Commit

Permalink
url: implement URL.canParse
Browse files Browse the repository at this point in the history
PR-URL: #47179
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Debadree Chatterjee <debadree333@gmail.com>
  • Loading branch information
KhafraDev authored and RafaelGSS committed Apr 7, 2023
1 parent 337123d commit ef62e5a
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 2 deletions.
14 changes: 14 additions & 0 deletions benchmark/url/whatwgurl-canParse.js
@@ -0,0 +1,14 @@
'use strict';
const common = require('../common.js');

const bench = common.createBenchmark(main, {
type: Object.keys(common.urls),
n: [25e6],
});

function main({ type, n }) {
bench.start();
for (let i = 0; i < n; i += 1)
URL.canParse(common.urls[type]);
bench.end(n);
}
21 changes: 21 additions & 0 deletions doc/api/url.md
Expand Up @@ -662,6 +662,27 @@ added: v16.7.0
Removes the stored {Blob} identified by the given ID. Attempting to revoke a
ID that isn't registered will silently fail.

#### `URL.canParse(input[, base])`

<!-- YAML
added: REPLACEME
-->

* `input` {string} The absolute or relative input URL to parse. If `input`
is relative, then `base` is required. If `input` is absolute, the `base`
is ignored. If `input` is not a string, it is [converted to a string][] first.
* `base` {string} The base URL to resolve against if the `input` is not
absolute. If `base` is not a string, it is [converted to a string][] first.
* Returns: {boolean}

Checks if an `input` relative to the `base` can be parsed to a `URL`.

```js
const isValid = URL.canParse('/foo', 'https://example.org/'); // true

const isNotValid = URL.canParse('/foo'); // false
```

### Class: `URLSearchParams`

<!-- YAML
Expand Down
17 changes: 17 additions & 0 deletions lib/internal/url.js
Expand Up @@ -88,6 +88,7 @@ const {
domainToASCII: _domainToASCII,
domainToUnicode: _domainToUnicode,
parse,
canParse: _canParse,
updateUrl,
} = internalBinding('url');

Expand Down Expand Up @@ -789,6 +790,16 @@ class URL {
// If there's an error, it's ignored.
}
}

static canParse(url, base = undefined) {
url = `${url}`;

if (base !== undefined) {
base = `${base}`;
}

return _canParse(url, base);
}
}

ObjectDefineProperties(URL.prototype, {
Expand All @@ -812,6 +823,12 @@ ObjectDefineProperties(URL.prototype, {
ObjectDefineProperties(URL, {
createObjectURL: kEnumerableProperty,
revokeObjectURL: kEnumerableProperty,
canParse: {
__proto__: null,
configurable: true,
writable: true,
enumerable: true,
},
});

// application/x-www-form-urlencoded parser
Expand Down
26 changes: 26 additions & 0 deletions src/node_url.cc
Expand Up @@ -93,6 +93,30 @@ void Parse(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(true);
}

void CanParse(const FunctionCallbackInfo<Value>& args) {
CHECK_GE(args.Length(), 2);
CHECK(args[0]->IsString()); // input
// args[1] // base url

Environment* env = Environment::GetCurrent(args);
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());

Utf8Value input(env->isolate(), args[0]);
ada::result base;
ada::url* base_pointer = nullptr;
if (args[1]->IsString()) {
base = ada::parse(Utf8Value(env->isolate(), args[1]).ToString());
if (!base) {
return args.GetReturnValue().Set(false);
}
base_pointer = &base.value();
}
ada::result out = ada::parse(input.ToStringView(), base_pointer);

args.GetReturnValue().Set(out.has_value());
}

void DomainToASCII(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK_GE(args.Length(), 1);
Expand Down Expand Up @@ -285,6 +309,7 @@ void Initialize(Local<Object> target,
void* priv) {
SetMethod(context, target, "parse", Parse);
SetMethod(context, target, "updateUrl", UpdateUrl);
SetMethodNoSideEffect(context, target, "canParse", CanParse);
SetMethodNoSideEffect(context, target, "formatUrl", FormatUrl);

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

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Parse);
registry->Register(CanParse);
registry->Register(UpdateUrl);
registry->Register(FormatUrl);

Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/wpt/README.md
Expand Up @@ -27,7 +27,7 @@ Last update:
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
- resources: https://github.com/web-platform-tests/wpt/tree/919874f84f/resources
- streams: https://github.com/web-platform-tests/wpt/tree/51750bc8d7/streams
- url: https://github.com/web-platform-tests/wpt/tree/84caeb6fbd/url
- url: https://github.com/web-platform-tests/wpt/tree/7c5c3cc125/url
- user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/d8dbe6990b/wasm/jsapi
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
Expand Down
42 changes: 42 additions & 0 deletions test/fixtures/wpt/url/url-statics-canparse.any.js
@@ -0,0 +1,42 @@
// This intentionally does not use resources/urltestdata.json to preserve resources.
[
{
"url": undefined,
"base": undefined,
"expected": false
},
{
"url": "a:b",
"base": undefined,
"expected": true
},
{
"url": undefined,
"base": "a:b",
"expected": false
},
{
"url": "a:/b",
"base": undefined,
"expected": true
},
{
"url": undefined,
"base": "a:/b",
"expected": true
},
{
"url": "https://test:test",
"base": undefined,
"expected": false
},
{
"url": "a",
"base": "https://b/",
"expected": true
}
].forEach(({ url, base, expected }) => {
test(() => {
assert_equals(URL.canParse(url, base), expected);
}, `URL.canParse(${url}, ${base})`);
});
2 changes: 1 addition & 1 deletion test/fixtures/wpt/versions.json
Expand Up @@ -68,7 +68,7 @@
"path": "streams"
},
"url": {
"commit": "84caeb6fbdf45129f57c67448e6113ee1ced9fb3",
"commit": "7c5c3cc125979b4768d414471e6ab655b473aae8",
"path": "url"
},
"user-timing": {
Expand Down

0 comments on commit ef62e5a

Please sign in to comment.