summaryrefslogtreecommitdiff
path: root/runtime/js/01_web_util.js
blob: 5960691eb26569d5f02f6221154df396e6e5da02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";

((window) => {
  const illegalConstructorKey = Symbol("illegalConstructorKey");

  function requiredArguments(
    name,
    length,
    required,
  ) {
    if (length < required) {
      const errMsg = `${name} requires at least ${required} argument${
        required === 1 ? "" : "s"
      }, but only ${length} present`;
      throw new TypeError(errMsg);
    }
  }

  const objectCloneMemo = new WeakMap();

  function cloneArrayBuffer(
    srcBuffer,
    srcByteOffset,
    srcLength,
    _cloneConstructor,
  ) {
    // this function fudges the return type but SharedArrayBuffer is disabled for a while anyway
    return srcBuffer.slice(
      srcByteOffset,
      srcByteOffset + srcLength,
    );
  }

  /** Clone a value in a similar way to structured cloning.  It is similar to a
 * StructureDeserialize(StructuredSerialize(...)). */
  function cloneValue(value) {
    // Performance optimization for buffers, otherwise
    // `serialize/deserialize` will allocate new buffer.
    if (value instanceof ArrayBuffer) {
      const cloned = cloneArrayBuffer(
        value,
        0,
        value.byteLength,
        ArrayBuffer,
      );
      objectCloneMemo.set(value, cloned);
      return cloned;
    }
    if (ArrayBuffer.isView(value)) {
      const clonedBuffer = cloneValue(value.buffer);
      // Use DataViewConstructor type purely for type-checking, can be a
      // DataView or TypedArray.  They use the same constructor signature,
      // only DataView has a length in bytes and TypedArrays use a length in
      // terms of elements, so we adjust for that.
      let length;
      if (value instanceof DataView) {
        length = value.byteLength;
      } else {
        length = value.length;
      }
      return new (value.constructor)(
        clonedBuffer,
        value.byteOffset,
        length,
      );
    }

    try {
      return Deno.core.deserialize(Deno.core.serialize(value));
    } catch (e) {
      if (e instanceof TypeError) {
        throw new DOMException("Uncloneable value", "DataCloneError");
      }
      throw e;
    }
  }

  const handlerSymbol = Symbol("eventHandlers");
  function makeWrappedHandler(handler) {
    function wrappedHandler(...args) {
      if (typeof wrappedHandler.handler !== "function") {
        return;
      }
      return wrappedHandler.handler.call(this, ...args);
    }
    wrappedHandler.handler = handler;
    return wrappedHandler;
  }
  function defineEventHandler(emitter, name, defaultValue = undefined) {
    // HTML specification section 8.1.5.1
    Object.defineProperty(emitter, `on${name}`, {
      get() {
        return this[handlerSymbol]?.get(name)?.handler ?? defaultValue;
      },
      set(value) {
        if (!this[handlerSymbol]) {
          this[handlerSymbol] = new Map();
        }
        let handlerWrapper = this[handlerSymbol]?.get(name);
        if (handlerWrapper) {
          handlerWrapper.handler = value;
        } else {
          handlerWrapper = makeWrappedHandler(value);
          this.addEventListener(name, handlerWrapper);
        }
        this[handlerSymbol].set(name, handlerWrapper);
      },
      configurable: true,
      enumerable: true,
    });
  }
  window.__bootstrap.webUtil = {
    illegalConstructorKey,
    requiredArguments,
    defineEventHandler,
    cloneValue,
  };
})(this);