Skip to content

Commit c7de98a

Browse files
jasnellevanlucas
authored andcommitted
url: add ToObject method to native URL class
Provides a factory method to convert a native URL class into a JS URL object. ```c++ Environment* env = ... URL url("http://example.org/a/b/c?query#fragment"); MaybeLocal<Value> val = url.ToObject(env); ``` PR-URL: #12507 Reviewed-By: James M Snell <[email protected]>
1 parent 4b6097d commit c7de98a

File tree

5 files changed

+118
-17
lines changed

5 files changed

+118
-17
lines changed

lib/internal/bootstrap_node.js

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454

5555
_process.setupRawDebug();
5656

57+
// Ensure setURLConstructor() is called before the native
58+
// URL::ToObject() method is used.
59+
NativeModule.require('internal/url');
60+
5761
Object.defineProperty(process, 'argv0', {
5862
enumerable: true,
5963
configurable: false,

lib/internal/url.js

+25
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,31 @@ function getPathFromURL(path) {
14001400
return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
14011401
}
14021402

1403+
function NativeURL(ctx) {
1404+
this[context] = ctx;
1405+
}
1406+
NativeURL.prototype = URL.prototype;
1407+
1408+
function constructUrl(flags, protocol, username, password,
1409+
host, port, path, query, fragment) {
1410+
var ctx = new URLContext();
1411+
ctx.flags = flags;
1412+
ctx.scheme = protocol;
1413+
ctx.username = username;
1414+
ctx.password = password;
1415+
ctx.port = port;
1416+
ctx.path = path;
1417+
ctx.query = query;
1418+
ctx.fragment = fragment;
1419+
ctx.host = host;
1420+
const url = new NativeURL(ctx);
1421+
url[searchParams] = new URLSearchParams();
1422+
url[searchParams][context] = url;
1423+
initSearchParams(url[searchParams], query);
1424+
return url;
1425+
}
1426+
binding.setURLConstructor(constructUrl);
1427+
14031428
module.exports = {
14041429
toUSVString,
14051430
getPathFromURL,

src/env.h

+1
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ namespace node {
249249
V(tls_wrap_constructor_template, v8::FunctionTemplate) \
250250
V(tty_constructor_template, v8::FunctionTemplate) \
251251
V(udp_constructor_function, v8::Function) \
252+
V(url_constructor_function, v8::Function) \
252253
V(write_wrap_constructor_function, v8::Function) \
253254

254255
class Environment;

src/node_url.cc

+79-17
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ using v8::HandleScope;
2727
using v8::Integer;
2828
using v8::Isolate;
2929
using v8::Local;
30+
using v8::MaybeLocal;
3031
using v8::Null;
3132
using v8::Object;
3233
using v8::String;
34+
using v8::TryCatch;
3335
using v8::Undefined;
3436
using v8::Value;
3537

@@ -1226,6 +1228,29 @@ namespace url {
12261228
}
12271229
}
12281230

1231+
static inline void SetArgs(Environment* env,
1232+
Local<Value> argv[],
1233+
const struct url_data* url) {
1234+
Isolate* isolate = env->isolate();
1235+
argv[ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url->flags);
1236+
if (url->flags & URL_FLAGS_HAS_SCHEME)
1237+
argv[ARG_PROTOCOL] = OneByteString(isolate, url->scheme.c_str());
1238+
if (url->flags & URL_FLAGS_HAS_USERNAME)
1239+
argv[ARG_USERNAME] = UTF8STRING(isolate, url->username);
1240+
if (url->flags & URL_FLAGS_HAS_PASSWORD)
1241+
argv[ARG_PASSWORD] = UTF8STRING(isolate, url->password);
1242+
if (url->flags & URL_FLAGS_HAS_HOST)
1243+
argv[ARG_HOST] = UTF8STRING(isolate, url->host);
1244+
if (url->flags & URL_FLAGS_HAS_QUERY)
1245+
argv[ARG_QUERY] = UTF8STRING(isolate, url->query);
1246+
if (url->flags & URL_FLAGS_HAS_FRAGMENT)
1247+
argv[ARG_FRAGMENT] = UTF8STRING(isolate, url->fragment);
1248+
if (url->port > -1)
1249+
argv[ARG_PORT] = Integer::New(isolate, url->port);
1250+
if (url->flags & URL_FLAGS_HAS_PATH)
1251+
argv[ARG_PATH] = Copy(env, url->path);
1252+
}
1253+
12291254
static void Parse(Environment* env,
12301255
Local<Value> recv,
12311256
const char* input,
@@ -1267,23 +1292,7 @@ namespace url {
12671292
undef,
12681293
undef,
12691294
};
1270-
argv[ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url.flags);
1271-
if (url.flags & URL_FLAGS_HAS_SCHEME)
1272-
argv[ARG_PROTOCOL] = OneByteString(isolate, url.scheme.c_str());
1273-
if (url.flags & URL_FLAGS_HAS_USERNAME)
1274-
argv[ARG_USERNAME] = UTF8STRING(isolate, url.username);
1275-
if (url.flags & URL_FLAGS_HAS_PASSWORD)
1276-
argv[ARG_PASSWORD] = UTF8STRING(isolate, url.password);
1277-
if (url.flags & URL_FLAGS_HAS_HOST)
1278-
argv[ARG_HOST] = UTF8STRING(isolate, url.host);
1279-
if (url.flags & URL_FLAGS_HAS_QUERY)
1280-
argv[ARG_QUERY] = UTF8STRING(isolate, url.query);
1281-
if (url.flags & URL_FLAGS_HAS_FRAGMENT)
1282-
argv[ARG_FRAGMENT] = UTF8STRING(isolate, url.fragment);
1283-
if (url.port > -1)
1284-
argv[ARG_PORT] = Integer::New(isolate, url.port);
1285-
if (url.flags & URL_FLAGS_HAS_PATH)
1286-
argv[ARG_PATH] = Copy(env, url.path);
1295+
SetArgs(env, argv, &url);
12871296
(void)cb->Call(context, recv, arraysize(argv), argv);
12881297
} else if (error_cb->IsFunction()) {
12891298
Local<Value> argv[2] = { undef, undef };
@@ -1418,6 +1427,58 @@ namespace url {
14181427
v8::NewStringType::kNormal).ToLocalChecked());
14191428
}
14201429

1430+
// This function works by calling out to a JS function that creates and
1431+
// returns the JS URL object. Be mindful of the JS<->Native boundary
1432+
// crossing that is required.
1433+
const Local<Value> URL::ToObject(Environment* env) const {
1434+
Isolate* isolate = env->isolate();
1435+
Local<Context> context = env->context();
1436+
HandleScope handle_scope(isolate);
1437+
Context::Scope context_scope(context);
1438+
1439+
const Local<Value> undef = Undefined(isolate);
1440+
1441+
if (context_.flags & URL_FLAGS_FAILED)
1442+
return Local<Value>();
1443+
1444+
Local<Value> argv[9] = {
1445+
undef,
1446+
undef,
1447+
undef,
1448+
undef,
1449+
undef,
1450+
undef,
1451+
undef,
1452+
undef,
1453+
undef,
1454+
};
1455+
SetArgs(env, argv, &context_);
1456+
1457+
TryCatch try_catch(isolate);
1458+
1459+
// The SetURLConstructor method must have been called already to
1460+
// set the constructor function used below. SetURLConstructor is
1461+
// called automatically when the internal/url.js module is loaded
1462+
// during the internal/bootstrap_node.js processing.
1463+
MaybeLocal<Value> ret =
1464+
env->url_constructor_function()
1465+
->Call(env->context(), undef, 9, argv);
1466+
1467+
if (ret.IsEmpty()) {
1468+
ClearFatalExceptionHandlers(env);
1469+
FatalException(isolate, try_catch);
1470+
}
1471+
1472+
return ret.ToLocalChecked();
1473+
}
1474+
1475+
static void SetURLConstructor(const FunctionCallbackInfo<Value>& args) {
1476+
Environment* env = Environment::GetCurrent(args);
1477+
CHECK_EQ(args.Length(), 1);
1478+
CHECK(args[0]->IsFunction());
1479+
env->set_url_constructor_function(args[0].As<Function>());
1480+
}
1481+
14211482
static void Init(Local<Object> target,
14221483
Local<Value> unused,
14231484
Local<Context> context,
@@ -1428,6 +1489,7 @@ namespace url {
14281489
env->SetMethod(target, "toUSVString", ToUSVString);
14291490
env->SetMethod(target, "domainToASCII", DomainToASCII);
14301491
env->SetMethod(target, "domainToUnicode", DomainToUnicode);
1492+
env->SetMethod(target, "setURLConstructor", SetURLConstructor);
14311493

14321494
#define XX(name, _) NODE_DEFINE_CONSTANT(target, name);
14331495
FLAGS(XX)

src/node_url.h

+9
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@
44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

66
#include "node.h"
7+
#include "env.h"
8+
#include "env-inl.h"
9+
710
#include <string>
811

912
namespace node {
1013
namespace url {
1114

15+
using v8::Local;
16+
using v8::Value;
17+
18+
1219
#define BIT_AT(a, i) \
1320
(!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
1421
(1 << ((unsigned int) (i) & 7))))
@@ -619,6 +626,8 @@ class URL {
619626
return ret;
620627
}
621628

629+
const Local<Value> ToObject(Environment* env) const;
630+
622631
private:
623632
struct url_data context_;
624633
};

0 commit comments

Comments
 (0)