summaryrefslogtreecommitdiff
path: root/cli/js/web
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/web')
-rw-r--r--cli/js/web/navigator.ts12
-rw-r--r--cli/js/web/permissions.ts181
2 files changed, 193 insertions, 0 deletions
diff --git a/cli/js/web/navigator.ts b/cli/js/web/navigator.ts
new file mode 100644
index 000000000..351c311d0
--- /dev/null
+++ b/cli/js/web/navigator.ts
@@ -0,0 +1,12 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { PermissionsImpl as Permissions } from "./permissions.ts";
+
+export class NavigatorImpl implements Navigator {
+ permissions = new Permissions();
+}
+
+Object.defineProperty(NavigatorImpl, "name", {
+ value: "Navigator",
+ configurable: true,
+});
diff --git a/cli/js/web/permissions.ts b/cli/js/web/permissions.ts
new file mode 100644
index 000000000..b8ddc26a7
--- /dev/null
+++ b/cli/js/web/permissions.ts
@@ -0,0 +1,181 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import * as permissionsOps from "../ops/permissions.ts";
+import { EventTargetImpl as EventTarget } from "./event_target.ts";
+
+const permissionNames = [
+ "read",
+ "write",
+ "net",
+ "env",
+ "run",
+ "plugin",
+ "hrtime",
+] as const;
+
+type PermissionName = typeof permissionNames[number];
+
+interface RunPermissionDescriptor {
+ name: "run";
+}
+
+interface ReadPermissionDescriptor {
+ name: "read";
+ path?: string;
+}
+
+interface WritePermissionDescriptor {
+ name: "write";
+ path?: string;
+}
+
+interface NetPermissionDescriptor {
+ name: "net";
+ url?: string;
+}
+
+interface EnvPermissionDescriptor {
+ name: "env";
+}
+
+interface PluginPermissionDescriptor {
+ name: "plugin";
+}
+
+interface HrtimePermissionDescriptor {
+ name: "hrtime";
+}
+
+type DenoPermissionDescriptor =
+ | RunPermissionDescriptor
+ | ReadPermissionDescriptor
+ | WritePermissionDescriptor
+ | NetPermissionDescriptor
+ | EnvPermissionDescriptor
+ | PluginPermissionDescriptor
+ | HrtimePermissionDescriptor;
+
+interface StatusCacheValue {
+ state: PermissionState;
+ status: PermissionStatusImpl;
+}
+
+export class PermissionStatusImpl extends EventTarget
+ implements PermissionStatus {
+ #state: { state: PermissionState };
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ onchange: ((this: PermissionStatus, event: Event) => any) | null = null;
+
+ get state(): PermissionState {
+ return this.#state.state;
+ }
+
+ constructor(state: { state: PermissionState }) {
+ super();
+ this.#state = state;
+ }
+
+ dispatchEvent(event: Event): boolean {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let dispatched = super.dispatchEvent(event as any);
+ if (dispatched && this.onchange) {
+ this.onchange.call(this, event);
+ dispatched = !event.defaultPrevented;
+ }
+ return dispatched;
+ }
+
+ get [Symbol.toStringTag](): string {
+ return "PermissionStatus";
+ }
+}
+
+/** A cache of `PermissionStatus` objects and their last known state. */
+const statusCache = new Map<string, StatusCacheValue>();
+
+/** Cache the state of a descriptor and return its `PermissionStatus`. */
+function cache(
+ desc: DenoPermissionDescriptor,
+ state: PermissionState
+): PermissionStatusImpl {
+ let key = desc.name;
+ if ((desc.name === "read" || desc.name === "write") && desc.path) {
+ key += `-${desc.path}`;
+ } else if (desc.name === "net" && desc.url) {
+ key += `-${desc.url}`;
+ }
+ if (statusCache.has(key)) {
+ const status = statusCache.get(key)!;
+ if (status.state !== state) {
+ status.state = state;
+ status.status.dispatchEvent(new Event("change", { cancelable: false }));
+ }
+ return status.status;
+ }
+ const status: { state: PermissionState; status?: PermissionStatusImpl } = {
+ state,
+ };
+ status.status = new PermissionStatusImpl(status);
+ statusCache.set(key, status as StatusCacheValue);
+ return status.status;
+}
+
+function isValidDescriptor(
+ desc: PermissionDescriptor | DenoPermissionDescriptor
+): desc is DenoPermissionDescriptor {
+ return permissionNames.includes(desc.name as PermissionName);
+}
+
+export class PermissionsImpl implements Permissions {
+ query(
+ desc: PermissionDescriptor | DenoPermissionDescriptor
+ ): Promise<PermissionStatus> {
+ if (!isValidDescriptor(desc)) {
+ return Promise.reject(
+ new TypeError(
+ `The provided value "${desc.name}" is not a valid permission name.`
+ )
+ );
+ }
+ const state = permissionsOps.query(desc);
+ return Promise.resolve(cache(desc, state) as PermissionStatus);
+ }
+
+ revoke(
+ desc: PermissionDescriptor | DenoPermissionDescriptor
+ ): Promise<PermissionStatus> {
+ if (!isValidDescriptor(desc)) {
+ return Promise.reject(
+ new TypeError(
+ `The provided value "${desc.name}" is not a valid permission name.`
+ )
+ );
+ }
+ const state = permissionsOps.revoke(desc);
+ return Promise.resolve(cache(desc, state) as PermissionStatus);
+ }
+
+ request(
+ desc: PermissionDescriptor | DenoPermissionDescriptor
+ ): Promise<PermissionStatus> {
+ if (!isValidDescriptor(desc)) {
+ return Promise.reject(
+ new TypeError(
+ `The provided value "${desc.name}" is not a valid permission name.`
+ )
+ );
+ }
+ const state = permissionsOps.request(desc);
+ return Promise.resolve(cache(desc, state) as PermissionStatus);
+ }
+}
+
+Object.defineProperty(PermissionStatusImpl, "name", {
+ value: "PermissionStatus",
+ configurable: true,
+});
+Object.defineProperty(PermissionsImpl, "name", {
+ value: "Permissions",
+ configurable: true,
+});