fix(op_crates/web): Use WorkerLocation for location in workers (#9084)

This commit is contained in:
Nayeem Rahman 2021-01-17 15:28:54 +00:00 committed by GitHub
parent f4dbb267c6
commit 7db0605d45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 277 additions and 100 deletions

View file

@ -49,3 +49,75 @@ declare function confirm(message?: string): boolean;
* @param defaultValue
*/
declare function prompt(message?: string, defaultValue?: string): string | null;
// TODO(nayeemrmn): Move this to `op_crates/web` where its implementation is.
// The types there must first be split into window, worker and global types.
/** The location (URL) of the object it is linked to. Changes done on it are
* reflected on the object it relates to. Accessible via
* `globalThis.location`. */
declare class Location {
constructor();
/** Returns a DOMStringList object listing the origins of the ancestor
* browsing contexts, from the parent browsing context to the top-level
* browsing context.
*
* Always empty in Deno. */
readonly ancestorOrigins: DOMStringList;
/** Returns the Location object's URL's fragment (includes leading "#" if
* non-empty).
*
* Cannot be set in Deno. */
hash: string;
/** Returns the Location object's URL's host and port (if different from the
* default port for the scheme).
*
* Cannot be set in Deno. */
host: string;
/** Returns the Location object's URL's host.
*
* Cannot be set in Deno. */
hostname: string;
/** Returns the Location object's URL.
*
* Cannot be set in Deno. */
href: string;
toString(): string;
/** Returns the Location object's URL's origin. */
readonly origin: string;
/** Returns the Location object's URL's path.
*
* Cannot be set in Deno. */
pathname: string;
/** Returns the Location object's URL's port.
*
* Cannot be set in Deno. */
port: string;
/** Returns the Location object's URL's scheme.
*
* Cannot be set in Deno. */
protocol: string;
/** Returns the Location object's URL's query (includes leading "?" if
* non-empty).
*
* Cannot be set in Deno. */
search: string;
/** Navigates to the given URL.
*
* Cannot be set in Deno. */
assign(url: string): void;
/** Reloads the current page.
*
* Disabled in Deno. */
reload(): void;
/** @deprecated */
reload(forcedReload: boolean): void;
/** Removes the current page from the session history and navigates to the
* given URL.
*
* Disabled in Deno. */
replace(url: string): void;
}
// TODO(nayeemrmn): Move this to `op_crates/web` where its implementation is.
// The types there must first be split into window, worker and global types.
declare var location: Location;

View file

@ -58,3 +58,26 @@ declare var onerror:
declare var close: () => void;
declare var name: string;
declare var postMessage: (message: any) => void;
// TODO(nayeemrmn): Move this to `op_crates/web` where its implementation is.
// The types there must first be split into window, worker and global types.
/** The absolute location of the script executed by the Worker. Such an object
* is initialized for each worker and is available via the
* WorkerGlobalScope.location property obtained by calling self.location. */
declare class WorkerLocation {
constructor();
readonly hash: string;
readonly host: string;
readonly hostname: string;
readonly href: string;
toString(): string;
readonly origin: string;
readonly pathname: string;
readonly port: string;
readonly protocol: string;
readonly search: string;
}
// TODO(nayeemrmn): Move this to `op_crates/web` where its implementation is.
// The types there must first be split into window, worker and global types.
declare var location: WorkerLocation;

View file

@ -1437,7 +1437,10 @@ fn location_arg<'a, 'b>() -> Arg<'a, 'b> {
if url.is_err() {
return Err("Failed to parse URL".to_string());
}
if !["http", "https"].contains(&url.unwrap().scheme()) {
let mut url = url.unwrap();
url.set_username("").unwrap();
url.set_password(None).unwrap();
if !["http", "https"].contains(&url.scheme()) {
return Err("Expected protocol \"http\" or \"https\"".to_string());
}
Ok(())

View file

@ -6,12 +6,10 @@ Location {
hostname: [Getter/Setter],
href: [Getter/Setter],
origin: [Getter],
password: [Getter/Setter],
pathname: [Getter/Setter],
port: [Getter/Setter],
protocol: [Getter/Setter],
search: [Getter/Setter],
username: [Getter/Setter],
ancestorOrigins: [Getter],
assign: [Function: assign],
reload: [Function: reload],

View file

@ -0,0 +1 @@
console.log(location.href);

View file

@ -0,0 +1,3 @@
[WILDCARD]
https://baz/qux
[WILDCARD]

View file

@ -2651,6 +2651,11 @@ itest!(_078_unload_on_exit {
output: "078_unload_on_exit.ts.out",
});
itest!(_079_location_authentication {
args: "run --location https://foo:bar@baz/qux 079_location_authentication.ts",
output: "079_location_authentication.ts.out",
});
itest!(js_import_detect {
args: "run --quiet --reload js_import_detect.ts",
output: "js_import_detect.ts.out",

View file

@ -1,4 +1,6 @@
onmessage = function (): void {
postMessage(self.location.href);
postMessage(
`${location.href}, ${location instanceof WorkerLocation}`,
);
close();
};

View file

@ -648,7 +648,7 @@ Deno.test({
new URL("subdir/worker_location.ts", import.meta.url).href;
const w = new Worker(workerModuleHref, { type: "module" });
w.onmessage = (e): void => {
assertEquals(e.data, workerModuleHref);
assertEquals(e.data, `${workerModuleHref}, true`);
promise.resolve();
};
w.postMessage("Hello, world!");

View file

@ -4,12 +4,19 @@
const { URL } = window.__bootstrap.url;
const locationConstructorKey = Symbol("locationConstuctorKey");
// The differences between the definitions of `Location` and `WorkerLocation`
// are because of the `LegacyUnforgeable` attribute only specified upon
// `Location`'s properties. See:
// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface
// - https://heycam.github.io/webidl/#LegacyUnforgeable
class Location {
constructor(href, key) {
constructor(href = null, key = null) {
if (key != locationConstructorKey) {
throw new TypeError("Illegal constructor.");
}
const url = new URL(href);
url.username = "";
url.password = "";
Object.defineProperties(this, {
hash: {
get() {
@ -49,7 +56,7 @@
},
href: {
get() {
return href;
return url.href;
},
set() {
throw new DOMException(
@ -65,18 +72,6 @@
},
enumerable: true,
},
password: {
get() {
return url.password;
},
set() {
throw new DOMException(
`Cannot set "location.password".`,
"NotSupportedError",
);
},
enumerable: true,
},
pathname: {
get() {
return url.pathname;
@ -125,18 +120,6 @@
},
enumerable: true,
},
username: {
get() {
return url.username;
},
set() {
throw new DOMException(
`Cannot set "location.username".`,
"NotSupportedError",
);
},
enumerable: true,
},
ancestorOrigins: {
get() {
// TODO(nayeemrmn): Replace with a `DOMStringList` instance.
@ -177,7 +160,7 @@
},
toString: {
value: function toString() {
return href;
return url.href;
},
enumerable: true,
},
@ -192,10 +175,144 @@
},
});
const workerLocationUrls = new WeakMap();
class WorkerLocation {
constructor(href = null, key = null) {
if (key != locationConstructorKey) {
throw new TypeError("Illegal constructor.");
}
const url = new URL(href);
url.username = "";
url.password = "";
workerLocationUrls.set(this, url);
}
}
Object.defineProperties(WorkerLocation.prototype, {
hash: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.hash;
},
configurable: true,
enumerable: true,
},
host: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.host;
},
configurable: true,
enumerable: true,
},
hostname: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.hostname;
},
configurable: true,
enumerable: true,
},
href: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.href;
},
configurable: true,
enumerable: true,
},
origin: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.origin;
},
configurable: true,
enumerable: true,
},
pathname: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.pathname;
},
configurable: true,
enumerable: true,
},
port: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.port;
},
configurable: true,
enumerable: true,
},
protocol: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.protocol;
},
configurable: true,
enumerable: true,
},
search: {
get() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.search;
},
configurable: true,
enumerable: true,
},
toString: {
value: function toString() {
const url = workerLocationUrls.get(this);
if (url == null) {
throw new TypeError("Illegal invocation.");
}
return url.href;
},
configurable: true,
enumerable: true,
writable: true,
},
[Symbol.toStringTag]: {
value: "WorkerLocation",
configurable: true,
},
});
let location = null;
let workerLocation = null;
function setLocationHref(href) {
location = new Location(href, locationConstructorKey);
workerLocation = new WorkerLocation(href, locationConstructorKey);
}
window.__bootstrap = (window.__bootstrap || {});
@ -205,6 +322,11 @@
configurable: true,
writable: true,
},
workerLocationConstructorDescriptor: {
value: WorkerLocation,
configurable: true,
writable: true,
},
locationDescriptor: {
get() {
if (location == null) {
@ -219,6 +341,18 @@
},
enumerable: true,
},
workerLocationDescriptor: {
get() {
if (workerLocation == null) {
throw new Error(
`Assertion: "globalThis.location" must be defined in a worker.`,
);
}
return workerLocation;
},
configurable: true,
enumerable: true,
},
setLocationHref,
getLocationHref() {
return location?.href;

View file

@ -313,69 +313,3 @@ declare var FileReader: {
readonly EMPTY: number;
readonly LOADING: number;
};
/** The location (URL) of the object it is linked to. Changes done on it are
* reflected on the object it relates to. Accessible via
* `globalThis.location`. */
declare class Location {
constructor();
/** Returns a DOMStringList object listing the origins of the ancestor
* browsing contexts, from the parent browsing context to the top-level
* browsing context.
*
* Always empty in Deno. */
readonly ancestorOrigins: DOMStringList;
/** Returns the Location object's URL's fragment (includes leading "#" if non-empty).
*
* Cannot be set in Deno. */
hash: string;
/** Returns the Location object's URL's host and port (if different from the default port for the scheme).
*
* Cannot be set in Deno. */
host: string;
/** Returns the Location object's URL's host.
*
* Cannot be set in Deno. */
hostname: string;
/** Returns the Location object's URL.
*
* Cannot be set in Deno. */
href: string;
toString(): string;
/** Returns the Location object's URL's origin. */
readonly origin: string;
/** Returns the Location object's URL's path.
*
* Cannot be set in Deno. */
pathname: string;
/** Returns the Location object's URL's port.
*
* Cannot be set in Deno. */
port: string;
/** Returns the Location object's URL's scheme.
*
* Cannot be set in Deno. */
protocol: string;
/** Returns the Location object's URL's query (includes leading "?" if
* non-empty).
*
* Cannot be set in Deno. */
search: string;
/** Navigates to the given URL.
*
* Cannot be set in Deno. */
assign(url: string): void;
/** Reloads the current page.
*
* Disabled in Deno. */
reload(): void;
/** @deprecated */
reload(forcedReload: boolean): void;
/** Removes the current page from the session history and navigates to the
* given URL.
*
* Disabled in Deno. */
replace(url: string): void;
}
declare var location: Location;

View file

@ -197,8 +197,6 @@ delete Object.prototype.__proto__;
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
const windowOrWorkerGlobalScope = {
Location: location.locationConstructorDescriptor,
location: location.locationDescriptor,
Blob: util.nonEnumerable(fetch.Blob),
ByteLengthQueuingStrategy: util.nonEnumerable(
streams.ByteLengthQueuingStrategy,
@ -257,6 +255,8 @@ delete Object.prototype.__proto__;
windowOrWorkerGlobalScope.console.enumerable = false;
const mainRuntimeGlobalProperties = {
Location: location.locationConstructorDescriptor,
location: location.locationDescriptor,
Window: globalInterfaces.windowConstructorDescriptor,
window: util.readOnly(globalThis),
self: util.readOnly(globalThis),
@ -272,6 +272,8 @@ delete Object.prototype.__proto__;
};
const workerRuntimeGlobalProperties = {
WorkerLocation: location.workerLocationConstructorDescriptor,
location: location.workerLocationDescriptor,
WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
DedicatedWorkerGlobalScope:
globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,