char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somport web extension to typescript44fe2c6

main
6.0 KiB213 linesraw
1import {
2  ObjectCreate,
3  ObjectFreeze as freeze,
4  ReflectApply as apply,
5  SymbolIterator,
6  TypeError,
7  WeakMap,
8  WeakMapPrototypeSet as set,
9} from "ext:core/primordials";
10import { onRuntimeStart } from "ext:core/bootstrap";
11import {
12  op_navigator_metadata as getMetadata,
13  op_navigator_user_agent_data as getUserAgentData,
14} from "ext:core/ops";
15import { isObject, required, state, string, usv } from "./idl.js";
16import { registerUncloneable } from "./serialization.js";
17
18const secret = ObjectCreate(null) as { readonly navigatorToken: unique symbol };
19interface NavigatorMetadata {
20  platform: string;
21  hardwareConcurrency: number;
22}
23interface Brand {
24  brand: string;
25  version: string;
26}
27interface UserAgentData {
28  brands: readonly Brand[];
29  mobile: boolean;
30  platform: string;
31  architecture: string;
32  bitness: string;
33  model: string;
34  platformVersion: string;
35  uaFullVersion: string;
36  fullVersionList: readonly Brand[];
37  wow64: boolean;
38}
39interface EntropyValues {
40  brands: Brand[];
41  mobile: boolean;
42  platform: string;
43  architecture?: string;
44  bitness?: string;
45  model?: string;
46  platformVersion?: string;
47  uaFullVersion?: string;
48  fullVersionList?: Brand[];
49  wow64?: boolean;
50}
51const navigators = new WeakMap<object, NavigatorMetadata>();
52registerUncloneable(navigators);
53const metadata = ObjectCreate(null) as NavigatorMetadata,
54  uaRecord = ObjectCreate(null) as UserAgentData;
55let userAgent: string;
56const languages = freeze(["en-US"]);
57onRuntimeStart(() => {
58  const info = getMetadata(null);
59  metadata.platform = info[0];
60  metadata.hardwareConcurrency = info[1];
61  userAgent = info[2];
62  const raw = getUserAgentData(null);
63  const frozenBrands: Brand[] = [];
64  for (let i = 0; i < raw.brands.length; i++) {
65    frozenBrands[frozenBrands.length] = freeze({
66      brand: raw.brands[i].brand,
67      version: raw.brands[i].version,
68    });
69  }
70  uaRecord.brands = freeze(frozenBrands);
71  uaRecord.mobile = raw.mobile;
72  uaRecord.platform = raw.platform;
73  uaRecord.architecture = raw.architecture;
74  uaRecord.bitness = raw.bitness;
75  uaRecord.model = raw.model;
76  uaRecord.platformVersion = raw.platformVersion;
77  uaRecord.uaFullVersion = raw.uaFullVersion;
78  uaRecord.fullVersionList = raw.fullVersionList;
79  uaRecord.wow64 = raw.wow64;
80});
81const uaDataStates = new WeakMap<object, UserAgentData>();
82registerUncloneable(uaDataStates);
83
84function copyBrands(list: readonly Brand[]): Brand[] {
85  const out: Brand[] = [];
86  for (let i = 0; i < list.length; i++) {
87    out[out.length] = { brand: list[i].brand, version: list[i].version };
88  }
89  return out;
90}
91
92class NavigatorUAData {
93  constructor(key: typeof secret);
94  constructor() {
95    if (arguments[0] !== secret) throw new TypeError("Illegal constructor");
96    set(uaDataStates, this, uaRecord);
97  }
98  get brands() {
99    return state(uaDataStates, this).brands;
100  }
101  get mobile() {
102    return state(uaDataStates, this).mobile;
103  }
104  get platform() {
105    return state(uaDataStates, this).platform;
106  }
107  async getHighEntropyValues(hints: unknown): Promise<EntropyValues> {
108    const current = state(uaDataStates, this);
109    required(arguments.length, 1);
110    if (!isObject(hints)) {
111      throw new TypeError("Expected an iterable of strings");
112    }
113    const method = hints[SymbolIterator];
114    if (typeof method !== "function") {
115      throw new TypeError("Expected an iterable of strings");
116    }
117    const iterator = apply(method, hints, []);
118    if (!isObject(iterator)) throw new TypeError("Invalid hints iterator");
119    const next = iterator.next;
120    if (typeof next !== "function") {
121      throw new TypeError("Invalid hints iterator");
122    }
123    const result: EntropyValues = {
124      brands: copyBrands(current.brands),
125      mobile: current.mobile,
126      platform: current.platform,
127    };
128    while (true) {
129      const step = apply(next, iterator, []);
130      if (!isObject(step)) throw new TypeError("Invalid hints iterator result");
131      if (step.done) break;
132      const hint = usv(step.value);
133      if (hint === "architecture") result.architecture = current.architecture;
134      else if (hint === "bitness") result.bitness = current.bitness;
135      else if (hint === "model") result.model = current.model;
136      else if (hint === "platformVersion") {
137        result.platformVersion = current.platformVersion;
138      } else if (hint === "uaFullVersion") {
139        result.uaFullVersion = current.uaFullVersion;
140      } else if (hint === "fullVersionList") {
141        result.fullVersionList = copyBrands(current.fullVersionList);
142      } else if (hint === "wow64") result.wow64 = current.wow64;
143    }
144    return result;
145  }
146  toJSON(): Pick<UserAgentData, "brands" | "mobile" | "platform"> {
147    const current = state(uaDataStates, this);
148    return {
149      brands: copyBrands(current.brands),
150      mobile: current.mobile,
151      platform: current.platform,
152    };
153  }
154}
155
156const uaData = new NavigatorUAData(secret);
157
158class WorkerNavigator {
159  constructor(key: typeof secret);
160  constructor() {
161    if (arguments[0] !== secret) throw new TypeError("Illegal constructor");
162    set(navigators, this, metadata);
163  }
164  get appCodeName() {
165    state(navigators, this);
166    return "Mozilla";
167  }
168  get appName() {
169    state(navigators, this);
170    return "Netscape";
171  }
172  get appVersion() {
173    state(navigators, this);
174    return "4.0";
175  }
176  get platform() {
177    return state(navigators, this).platform;
178  }
179  get product() {
180    state(navigators, this);
181    return "Gecko";
182  }
183  get userAgent() {
184    state(navigators, this);
185    return userAgent;
186  }
187  set userAgent(value) {
188    state(navigators, this);
189    userAgent = string(value);
190  }
191  get language() {
192    state(navigators, this);
193    return languages[0];
194  }
195  get languages() {
196    state(navigators, this);
197    return languages;
198  }
199  get hardwareConcurrency() {
200    return state(navigators, this).hardwareConcurrency;
201  }
202  get onLine() {
203    state(navigators, this);
204    return true;
205  }
206  get userAgentData() {
207    state(navigators, this);
208    return uaData;
209  }
210}
211
212const navigator = new WorkerNavigator(secret);
213export { navigator, NavigatorUAData, userAgent, WorkerNavigator };