diff --git a/extensions/websocket/01_websocket.js b/extensions/websocket/01_websocket.js index 38cba866242d7b..f6e285b7653f54 100644 --- a/extensions/websocket/01_websocket.js +++ b/extensions/websocket/01_websocket.js @@ -1,12 +1,37 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. "use strict"; +/// + ((window) => { const core = window.Deno.core; const { URL } = window.__bootstrap.url; const webidl = window.__bootstrap.webidl; const { HTTP_TOKEN_CODE_POINT_RE } = window.__bootstrap.infra; const { DOMException } = window.__bootstrap.domException; + const { Blob } = globalThis.__bootstrap.file; + const { + ArrayBuffer, + ArrayBufferIsView, + ArrayPrototypeJoin, + DataView, + ErrorPrototypeToString, + ObjectDefineProperty, + Map, + MapPrototypeGet, + MapPrototypeSet, + Set, + Symbol, + String, + StringPrototypeToLowerCase, + StringPrototypeEndsWith, + FunctionPrototypeCall, + RegExpPrototypeTest, + ObjectDefineProperties, + ArrayPrototypeMap, + ArrayPrototypeSome, + PromisePrototypeThen, + } = window.__bootstrap.primordials; webidl.converters["sequence or DOMString"] = (V, opts) => { // Union for (sequence or DOMString) @@ -24,10 +49,11 @@ return webidl.converters["Blob"](V, opts); } if (typeof V === "object") { + // TODO(littledivy): use primordial for SharedArrayBuffer if (V instanceof ArrayBuffer || V instanceof SharedArrayBuffer) { return webidl.converters["ArrayBuffer"](V, opts); } - if (ArrayBuffer.isView(V)) { + if (ArrayBufferIsView(V)) { return webidl.converters["ArrayBufferView"](V, opts); } } @@ -58,7 +84,7 @@ if (typeof wrappedHandler.handler !== "function") { return; } - return wrappedHandler.handler.call(this, ...args); + return FunctionPrototypeCall(wrappedHandler.handler, this, ...args); } wrappedHandler.handler = handler; return wrappedHandler; @@ -66,22 +92,25 @@ // TODO(lucacasonato) reuse when we can reuse code between web crates function defineEventHandler(emitter, name) { // HTML specification section 8.1.5.1 - Object.defineProperty(emitter, `on${name}`, { + ObjectDefineProperty(emitter, `on${name}`, { get() { - return this[handlerSymbol]?.get(name)?.handler; + if (!this[handlerSymbol]) { + return null; + } + return MapPrototypeGet(this[handlerSymbol], name)?.handler; }, set(value) { if (!this[handlerSymbol]) { this[handlerSymbol] = new Map(); } - let handlerWrapper = this[handlerSymbol]?.get(name); + let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name); if (handlerWrapper) { handlerWrapper.handler = value; } else { handlerWrapper = makeWrappedHandler(value); this.addEventListener(name, handlerWrapper); } - this[handlerSymbol].set(name, handlerWrapper); + MapPrototypeSet(this[handlerSymbol], name, handlerWrapper); }, configurable: true, enumerable: true, @@ -194,7 +223,7 @@ ); } - if (wsURL.hash !== "" || wsURL.href.endsWith("#")) { + if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) { throw new DOMException( "Fragments are not allowed in a WebSocket URL.", "SyntaxError", @@ -210,7 +239,10 @@ } if ( - protocols.length !== new Set(protocols.map((p) => p.toLowerCase())).size + protocols.length !== + new Set( + ArrayPrototypeMap(protocols, (p) => StringPrototypeToLowerCase(p)), + ).size ) { throw new DOMException( "Can't supply multiple times the same protocol.", @@ -219,7 +251,11 @@ } if ( - protocols.some((protocol) => !HTTP_TOKEN_CODE_POINT_RE.test(protocol)) + ArrayPrototypeSome( + protocols, + (protocol) => + !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, protocol), + ) ) { throw new DOMException( "Invalid protocol value.", @@ -227,46 +263,53 @@ ); } - core.opAsync("op_ws_create", { - url: wsURL.href, - protocols: protocols.join(", "), - }).then((create) => { - this[_rid] = create.rid; - this[_extensions] = create.extensions; - this[_protocol] = create.protocol; - - if (this[_readyState] === CLOSING) { - core.opAsync("op_ws_close", { - rid: this[_rid], - }).then(() => { - this[_readyState] = CLOSED; - - const errEvent = new ErrorEvent("error"); - this.dispatchEvent(errEvent); - - const event = new CloseEvent("close"); + PromisePrototypeThen( + core.opAsync("op_ws_create", { + url: wsURL.href, + protocols: ArrayPrototypeJoin(protocols, ", "), + }), + (create) => { + this[_rid] = create.rid; + this[_extensions] = create.extensions; + this[_protocol] = create.protocol; + + if (this[_readyState] === CLOSING) { + PromisePrototypeThen( + core.opAsync("op_ws_close", { + rid: this[_rid], + }), + () => { + this[_readyState] = CLOSED; + + const errEvent = new ErrorEvent("error"); + this.dispatchEvent(errEvent); + + const event = new CloseEvent("close"); + this.dispatchEvent(event); + tryClose(this[_rid]); + }, + ); + } else { + this[_readyState] = OPEN; + const event = new Event("open"); this.dispatchEvent(event); - tryClose(this[_rid]); - }); - } else { - this[_readyState] = OPEN; - const event = new Event("open"); - this.dispatchEvent(event); - this.#eventLoop(); - } - }).catch((err) => { - this[_readyState] = CLOSED; + this.#eventLoop(); + } + }, + (err) => { + this[_readyState] = CLOSED; - const errorEv = new ErrorEvent( - "error", - { error: err, message: err.toString() }, - ); - this.dispatchEvent(errorEv); + const errorEv = new ErrorEvent( + "error", + { error: err, message: ErrorPrototypeToString(err) }, + ); + this.dispatchEvent(errorEv); - const closeEv = new CloseEvent("close"); - this.dispatchEvent(closeEv); - }); + const closeEv = new CloseEvent("close"); + this.dispatchEvent(closeEv); + }, + ); } send(data) { @@ -287,19 +330,23 @@ const sendTypedArray = (ta) => { this[_bufferedAmount] += ta.byteLength; - core.opAsync("op_ws_send", { - rid: this[_rid], - kind: "binary", - }, ta).then(() => { - this[_bufferedAmount] -= ta.byteLength; - }); + PromisePrototypeThen( + core.opAsync("op_ws_send", { + rid: this[_rid], + kind: "binary", + }, ta), + () => { + this[_bufferedAmount] -= ta.byteLength; + }, + ); }; if (data instanceof Blob) { - data.slice().arrayBuffer().then((ab) => - sendTypedArray(new DataView(ab)) + PromisePrototypeThen( + data.slice().arrayBuffer(), + (ab) => sendTypedArray(new DataView(ab)), ); - } else if (ArrayBuffer.isView(data)) { + } else if (ArrayBufferIsView(data)) { sendTypedArray(data); } else if (data instanceof ArrayBuffer) { sendTypedArray(new DataView(data)); @@ -307,13 +354,16 @@ const string = String(data); const d = core.encode(string); this[_bufferedAmount] += d.byteLength; - core.opAsync("op_ws_send", { - rid: this[_rid], - kind: "text", - text: string, - }).then(() => { - this[_bufferedAmount] -= d.byteLength; - }); + PromisePrototypeThen( + core.opAsync("op_ws_send", { + rid: this[_rid], + kind: "text", + text: string, + }), + () => { + this[_bufferedAmount] -= d.byteLength; + }, + ); } } @@ -357,20 +407,23 @@ } else if (this[_readyState] === OPEN) { this[_readyState] = CLOSING; - core.opAsync("op_ws_close", { - rid: this[_rid], - code, - reason, - }).then(() => { - this[_readyState] = CLOSED; - const event = new CloseEvent("close", { - wasClean: true, - code: code ?? 1005, + PromisePrototypeThen( + core.opAsync("op_ws_close", { + rid: this[_rid], + code, reason, - }); - this.dispatchEvent(event); - tryClose(this[_rid]); - }); + }), + () => { + this[_readyState] = CLOSED; + const event = new CloseEvent("close", { + wasClean: true, + code: code ?? 1005, + reason, + }); + this.dispatchEvent(event); + tryClose(this[_rid]); + }, + ); } } @@ -443,7 +496,7 @@ } } - Object.defineProperties(WebSocket, { + ObjectDefineProperties(WebSocket, { CONNECTING: { value: 0, },