Skip to content

Commit 176ccdd

Browse files
Add support for setting initialization-time script location (#278)
This adds an `--init-location` CLI arg that can be used to set a location for use during wizening. This is meant to enable working around issues with libraries that use `globalThis.location` during top-level evaluation. An example of a library that does this, and is used as a dependency in a number of projects, is [cfworker](https://github.com/cfworker/cfworker/blob/b9a093a54ba81ef58960cb43574169df021ecc70/packages/json-schema/src/dereference.ts#L68-L75). The location will not be used for anything runtime-internal. Specifically, it'll not be used to resolve relative paths for module loading purposes or file handling in general. This also adds some needed test infrastructure and two tests: one for trying to use `globalThis.location` in top-level code without using `--init-location`, and another for successfully using it with the arg.
1 parent 0a64399 commit 176ccdd

File tree

14 files changed

+84
-3
lines changed

14 files changed

+84
-3
lines changed

builtins/web/worker-location.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,35 @@ namespace builtins::web::worker_location {
1212

1313
JS::PersistentRooted<JSObject *> WorkerLocation::url;
1414

15+
namespace {
16+
DEF_ERR(LocationNotSetError, JSEXN_TYPEERR, "{0} can only be used during request handling, "
17+
"or if an initialization-time location was set "
18+
"using `--init-location`", 1)
19+
bool ensure_location_access(JSContext *cx, const char *name) {
20+
auto *engine = api::Engine::get(cx);
21+
22+
if (engine->state() == api::EngineState::Initialized) {
23+
return true;
24+
}
25+
26+
if (engine->state() == api::EngineState::ScriptPreInitializing && WorkerLocation::url.get()) {
27+
return true;
28+
}
29+
30+
return api::throw_error(cx, LocationNotSetError, name);
31+
}
32+
} // namespace
33+
1534
#define WorkerLocation_ACCESSOR_GET(field) \
1635
bool field##_get(JSContext *cx, unsigned argc, JS::Value *vp) { \
1736
auto result = WorkerLocation::MethodHeaderWithName(0, cx, argc, vp, __func__); \
1837
if (result.isErr()) { \
1938
return false; \
2039
} \
2140
auto [args, self] = result.unwrap(); \
22-
REQUEST_HANDLER_ONLY("location." #field) \
41+
if (!ensure_location_access(cx, "location." #field)) { \
42+
return false; \
43+
} \
2344
return url::URL::field(cx, WorkerLocation::url, args.rval()); \
2445
}
2546

@@ -80,7 +101,28 @@ bool WorkerLocation::init_class(JSContext *cx, JS::HandleObject global) {
80101
}
81102

82103
bool install(api::Engine *engine) {
83-
return WorkerLocation::init_class(engine->cx(), engine->global());
104+
if (!WorkerLocation::init_class(engine->cx(), engine->global())) {
105+
return false;
106+
}
107+
108+
const auto &init_location = engine->init_location();
109+
if (init_location) {
110+
// Set the URL for `globalThis.location` to the configured value.
111+
JSContext *cx = engine->cx();
112+
JS::RootedObject url_instance(
113+
cx, JS_NewObjectWithGivenProto(cx, &url::URL::class_, url::URL::proto_obj));
114+
if (!url_instance) {
115+
return false;
116+
}
117+
118+
auto *uri_bytes = new uint8_t[init_location->size() + 1];
119+
std::copy(init_location->begin(), init_location->end(), uri_bytes);
120+
jsurl::SpecString spec(uri_bytes, init_location->size(), init_location->size());
121+
122+
WorkerLocation::url = url::URL::create(cx, url_instance, spec);
123+
}
124+
125+
return true;
84126
}
85127

86128
} // namespace builtins::web::worker_location

componentize.sh.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ usage() {
1717
echo " Specifying '--strip-path-prefix' will cause the provided prefix to be stripped from paths in stack traces and the debugger"
1818
echo " Specifying '--legacy-script' causes evaluation as a legacy JS script instead of a module"
1919
echo " Specifying '--wpt-mode' enables WPT compatibility mode"
20+
echo " Specifying '--init-location url' allows setting the URL to use for 'globalThis.location' during initialization"
2021
exit 1
2122
}
2223

@@ -55,6 +56,10 @@ do
5556
STARLING_ARGS="$STARLING_ARGS $1 $2"
5657
shift 2
5758
;;
59+
--init-location)
60+
STARLING_ARGS="$STARLING_ARGS $1 $2"
61+
shift 2
62+
;;
5863
-v|--verbose)
5964
STARLING_ARGS="$1 $STARLING_ARGS"
6065
VERBOSE=1

include/config-parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ class ConfigParser {
9999
}
100100
} else if (args[i] == "--wpt-mode") {
101101
config_->wpt_mode = true;
102+
} else if (args[i] == "--init-location") {
103+
if (i + 1 < args.size()) {
104+
config_->init_location = mozilla::Some(args[i + 1]);
105+
i++;
106+
}
102107
} else if (args[i].starts_with("--")) {
103108
std::cerr << "Unknown option: " << args[i] << std::endl;
104109
exit(1);

include/extension-api.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct EngineConfig {
3232
mozilla::Maybe<std::string> content_script_path = mozilla::Nothing();
3333
mozilla::Maybe<std::string> content_script = mozilla::Nothing();
3434
mozilla::Maybe<std::string> path_prefix = mozilla::Nothing();
35+
mozilla::Maybe<std::string> init_location = mozilla::Nothing();
3536
bool module_mode = true;
3637

3738
/**
@@ -84,6 +85,7 @@ class Engine {
8485
EngineState state();
8586
bool debugging_enabled();
8687
bool wpt_mode();
88+
const mozilla::Maybe<std::string> &init_location() const;
8789

8890
void finish_pre_initialization();
8991

runtime/engine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,9 @@ bool Engine::debugging_enabled() {
520520
return config_->debugging;
521521
}
522522
bool Engine::wpt_mode() { return config_->wpt_mode; }
523+
const mozilla::Maybe<std::string> &Engine::init_location() const {
524+
return config_->init_location;
525+
}
523526

524527
void Engine::finish_pre_initialization() {
525528
MOZ_ASSERT(state_ == EngineState::ScriptPreInitializing);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
stdout [0] :: Log: localhost
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Componentizing e2e/init-location/init-location.js into e2e/init-location/init-location.wasm
2+
Log: http://foo.bar/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
console.log(self.location.href);
2+
3+
addEventListener("fetch", (event) => {
4+
console.log(self.location.hostname);
5+
event.respondWith(new Response("ok"));
6+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--init-location http://foo.bar/

tests/e2e/no-init-location/expect_wizer_fail

Whitespace-only changes.

0 commit comments

Comments
 (0)