Skip to main content

atmos/os_lib/js/
builtins.rs

1//! JS 標準ライブラリ(実用コア)。
2//!
3//! グローバル関数・console・Math・JSON と、文字列/配列/オブジェクトのメソッドを
4//! ネイティブ関数として提供する。メソッドは `this`(レシーバ)を受け取る。
5
6use alloc::format;
7use alloc::rc::Rc;
8use alloc::string::String;
9use alloc::string::ToString;
10use alloc::vec;
11use alloc::vec::Vec;
12use core::cell::RefCell;
13use core::sync::atomic::{AtomicU64, Ordering};
14
15use super::interp::{Interp, Scope};
16use super::value::*;
17
18/// `window.` 経由でも読めるようにするグローバル名。
19///
20/// ブラウザではグローバルと `window` のプロパティは同じ物を指す。
21/// この処理系は別々に持っているので、写して差を埋める。
22const WINDOW_MIRRORED_GLOBALS: &[&str] = &[
23    "navigator", "screen", "location", "document", "history",
24    "localStorage", "sessionStorage", "performance", "console",
25    "JSON", "Math", "Date", "Promise", "Array", "Object", "String",
26    "Number", "Boolean", "RegExp", "Error", "Map", "Set", "Symbol",
27    "XMLHttpRequest", "URL", "URLSearchParams", "Path2D", "ImageData", "Range", "NodeFilter",
28    "customElements", "ReadableStream", "WritableStream", "TransformStream", "MediaSource", "Worker",
29    "TextEncoderStream", "TextDecoderStream",
30];
31
32/// `Array.prototype` に並べるメソッド名。
33///
34/// 実体は `props_methods::object_get` が返す同じ関数を指す。
35const ARRAY_PROTO_METHODS: &[&str] = &[
36    "push", "pop", "shift", "unshift", "slice", "splice", "concat",
37    "join", "reverse", "sort", "indexOf", "lastIndexOf", "includes",
38    "find", "findIndex", "findLast", "findLastIndex", "filter", "map",
39    "forEach", "reduce", "reduceRight", "some", "every", "flat",
40    "flatMap", "fill", "copyWithin", "at", "keys", "values", "entries",
41    "toString",
42];
43
44/// グローバルスコープへ組込みを登録。
45/// `String.prototype` に並べるメソッド名。
46///
47/// 実体は `props_methods::string_get` が返す同じ関数を指す。
48/// ここに無いものはプロトタイプ経由では読めないが、
49/// 実体(`"x".foo()`)からは従来どおり解決できる。
50const STRING_PROTO_METHODS: &[&str] = &[
51    "charAt", "charCodeAt", "codePointAt", "localeCompare", "indexOf",
52    "lastIndexOf", "includes", "startsWith", "endsWith", "slice",
53    "substring", "substr", "toUpperCase", "toLowerCase", "trim",
54    "trimStart", "trimEnd", "split", "replace", "replaceAll", "match",
55    "matchAll", "search", "repeat", "concat", "padStart", "padEnd",
56    "at", "toString", "valueOf", "normalize",
57];
58
59pub fn install(global: &Rc<RefCell<Scope>>) {
60    let mut g = global.borrow_mut();
61
62    // console
63    let console = Obj::plain();
64    {
65        let mut c = console.borrow_mut();
66        c.props.insert("log".into(), nv("console.log", console_log));
67        c.props
68            .insert("error".into(), nv("console.error", console_log));
69        c.props
70            .insert("warn".into(), nv("console.warn", console_log));
71        c.props
72            .insert("info".into(), nv("console.info", console_log));
73        c.props
74            .insert("debug".into(), nv("console.debug", console_log));
75        c.props
76            .insert("trace".into(), nv("console.trace", console_log));
77        c.props.insert("dir".into(), nv("console.dir", console_log));
78        c.props
79            .insert("table".into(), nv("console.table", console_log));
80        c.props
81            .insert("group".into(), nv("console.group", console_log));
82        c.props
83            .insert("groupEnd".into(), nv("console.groupEnd", dom_noop));
84        c.props.insert(
85            "groupCollapsed".into(),
86            nv("console.groupCollapsed", console_log),
87        );
88        c.props
89            .insert("count".into(), nv("console.count", dom_noop));
90        c.props
91            .insert("assert".into(), nv("console.assert", console_log));
92        c.props.insert("time".into(), nv("console.time", dom_noop));
93        c.props
94            .insert("timeEnd".into(), nv("console.timeEnd", dom_noop));
95    }
96    g.vars.insert("console".into(), Value::Object(console));
97
98    // Math
99    let math = Obj::plain();
100    {
101        let mut m = math.borrow_mut();
102        m.props
103            .insert("PI".into(), Value::Number(core::f64::consts::PI));
104        m.props
105            .insert("E".into(), Value::Number(core::f64::consts::E));
106        m.props.insert("floor".into(), nv("Math.floor", math_floor));
107        m.props.insert("ceil".into(), nv("Math.ceil", math_ceil));
108        m.props.insert("round".into(), nv("Math.round", math_round));
109        m.props.insert("trunc".into(), nv("Math.trunc", math_trunc));
110        m.props.insert("abs".into(), nv("Math.abs", math_abs));
111        m.props.insert("sign".into(), nv("Math.sign", math_sign));
112        m.props.insert("sqrt".into(), nv("Math.sqrt", math_sqrt));
113        m.props.insert("pow".into(), nv("Math.pow", math_pow));
114        m.props.insert("min".into(), nv("Math.min", math_min));
115        m.props.insert("max".into(), nv("Math.max", math_max));
116        m.props
117            .insert("random".into(), nv("Math.random", math_random));
118        m.props.insert("sin".into(), nv("Math.sin", math_sin));
119        m.props.insert("cos".into(), nv("Math.cos", math_cos));
120        m.props.insert("tan".into(), nv("Math.tan", math_tan));
121        m.props.insert("asin".into(), nv("Math.asin", math_asin));
122        m.props.insert("acos".into(), nv("Math.acos", math_acos));
123        m.props.insert("atan".into(), nv("Math.atan", math_atan));
124        m.props.insert("atan2".into(), nv("Math.atan2", math_atan2));
125        m.props.insert("log".into(), nv("Math.log", math_log));
126        m.props.insert("log2".into(), nv("Math.log2", math_log2));
127        m.props.insert("log10".into(), nv("Math.log10", math_log10));
128        m.props.insert("cbrt".into(), nv("Math.cbrt", math_cbrt));
129        m.props.insert("hypot".into(), nv("Math.hypot", math_hypot));
130        m.props.insert("exp".into(), nv("Math.exp", math_exp));
131        m.props.insert("fround".into(), nv("Math.fround", math_fround));
132        m.props.insert("f16round".into(), nv("Math.f16round", math_f16round));
133        m.props.insert("clamp".into(), nv("Math.clamp", math_clamp));
134        m.props.insert("sumPrecise".into(), nv("Math.sumPrecise", math_sum_precise));
135        m.props.insert("clz32".into(), nv("Math.clz32", math_clz32));
136        m.props.insert("imul".into(), nv("Math.imul", math_imul));
137        m.props.insert("sinh".into(), nv("Math.sinh", math_sinh));
138        m.props.insert("cosh".into(), nv("Math.cosh", math_cosh));
139        m.props.insert("tanh".into(), nv("Math.tanh", math_tanh));
140        m.props.insert("asinh".into(), nv("Math.asinh", math_asinh));
141        m.props.insert("acosh".into(), nv("Math.acosh", math_acosh));
142        m.props.insert("atanh".into(), nv("Math.atanh", math_atanh));
143        m.props.insert("expm1".into(), nv("Math.expm1", math_expm1));
144        m.props.insert("log1p".into(), nv("Math.log1p", math_log1p));
145        // `Math.LN2`/`LN10`/`LOG2E`/`LOG10E`/`SQRT2`/`SQRT1_2`(ES1 以来の定数群)が
146        // `PI`/`E` 以外一つも登録されておらず丸ごと未対応だった。
147        m.props.insert("LN2".into(), Value::Number(core::f64::consts::LN_2));
148        m.props.insert("LN10".into(), Value::Number(core::f64::consts::LN_10));
149        m.props.insert("LOG2E".into(), Value::Number(core::f64::consts::LOG2_E));
150        m.props.insert("LOG10E".into(), Value::Number(core::f64::consts::LOG10_E));
151        m.props.insert("SQRT2".into(), Value::Number(core::f64::consts::SQRT_2));
152        m.props.insert("SQRT1_2".into(), Value::Number(1.0 / core::f64::consts::SQRT_2));
153        m.props
154            .insert("PI".into(), Value::Number(core::f64::consts::PI));
155        m.props
156            .insert("E".into(), Value::Number(core::f64::consts::E));
157    }
158    g.vars.insert("Math".into(), Value::Object(math));
159
160    // `Intl`(`Intl.NumberFormat`/`Intl.DateTimeFormat`/`Intl.Collator` 等)が
161    // グローバルオブジェクトに丸ごと存在しなかった。ロケール辞書・タイムゾーン
162    // データベースを伴う本格実装は本キャンペーンの規模を超えるため、既に実装
163    // 済みの `Number.prototype.toLocaleString`(既定の3桁カンマ区切り)を
164    // `.format()` から呼べるようにする最小限の `Intl.NumberFormat` のみを追加し、
165    // `new Intl.NumberFormat().format(n)` という最頻出イディオムだけを救う
166    // (`DateTimeFormat`/`Collator`/ロケール・オプション引数は非対応のまま)。
167    g.vars.insert("Intl".into(), create_intl_object());
168
169    // `CSS.escape(str)`(CSSOM。`document.querySelector('#' + CSS.escape(userId))`
170    // という定番イディオムで使われる)が `CSS` 名前空間自体丸ごと未対応だった。
171    // `CSS.supports()`(`@supports` の条件式評価を実装した際に新設した
172    // `css::supports_condition_matches` をそのまま再利用できるようになったため
173    // 追加。かつては「CSS エンジンとの結合が必要で改修規模が異なる」として
174    // 対象外にしていたが、その結合自体が既に完了済みとなった)。
175    {
176        let css_ns = Obj::plain();
177        css_ns
178            .borrow_mut()
179            .props
180            .insert("escape".into(), nv("CSS.escape", css_escape_static));
181        css_ns
182            .borrow_mut()
183            .props
184            .insert("supports".into(), nv("CSS.supports", css_supports_static));
185        // `CSS.number()`/`.percent()`/`.px()`/`.em()`/`.rem()`/`.deg()`
186        // (CSS Typed OM のうち値+単位オブジェクトを構築するだけの
187        // ファクトリ関数群。丸ごと未対応だった。詳細は`regexp.rs`の
188        // `make_css_unit_value`参照。2026-07-18 発見・実装)。
189        {
190            let mut b = css_ns.borrow_mut();
191            b.props.insert("number".into(), nv("CSS.number", css_number_static));
192            b.props.insert("percent".into(), nv("CSS.percent", css_percent_static));
193            b.props.insert("px".into(), nv("CSS.px", css_px_static));
194            b.props.insert("em".into(), nv("CSS.em", css_em_static));
195            b.props.insert("rem".into(), nv("CSS.rem", css_rem_static));
196            b.props.insert("deg".into(), nv("CSS.deg", css_deg_static));
197        }
198        g.vars.insert("CSS".into(), Value::Object(css_ns));
199    }
200
201    // JSON
202    let json = Obj::plain();
203    {
204        let mut j = json.borrow_mut();
205        j.props.insert("parse".into(), nv("JSON.parse", json_parse));
206        j.props
207            .insert("stringify".into(), nv("JSON.stringify", json_stringify));
208        // `JSON.rawJSON`/`JSON.isRawJSON`(ES2025)が丸ごと未対応だった。
209        // 2026-07-18 発見・実装。
210        j.props
211            .insert("rawJSON".into(), nv("JSON.rawJSON", json_raw_json));
212        j.props
213            .insert("isRawJSON".into(), nv("JSON.isRawJSON", json_is_raw_json));
214    }
215    g.vars.insert("JSON".into(), Value::Object(json));
216
217    // Object(コンストラクタ兼名前空間)
218    let object_ctor = Obj::native("Object", object_ctor);
219    object_ctor
220        .borrow_mut()
221        .props
222        .insert("keys".into(), nv("Object.keys", object_keys));
223    object_ctor
224        .borrow_mut()
225        .props
226        .insert("values".into(), nv("Object.values", object_values));
227    object_ctor
228        .borrow_mut()
229        .props
230        .insert("entries".into(), nv("Object.entries", object_entries));
231    object_ctor
232        .borrow_mut()
233        .props
234        .insert("assign".into(), nv("Object.assign", object_assign));
235    object_ctor.borrow_mut().props.insert(
236        "fromEntries".into(),
237        nv("Object.fromEntries", object_from_entries),
238    );
239    object_ctor.borrow_mut().props.insert(
240        "getOwnPropertyNames".into(),
241        nv("Object.getOwnPropertyNames", object_keys),
242    );
243    object_ctor
244        .borrow_mut()
245        .props
246        .insert("freeze".into(), nv("Object.freeze", object_freeze));
247    object_ctor.borrow_mut().props.insert(
248        "isFrozen".into(),
249        nv("Object.isFrozen", object_is_frozen),
250    );
251    object_ctor
252        .borrow_mut()
253        .props
254        .insert("seal".into(), nv("Object.seal", object_seal));
255    object_ctor
256        .borrow_mut()
257        .props
258        .insert("isSealed".into(), nv("Object.isSealed", object_is_sealed));
259    object_ctor.borrow_mut().props.insert(
260        "preventExtensions".into(),
261        nv("Object.preventExtensions", object_prevent_extensions),
262    );
263    object_ctor.borrow_mut().props.insert(
264        "isExtensible".into(),
265        nv("Object.isExtensible", object_is_extensible),
266    );
267    // `Object.is(a, b)`(ES2015、SameValue)が丸ごと欠落していた。`===` と違い
268    // `NaN` 同士は等しいと判定し、`Object.is(0, -0)` は `false`(`===`/SameValueZero とは
269    // ここだけ異なる、符号付きゼロを区別する点が特徴)。
270    object_ctor
271        .borrow_mut()
272        .props
273        .insert("is".into(), nv("Object.is", object_is));
274    object_ctor
275        .borrow_mut()
276        .props
277        .insert("create".into(), nv("Object.create", object_create));
278    object_ctor.borrow_mut().props.insert(
279        "defineProperty".into(),
280        nv("Object.defineProperty", object_define_property),
281    );
282    object_ctor.borrow_mut().props.insert(
283        "defineProperties".into(),
284        nv("Object.defineProperties", object_define_properties),
285    );
286    object_ctor.borrow_mut().props.insert(
287        "getOwnPropertyDescriptor".into(),
288        nv(
289            "Object.getOwnPropertyDescriptor",
290            object_get_own_property_descriptor,
291        ),
292    );
293    object_ctor.borrow_mut().props.insert(
294        "getOwnPropertyDescriptors".into(),
295        nv(
296            "Object.getOwnPropertyDescriptors",
297            object_get_own_property_descriptors,
298        ),
299    );
300    object_ctor
301        .borrow_mut()
302        .props
303        .insert("hasOwn".into(), nv("Object.hasOwn", object_has_own_static));
304    object_ctor
305        .borrow_mut()
306        .props
307        .insert("groupBy".into(), nv("Object.groupBy", object_group_by));
308    object_ctor.borrow_mut().props.insert(
309        "getPrototypeOf".into(),
310        nv("Object.getPrototypeOf", object_get_prototype_of),
311    );
312    object_ctor.borrow_mut().props.insert(
313        "setPrototypeOf".into(),
314        nv("Object.setPrototypeOf", object_set_prototype_of),
315    );
316    // `Object.prototype`(丸ごと未対応だった。`Object.prototype.toString.call(x)` という
317    // `lodash` 等で広く使われる型判定イディオムは、`Object` に `.prototype` が全く無いと
318    // 呼び出しようが無いため、`obj_to_string()` 自体を直しても実質使えない状態だった)。
319    // この処理系の「組込み Array/Object は実際の `.prototype` を持たない簡略実装」という
320    // 既存方針の中で、`Object.prototype` だけは最低限アクセス可能にする。
321    let object_proto = Obj::plain();
322    object_proto
323        .borrow_mut()
324        .props
325        .insert("toString".into(), nv("toString", obj_to_string));
326    object_ctor
327        .borrow_mut()
328        .props
329        .insert("prototype".into(), Value::Object(object_proto.clone()));
330    g.vars.insert("Object".into(), Value::Object(object_ctor));
331
332    // Array
333    let array_ctor = Obj::native("Array", array_ctor);
334    let array_proto = Obj::plain();
335    let unscopables_obj = Obj::plain();
336    {
337        let mut u = unscopables_obj.borrow_mut();
338        for key in [
339            "copyWithin", "entries", "fill", "find", "findIndex",
340            "findLast", "findLastIndex", "flat", "flatMap", "includes",
341            "keys", "values", "at",
342        ] {
343            u.props.insert(key.into(), Value::Bool(true));
344        }
345    }
346    array_proto
347        .borrow_mut()
348        .props
349        .insert("Symbol(Symbol.unscopables)".into(), Value::Object(unscopables_obj));
350    // 【2026-09-05】`Array.prototype` はオブジェクトとしては在ったが
351    // **メソッドが 1 つも入っていなかった**(`Symbol.unscopables` のみ)。
352    //
353    // 配列メソッドは実体からの動的解決(`props_methods::object_get`)だけで
354    // 賄っており、`Array.prototype.slice` のように**プロトタイプから直接
355    // 読む**形が `undefined` になっていた。
356    // jQuery 1.8.2 の先頭が
357    // `j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf`
358    // と読むため、続く `k.call(...)` で
359    // 「Cannot read properties of undefined (reading 'call')」になっていた。
360    //
361    // `String.prototype` と同じく、同じ解決口を使って並べる。
362    // 実体側と同じ関数を指すので挙動は一致し、二重実装にならない。
363    {
364        let probe = Obj::array(alloc::vec::Vec::new());
365        let mut ap = array_proto.borrow_mut();
366        for key in ARRAY_PROTO_METHODS {
367            let v = super::builtins::props_methods::object_get(&probe, key);
368            if !matches!(v, Value::Undefined) {
369                ap.props.insert(alloc::string::String::from(*key), v);
370            }
371        }
372    }
373    array_ctor
374        .borrow_mut()
375        .props
376        .insert("prototype".into(), Value::Object(array_proto));
377    array_ctor
378        .borrow_mut()
379        .props
380        .insert("isArray".into(), nv("Array.isArray", array_is_array));
381    array_ctor
382        .borrow_mut()
383        .props
384        .insert("from".into(), nv("Array.from", array_from));
385    array_ctor
386        .borrow_mut()
387        .props
388        .insert("of".into(), nv("Array.of", array_of));
389    array_ctor
390        .borrow_mut()
391        .props
392        .insert("fromAsync".into(), nv("Array.fromAsync", array_from_async));
393    g.vars.insert("Array".into(), Value::Object(array_ctor));
394
395    // 単純な型変換コンストラクタ
396    let string_ctor_v = nv("String", string_ctor);
397    if let Value::Object(sc) = &string_ctor_v {
398        let mut b = sc.borrow_mut();
399        b.props.insert(
400            "fromCharCode".into(),
401            nv("String.fromCharCode", string_from_char_code),
402        );
403        b.props.insert(
404            "fromCodePoint".into(),
405            nv("String.fromCodePoint", string_from_code_point),
406        );
407        b.props.insert("raw".into(), nv("String.raw", string_raw));
408        // 【2026-09-05】`String.prototype` が丸ごと存在しなかった。
409        //
410        // 文字列のメソッドは実体(プリミティブ)からの動的解決
411        // (`props_methods::string_get`)だけで賄っており、
412        // `String.prototype.trim` のように**プロトタイプから直接読む**
413        // 形が `undefined` になっていた。
414        // jQuery 1.8.2 の先頭が
415        // `o=String.prototype.trim` を読むため、**本体が実行時に落ち**、
416        // 続く `$` も未定義になっていた(実測)。
417        //
418        // 同じ解決口を使ってメソッドを並べる。実体側と同じ関数を指すので
419        // 挙動は一致し、二重実装にならない。
420        let string_proto = Obj::plain();
421        {
422            let mut sp = string_proto.borrow_mut();
423            for key in STRING_PROTO_METHODS {
424                let v = super::builtins::props_methods::string_get("", key);
425                if !matches!(v, Value::Undefined) {
426                    sp.props.insert(alloc::string::String::from(*key), v);
427                }
428            }
429        }
430        b.props
431            .insert("prototype".into(), Value::Object(string_proto));
432    }
433    g.vars.insert("String".into(), string_ctor_v);
434    // `Number.parseFloat`/`Number.parseInt`(ES2015)は仕様上グローバル `parseFloat`/
435    // `parseInt` と「同一の関数オブジェクト」(`Number.parseFloat === parseFloat` が
436    // `true`)でなければならないが、以前はそれぞれ別々に `nv(...)` していたため
437    // 別オブジェクトになり `===` が `false` になっていた(`.name` も本来どちらも
438    // `"parseFloat"`/`"parseInt"` であるべきところ `"Number.parseFloat"` 等になっていた)。
439    // 1回だけ構築して両方の束縛先で同じ `Value`(同じ `Rc`)を共有する。
440    let parse_float_v = nv("parseFloat", global_parse_float);
441    let parse_int_v = nv("parseInt", global_parse_int);
442    let number_ctor_v = nv("Number", number_ctor);
443    if let Value::Object(nc) = &number_ctor_v {
444        let mut b = nc.borrow_mut();
445        b.props.insert(
446            "isInteger".into(),
447            nv("Number.isInteger", number_is_integer),
448        );
449        b.props.insert(
450            "isSafeInteger".into(),
451            nv("Number.isSafeInteger", number_is_safe_integer),
452        );
453        b.props.insert(
454            "isFinite".into(),
455            nv("Number.isFinite", number_is_finite_static),
456        );
457        b.props
458            .insert("isNaN".into(), nv("Number.isNaN", number_is_nan_static));
459        b.props.insert("parseFloat".into(), parse_float_v.clone());
460        b.props.insert("parseInt".into(), parse_int_v.clone());
461        b.props
462            .insert("MAX_SAFE_INTEGER".into(), Value::Number(9007199254740991.0));
463        b.props.insert(
464            "MIN_SAFE_INTEGER".into(),
465            Value::Number(-9007199254740991.0),
466        );
467        b.props
468            .insert("EPSILON".into(), Value::Number(f64::EPSILON));
469        b.props.insert("MAX_VALUE".into(), Value::Number(f64::MAX));
470        // `Number.MIN_VALUE`(0 に最も近い正の表現可能な値 = 最小の非正規化数
471        // `5e-324`。ES1 以来の定数)と `Number.NaN`(グローバル `NaN` とは別に
472        // `Number` 名前空間側にも仕様上存在する)が丸ごと未登録だった。
473        // `f64::MIN_POSITIVE`(Rust 標準の「最小の正規化数」約 `2.2e-308`)とは
474        // 別物なので注意(仕様の `Number.MIN_VALUE` は非正規化数まで含めた最小値)。
475        // `MIN_SAFE_INTEGER`(安全な整数の下限、大きな負の値)とも紛らわしいが
476        // 別物。
477        b.props
478            .insert("MIN_VALUE".into(), Value::Number(f64::from_bits(1)));
479        b.props.insert("NaN".into(), Value::Number(f64::NAN));
480        b.props
481            .insert("POSITIVE_INFINITY".into(), Value::Number(f64::INFINITY));
482        b.props
483            .insert("NEGATIVE_INFINITY".into(), Value::Number(f64::NEG_INFINITY));
484    }
485    g.vars.insert("Number".into(), number_ctor_v);
486    g.vars.insert("Boolean".into(), nv("Boolean", boolean_ctor));
487
488    // BigInt コンストラクタ + 静的メソッド asIntN / asUintN。
489    let bigint_ctor_v = nv("BigInt", bigint_ctor);
490    if let Value::Object(bc) = &bigint_ctor_v {
491        let mut b = bc.borrow_mut();
492        b.props
493            .insert("asIntN".into(), nv("BigInt.asIntN", bigint_as_int_n));
494        b.props
495            .insert("asUintN".into(), nv("BigInt.asUintN", bigint_as_uint_n));
496    }
497    g.vars.insert("BigInt".into(), bigint_ctor_v);
498
499    // グローバル関数
500    g.vars.insert("parseInt".into(), parse_int_v);
501    g.vars.insert("parseFloat".into(), parse_float_v);
502    g.vars.insert("isNaN".into(), nv("isNaN", global_is_nan));
503    g.vars
504        .insert("isFinite".into(), nv("isFinite", global_is_finite));
505    // `eval`(ES1 以来。文字列以外の引数はそのまま返す仕様)と `Function` コンストラクタ
506    // (ES1。動的にソースからコードを生成する定番イディオムの2大対象)が丸ごと欠落して
507    // いたのを実装。この処理系は「常にグローバルスコープで実行する」簡略実装(indirect
508    // eval と同じセマンティクス。direct eval が呼び出し元のレキシカルスコープを見る挙動は
509    // 対象外)。
510    g.vars.insert("eval".into(), nv("eval", global_eval));
511    // 【2026-09-26】`Function.prototype` が存在しなかった(`String.prototype` 等と同じ欠落)。
512    // 関数のメソッド(call/apply/bind/toString)は実体からの動的解決だけで賄っており、
513    // `Function.prototype.toString.call(f)` のように**プロトタイプから直接読む**形が
514    // undefined になっていた。OpenStreetMap 埋め込み(Leaflet 同梱の JS)が起動時にこれを読み、
515    // 「Cannot read properties of undefined (reading 'toString')」で止まっていた。
516    // 同じ解決口から取ったメソッドを並べる(実体側と同じ関数を指すので二重実装にならない)。
517    let function_ctor_v = nv("Function", function_ctor);
518    if let Value::Object(fc) = &function_ctor_v {
519        let function_proto = Obj::plain();
520        {
521            let mut fp = function_proto.borrow_mut();
522            for key in ["call", "apply", "bind"] {
523                let v = super::builtins::props_methods::function_method(key);
524                if !matches!(v, Value::Undefined) {
525                    fp.props.insert(alloc::string::String::from(key), v);
526                }
527            }
528            fp.props.insert(
529                "toString".into(),
530                nv("toString", super::builtins::props_methods::fn_to_string),
531            );
532        }
533        fc.borrow_mut()
534            .props
535            .insert("prototype".into(), Value::Object(function_proto));
536    }
537    g.vars.insert("Function".into(), function_ctor_v);
538
539    // location(window/document/global で共有する単一オブジェクト。
540    // 値は base_url 設定時に update_location が埋める。install 時は空)。
541    let location = Obj::plain();
542    {
543        let mut l = location.borrow_mut();
544        for k in [
545            "href", "protocol", "username", "password", "host", "hostname", "port", "origin",
546            "pathname", "search", "hash",
547        ] {
548            l.props.insert(String::from(k), Value::str(""));
549        }
550        l.props
551            .insert("assign".into(), nv("location.assign", location_assign));
552        l.props
553            .insert("replace".into(), nv("location.replace", location_replace));
554        l.props
555            .insert("reload".into(), nv("location.reload", location_reload));
556        l.props.insert(
557            "toString".into(),
558            nv("location.toString", location_to_string),
559        );
560        // ホスト連携用マーカー。set_property がこのオブジェクトを location と判定し、
561        // href/hash/pathname/search 代入をナビゲーション要求に変換する。
562        l.props.insert("_is_location".into(), Value::Bool(true));
563    }
564
565    // history(SPA ルーティング。pushState/replaceState で location を書換え(再読込なし))。
566    let history = Obj::plain();
567    {
568        let mut h = history.borrow_mut();
569        h.props.insert("length".into(), Value::Number(1.0));
570        h.props.insert("state".into(), Value::Null);
571        h.props
572            .insert("scrollRestoration".into(), Value::str("auto"));
573        h.props.insert(
574            "pushState".into(),
575            nv("history.pushState", history_push_state),
576        );
577        h.props.insert(
578            "replaceState".into(),
579            nv("history.replaceState", history_replace_state),
580        );
581        h.props
582            .insert("back".into(), nv("history.back", history_back));
583        h.props
584            .insert("forward".into(), nv("history.forward", history_forward));
585        h.props.insert("go".into(), nv("history.go", history_go));
586    }
587
588    // document(DOM ホストオブジェクト)
589    let document = Obj::host("document");
590    {
591        let mut d = document.borrow_mut();
592        // Node としての種別(DOCUMENT_NODE)。【2026-10-20】無かったため YouTube の player の
593        // 所有文書の取得(`f.nodeType==9?f:f.ownerDocument||f.document`)が document で undefined を返していた。
594        d.props.insert("nodeType".into(), Value::Number(9.0));
595        d.props.insert("nodeName".into(), Value::str("#document"));
596        d.props.insert("ownerDocument".into(), Value::Null);
597        d.props.insert(
598            "getElementById".into(),
599            nv("getElementById", document_get_element_by_id),
600        );
601        d.props.insert(
602            "querySelector".into(),
603            nv("querySelector", document_query_selector),
604        );
605        d.props.insert(
606            "querySelectorAll".into(),
607            nv("querySelectorAll", document_query_selector_all),
608        );
609        // `document.elementFromPoint(x,y)`/`.elementsFromPoint(x,y)`(座標ヒット
610        // テストの定番 API)が丸ごと未対応だった。この処理系に z-index を考慮した
611        // 合成順スタックは無いため、レイアウト確定後の `rects`(`getBoundingClientRect`
612        // と同じ発生源)から座標を含む矩形を集め、最も面積が小さい(=最も深く
613        // ネストした)要素を「最前面」の近似として返す簡略実装。
614        d.props.insert(
615            "elementFromPoint".into(),
616            nv("elementFromPoint", document_element_from_point),
617        );
618        d.props.insert(
619            "elementsFromPoint".into(),
620            nv("elementsFromPoint", document_elements_from_point),
621        );
622        d.props.insert(
623            "createElement".into(),
624            nv("document.createElement", document_create_element),
625        );
626        d.props.insert(
627            "createElementNS".into(),
628            nv("document.createElementNS", document_create_element_ns),
629        );
630        d.props.insert(
631            "createTextNode".into(),
632            nv("document.createTextNode", document_create_text_node),
633        );
634        d.props.insert(
635            "createComment".into(),
636            nv("document.createComment", document_create_comment),
637        );
638        // `document.createEvent(name)`(DOM Level 2 の古い構築方法。`initEvent` で初期化する)。
639        d.props.insert(
640            "createEvent".into(),
641            nv("document.createEvent", document_create_event),
642        );
643        // `document.createDocumentFragment()` が丸ごと未対応だった。
644        d.props.insert(
645            "createDocumentFragment".into(),
646            nv("document.createDocumentFragment", document_create_document_fragment),
647        );
648        d.props
649            .insert("location".into(), Value::Object(location.clone()));
650        d.props.insert(
651            "getElementsByClassName".into(),
652            nv(
653                "getElementsByClassName",
654                document_get_elements_by_class_name,
655            ),
656        );
657        d.props.insert(
658            "getElementsByTagName".into(),
659            nv("getElementsByTagName", document_get_elements_by_tag_name),
660        );
661        d.props.insert(
662            "getElementsByTagNameNS".into(),
663            nv("getElementsByTagNameNS", document_get_elements_by_tag_name_ns),
664        );
665        d.props.insert(
666            "getElementsByName".into(),
667            nv("getElementsByName", document_get_elements_by_name),
668        );
669        d.props.insert("title".into(), Value::str(""));
670        d.props.insert("activeElement".into(), Value::Null);
671        // `document`にも`EventTarget`風の汎用リスナ格納`_listeners`を持たせる
672        // (`click`/`keydown`等、`window`と共有しない汎用イベント種別の
673        // `addEventListener`受け皿。2026-07-16 発見・実装。詳細は
674        // `document_add_event_listener`のコメント参照)。
675        d.props.insert("_listeners".into(), Value::Object(Obj::plain()));
676        d.props.insert("readyState".into(), Value::str("complete"));
677        // `document.designMode`(`"on"`/`"off"`。HTML5、丸ごと未対応だった)。
678        // `element.contentEditable` と同じ理由でこの処理系にはキャレット/テキスト
679        // 編集機構自体が無いため、実際のページ全体編集可能化は行わず状態の読み書き
680        // のみを提供する簡略実装(既定 `"off"`)。
681        d.props.insert("designMode".into(), Value::str("off"));
682        // Fullscreen API(標準 + YouTube 等の Web プレーヤーが参照する webkit/moz/ms プレフィックス互換)。
683        d.props.insert("fullscreenElement".into(), Value::Null);
684        d.props.insert("webkitFullscreenElement".into(), Value::Null);
685        d.props.insert("mozFullScreenElement".into(), Value::Null);
686        d.props.insert("msFullscreenElement".into(), Value::Null);
687        d.props.insert("fullscreenEnabled".into(), Value::Bool(true));
688        d.props.insert("webkitFullscreenEnabled".into(), Value::Bool(true));
689        d.props.insert("mozFullScreenEnabled".into(), Value::Bool(true));
690        d.props.insert("msFullscreenEnabled".into(), Value::Bool(true));
691        d.props.insert("onfullscreenchange".into(), Value::Null);
692        d.props.insert("onwebkitfullscreenchange".into(), Value::Null);
693        d.props.insert("onfullscreenerror".into(), Value::Null);
694        d.props.insert(
695            "exitFullscreen".into(),
696            nv("document.exitFullscreen", document_exit_fullscreen),
697        );
698        d.props.insert(
699            "webkitExitFullscreen".into(),
700            nv("document.webkitExitFullscreen", document_exit_fullscreen),
701        );
702        d.props.insert(
703            "webkitCancelFullScreen".into(),
704            nv("document.webkitCancelFullScreen", document_exit_fullscreen),
705        );
706        d.props.insert(
707            "mozCancelFullScreen".into(),
708            nv("document.mozCancelFullScreen", document_exit_fullscreen),
709        );
710        d.props.insert(
711            "msExitFullscreen".into(),
712            nv("document.msExitFullscreen", document_exit_fullscreen),
713        );
714        // Pointer Lock API(丸ごと未対応だった)。Fullscreen API と同じ
715        // 「状態のみ追跡する」簡略方針(`pointerlockchange` イベントは対象外)。
716        d.props.insert("pointerLockElement".into(), Value::Null);
717        d.props.insert(
718            "exitPointerLock".into(),
719            nv("document.exitPointerLock", document_exit_pointer_lock),
720        );
721        // Picture-in-Picture API(丸ごと未対応だった)。Fullscreen/Pointer Lock
722        // API と同じ「状態のみ追跡する」簡略方針
723        // (`enterpictureinpicture`/`leavepictureinpicture` イベントは対象外)。
724        d.props.insert("pictureInPictureElement".into(), Value::Null);
725        d.props.insert("pictureInPictureEnabled".into(), Value::Bool(true));
726        d.props.insert(
727            "exitPictureInPicture".into(),
728            nv("document.exitPictureInPicture", document_exit_picture_in_picture),
729        );
730        // Storage Access API(`document.hasStorageAccess()`/
731        // `.requestStorageAccess()`。丸ごと未対応だった。サードパーティ
732        // iframe からの Cookie アクセス許可を確認/要求する定番パターン)。
733        // この処理系にはサードパーティ Cookie 分離/ブロック機構自体が無く
734        // ストレージは常にアクセス可能なため、`true`/成功を返すのは楽観視の
735        // 偽装ではなく実態に即した誠実な近似。
736        d.props.insert(
737            "hasStorageAccess".into(),
738            nv("document.hasStorageAccess", document_has_storage_access),
739        );
740        d.props.insert(
741            "requestStorageAccess".into(),
742            nv("document.requestStorageAccess", document_request_storage_access),
743        );
744        // `document.addEventListener('DOMContentLoaded'|'load', fn)` が単なる no-op で
745        // リスナが一切発火しない黙殺バグだった(`window.addEventListener` 側は既に
746        // 同じイベントを即時発火する実装があったが、`document` 側は同じ関数を配線
747        // し忘れていた非対称な状態。`document.addEventListener('DOMContentLoaded', ...)`
748        // は `window` 版よりもむしろ広く使われる定番パターン)。`document_add_event_
749        // listener`(`window_api.rs`)が固定種別は `window` と共有し、それ以外
750        // (`click`/`keydown`等の汎用種別)は `document` 自身の `_listeners` へ
751        // 振り分ける(2026-07-16、`document.addEventListener('click', ...)` の
752        // イベント委譲パターン対応で拡張)。
753        d.props.insert(
754            "addEventListener".into(),
755            nv("document.addEventListener", document_add_event_listener),
756        );
757        // `document.removeEventListener`(丸ごと未対応だった。`addEventListener`
758        // は `window` 版を再利用する形で配線済みだったが、対になる
759        // `removeEventListener` 自体が登録されておらず「関数ではない」
760        // `TypeError` になっていた)。
761        d.props.insert(
762            "removeEventListener".into(),
763            nv("document.removeEventListener", document_remove_event_listener),
764        );
765        // `document.dispatchEvent`(丸ごと未対応だった。`addEventListener`/
766        // `removeEventListener` と同じ兄弟ギャップ)。
767        d.props.insert(
768            "dispatchEvent".into(),
769            nv("document.dispatchEvent", document_dispatch_event),
770        );
771        d.props
772            .insert("write".into(), nv("document.write", dom_noop));
773        d.props.insert(
774            "createRange".into(),
775            nv("document.createRange", document_create_range),
776        );
777        // `document.createTreeWalker(root, whatToShow?, filter?)`(丸ごと
778        // 未対応だった。DOM 部分木を走査する定番パターン)。
779        d.props.insert(
780            "createTreeWalker".into(),
781            nv("document.createTreeWalker", document_create_tree_walker),
782        );
783        // `document.createNodeIterator(root, whatToShow?, filter?)`(丸ごと
784        // 未対応だった)。`TreeWalker` とは異なり内部状態が「参照ノード+
785        // 前後どちら側を指しているか」のポインタモデルで、`nextNode()`/
786        // `previousNode()` を交互に呼ぶと同じノードへ一旦戻る仕様上の挙動がある。
787        d.props.insert(
788            "createNodeIterator".into(),
789            nv("document.createNodeIterator", document_create_node_iterator),
790        );
791        // `document.importNode(externalNode, deep)`/`document.adoptNode(node)`
792        // (丸ごと未対応だった。`<template>` の中身を取り込む定番パターン
793        // `document.importNode(template.content, true)` 等で使われる)。
794        d.props.insert(
795            "importNode".into(),
796            nv("document.importNode", doc_import_node),
797        );
798        d.props.insert(
799            "adoptNode".into(),
800            nv("document.adoptNode", doc_adopt_node),
801        );
802        // `document.createAttribute(name)`(丸ごと未対応だった。`getAttributeNode`
803        // と対になる `Attr` ノード新規作成パターン)。
804        d.props.insert(
805            "createAttribute".into(),
806            nv("document.createAttribute", document_create_attribute),
807        );
808        d.props.insert(
809            "getSelection".into(),
810            nv("document.getSelection", document_get_selection),
811        );
812        d.props.insert(
813            "execCommand".into(),
814            nv("document.execCommand", document_exec_command),
815        );
816        // `document.getAnimations()`(丸ごと未対応だった。`element.
817        // getAnimations()` と同じく実行中アニメーションを一切保持していない
818        // ため常に空配列を返す簡略実装)。
819        d.props.insert(
820            "getAnimations".into(),
821            nv("document.getAnimations", |_, _, _| Ok(Value::Object(Obj::array(alloc::vec![])))),
822        );
823        d.props.insert(
824            "hasFocus".into(),
825            nv("document.hasFocus", |_, _, _| Ok(Value::Bool(true))),
826        );
827        // `document.visibilityState`/`.hidden`(Page Visibility API。丸ごと
828        // 未対応だった。`visibilitychange`イベント自体は前サイクルで登録経路
829        // のみ実装済みだったが、対になる状態プロパティが欠けていた)。この
830        // OS にはタブ/ウィンドウのバックグラウンド化という概念自体が無く
831        // GUI は常時フォアグラウンドのため、`document.hasFocus()`と同じく
832        // 常に「可視」を返す誠実な簡略実装。
833        d.props.insert("visibilityState".into(), Value::str("visible"));
834        d.props.insert("hidden".into(), Value::Bool(false));
835        // `document.characterSet`/`.compatMode`/`.contentType`/`.doctype`
836        // (丸ごと未対応だった。2026-07-16 発見)。この処理系は常に UTF-8
837        // でパースし `<!DOCTYPE html>` 前提の標準準拠モードのみをサポート
838        // するため、`characterSet`/`compatMode`/`contentType`は固定値の
839        // 誠実な簡略実装。`doctype`(`DocumentType` ノード)はパース時に
840        // 一切モデル化していない(HTML パーサが `<!DOCTYPE ...>` を読み
841        // 飛ばすのみでノード化しない)ため、「DocumentType ノードが存在
842        // しない文書」を表す仕様どおりの `null` を返す。
843        d.props.insert("characterSet".into(), Value::str("UTF-8"));
844        d.props.insert("compatMode".into(), Value::str("CSS1Compat"));
845        d.props.insert("contentType".into(), Value::str("text/html"));
846        d.props.insert("doctype".into(), Value::Null);
847        // `document.currentScript`(丸ごと未対応だった。2026-07-16 発見)。
848        // 仕様上「同期実行中の非モジュールクラシックスクリプトの実行中のみ
849        // 対応する `<script>` 要素、それ以外(イベントハンドラ・タイマー・
850        // 実行完了後)は常に `null`」だが、この処理系には「現在実行中の
851        // script 要素」を追跡する仕組みが無い(`<script>` はパース時に一括
852        // 逐次評価されるのみで、実行スタックとノードの紐付けを持たない)
853        // ため、`getAnimations()`と同方針の「状態追跡なし」の誠実な簡略
854        // 実装として常に `null` を返す。
855        d.props.insert("currentScript".into(), Value::Null);
856        // `document.implementation`(`DOMImplementation`。丸ごと未対応
857        // だった。2026-07-16 発見)。`hasFeature()` は仕様上「常に `true`
858        // を返すレガシーな no-op」(実際の機能判定は行わない)と明記されて
859        // いるため引数を無視して固定 `true` を返す誠実な実装。
860        // `createDocumentType()` は他ノードから独立した単体オブジェクトを
861        // 組み立てるだけで済むため対応。一方 `createDocument()`/
862        // `createHTMLDocument()` はこの処理系が単一のグローバル `document`
863        // シングルトンしか持たず独立した2つ目の文書ツリーを表現できない
864        // ため、`customElements`/`<canvas>` と同格の大規模改修が必要と
865        // 判断し本サイクルでは対象外とする(詳細は TODO.md 参照)。
866        let implementation = Obj::plain();
867        {
868            let mut im = implementation.borrow_mut();
869            im.props.insert(
870                "hasFeature".into(),
871                nv("DOMImplementation.hasFeature", |_, _, _| Ok(Value::Bool(true))),
872            );
873            im.props.insert(
874                "createDocumentType".into(),
875                nv("DOMImplementation.createDocumentType", dom_implementation_create_document_type),
876            );
877            im.props.insert(
878                "createHTMLDocument".into(),
879                nv("DOMImplementation.createHTMLDocument", dom_implementation_create_html_document),
880            );
881            im.props.insert(
882                "createDocument".into(),
883                nv("DOMImplementation.createDocument", dom_implementation_create_document),
884            );
885        }
886        d.props
887            .insert("implementation".into(), Value::Object(implementation));
888    }
889    /// `document.implementation.createDocumentType(qualifiedName, publicId,
890    /// systemId)`(丸ごと未対応だった。2026-07-16 発見)。この処理系には
891    /// `DocumentType` 専用の内部表現が無いため、`element.attributes`/
892    /// `getAttributeNode` と同じ「読み取り専用簡略実装」方針で、`{name,
893    /// publicId, systemId}` の平プレーンオブジェクトを返す(どのノードにも
894    /// 接続されず `document.doctype` へも反映されない独立した値)。
895    fn dom_implementation_create_document_type(
896        _it: &mut Interp,
897        _t: Value,
898        a: &[Value],
899    ) -> Result<Value, Value> {
900        let obj = Obj::plain();
901        {
902            let mut b = obj.borrow_mut();
903            b.props.insert("nodeType".into(), Value::Number(10.0));
904            b.props.insert("name".into(), Value::str(arg(a, 0).to_js_string()));
905            b.props.insert("publicId".into(), Value::str(arg(a, 1).to_js_string()));
906            b.props.insert("systemId".into(), Value::str(arg(a, 2).to_js_string()));
907        }
908        Ok(Value::Object(obj))
909    }
910    fn dom_implementation_create_html_document(
911        _it: &mut Interp,
912        _t: Value,
913        a: &[Value],
914    ) -> Result<Value, Value> {
915        let title = if !a.is_empty() && !matches!(a[0], Value::Undefined) {
916            a[0].to_js_string()
917        } else {
918            String::new()
919        };
920        let doc = Obj::plain();
921        {
922            let mut b = doc.borrow_mut();
923            b.props.insert("nodeType".into(), Value::Number(9.0));
924            b.props.insert("nodeName".into(), Value::str("#document"));
925            b.props.insert("title".into(), Value::str(&title));
926            b.props.insert("body".into(), Value::Null);
927            b.props.insert("head".into(), Value::Null);
928            b.props.insert("documentElement".into(), Value::Null);
929            b.props.insert("createElement".into(), nv("createElement", document_create_element));
930        }
931        Ok(Value::Object(doc))
932    }
933    fn dom_implementation_create_document(
934        _it: &mut Interp,
935        _t: Value,
936        a: &[Value],
937    ) -> Result<Value, Value> {
938        let ns = arg(a, 0);
939        let qname = arg(a, 1);
940        let doc = Obj::plain();
941        {
942            let mut b = doc.borrow_mut();
943            b.props.insert("nodeType".into(), Value::Number(9.0));
944            b.props.insert("nodeName".into(), Value::str("#document"));
945            b.props.insert("namespaceURI".into(), ns);
946            b.props.insert("qualifiedName".into(), qname);
947            b.props.insert("createElement".into(), nv("createElement", document_create_element));
948        }
949        Ok(Value::Object(doc))
950    }
951    g.vars
952        .insert("document".into(), Value::Object(document.clone()));
953
954    // window(最小限。document への参照と no-op を提供)
955    let window = Obj::plain();
956    let screen_obj = Obj::plain();
957    {
958        let mut w = window.borrow_mut();
959        w.props.insert("document".into(), Value::Object(document));
960        w.props
961            .insert("location".into(), Value::Object(location.clone()));
962        w.props
963            .insert("history".into(), Value::Object(history.clone()));
964        w.props.insert(
965            "addEventListener".into(),
966            nv("window.addEventListener", window_add_event_listener),
967        );
968        w.props.insert(
969            "removeEventListener".into(),
970            nv("window.removeEventListener", window_remove_event_listener),
971        );
972        w.props.insert(
973            "dispatchEvent".into(),
974            nv("window.dispatchEvent", window_dispatch_event),
975        );
976        // `window.print()`(丸ごと未対応だった)。実際の印刷パイプライン自体が
977        // 無いため、`focus`/`blur`/`scrollIntoView` と同じ無害な no-op とする。
978        w.props.insert("print".into(), nv("window.print", dom_noop));
979        w.props.insert("scrollY".into(), Value::Number(0.0));
980        w.props.insert("pageYOffset".into(), Value::Number(0.0));
981        w.props.insert("scrollX".into(), Value::Number(0.0));
982        w.props.insert("pageXOffset".into(), Value::Number(0.0));
983        w.props
984            .insert("scrollTo".into(), nv("scrollTo", window_scroll_to));
985        w.props
986            .insert("scroll".into(), nv("scroll", window_scroll_to));
987        // `window.scrollBy(x, y)`(丸ごと未対応だった。`window.`修飾無しの
988        // bare `scrollBy`〔グローバル関数〕は登録されていたが、`window.
989        // scrollBy(...)`とプロパティ経由でアクセスする形は登録が漏れており
990        // `TypeError: not a function`になっていた。`scrollTo`/`scroll`は
991        // 両方の形が登録済みだったのに`scrollBy`だけこの非対称漏れが
992        // あった。2026-07-16 発見・実装)。
993        w.props
994            .insert("scrollBy".into(), nv("window.scrollBy", window_scroll_by));
995        w.props
996            .insert("matchMedia".into(), nv("matchMedia", window_match_media));
997        // `window.postMessage(data, targetOrigin)`(丸ごと未対応だった)。
998        // 別ウィンドウ/フレーム間通信の仕様上の非同期性・`targetOrigin` に
999        // よる送信先絞り込みは実装せず、同一 `window` 上の 'message' リスナへ
1000        // 同期的に配送する簡略実装(この処理系にはフレーム分離が無いため
1001        // 実用上大きな差は生じない)。
1002        w.props
1003            .insert("postMessage".into(), nv("postMessage", window_post_message));
1004        w.props.insert("alert".into(), nv("alert", console_log));
1005        // `window.confirm`/`window.prompt`(丸ごと未対応だった。`alert`は
1006        // 既に`console_log`で代替されていたのに、対になるこの2つだけ
1007        // グローバルにも`window`にも一切登録が無く「関数ではない」
1008        // `TypeError`になっていた。2026-07-16 発見・実装)。
1009        w.props
1010            .insert("confirm".into(), nv("window.confirm", window_confirm));
1011        w.props
1012            .insert("prompt".into(), nv("window.prompt", window_prompt));
1013        // `window.open`/`.close`/`.focus`/`.blur`(丸ごと未対応だった。同じ
1014        // 監査で発見。呼び出すと`TypeError`になっていた。2026-07-16
1015        // 発見・実装)。
1016        w.props
1017            .insert("open".into(), nv("window.open", window_open));
1018        w.props
1019            .insert("close".into(), nv("window.close", window_close));
1020        // `window.stop()`(丸ごと未対応だった。詳細は`window_stop`参照。
1021        // 2026-07-17 発見・実装)。
1022        w.props
1023            .insert("stop".into(), nv("window.stop", window_stop));
1024        w.props
1025            .insert("focus".into(), nv("window.focus", window_focus));
1026        w.props
1027            .insert("blur".into(), nv("window.blur", window_focus));
1028        // `window.getSelection()`(丸ごと未対応だった。仕様上こちらが本来の
1029        // 正規の場所で、`document.getSelection()`はレガシーな別名。この
1030        // 処理系は逆に`document.getSelection`だけを実装しており、より
1031        // 一般的に使われる`window.getSelection().toString()`等のイディオムが
1032        // `TypeError`になっていた。2026-07-16 発見・実装。同じ`document_get_
1033        // selection`実装を再利用)。
1034        w.props.insert(
1035            "getSelection".into(),
1036            nv("window.getSelection", document_get_selection),
1037        );
1038        // `window.self`/`.top`/`.parent`/`.frames`/`.name`(丸ごと未対応
1039        // だった。bare `self`/`top`/`parent`/`frames`〔`g.vars`〕と同じ
1040        // 理由。この処理系には独立JSレルムを持つiframeが無く常に単一
1041        // フレームのため、仕様どおり自分自身を指す。`.name`はプロパティ
1042        // として読み書き可能な単なる文字列で既定は空文字列)。
1043        w.props.insert("self".into(), Value::Object(window.clone()));
1044        w.props.insert("top".into(), Value::Object(window.clone()));
1045        w.props.insert("parent".into(), Value::Object(window.clone()));
1046        w.props.insert("frames".into(), Value::Object(window.clone()));
1047        w.props.insert("name".into(), Value::str(""));
1048        w.props.insert(
1049            "getComputedStyle".into(),
1050            nv("getComputedStyle", get_computed_style),
1051        );
1052        w.props.insert(
1053            "localStorage".into(),
1054            Value::Object(Obj::host("storage:local")),
1055        );
1056        w.props.insert(
1057            "sessionStorage".into(),
1058            Value::Object(Obj::host("storage:session")),
1059        );
1060        // viewport dimensions(VIEWPORT_HINT がセットされていれば使う)
1061        let vw = super::super::layout::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
1062        let vh = super::super::layout::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
1063        let vw_f = if vw > 0 { vw as f64 } else { 1024.0 };
1064        let vh_f = if vh > 0 { vh as f64 } else { 600.0 };
1065        w.props.insert("innerWidth".into(), Value::Number(vw_f));
1066        w.props.insert("innerHeight".into(), Value::Number(vh_f));
1067        w.props.insert("outerWidth".into(), Value::Number(vw_f));
1068        w.props.insert("outerHeight".into(), Value::Number(vh_f));
1069        w.props
1070            .insert("devicePixelRatio".into(), Value::Number(1.0));
1071        // (`window.scrollBy` は上の方で `window_scroll_by` へ正しく登録済み。
1072        // ここに以前あった `nv("scrollBy", window_scroll_to)` という重複
1073        // 登録は、後勝ちの `props.insert` で正しい登録を静かに上書きし
1074        // `window.scrollBy(dx,dy)` が実質 `scrollTo` と誤動作するバグ
1075        // だった。2026-07-16 発見・削除)。
1076        // `window.screen`(丸ごと未対応だった。`screen.width`/`.height`/
1077        // `.availWidth`/`.availHeight`/`.colorDepth`/`.pixelDepth`/
1078        // `.orientation.type` というレスポンシブ分岐の定番パターンで参照される
1079        // 標準オブジェクト。タスクバー等が無いためウインドウ全体を占有する
1080        // ビューポート寸法と `avail*` を同一値にする簡略実装)。
1081        {
1082            let mut s = screen_obj.borrow_mut();
1083            s.props.insert("width".into(), Value::Number(vw_f));
1084            s.props.insert("height".into(), Value::Number(vh_f));
1085            s.props.insert("availWidth".into(), Value::Number(vw_f));
1086            s.props.insert("availHeight".into(), Value::Number(vh_f));
1087            s.props.insert("colorDepth".into(), Value::Number(24.0));
1088            s.props.insert("pixelDepth".into(), Value::Number(24.0));
1089            let orientation = Obj::plain();
1090            let otype = if vw_f >= vh_f { "landscape-primary" } else { "portrait-primary" };
1091            orientation.borrow_mut().props.insert("type".into(), Value::str(otype));
1092            orientation.borrow_mut().props.insert("angle".into(), Value::Number(0.0));
1093            // `screen.orientation.lock()`/`.unlock()`(丸ごと未対応だった)。
1094            // 実際にディスプレイの向きを固定する機構は無いため、`fullscreenElement`
1095            // 等と同じ「状態のみ追跡する」簡略実装とする(`lock()` は要求された
1096            // 向きへ `type` を書き換えるだけ、`unlock()` は現在のビューポート寸法
1097            // から求めた自然な向きへ戻す)。
1098            orientation
1099                .borrow_mut()
1100                .props
1101                .insert("lock".into(), nv("screen.orientation.lock", screen_orientation_lock));
1102            orientation.borrow_mut().props.insert(
1103                "unlock".into(),
1104                nv("screen.orientation.unlock", screen_orientation_unlock),
1105            );
1106            s.props.insert("orientation".into(), Value::Object(orientation));
1107        }
1108        w.props.insert("screen".into(), Value::Object(screen_obj.clone()));
1109    }
1110    // `window.cookieStore`(Cookie Store API。丸ごと未対応だった。従来の
1111    // `document.cookie` 文字列パースに代わる非同期・構造化された定番の
1112    // 読み書き方式)。既存の `COOKIES`(`document.cookie` の実データ)を
1113    // そのまま共有するため、両方の API 経由での読み書きが相互に反映される。
1114    let cookie_store = Obj::plain();
1115    {
1116        let mut cs = cookie_store.borrow_mut();
1117        cs.props.insert("get".into(), nv("cookieStore.get", cookie_store_get));
1118        cs.props.insert("getAll".into(), nv("cookieStore.getAll", cookie_store_get_all));
1119        cs.props.insert("set".into(), nv("cookieStore.set", cookie_store_set));
1120        cs.props.insert("delete".into(), nv("cookieStore.delete", cookie_store_delete));
1121    }
1122    window
1123        .borrow_mut()
1124        .props
1125        .insert("cookieStore".into(), Value::Object(cookie_store.clone()));
1126
1127    // `window.customElements`(Web Components / Custom Elements API)
1128    let custom_elements = Obj::plain();
1129    {
1130        let mut ce = custom_elements.borrow_mut();
1131        ce.props.insert("define".into(), nv("customElements.define", custom_elements_define));
1132        ce.props.insert("get".into(), nv("customElements.get", custom_elements_get));
1133        ce.props.insert("whenDefined".into(), nv("customElements.whenDefined", custom_elements_when_defined));
1134        ce.props.insert("upgrade".into(), nv("customElements.upgrade", custom_elements_upgrade));
1135        ce.props.insert("_registry".into(), Value::Object(Obj::plain()));
1136    }
1137    window
1138        .borrow_mut()
1139        .props
1140        .insert("customElements".into(), Value::Object(custom_elements.clone()));
1141
1142    // WHATWG Streams API
1143    window
1144        .borrow_mut()
1145        .props
1146        .insert("ReadableStream".into(), nv("ReadableStream", readable_stream_ctor));
1147    window
1148        .borrow_mut()
1149        .props
1150        .insert("WritableStream".into(), nv("WritableStream", writable_stream_ctor));
1151    window
1152        .borrow_mut()
1153        .props
1154        .insert("TransformStream".into(), nv("TransformStream", transform_stream_ctor));
1155
1156    // W3C Media Source Extensions (MSE)
1157    let media_source_ctor_obj = Obj::native("MediaSource", media_source_ctor);
1158    media_source_ctor_obj.borrow_mut().props.insert(
1159        "isTypeSupported".into(),
1160        nv("MediaSource.isTypeSupported", media_source_is_type_supported),
1161    );
1162    let media_source_ctor_val = Value::Object(media_source_ctor_obj);
1163    window
1164        .borrow_mut()
1165        .props
1166        .insert("MediaSource".into(), media_source_ctor_val.clone());
1167    let worker_ctor_val = nv("Worker", worker_ctor);
1168    window
1169        .borrow_mut()
1170        .props
1171        .insert("Worker".into(), worker_ctor_val.clone());
1172    let text_encoder_stream_ctor_val = nv("TextEncoderStream", text_encoder_stream_ctor);
1173    let text_decoder_stream_ctor_val = nv("TextDecoderStream", text_decoder_stream_ctor);
1174    window
1175        .borrow_mut()
1176        .props
1177        .insert("TextEncoderStream".into(), text_encoder_stream_ctor_val.clone());
1178    window
1179        .borrow_mut()
1180        .props
1181        .insert("TextDecoderStream".into(), text_decoder_stream_ctor_val.clone());
1182
1183    g.vars.insert("window".into(), Value::Object(window));
1184    g.vars.insert("screen".into(), Value::Object(screen_obj));
1185    g.vars.insert("cookieStore".into(), Value::Object(cookie_store));
1186    g.vars.insert("customElements".into(), Value::Object(custom_elements));
1187    g.vars.insert("ReadableStream".into(), nv("ReadableStream", readable_stream_ctor));
1188    g.vars.insert("WritableStream".into(), nv("WritableStream", writable_stream_ctor));
1189    g.vars.insert("TransformStream".into(), nv("TransformStream", transform_stream_ctor));
1190    g.vars.insert("MediaSource".into(), media_source_ctor_val);
1191    g.vars.insert("Worker".into(), worker_ctor_val);
1192    g.vars.insert("TextEncoderStream".into(), text_encoder_stream_ctor_val);
1193    g.vars.insert("TextDecoderStream".into(), text_decoder_stream_ctor_val);
1194    g.vars.insert(
1195        "innerWidth".into(),
1196        Value::Number({
1197            let v =
1198                super::super::layout::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
1199            if v > 0 {
1200                v as f64
1201            } else {
1202                1024.0
1203            }
1204        }),
1205    );
1206    g.vars.insert(
1207        "innerHeight".into(),
1208        Value::Number({
1209            let v =
1210                super::super::layout::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
1211            if v > 0 {
1212                v as f64
1213            } else {
1214                600.0
1215            }
1216        }),
1217    );
1218    g.vars.insert(
1219        "getComputedStyle".into(),
1220        nv("getComputedStyle", get_computed_style),
1221    );
1222    g.vars.insert(
1223        "localStorage".into(),
1224        Value::Object(Obj::host("storage:local")),
1225    );
1226    g.vars.insert(
1227        "sessionStorage".into(),
1228        Value::Object(Obj::host("storage:session")),
1229    );
1230
1231    // URL / URLSearchParams / URLPattern
1232    g.vars.insert(
1233        "URLSearchParams".into(),
1234        nv("URLSearchParams", url_search_params_ctor),
1235    );
1236    g.vars.insert(
1237        "URLPattern".into(),
1238        nv("URLPattern", url_pattern_ctor),
1239    );
1240    g.vars
1241        .insert("FormData".into(), nv("FormData", form_data_ctor));
1242    // `URL.canParse(url, base?)`(ES2023/WHATWG。`new URL()` を try/catch で
1243    // 包む定番の存在確認イディオムを1メソッドで完結させる)が丸ごと未対応
1244    // だった。
1245    // `URL.createObjectURL(blob)`/`.revokeObjectURL(url)`(`Blob`/`File` を
1246    // 一意な `blob:` URL へ変換する定番パターン)が丸ごと未対応だった。
1247    {
1248        let url_ctor_val = nv("URL", url_ctor);
1249        if let Value::Object(o) = &url_ctor_val {
1250            let mut b = o.borrow_mut();
1251            b.props.insert("canParse".into(), nv("URL.canParse", url_can_parse));
1252            // `URL.parse(url, base?)`(ES2024/WHATWG。丸ごと未対応だった。詳細は
1253            // url_parse 参照)。
1254            b.props.insert("parse".into(), nv("URL.parse", url_parse));
1255            b.props.insert(
1256                "createObjectURL".into(),
1257                nv("URL.createObjectURL", url_create_object_url),
1258            );
1259            b.props.insert(
1260                "revokeObjectURL".into(),
1261                nv("URL.revokeObjectURL", url_revoke_object_url),
1262            );
1263        }
1264        g.vars.insert("URL".into(), url_ctor_val);
1265    }
1266
1267    // XMLSerializer(丸ごと未対応だった。`new XMLSerializer().serializeToString(node)`
1268    // という定番パターン。既存の `outerHTML` ゲッターと同じ
1269    // `DomBridge::get_outer_html` を再利用する。詳細は xml_serializer_ctor 参照)。
1270    g.vars
1271        .insert("XMLSerializer".into(), nv("XMLSerializer", xml_serializer_ctor));
1272
1273    // URI エンコード
1274    g.vars.insert(
1275        "encodeURIComponent".into(),
1276        nv("encodeURIComponent", js_encode_uri_component),
1277    );
1278    g.vars.insert(
1279        "decodeURIComponent".into(),
1280        nv("decodeURIComponent", js_decode_uri_component),
1281    );
1282    g.vars
1283        .insert("encodeURI".into(), nv("encodeURI", js_encode_uri));
1284    g.vars
1285        .insert("decodeURI".into(), nv("decodeURI", js_decode_uri));
1286    // `escape`/`unescape`(Annex B.2.1)が丸ごと未対応だった。`encodeURI` 系より
1287    // 古い遺産だが、実務コードになお残る(詳細は各関数の実装コメント参照)。
1288    g.vars.insert("escape".into(), nv("escape", js_escape));
1289    g.vars.insert("unescape".into(), nv("unescape", js_unescape));
1290    // `new ClipboardItem({mimeType: blob})`(丸ごと未対応だった。
1291    // `navigator.clipboard.write()` に渡す定番パターン)。
1292    g.vars.insert("ClipboardItem".into(), nv("ClipboardItem", clipboard_item_ctor));
1293    // `new EyeDropper()`(EyeDropper API。丸ごと未対応だった。`.open()` で
1294    // 画面上の色を拾うカラーピッカー UI を開く定番パターン)。この処理系には
1295    // 実際のピッカー UI が無くいかなる色も本当には拾えないため、
1296    // `navigator.share` と同じ「できないことを楽観視しない」方針で、
1297    // ユーザーがピッカーをキャンセルした場合と同じ仕様どおりの `AbortError`
1298    // で常に拒否する。
1299    g.vars.insert("EyeDropper".into(), nv("EyeDropper", eye_dropper_ctor));
1300
1301    // base64
1302    g.vars.insert("btoa".into(), nv("btoa", btoa));
1303    g.vars.insert("atob".into(), nv("atob", atob));
1304
1305    // navigator(最小限)
1306    let navigator = Obj::plain();
1307    {
1308        let mut n = navigator.borrow_mut();
1309        n.props.insert(
1310            "userAgent".into(),
1311            Value::str("AtmOS/1.0 (Raspberry Pi; aarch64) AtmOSBrowser/1.0"),
1312        );
1313        n.props.insert("platform".into(), Value::str("AtmOS"));
1314        n.props.insert("language".into(), Value::str("ja-JP"));
1315        n.props.insert("onLine".into(), Value::Bool(true));
1316        n.props.insert("vendor".into(), Value::str("Sugimura Lab"));
1317        // `navigator.cookieEnabled`/`.hardwareConcurrency`/`.maxTouchPoints`/
1318        // `.languages`/`.webdriver`(丸ごと未対応だった。いずれも
1319        // `if (!navigator.cookieEnabled) {...}` 等のフィーチャー検出で
1320        // 参照される定番プロパティ)。`hardwareConcurrency` は QEMU 起動時の
1321        // `-smp 4`(RPi3B+ の実コア数)に合わせた固定値、`maxTouchPoints` は
1322        // タッチスクリーンが無いため 0、`webdriver` は自動操作されていない
1323        // ため `false` の固定値。
1324        n.props.insert("cookieEnabled".into(), Value::Bool(true));
1325        n.props
1326            .insert("hardwareConcurrency".into(), Value::Number(4.0));
1327        n.props.insert("maxTouchPoints".into(), Value::Number(0.0));
1328        n.props.insert(
1329            "languages".into(),
1330            Value::Object(Obj::array(alloc::vec![Value::str("ja-JP")])),
1331        );
1332        n.props.insert("webdriver".into(), Value::Bool(false));
1333        // `navigator.userAgentData`(Client Hints API。丸ごと未対応
1334        // だった。`navigator.userAgent`文字列パースに代わる構造化版の
1335        // 定番フィーチャー検出パターン。2026-07-17 発見・実装)。既存の
1336        // `userAgent`文字列("AtmOSBrowser/1.0")/`platform`("AtmOS")と
1337        // 整合する固定値を返す誠実な簡略実装。
1338        let ua_data = Obj::plain();
1339        {
1340            let mut u = ua_data.borrow_mut();
1341            let brand_entry = Obj::plain();
1342            brand_entry.borrow_mut().props.insert("brand".into(), Value::str("AtmOSBrowser"));
1343            brand_entry.borrow_mut().props.insert("version".into(), Value::str("1"));
1344            u.props.insert(
1345                "brands".into(),
1346                Value::Object(Obj::array(alloc::vec![Value::Object(brand_entry)])),
1347            );
1348            u.props.insert("mobile".into(), Value::Bool(false));
1349            u.props.insert("platform".into(), Value::str("AtmOS"));
1350            u.props.insert(
1351                "getHighEntropyValues".into(),
1352                nv("getHighEntropyValues", navigator_get_high_entropy_values),
1353            );
1354        }
1355        n.props.insert("userAgentData".into(), Value::Object(ua_data));
1356    }
1357    // `navigator.clipboard`(丸ごと未対応だった。`navigator.clipboard.writeText(...)`/
1358    // `readText()` というコピー&ペースト UI の定番パターン)。この OS には実際の
1359    // OS 全体で共有されるクリップボードが無いため、単純なプロセス内の文字列保持
1360    // (`_clipboard` prop)で近似する。
1361    let clipboard = Obj::plain();
1362    {
1363        let mut c = clipboard.borrow_mut();
1364        c.props.insert("_clipboard".into(), Value::str(""));
1365        c.props.insert("writeText".into(), nv("writeText", clipboard_write_text));
1366        c.props.insert("readText".into(), nv("readText", clipboard_read_text));
1367        // `navigator.clipboard.write(items)`/`.read()`(`ClipboardItem` 配列版。
1368        // 丸ごと未対応だった。`text/plain` エントリのみ対応する簡略実装で、
1369        // `writeText`/`readText` と同じ `_clipboard` prop を共有する)。
1370        c.props.insert("write".into(), nv("write", navigator_clipboard_write));
1371        c.props.insert("read".into(), nv("read", navigator_clipboard_read));
1372    }
1373    navigator
1374        .borrow_mut()
1375        .props
1376        .insert("clipboard".into(), Value::Object(clipboard));
1377    // `navigator.storage`(StorageManager API。`.estimate()`/`.persist()`/
1378    // `.persisted()`が丸ごと未対応だった。2026-07-17 発見)。この処理系
1379    // には実際のディスク使用量計測・永続化権限の概念が無い(SylFSへの
1380    // 書き込みは常に永続化される)ため、`usage: 0`(未計測の誠実な簡略値)・
1381    // `quota`は固定の代表値、`persist`/`persisted`は常に`true`を返す
1382    // 簡略実装。
1383    let storage_mgr = Obj::plain();
1384    {
1385        let mut s = storage_mgr.borrow_mut();
1386        s.props.insert("estimate".into(), nv("estimate", navigator_storage_estimate));
1387        s.props.insert("persist".into(), nv("persist", navigator_storage_persist));
1388        s.props.insert("persisted".into(), nv("persisted", navigator_storage_persist));
1389    }
1390    navigator
1391        .borrow_mut()
1392        .props
1393        .insert("storage".into(), Value::Object(storage_mgr));
1394    // `navigator.sendBeacon(url, data)`(丸ごと未対応だった。ページ離脱時の
1395    // 計測データ送信に使われる定番パターン。詳細は navigator_send_beacon 参照)。
1396    navigator
1397        .borrow_mut()
1398        .props
1399        .insert("sendBeacon".into(), nv("sendBeacon", navigator_send_beacon));
1400    // `navigator.geolocation`(丸ごと未対応だった。`getCurrentPosition(success,
1401    // error, options)`/`watchPosition(...)`/`clearWatch(id)` という位置情報
1402    // 取得の定番パターン)。この OS には実際の位置情報ハードウェア/OS権限機構が
1403    // 一切無いため、常に `errorCallback` を `PERMISSION_DENIED`(実ブラウザで
1404    // 権限拒否時に返る値と同じコード)で同期的に呼ぶ簡略実装とする(`clipboard`
1405    // のような部分的な近似すら成立しない、この処理系に無い機能そのものであるため)。
1406    let geolocation = Obj::plain();
1407    {
1408        let mut geo = geolocation.borrow_mut();
1409        geo.props.insert(
1410            "getCurrentPosition".into(),
1411            nv("getCurrentPosition", geolocation_get_current_position),
1412        );
1413        geo.props
1414            .insert("watchPosition".into(), nv("watchPosition", geolocation_watch_position));
1415        geo.props
1416            .insert("clearWatch".into(), nv("clearWatch", geolocation_clear_watch));
1417    }
1418    navigator
1419        .borrow_mut()
1420        .props
1421        .insert("geolocation".into(), Value::Object(geolocation));
1422    // `navigator.permissions.query({name})`(丸ごと未対応だった。`if
1423    // ((await navigator.permissions.query({name:'geolocation'})).state ===
1424    // 'granted') {...}` という定番の機能可否事前判定パターン)。この処理系には
1425    // 実際の権限プロンプト UI が無いため、各 API の実装状況と整合する固定値を
1426    // 返す簡略実装とする(`geolocation` は常に `PERMISSION_DENIED` を返す実装
1427    // と整合させ `denied`、`Notification.permission` が常に `granted` と
1428    // 整合する `notifications` は `granted`、その他は `prompt`)。
1429    let permissions = Obj::plain();
1430    permissions
1431        .borrow_mut()
1432        .props
1433        .insert("query".into(), nv("permissions.query", permissions_query));
1434    navigator
1435        .borrow_mut()
1436        .props
1437        .insert("permissions".into(), Value::Object(permissions));
1438    // `navigator.wakeLock.request(type)`(Screen Wake Lock API。丸ごと未対応
1439    // だった。`await navigator.wakeLock.request('screen')` でスリープ抑制を
1440    // 要求する定番パターン)。この処理系には実際の電源管理機構が無いため、
1441    // `WakeLockSentinel` 相当の `{released, type, release()}` を状態のみ
1442    // 追跡する簡略実装で返す(`release` イベントは対象外)。
1443    let wake_lock = Obj::plain();
1444    wake_lock
1445        .borrow_mut()
1446        .props
1447        .insert("request".into(), nv("wakeLock.request", wake_lock_request));
1448    navigator
1449        .borrow_mut()
1450        .props
1451        .insert("wakeLock".into(), Value::Object(wake_lock));
1452    // `navigator.share(data)`/`navigator.canShare(data)`(Web Share API。
1453    // 丸ごと未対応だった。`await navigator.share({title, url})` でネイティブ
1454    // 共有シートを開く定番パターン)。この処理系には実際の共有先 UI が無い
1455    // ため、`navigator.geolocation` が実位置情報を取得できない以上
1456    // `PERMISSION_DENIED` を返すのと同じ「できないことを楽観視しない」方針で、
1457    // `canShare` は常に `false`、`share` は仕様どおり `AbortError` で拒否する
1458    // (何も共有できていないのに成功を偽装しない)。
1459    navigator
1460        .borrow_mut()
1461        .props
1462        .insert("canShare".into(), nv("navigator.canShare", navigator_can_share));
1463    navigator
1464        .borrow_mut()
1465        .props
1466        .insert("share".into(), nv("navigator.share", navigator_share));
1467    // `navigator.vibrate(pattern)`(Vibration API。丸ごと未対応だった)。
1468    // 仕様上この戻り値は「振動リクエストを受理したか」のみを表し、実際に
1469    // 振動モーターが存在するかとは無関係(ハードウェアの無い環境でも `true`
1470    // を返すのが仕様どおりの正しい挙動)なので、`geolocation` とは異なり
1471    // 常に `true` を返す簡略実装で問題ない。
1472    navigator
1473        .borrow_mut()
1474        .props
1475        .insert("vibrate".into(), nv("navigator.vibrate", navigator_vibrate));
1476    // `navigator.getBattery()`(Battery Status API。丸ごと未対応だった)。
1477    // このOSのターゲット(Raspberry Pi)はバッテリー非搭載で常時 AC 電源
1478    // 動作のため、「常時満充電で給電中」という値は楽観視の偽装ではなく
1479    // 実態に即した誠実な近似(`charging:true`/`level:1`/`chargingTime:0`/
1480    // `dischargingTime:Infinity`)。`levelchange` 等のイベントは対象外。
1481    navigator
1482        .borrow_mut()
1483        .props
1484        .insert("getBattery".into(), nv("navigator.getBattery", navigator_get_battery));
1485    // `navigator.connection`(Network Information API。丸ごと未対応
1486    // だった。`if (navigator.connection.saveData) {...}` という低速回線向け
1487    // 最適化分岐の定番パターン)。この処理系には実際の帯域/往復遅延の計測
1488    // 機構が無いため、`saveData:false`(節約モードを要求されていないと
1489    // 仮定する既定値)と一般的なブロードバンド相当の代表値を返す簡略実装
1490    // (`change` イベントは対象外)。
1491    let connection = Obj::plain();
1492    {
1493        let mut c = connection.borrow_mut();
1494        c.props.insert("type".into(), Value::str("ethernet"));
1495        c.props.insert("effectiveType".into(), Value::str("4g"));
1496        c.props.insert("downlink".into(), Value::Number(10.0));
1497        c.props.insert("rtt".into(), Value::Number(50.0));
1498        c.props.insert("saveData".into(), Value::Bool(false));
1499        c.props
1500            .insert("addEventListener".into(), nv("addEventListener", dom_noop));
1501        c.props
1502            .insert("removeEventListener".into(), nv("removeEventListener", dom_noop));
1503    }
1504    navigator
1505        .borrow_mut()
1506        .props
1507        .insert("connection".into(), Value::Object(connection));
1508    // `navigator.locks`(Web Locks API。丸ごと未対応だった)。この処理系には
1509    // 複数タブ/複数ワーカー間の実際の競合排他機構が無い(単一ドキュメント・
1510    // 単一スレッドの JS 実行モデル)ため、`request(name, callback)` は
1511    // コールバックを即座に同期呼び出しし、その戻り値で解決する Promise を
1512    // 返す簡略実装(`options` 引数中の `signal`/`mode`/`ifAvailable`/
1513    // `steal` は対象外)。`query()` は保持中/待機中とも常に空。
1514    let locks = Obj::plain();
1515    {
1516        let mut l = locks.borrow_mut();
1517        l.props.insert("request".into(), nv("navigator.locks.request", navigator_locks_request));
1518        l.props.insert("query".into(), nv("navigator.locks.query", navigator_locks_query));
1519    }
1520    navigator.borrow_mut().props.insert("locks".into(), Value::Object(locks));
1521    g.vars.insert("navigator".into(), Value::Object(navigator));
1522
1523    // performance.now(疑似単調増加)
1524    let performance = Obj::plain();
1525    performance
1526        .borrow_mut()
1527        .props
1528        .insert("now".into(), nv("performance.now", performance_now));
1529    // `performance.timeOrigin`(丸ごと未対応だった。`performance.now()`が
1530    // 相対時刻の基準とする実時刻を表す。ページ読込=プロセス起動という
1531    // この処理系の簡略化と整合させ、グローバル初期化時点の実時刻を
1532    // 固定値として使う)。
1533    performance
1534        .borrow_mut()
1535        .props
1536        .insert("timeOrigin".into(), Value::Number(epoch_ms_now()));
1537    // 【2026-10-03】`performance.timing`(Navigation Timing Level 1)と `performance.navigation`。
1538    // 非推奨だが全ブラウザが持ち、Cloudflare の beacon.min.js が `performance.timing.navigationStart`
1539    // を読んで落ちていた(sugi-lab.net 全ページで JS エラー 1 件)。この処理系は各段階の時刻を
1540    // 測っていないので、すべて読込開始時刻(timeOrigin)とする。起きていない unload/redirect は
1541    // 仕様どおり 0。
1542    {
1543        let origin = libm::floor(epoch_ms_now());
1544        let timing = Obj::plain();
1545        {
1546            let mut t = timing.borrow_mut();
1547            for key in [
1548                "navigationStart", "fetchStart", "domainLookupStart", "domainLookupEnd",
1549                "connectStart", "connectEnd", "secureConnectionStart", "requestStart",
1550                "responseStart", "responseEnd", "domLoading", "domInteractive",
1551                "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete",
1552                "loadEventStart", "loadEventEnd",
1553            ] {
1554                t.props.insert(key.into(), Value::Number(origin));
1555            }
1556            for key in ["unloadEventStart", "unloadEventEnd", "redirectStart", "redirectEnd"] {
1557                t.props.insert(key.into(), Value::Number(0.0));
1558            }
1559        }
1560        let navigation = Obj::plain();
1561        {
1562            let mut n = navigation.borrow_mut();
1563            n.props.insert("type".into(), Value::Number(0.0)); // TYPE_NAVIGATE
1564            n.props.insert("redirectCount".into(), Value::Number(0.0));
1565        }
1566        let mut p = performance.borrow_mut();
1567        p.props.insert("timing".into(), Value::Object(timing));
1568        p.props.insert("navigation".into(), Value::Object(navigation));
1569    }
1570    // Performance Timeline / User Timing API(`mark`/`measure`/
1571    // `getEntriesByType`/`getEntriesByName`/`clearMarks`/`clearMeasures`)が
1572    // `now()` 以外丸ごと未対応だった(プロファイリングの定番イディオム
1573    // `performance.mark('start'); ...; performance.measure('x','start')` が
1574    // 軒並み TypeError)。
1575    {
1576        let mut p = performance.borrow_mut();
1577        p.props.insert("_entries".into(), Value::Object(Obj::array(alloc::vec::Vec::new())));
1578        p.props.insert("mark".into(), nv("mark", perf_mark));
1579        p.props.insert("measure".into(), nv("measure", perf_measure));
1580        // `performance.getEntries()`(フィルタ無しの全件取得。丸ごと未対応
1581        // だった。`getEntriesByType`/`getEntriesByName` は既に対応済み
1582        // だったが、素の `getEntries()` 自体が欠けていた)。
1583        p.props.insert("getEntries".into(), nv("getEntries", perf_get_entries));
1584        p.props.insert(
1585            "getEntriesByType".into(),
1586            nv("getEntriesByType", perf_get_entries_by_type),
1587        );
1588        p.props.insert(
1589            "getEntriesByName".into(),
1590            nv("getEntriesByName", perf_get_entries_by_name),
1591        );
1592        p.props.insert("clearMarks".into(), nv("clearMarks", perf_clear_marks));
1593        p.props
1594            .insert("clearMeasures".into(), nv("clearMeasures", perf_clear_measures));
1595        // `PerformanceObserver` が監視対象を登録する先(丸ごと未対応だった。
1596        // 詳細は performance_observer_ctor 参照)。
1597        p.props
1598            .insert("_observers".into(), Value::Object(Obj::array(alloc::vec::Vec::new())));
1599    }
1600    g.vars
1601        .insert("performance".into(), Value::Object(performance));
1602    let performance_observer_ctor_val = nv("PerformanceObserver", performance_observer_ctor);
1603    // `PerformanceObserver.supportedEntryTypes`(静的プロパティ。丸ごと未対応
1604    // だった)。この処理系のパフォーマンスタイムラインが実際に生成する
1605    // エントリ種別は `mark`/`measure` のみのため、誠実にその2種のみを返す
1606    // (`navigation`/`resource`等、実装していない種別は含めない)。
1607    if let Value::Object(po) = &performance_observer_ctor_val {
1608        po.borrow_mut().props.insert(
1609            "supportedEntryTypes".into(),
1610            Value::Object(Obj::array(alloc::vec![Value::str("mark"), Value::str("measure")])),
1611        );
1612    }
1613    g.vars
1614        .insert("PerformanceObserver".into(), performance_observer_ctor_val);
1615
1616    // ArrayBuffer / TypedArray 全種別(簡易実装。内部表現は通常の
1617    // ObjKind::Array(Vec<Value::Number>) を流用し、buffer は別インスタンスの独立コピー
1618    // — 真の共有メモリビューではない。BigInt64Array/BigUint64Array は対象外)。
1619    let ab_ctor = nv("ArrayBuffer", arraybuffer_ctor);
1620    if let Value::Object(o) = &ab_ctor {
1621        o.borrow_mut()
1622            .props
1623            .insert("isView".into(), nv("isView", arraybuffer_is_view));
1624    }
1625    g.vars.insert("ArrayBuffer".into(), ab_ctor);
1626    let mut register_typed_array = |name: &str, ctor: NativeFn, from_fn: NativeFn| {
1627        let f = nv(name, ctor);
1628        if let Value::Object(o) = &f {
1629            o.borrow_mut()
1630                .props
1631                .insert("from".into(), nv("from", from_fn));
1632            let proto = Obj::plain();
1633            proto.borrow_mut().proto = Some(object_proto.clone());
1634            o.borrow_mut()
1635                .props
1636                .insert("prototype".into(), Value::Object(proto.clone()));
1637            proto
1638                .borrow_mut()
1639                .props
1640                .insert("constructor".into(), f.clone());
1641        }
1642        g.vars.insert(String::from(name), f);
1643    };
1644    register_typed_array("Uint8Array", uint8array_ctor, uint8array_from);
1645    register_typed_array("Int8Array", int8array_ctor, int8array_from);
1646    register_typed_array(
1647        "Uint8ClampedArray",
1648        uint8clampedarray_ctor,
1649        uint8clampedarray_from,
1650    );
1651    register_typed_array("Int16Array", int16array_ctor, int16array_from);
1652    register_typed_array("Uint16Array", uint16array_ctor, uint16array_from);
1653    register_typed_array("Int32Array", int32array_ctor, int32array_from);
1654    register_typed_array("Uint32Array", uint32array_ctor, uint32array_from);
1655    register_typed_array("Float16Array", float16array_ctor, float16array_from);
1656    register_typed_array("Float32Array", float32array_ctor, float32array_from);
1657    register_typed_array("Float64Array", float64array_ctor, float64array_from);
1658    // `Uint8Array.fromBase64`/`fromHex`(ES2024/2025 静的メソッド)。
1659    if let Some(Value::Object(u8a)) = g.vars.get("Uint8Array").cloned() {
1660        u8a.borrow_mut()
1661            .props
1662            .insert("fromBase64".into(), nv("fromBase64", ta_from_base64));
1663        u8a.borrow_mut()
1664            .props
1665            .insert("fromHex".into(), nv("fromHex", ta_from_hex));
1666    }
1667    // DataView(ES2015)。丸ごと未対応で `new DataView(buf)` が
1668    // `DataView is not defined` になっていた。ArrayBuffer が実バイト列を持たない
1669    // 独立コピー簡易実装なのと同様、内部にバイト列(`ObjKind::Array` 流用)を
1670    // 独自に確保する(真の共有メモリビューではない)。
1671    g.vars.insert("DataView".into(), nv("DataView", dataview_ctor));
1672    // Blob(File API の基礎コンテナ。丸ごと未対応だった。File/FileReader/実 I/O は
1673    // この OS には実ファイル選択 UI が存在しないため対象外のまま、メモリ上のバイト列
1674    // コンテナとしての Blob 単体のみ対応する)。
1675    g.vars.insert("Blob".into(), nv("Blob", blob_ctor));
1676    // `File`(`Blob` を継承し `name`/`lastModified` を追加するファイル API の
1677    // 基礎コンストラクタ)が丸ごと未対応だった。`Blob` は既に対応済みだったため、
1678    // `new File(['x'], 'a.txt', {type, lastModified})` という定番パターンが
1679    // `File is not defined` になっていた。
1680    g.vars.insert("File".into(), nv("File", file_ctor));
1681    // `new Option(text?, value?, defaultSelected?, selected?)`(HTML5。
1682    // `document.createElement('option')` + `.text`/`.value`/`.selected` の
1683    // 個別代入を1呼び出しに縮める定番ショートハンド)が丸ごと未対応だった
1684    // (`Option is not defined`)。`defaultSelected` は `selected` 属性の
1685    // 初期値として、`selected` は同じ意味の別名として扱う簡略方針(本物の
1686    // `defaultSelected`/`selected` プロパティ分離は `input.defaultValue` と
1687    // 同種の既知の未対応)。
1688    g.vars.insert("Option".into(), nv("Option", option_ctor));
1689    // `FileReader`(`Blob`/`File` の内容を非同期に読み出す定番の従来型 API。
1690    // `blob.text()`/`.arrayBuffer()` という Promise 版は既に対応済みだったが、
1691    // `reader.readAsText(blob)` + `reader.onload = ...` という古典的な
1692    // イベントベースの書き方自体が丸ごと未対応だった)が丸ごと未対応だった。
1693    // 実際の非同期 I/O が無いこの処理系では読み出しは同期的に完了するため、
1694    // 完了後すぐに `onload`/`onloadend` を呼ぶ簡略実装とする。
1695    g.vars
1696        .insert("FileReader".into(), nv("FileReader", file_reader_ctor));
1697
1698    // Notification(丸ごと未対応だった)。この OS には通知 UI 自体が無いため、
1699    // `element.requestFullscreen()`/`document.execCommand` と同じ「実際の見た目の
1700    // 変化は伴わないが JS 側の契約(プロパティ/Promise の形)は満たす」簡略実装
1701    // とする。許可拒否ダイアログが存在しないため常に `"granted"`。詳細は
1702    // notification_ctor 参照。
1703    {
1704        let notification_ctor_val = nv("Notification", notification_ctor);
1705        if let Value::Object(o) = &notification_ctor_val {
1706            let mut b = o.borrow_mut();
1707            b.props.insert("permission".into(), Value::str("granted"));
1708            b.props.insert(
1709                "requestPermission".into(),
1710                nv("requestPermission", notification_request_permission),
1711            );
1712        }
1713        g.vars.insert("Notification".into(), notification_ctor_val);
1714    }
1715
1716    // TextEncoder / TextDecoder
1717    g.vars
1718        .insert("TextEncoder".into(), nv("TextEncoder", text_encoder_ctor));
1719    g.vars
1720        .insert("TextDecoder".into(), nv("TextDecoder", text_decoder_ctor));
1721
1722    // DOMParser(DOM Parsing and Serialization API。HTML文字列からDOMツリーを生成)
1723    g.vars
1724        .insert("DOMParser".into(), nv("DOMParser", dom_parser_ctor));
1725
1726    // crypto
1727    let crypto = Obj::plain();
1728    {
1729        let mut c = crypto.borrow_mut();
1730        c.props
1731            .insert("randomUUID".into(), nv("randomUUID", crypto_random_uuid));
1732        c.props.insert(
1733            "getRandomValues".into(),
1734            nv("getRandomValues", crypto_get_random_values),
1735        );
1736        // `crypto.subtle`(SubtleCrypto)が丸ごと未対応だった。`digest('SHA-256',
1737        // data)`のみの部分実装(詳細はbuiltins/crypto_subtle.rsのモジュール
1738        // doc参照。2026-07-18 発見・実装)。
1739        let subtle = Obj::plain();
1740        subtle
1741            .borrow_mut()
1742            .props
1743            .insert("digest".into(), nv("digest", subtle_digest));
1744        c.props.insert("subtle".into(), Value::Object(subtle));
1745    }
1746    g.vars.insert("crypto".into(), Value::Object(crypto));
1747
1748    // structuredClone
1749    g.vars.insert(
1750        "structuredClone".into(),
1751        nv("structuredClone", structured_clone),
1752    );
1753
1754    // AbortController(fetch のキャンセル等。signal.aborted を立てる最小実装)
1755    g.vars.insert(
1756        "AbortController".into(),
1757        nv("AbortController", abort_controller_ctor),
1758    );
1759    // `AbortSignal`(ES2022。`AbortSignal.timeout(ms)`/`.abort(reason)` という
1760    // fetch タイムアウト実装の定番イディオムで使われる静的ファクトリ)が、
1761    // `AbortController` 経由でしか signal を得られず丸ごと未対応だった。
1762    // `new AbortSignal()` は仕様上直接構築不可のため、静的メソッドのみを持つ
1763    // ただのオブジェクトとして登録する。
1764    // 【2026-10-17】素のオブジェクトだったため `AbortSignal.prototype` が無く、
1765    // `typeof AbortSignal.prototype.throwIfAborted` で判定するポリフィル(YouTube)が落ちていた。
1766    // 直接は構築できない関数(ブラウザと同じ)にし、prototype は下の補完で付く。
1767    {
1768        let abort_signal_ns = Obj::native("AbortSignal", abort_signal_illegal_ctor);
1769        abort_signal_ns
1770            .borrow_mut()
1771            .props
1772            .insert("timeout".into(), nv("AbortSignal.timeout", abort_signal_timeout));
1773        abort_signal_ns
1774            .borrow_mut()
1775            .props
1776            .insert("abort".into(), nv("AbortSignal.abort", abort_signal_abort_static));
1777        abort_signal_ns
1778            .borrow_mut()
1779            .props
1780            .insert("any".into(), nv("AbortSignal.any", abort_signal_any));
1781        g.vars
1782            .insert("AbortSignal".into(), Value::Object(abort_signal_ns));
1783    }
1784    // EventTarget(丸ごと未対応だった。DOM 要素以外で独自のイベント発行/購読の
1785    // 基盤として使う定番パターン。他の EventTarget 風オブジェクト[XHR/AbortSignal]と
1786    // 違い、型ごとに複数リスナを保持する仕様どおりの実装)。
1787    // EventTarget.prototype(ShadyDOM が native のメソッドを写す元。Node/Window の原型の親)。
1788    let event_target_proto = Obj::plain();
1789    {
1790        let mut b = event_target_proto.borrow_mut();
1791        b.props.insert("addEventListener".into(), nv("addEventListener", event_target_proto_add));
1792        b.props.insert("removeEventListener".into(), nv("removeEventListener", event_target_proto_remove));
1793        b.props.insert("dispatchEvent".into(), nv("dispatchEvent", event_target_proto_dispatch));
1794    }
1795    {
1796        let et_ctor = nv("EventTarget", event_target_ctor);
1797        if let Value::Object(c) = &et_ctor {
1798            c.borrow_mut().props.insert("prototype".into(), Value::Object(event_target_proto.clone()));
1799            event_target_proto.borrow_mut().props.insert("constructor".into(), et_ctor.clone());
1800        }
1801        g.vars.insert("EventTarget".into(), et_ctor);
1802    }
1803    // MessageChannel/MessagePort(丸ごと未対応だった。`new MessageChannel()` の
1804    // `.port1`/`.port2` 双方向チャネルで Worker 間通信を模す定番パターン。
1805    // 詳細は message_channel_ctor 参照)。
1806    g.vars
1807        .insert("MessageChannel".into(), nv("MessageChannel", message_channel_ctor));
1808    // BroadcastChannel(丸ごと未対応だった。`new BroadcastChannel(name)` で同名の
1809    // 他インスタンス全てへブロードキャストする定番パターン。詳細は
1810    // broadcast_channel_ctor 参照)。
1811    g.vars
1812        .insert("BroadcastChannel".into(), nv("BroadcastChannel", broadcast_channel_ctor));
1813    g.vars.insert("CSSStyleSheet".into(), Value::Object(Obj::plain()));
1814    g.vars.insert("CSSStyleRule".into(), Value::Object(Obj::plain()));
1815    // DOM コンストラクタ階層 (Node -> Element -> HTMLElement -> HTMLXxxElement)
1816    // 仕様どおり TypeError("Illegal constructor") を投げる NativeFunction コンストラクタと
1817    // 正しい prototype 継承チェーンを構築し、`el instanceof HTMLInputElement` や `el instanceof Node` を可能にする。
1818    {
1819        let make_dom_cls = |name: &str, parent_proto: Option<&ObjRef>| -> (Value, ObjRef) {
1820            // `new Node()`/`new HTMLElement()`等(仕様上どれも直接構築不可の
1821            // 抽象/イリーガルコンストラクタ)は`TypeError`を投げるべきだが、
1822            // 以前は`it.error()`(`Error.prototype`に繋がる汎用エラー)を
1823            // 使っていたため、メッセージ文字列こそ"TypeError: ..."だが
1824            // `instanceof TypeError`は満たさないバグだった(2026-07-18
1825            // 発見・修正)。
1826            // カスタム要素のクラスの `super()` では this が DOM 要素のハンドル(`construct_object`
1827            // が用意する)。そのときは何もしない。
1828            let ctor = Obj::native(name, |it, this, _| match &this {
1829                Value::Object(o) if matches!(o.borrow().kind, ObjKind::DomElement(_)) => Ok(this.clone()),
1830                _ => Err(it.type_error("Illegal constructor")),
1831            });
1832            let proto = Obj::plain();
1833            if let Some(parent) = parent_proto {
1834                proto.borrow_mut().proto = Some(parent.clone());
1835            }
1836            proto
1837                .borrow_mut()
1838                .props
1839                .insert("constructor".into(), Value::Object(ctor.clone()));
1840            ctor.borrow_mut()
1841                .props
1842                .insert("prototype".into(), Value::Object(proto.clone()));
1843            (Value::Object(ctor), proto)
1844        };
1845
1846        let (node_ctor, node_proto) = make_dom_cls("Node", Some(&event_target_proto));
1847        let (element_ctor, element_proto) = make_dom_cls("Element", Some(&node_proto));
1848        dom_forward::install(&node_proto, &element_proto);
1849        let (html_element_ctor, html_element_proto) = make_dom_cls("HTMLElement", Some(&element_proto));
1850
1851        let consts: &[(&str, f64)] = &[
1852            ("ELEMENT_NODE", 1.0),
1853            ("ATTRIBUTE_NODE", 2.0),
1854            ("TEXT_NODE", 3.0),
1855            ("CDATA_SECTION_NODE", 4.0),
1856            ("PROCESSING_INSTRUCTION_NODE", 7.0),
1857            ("COMMENT_NODE", 8.0),
1858            ("DOCUMENT_NODE", 9.0),
1859            ("DOCUMENT_TYPE_NODE", 10.0),
1860            ("DOCUMENT_FRAGMENT_NODE", 11.0),
1861            ("DOCUMENT_POSITION_DISCONNECTED", 1.0),
1862            ("DOCUMENT_POSITION_PRECEDING", 2.0),
1863            ("DOCUMENT_POSITION_FOLLOWING", 4.0),
1864            ("DOCUMENT_POSITION_CONTAINS", 8.0),
1865            ("DOCUMENT_POSITION_CONTAINED_BY", 16.0),
1866            ("DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", 32.0),
1867        ];
1868        if let Value::Object(c) = &node_ctor {
1869            for (name, val) in consts {
1870                c.borrow_mut()
1871                    .props
1872                    .insert(String::from(*name), Value::Number(*val));
1873            }
1874        }
1875
1876        g.vars.insert("Node".into(), node_ctor);
1877        g.vars.insert("Element".into(), element_ctor);
1878        g.vars.insert("HTMLElement".into(), html_element_ctor);
1879        // その他の DOM インターフェース(`instanceof`・`X.prototype` の参照・ポリフィルの存在確認用)。
1880        // 【2026-10-17】`Document is not defined` 等で YouTube のポリフィルが落ちていた。
1881        {
1882            let (_, chardata_proto) = {
1883                let (c, p) = make_dom_cls("CharacterData", Some(&node_proto));
1884                g.vars.insert("CharacterData".into(), c);
1885                ((), p)
1886            };
1887            for name in ["Text", "Comment", "CDATASection", "ProcessingInstruction"] {
1888                let (c, _) = make_dom_cls(name, Some(&chardata_proto));
1889                g.vars.insert(name.into(), c);
1890            }
1891            // 【2026-10-19】`Window`(大域オブジェクトの型。YouTube の視聴ページが参照し、
1892            // 「Window is not defined」で止まっていた)。`window instanceof Window` が真になるよう
1893            // window の原型を Window.prototype にする。
1894            let (win_ctor, win_proto) = make_dom_cls("Window", Some(&event_target_proto));
1895            g.vars.insert("Window".into(), win_ctor);
1896            if let Some(Value::Object(w)) = g.vars.get("window").cloned() {
1897                let mut wb = w.borrow_mut();
1898                if wb.proto.is_none() {
1899                    wb.proto = Some(win_proto.clone());
1900                }
1901            }
1902            let (doc_ctor, doc_proto) = make_dom_cls("Document", Some(&node_proto));
1903            dom_forward::install_document(&doc_proto);
1904            g.vars.insert("Document".into(), doc_ctor);
1905            let (hdoc, hdoc_proto) = make_dom_cls("HTMLDocument", Some(&doc_proto));
1906            g.vars.insert("HTMLDocument".into(), hdoc);
1907            // document の原型を HTMLDocument.prototype に(→ Document → Node → EventTarget)。
1908            if let Some(Value::Object(d)) = g.vars.get("document").cloned() {
1909                let mut db = d.borrow_mut();
1910                if db.proto.is_none() {
1911                    db.proto = Some(hdoc_proto.clone());
1912                }
1913            }
1914            for name in ["DocumentType", "Attr"] {
1915                let (c, _) = make_dom_cls(name, Some(&node_proto));
1916                g.vars.insert(name.into(), c);
1917            }
1918            let (df, df_proto) = make_dom_cls("DocumentFragment", Some(&node_proto));
1919            dom_forward::install_fragment(&df_proto);
1920            g.vars.insert("DocumentFragment".into(), df);
1921            // ShadowRoot は DocumentFragment を継承する(DOM 標準)。
1922            let (sr, _) = make_dom_cls("ShadowRoot", Some(&df_proto));
1923            g.vars.insert("ShadowRoot".into(), sr);
1924            let (svg, _) = make_dom_cls("SVGElement", Some(&element_proto));
1925            g.vars.insert("SVGElement".into(), svg);
1926            let (media, media_proto) = make_dom_cls("HTMLMediaElement", Some(&html_element_proto));
1927            g.vars.insert("HTMLMediaElement".into(), media);
1928            for name in ["HTMLVideoElement", "HTMLAudioElement"] {
1929                let (c, _) = make_dom_cls(name, Some(&media_proto));
1930                g.vars.insert(name.into(), c);
1931            }
1932            for name in [
1933                "HTMLDivElement", "HTMLSpanElement", "HTMLAnchorElement", "HTMLImageElement",
1934                "HTMLButtonElement", "HTMLFormElement", "HTMLSelectElement", "HTMLOptionElement",
1935                "HTMLTextAreaElement", "HTMLLabelElement", "HTMLScriptElement", "HTMLStyleElement",
1936                "HTMLLinkElement", "HTMLMetaElement", "HTMLHeadElement", "HTMLBodyElement",
1937                "HTMLHtmlElement", "HTMLTemplateElement", "HTMLIFrameElement", "HTMLCanvasElement",
1938                "HTMLParagraphElement", "HTMLHeadingElement", "HTMLUListElement", "HTMLOListElement",
1939                "HTMLLIElement", "HTMLTableElement", "HTMLTableRowElement", "HTMLTableCellElement",
1940                "HTMLSlotElement", "HTMLUnknownElement", "HTMLDialogElement", "HTMLPreElement",
1941                "HTMLBRElement", "HTMLHRElement", "HTMLSourceElement", "HTMLTrackElement",
1942            ] {
1943                if !g.vars.contains_key(name) {
1944                    let (c, _) = make_dom_cls(name, Some(&html_element_proto));
1945                    g.vars.insert(name.into(), c);
1946                }
1947            }
1948        }
1949        // `new Image(w, h)` / `new Audio(src)`(HTML の「名前付きコンストラクタ」。丸ごと未対応で
1950        // `Image is not defined` になっていた。YouTube のエラー報告・計測ビーコンが使う)。
1951        g.vars.insert("Image".into(), nv("Image", image_ctor));
1952        g.vars.insert("Audio".into(), nv("Audio", audio_ctor));
1953
1954        let (nodelist_ctor, nodelist_proto) = make_dom_cls("NodeList", None);
1955        let (htmlcollection_ctor, _) = make_dom_cls("HTMLCollection", None);
1956        let (radionodelist_ctor, _) = make_dom_cls("RadioNodeList", Some(&nodelist_proto));
1957        g.vars.insert("NodeList".into(), nodelist_ctor);
1958        g.vars.insert("HTMLCollection".into(), htmlcollection_ctor);
1959        g.vars.insert("RadioNodeList".into(), radionodelist_ctor);
1960
1961        let html_classes = &[
1962            "HTMLInputElement",
1963            "HTMLFormElement",
1964            "HTMLAnchorElement",
1965            "HTMLImageElement",
1966            "HTMLButtonElement",
1967            "HTMLSelectElement",
1968            "HTMLOptionElement",
1969            "HTMLTextAreaElement",
1970            "HTMLDivElement",
1971            "HTMLSpanElement",
1972            "HTMLScriptElement",
1973            "HTMLStyleElement",
1974            "HTMLIFrameElement",
1975            "HTMLCanvasElement",
1976            "HTMLParagraphElement",
1977            "HTMLHeadingElement",
1978            "HTMLTableElement",
1979            "HTMLTableRowElement",
1980            "HTMLTableCellElement",
1981            "HTMLUListElement",
1982            "HTMLLIElement",
1983            "HTMLTemplateElement",
1984            "HTMLUnknownElement",
1985        ];
1986
1987        for cls_name in html_classes {
1988            let (ctor, _) = make_dom_cls(cls_name, Some(&html_element_proto));
1989            if *cls_name == "HTMLScriptElement" {
1990                if let Value::Object(c) = &ctor {
1991                    c.borrow_mut().props.insert(
1992                        String::from("supports"),
1993                        nv("HTMLScriptElement.supports", |_: &mut Interp, _: Value, a: &[Value]| {
1994                            let ty = a.first().map(|v| v.to_js_string()).unwrap_or_default();
1995                            Ok(Value::Bool(matches!(ty.as_str(), "classic" | "module")))
1996                        }),
1997                    );
1998                }
1999            }
2000            g.vars.insert((*cls_name).into(), ctor);
2001        }
2002    }
2003
2004    // IntersectionObserver(DOM Ready 後にコールバックを全要素 visible で発火)
2005    g.vars.insert(
2006        "IntersectionObserver".into(),
2007        nv("IntersectionObserver", intersection_observer_ctor),
2008    );
2009
2010    // ResizeObserver(observe 時に即時コールバック発火)
2011    g.vars.insert(
2012        "ResizeObserver".into(),
2013        nv("ResizeObserver", resize_observer_ctor),
2014    );
2015
2016    // MutationObserver(observe/disconnect no-op として登録)
2017    g.vars.insert(
2018        "MutationObserver".into(),
2019        nv("MutationObserver", mutation_observer_ctor),
2020    );
2021
2022    // CustomEvent コンストラクタ
2023    g.vars
2024        .insert("CustomEvent".into(), nv("CustomEvent", custom_event_ctor));
2025
2026    // Event コンストラクタ(new Event('foo', {bubbles, cancelable}))
2027    // `Event.NONE`/`.CAPTURING_PHASE`/`.AT_TARGET`/`.BUBBLING_PHASE`
2028    // (`event.eventPhase === Event.AT_TARGET` という `Node.ELEMENT_NODE` と
2029    // 同型の定番パターンで使われる標準定数)が丸ごと未対応だった。
2030    {
2031        let event_ctor_val = nv("Event", event_ctor);
2032        if let Value::Object(o) = &event_ctor_val {
2033            let phase_consts: &[(&str, f64)] = &[
2034                ("NONE", 0.0),
2035                ("CAPTURING_PHASE", 1.0),
2036                ("AT_TARGET", 2.0),
2037                ("BUBBLING_PHASE", 3.0),
2038            ];
2039            for (name, val) in phase_consts {
2040                o.borrow_mut()
2041                    .props
2042                    .insert(String::from(*name), Value::Number(*val));
2043            }
2044        }
2045        g.vars.insert("Event".into(), event_ctor_val);
2046    }
2047
2048    // KeyboardEvent/MouseEvent コンストラクタ(丸ごと未対応だった。
2049    // `el.dispatchEvent(new KeyboardEvent('keydown', {key:'a'}))`/
2050    // `new MouseEvent('click', {clientX, clientY})` というシミュレーション
2051    // イベント構築の定番パターン。詳細は各 ctor 参照)。
2052    g.vars
2053        .insert("KeyboardEvent".into(), nv("KeyboardEvent", keyboard_event_ctor));
2054    g.vars
2055        .insert("MouseEvent".into(), nv("MouseEvent", mouse_event_ctor));
2056    // WheelEvent/FocusEvent コンストラクタ(丸ごと未対応だった。詳細は
2057    // 各 ctor 参照)。
2058    g.vars
2059        .insert("WheelEvent".into(), nv("WheelEvent", wheel_event_ctor));
2060    g.vars
2061        .insert("FocusEvent".into(), nv("FocusEvent", focus_event_ctor));
2062    // Touch/TouchEvent コンストラクタ(丸ごと未対応だった。詳細は各 ctor 参照)。
2063    g.vars.insert("Touch".into(), nv("Touch", touch_ctor));
2064    g.vars
2065        .insert("TouchEvent".into(), nv("TouchEvent", touch_event_ctor));
2066    // `DOMRect`/`DOMRectReadOnly`コンストラクタ(丸ごと未対応だった。
2067    // Geometry Interfaces Module Level 1 仕様準拠。2026-07-21 拡張)。
2068    let dom_rect_fn = nv("DOMRect", dom_rect_ctor);
2069    if let Value::Object(ref o) = dom_rect_fn {
2070        o.borrow_mut().props.insert("fromRect".into(), nv("fromRect", dom_rect_from_rect));
2071    }
2072    g.vars.insert("DOMRect".into(), dom_rect_fn.clone());
2073    g.vars.insert("DOMRectReadOnly".into(), dom_rect_fn);
2074
2075    // `DOMPoint`/`DOMPointReadOnly`コンストラクタ(丸ごと未対応だった。
2076    // Geometry Interfaces Module Level 1 仕様準拠。2026-07-21 拡張)。
2077    let dom_point_fn = nv("DOMPoint", dom_point_ctor);
2078    if let Value::Object(ref o) = dom_point_fn {
2079        o.borrow_mut().props.insert("fromPoint".into(), nv("fromPoint", dom_point_from_point));
2080    }
2081    g.vars.insert("DOMPoint".into(), dom_point_fn.clone());
2082    g.vars.insert("DOMPointReadOnly".into(), dom_point_fn);
2083    // `DOMMatrix`/`DOMMatrixReadOnly`/`DOMQuad`コンストラクタ(丸ごと未対応だった。
2084    // Geometry Interfaces Module Level 1 仕様準拠。2026-07-21 実装)。
2085    let dom_matrix_fn = nv("DOMMatrix", dom_matrix_ctor);
2086    if let Value::Object(ref o) = dom_matrix_fn {
2087        o.borrow_mut().props.insert("fromMatrix".into(), nv("fromMatrix", |it, _, a| dom_matrix_ctor(it, Value::Undefined, a)));
2088        o.borrow_mut().props.insert("fromFloat32Array".into(), nv("fromFloat32Array", |it, _, a| dom_matrix_ctor(it, Value::Undefined, a)));
2089        o.borrow_mut().props.insert("fromFloat64Array".into(), nv("fromFloat64Array", |it, _, a| dom_matrix_ctor(it, Value::Undefined, a)));
2090    }
2091    g.vars.insert("DOMMatrix".into(), dom_matrix_fn.clone());
2092    g.vars.insert("DOMMatrixReadOnly".into(), dom_matrix_fn);
2093
2094    let dom_quad_fn = nv("DOMQuad", dom_quad_ctor);
2095    if let Value::Object(ref o) = dom_quad_fn {
2096        o.borrow_mut().props.insert("fromRect".into(), nv("fromRect", |it, _, a| {
2097            let r = arg(a, 0);
2098            let rx = it.get_property(&r, "x").unwrap_or(Value::Number(0.0)).to_number();
2099            let ry = it.get_property(&r, "y").unwrap_or(Value::Number(0.0)).to_number();
2100            let rw = it.get_property(&r, "width").unwrap_or(Value::Number(0.0)).to_number();
2101            let rh = it.get_property(&r, "height").unwrap_or(Value::Number(0.0)).to_number();
2102            let p1 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx), Value::Number(ry)])?;
2103            let p2 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx + rw), Value::Number(ry)])?;
2104            let p3 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx + rw), Value::Number(ry + rh)])?;
2105            let p4 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx), Value::Number(ry + rh)])?;
2106            dom_quad_ctor(it, Value::Undefined, &[p1, p2, p3, p4])
2107        }));
2108    }
2109    g.vars.insert("DOMQuad".into(), dom_quad_fn);
2110    g.vars.insert("Path2D".into(), nv("Path2D", dom_path2d_ctor));
2111    g.vars.insert("ImageData".into(), nv("ImageData", dom_image_data_ctor));
2112
2113    let range_ctor_fn = nv("Range", |it, t, a| dom_document::document_create_range(it, t, a));
2114    if let Value::Object(ref ro) = range_ctor_fn {
2115        let mut rb = ro.borrow_mut();
2116        rb.props.insert(
2117            "START_TO_START".into(),
2118            Value::Number(crate::os_lib::dom_range::START_TO_START as f64),
2119        );
2120        rb.props.insert(
2121            "START_TO_END".into(),
2122            Value::Number(crate::os_lib::dom_range::START_TO_END as f64),
2123        );
2124        rb.props.insert(
2125            "END_TO_END".into(),
2126            Value::Number(crate::os_lib::dom_range::END_TO_END as f64),
2127        );
2128        rb.props.insert(
2129            "END_TO_START".into(),
2130            Value::Number(crate::os_lib::dom_range::END_TO_START as f64),
2131        );
2132    }
2133    g.vars.insert("Range".into(), range_ctor_fn);
2134
2135    // HashChangeEvent/PageTransitionEvent コンストラクタ(丸ごと未対応
2136    // だった。詳細は各 ctor 参照)。
2137    g.vars.insert(
2138        "HashChangeEvent".into(),
2139        nv("HashChangeEvent", hash_change_event_ctor),
2140    );
2141    g.vars.insert(
2142        "PageTransitionEvent".into(),
2143        nv("PageTransitionEvent", page_transition_event_ctor),
2144    );
2145    // PopStateEvent コンストラクタ(丸ごと未対応だった。詳細は
2146    // `pop_state_event_ctor` 参照。2026-07-17 発見・実装)。
2147    g.vars.insert(
2148        "PopStateEvent".into(),
2149        nv("PopStateEvent", pop_state_event_ctor),
2150    );
2151    // PointerEvent/InputEvent コンストラクタ(丸ごと未対応だった。詳細は
2152    // 各 ctor 参照)。
2153    g.vars
2154        .insert("PointerEvent".into(), nv("PointerEvent", pointer_event_ctor));
2155    g.vars
2156        .insert("InputEvent".into(), nv("InputEvent", input_event_ctor));
2157    // ProgressEvent/StorageEvent コンストラクタ(丸ごと未対応だった。詳細は
2158    // 各 ctor 参照)。
2159    g.vars
2160        .insert("ProgressEvent".into(), nv("ProgressEvent", progress_event_ctor));
2161    g.vars
2162        .insert("StorageEvent".into(), nv("StorageEvent", storage_event_ctor));
2163    // CommandEvent/ToggleEvent/FormDataEvent コンストラクタ(WHATWG HTML Standard 準拠)
2164    g.vars
2165        .insert("CommandEvent".into(), nv("CommandEvent", command_event_ctor));
2166    g.vars
2167        .insert("ToggleEvent".into(), nv("ToggleEvent", toggle_event_ctor));
2168    g.vars
2169        .insert("FormDataEvent".into(), nv("FormDataEvent", form_data_event_ctor));
2170
2171    // CompositionEvent/ClipboardEvent コンストラクタ(丸ごと未対応だった。
2172    // 詳細は各 ctor 参照)。
2173    g.vars.insert(
2174        "CompositionEvent".into(),
2175        nv("CompositionEvent", composition_event_ctor),
2176    );
2177    g.vars
2178        .insert("ClipboardEvent".into(), nv("ClipboardEvent", clipboard_event_ctor));
2179    // AnimationEvent/TransitionEvent/DragEvent/SubmitEvent コンストラクタ
2180    // (丸ごと未対応だった。詳細は各 ctor 参照)。
2181    g.vars
2182        .insert("AnimationEvent".into(), nv("AnimationEvent", animation_event_ctor));
2183    g.vars
2184        .insert("TransitionEvent".into(), nv("TransitionEvent", transition_event_ctor));
2185    g.vars.insert("DragEvent".into(), nv("DragEvent", drag_event_ctor));
2186    g.vars
2187        .insert("SubmitEvent".into(), nv("SubmitEvent", submit_event_ctor));
2188    // MessageEvent/ErrorEvent コンストラクタ(丸ごと未対応だった。詳細は
2189    // 各 ctor 参照)。
2190    g.vars
2191        .insert("MessageEvent".into(), nv("MessageEvent", message_event_ctor));
2192    g.vars.insert("ErrorEvent".into(), nv("ErrorEvent", error_event_ctor));
2193
2194    // window.scrollY / pageYOffset / scrollTo / scrollX
2195    g.vars.insert("scrollY".into(), Value::Number(0.0));
2196    g.vars.insert("pageYOffset".into(), Value::Number(0.0));
2197    g.vars.insert("scrollX".into(), Value::Number(0.0));
2198    g.vars.insert("pageXOffset".into(), Value::Number(0.0));
2199    g.vars
2200        .insert("scrollTo".into(), nv("scrollTo", window_scroll_to));
2201    g.vars
2202        .insert("scroll".into(), nv("scroll", window_scroll_to));
2203    // `window.scrollBy(x, y)`(丸ごと未対応だった。`dom_noop`のまま何もせず
2204    // 相対スクロールが一切機能しなかった。2026-07-16 発見・実装)。
2205    g.vars
2206        .insert("scrollBy".into(), nv("scrollBy", window_scroll_by));
2207    g.vars
2208        .insert("matchMedia".into(), nv("matchMedia", window_match_media));
2209
2210    // window.load イベント(addEventListener('load', cb) → cb を即発火)
2211    // 実装は window の addEventListener で load を即時処理。
2212    g.vars.insert("location".into(), Value::Object(location));
2213    g.vars.insert("history".into(), Value::Object(history));
2214    g.vars.insert("alert".into(), nv("alert", console_log));
2215    // `confirm`/`prompt`(bare修飾無し形。丸ごと未対応だった。理由は
2216    // `window.confirm`/`.prompt`と同じ。2026-07-16 発見・実装)。
2217    g.vars
2218        .insert("confirm".into(), nv("confirm", window_confirm));
2219    g.vars.insert("prompt".into(), nv("prompt", window_prompt));
2220    // `open`/`close`/`focus`/`blur`(bare修飾無し形。丸ごと未対応だった。
2221    // 理由は`window.open`等と同じ。2026-07-16 発見・実装)。
2222    g.vars.insert("open".into(), nv("open", window_open));
2223    g.vars.insert("close".into(), nv("close", window_close));
2224    // `stop`(bare修飾無し形。丸ごと未対応だった。2026-07-17 発見・実装)。
2225    g.vars.insert("stop".into(), nv("stop", window_stop));
2226    g.vars.insert("focus".into(), nv("focus", window_focus));
2227    g.vars.insert("blur".into(), nv("blur", window_focus));
2228    // `getSelection`(bare修飾無し形。丸ごと未対応だった。理由は
2229    // `window.getSelection`と同じ。2026-07-16 発見・実装)。
2230    g.vars
2231        .insert("getSelection".into(), nv("getSelection", document_get_selection));
2232
2233    // Map / Set コンストラクタ
2234    let map_ctor_v = nv("Map", map_ctor);
2235    if let Value::Object(mc) = &map_ctor_v {
2236        mc.borrow_mut()
2237            .props
2238            .insert("groupBy".into(), nv("Map.groupBy", map_group_by));
2239    }
2240    g.vars.insert("Map".into(), map_ctor_v);
2241    g.vars.insert("Set".into(), nv("Set", set_ctor));
2242
2243    // Promise コンストラクタ + 静的メソッド
2244    let promise_ctor_v = nv("Promise", promise_ctor);
2245    if let Value::Object(pc) = &promise_ctor_v {
2246        pc.borrow_mut().props.insert(
2247            "resolve".into(),
2248            nv("Promise.resolve", promise_resolve_static),
2249        );
2250        pc.borrow_mut()
2251            .props
2252            .insert("reject".into(), nv("Promise.reject", promise_reject_static));
2253        pc.borrow_mut()
2254            .props
2255            .insert("all".into(), nv("Promise.all", promise_all_static));
2256        pc.borrow_mut().props.insert(
2257            "allSettled".into(),
2258            nv("Promise.allSettled", promise_all_settled_static),
2259        );
2260        pc.borrow_mut()
2261            .props
2262            .insert("race".into(), nv("Promise.race", promise_race_static));
2263        pc.borrow_mut()
2264            .props
2265            .insert("any".into(), nv("Promise.any", promise_any_static));
2266        pc.borrow_mut().props.insert(
2267            "withResolvers".into(),
2268            nv("Promise.withResolvers", promise_with_resolvers_static),
2269        );
2270        pc.borrow_mut()
2271            .props
2272            .insert("try".into(), nv("Promise.try", promise_try_static));
2273    }
2274    g.vars.insert("Promise".into(), promise_ctor_v);
2275
2276    // Proxy コンストラクタ
2277    g.vars.insert("Proxy".into(), nv("Proxy", proxy_ctor));
2278
2279    // Reflect グローバルオブジェクト
2280    let reflect = Obj::plain();
2281    {
2282        let mut r = reflect.borrow_mut();
2283        r.props.insert("get".into(), nv("Reflect.get", reflect_get));
2284        r.props.insert("set".into(), nv("Reflect.set", reflect_set));
2285        r.props.insert("has".into(), nv("Reflect.has", reflect_has));
2286        r.props.insert(
2287            "deleteProperty".into(),
2288            nv("Reflect.deleteProperty", reflect_delete_property),
2289        );
2290        r.props
2291            .insert("ownKeys".into(), nv("Reflect.ownKeys", reflect_own_keys));
2292        r.props.insert(
2293            "getPrototypeOf".into(),
2294            nv("Reflect.getPrototypeOf", reflect_get_prototype_of),
2295        );
2296        r.props.insert(
2297            "defineProperty".into(),
2298            nv("Reflect.defineProperty", reflect_define_property),
2299        );
2300        r.props
2301            .insert("apply".into(), nv("Reflect.apply", reflect_apply));
2302        r.props.insert(
2303            "construct".into(),
2304            nv("Reflect.construct", reflect_construct),
2305        );
2306        // `setPrototypeOf`/`isExtensible`/`preventExtensions`/`getOwnPropertyDescriptor` が
2307        // 丸ごと欠落していた(`Object` 側の対応する静的メソッドと引数の形が同じなので
2308        // そのまま流用する)。
2309        r.props.insert(
2310            "setPrototypeOf".into(),
2311            nv("Reflect.setPrototypeOf", object_set_prototype_of),
2312        );
2313        r.props.insert(
2314            "isExtensible".into(),
2315            nv("Reflect.isExtensible", object_is_extensible),
2316        );
2317        r.props.insert(
2318            "preventExtensions".into(),
2319            nv("Reflect.preventExtensions", object_prevent_extensions),
2320        );
2321        r.props.insert(
2322            "getOwnPropertyDescriptor".into(),
2323            nv(
2324                "Reflect.getOwnPropertyDescriptor",
2325                object_get_own_property_descriptor,
2326            ),
2327        );
2328    }
2329    g.vars.insert("Reflect".into(), Value::Object(reflect));
2330
2331    // Iterator(ES2025)。コンストラクタ自体は呼び出し不可扱いとし、静的メソッド
2332    // `Iterator.from(iterable)` のみを提供する簡略実装。
2333    let iterator_ctor = nv("Iterator", |_: &mut Interp, _: Value, _: &[Value]| {
2334        Ok(Value::Undefined)
2335    });
2336    if let Value::Object(ic) = &iterator_ctor {
2337        ic.borrow_mut()
2338            .props
2339            .insert("from".into(), nv("Iterator.from", iterator_from));
2340        ic.borrow_mut()
2341            .props
2342            .insert("range".into(), nv("Iterator.range", iterator_range));
2343        ic.borrow_mut()
2344            .props
2345            .insert("concat".into(), nv("Iterator.concat", iterator_concat_static));
2346    }
2347    g.vars.insert("Iterator".into(), iterator_ctor);
2348
2349    // タイマー(イベントループのマクロタスク)。遅延は無視し、登録順に実行する。
2350    g.vars
2351        .insert("setTimeout".into(), nv("setTimeout", set_timeout));
2352    g.vars
2353        .insert("setInterval".into(), nv("setInterval", set_interval));
2354    g.vars
2355        .insert("clearTimeout".into(), nv("clearTimeout", clear_timeout));
2356    g.vars
2357        .insert("clearInterval".into(), nv("clearInterval", clear_interval));
2358    g.vars.insert(
2359        "queueMicrotask".into(),
2360        nv("queueMicrotask", queue_microtask),
2361    );
2362    // `reportError(error)`(丸ごと未対応だった。未捕捉例外と同じ経路で
2363    // `window.onerror`/`window.addEventListener('error', fn)` へ報告する
2364    // 標準グローバル関数。詳細は report_error 参照)。
2365    g.vars
2366        .insert("reportError".into(), nv("reportError", report_error));
2367    g.vars.insert(
2368        "requestAnimationFrame".into(),
2369        nv("requestAnimationFrame", request_animation_frame),
2370    );
2371    g.vars.insert(
2372        "cancelAnimationFrame".into(),
2373        nv("cancelAnimationFrame", clear_timeout),
2374    );
2375    g.vars.insert(
2376        "requestIdleCallback".into(),
2377        nv("requestIdleCallback", request_idle_callback),
2378    );
2379    g.vars.insert(
2380        "cancelIdleCallback".into(),
2381        nv("cancelIdleCallback", dom_noop),
2382    );
2383
2384    // Date。`RegExp`/`Map`/`Set` と同じ設計で、ネイティブコンストラクタが
2385    // `construct_object` が用意する `this` を無視して `ObjKind::DateObj` を積んだ
2386    // 新規オブジェクトを直接返す(メソッドは `.proto` 経由ではなく、`get_property` の
2387    // `ObjKind` 特殊扱いで解決される。`instanceof Date` は組込み Array/Object と同様に
2388    // false になる既知の簡略化)。
2389    let date_ctor_obj = Obj::native("Date", date_ctor);
2390    date_ctor_obj
2391        .borrow_mut()
2392        .props
2393        .insert("now".into(), nv("Date.now", date_now_static));
2394    date_ctor_obj
2395        .borrow_mut()
2396        .props
2397        .insert("parse".into(), nv("Date.parse", date_parse_static));
2398    // `Date.now`/`Date.parse` は登録済みだったが、3つ揃いのはずの `Date.UTC`
2399    // (年月日等の成分から epoch ミリ秒を直接得る静的ファクトリ。`new Date(...)`
2400    // の多引数コンストラクタと違い、これ自体は Date インスタンスではなく数値を
2401    // 返す)が丸ごと未対応だった。
2402    date_ctor_obj
2403        .borrow_mut()
2404        .props
2405        .insert("UTC".into(), nv("Date.UTC", date_utc_static));
2406    g.vars.insert("Date".into(), Value::Object(date_ctor_obj));
2407
2408    // Error 階層。各サブタイプの `.prototype` は `Error.prototype` を proto に持つため、
2409    // `new TypeError() instanceof Error` 等が正しく true になる(OrdinaryHasInstance 相当の
2410    // `instanceof` 実装と対で意味を持つ。詳細は spec/walkthrough.md 2026-07-07 参照)。
2411    let error_proto = Obj::plain();
2412    error_proto
2413        .borrow_mut()
2414        .props
2415        .insert("name".into(), Value::str("Error"));
2416    error_proto
2417        .borrow_mut()
2418        .props
2419        .insert("message".into(), Value::str(""));
2420    let error_ctor_obj = Obj::native("Error", error_ctor);
2421    error_ctor_obj
2422        .borrow_mut()
2423        .props
2424        .insert("prototype".into(), Value::Object(error_proto.clone()));
2425    error_proto
2426        .borrow_mut()
2427        .props
2428        .insert("constructor".into(), Value::Object(error_ctor_obj.clone()));
2429    error_ctor_obj
2430        .borrow_mut()
2431        .props
2432        .insert("isError".into(), nv("Error.isError", error_is_error));
2433    g.vars.insert("Error".into(), Value::Object(error_ctor_obj));
2434
2435    fn make_error_subtype(
2436        name: &str,
2437        error_proto: &ObjRef,
2438        ctor_fn: NativeFn,
2439    ) -> Value {
2440        let proto = Obj::plain();
2441        proto.borrow_mut().proto = Some(error_proto.clone());
2442        proto
2443            .borrow_mut()
2444            .props
2445            .insert("name".into(), Value::str(name));
2446        let ctor_obj = Obj::native(name, ctor_fn);
2447        ctor_obj
2448            .borrow_mut()
2449            .props
2450            .insert("prototype".into(), Value::Object(proto.clone()));
2451        proto
2452            .borrow_mut()
2453            .props
2454            .insert("constructor".into(), Value::Object(ctor_obj.clone()));
2455        Value::Object(ctor_obj)
2456    }
2457    g.vars.insert(
2458        "TypeError".into(),
2459        make_error_subtype("TypeError", &error_proto, type_error_ctor),
2460    );
2461    g.vars.insert(
2462        "RangeError".into(),
2463        make_error_subtype("RangeError", &error_proto, range_error_ctor),
2464    );
2465    g.vars.insert(
2466        "SyntaxError".into(),
2467        make_error_subtype("SyntaxError", &error_proto, syntax_error_ctor),
2468    );
2469    g.vars.insert(
2470        "SuppressedError".into(),
2471        make_error_subtype("SuppressedError", &error_proto, suppressed_error_ctor),
2472    );
2473    g.vars.insert(
2474        "AggregateError".into(),
2475        make_error_subtype("AggregateError", &error_proto, aggregate_error_ctor),
2476    );
2477
2478    // `DOMException`(丸ごと未対応だった。Web API 全般が送出する標準例外型。
2479    // `AbortController.abort()` 等の内部エラーはこれまで `name`/`message` のみ
2480    // 持つ簡易オブジェクトで代用していたが、`new DOMException(message, name)`
2481    // というユーザーコードからの直接構築や、レガシーな `.code` 数値定数
2482    // (`err.code === DOMException.NOT_FOUND_ERR` 等)に対応する本物の
2483    // グローバルコンストラクタ自体が存在しなかった。詳細は
2484    // dom_exception_ctor/make_dom_exception 参照)。
2485    {
2486        let dom_exception_ctor_val = nv("DOMException", dom_exception_ctor);
2487        if let Value::Object(o) = &dom_exception_ctor_val {
2488            let mut b = o.borrow_mut();
2489            let legacy_codes: &[(&str, f64)] = &[
2490                ("INDEX_SIZE_ERR", 1.0),
2491                ("HIERARCHY_REQUEST_ERR", 3.0),
2492                ("WRONG_DOCUMENT_ERR", 4.0),
2493                ("INVALID_CHARACTER_ERR", 5.0),
2494                ("NO_MODIFICATION_ALLOWED_ERR", 7.0),
2495                ("NOT_FOUND_ERR", 8.0),
2496                ("NOT_SUPPORTED_ERR", 9.0),
2497                ("INUSE_ATTRIBUTE_ERR", 10.0),
2498                ("INVALID_STATE_ERR", 11.0),
2499                ("SYNTAX_ERR", 12.0),
2500                ("INVALID_MODIFICATION_ERR", 13.0),
2501                ("NAMESPACE_ERR", 14.0),
2502                ("INVALID_ACCESS_ERR", 15.0),
2503                ("SECURITY_ERR", 18.0),
2504                ("NETWORK_ERR", 19.0),
2505                ("ABORT_ERR", 20.0),
2506                ("TIMEOUT_ERR", 23.0),
2507                ("DATA_CLONE_ERR", 25.0),
2508            ];
2509            for (name, code) in legacy_codes {
2510                b.props.insert(String::from(*name), Value::Number(*code));
2511            }
2512        }
2513        g.vars.insert("DOMException".into(), dom_exception_ctor_val);
2514    }
2515
2516    // NodeFilter インターフェース(W3C DOM Standard §6 Traversal)
2517    {
2518        let node_filter = Obj::plain();
2519        {
2520            let mut b = node_filter.borrow_mut();
2521            b.props.insert("FILTER_ACCEPT".into(), Value::Number(1.0));
2522            b.props.insert("FILTER_REJECT".into(), Value::Number(2.0));
2523            b.props.insert("FILTER_SKIP".into(), Value::Number(3.0));
2524            b.props.insert("SHOW_ALL".into(), Value::Number(0xFFFF_FFFFu32 as f64));
2525            b.props.insert("SHOW_ELEMENT".into(), Value::Number(0x1 as f64));
2526            b.props.insert("SHOW_ATTRIBUTE".into(), Value::Number(0x2 as f64));
2527            b.props.insert("SHOW_TEXT".into(), Value::Number(0x4 as f64));
2528            b.props.insert("SHOW_CDATA_SECTION".into(), Value::Number(0x8 as f64));
2529            b.props.insert("SHOW_ENTITY_REFERENCE".into(), Value::Number(0x10 as f64));
2530            b.props.insert("SHOW_ENTITY".into(), Value::Number(0x20 as f64));
2531            b.props.insert("SHOW_PROCESSING_INSTRUCTION".into(), Value::Number(0x40 as f64));
2532            b.props.insert("SHOW_COMMENT".into(), Value::Number(0x80 as f64));
2533            b.props.insert("SHOW_DOCUMENT".into(), Value::Number(0x100 as f64));
2534            b.props.insert("SHOW_DOCUMENT_TYPE".into(), Value::Number(0x200 as f64));
2535            b.props.insert("SHOW_DOCUMENT_FRAGMENT".into(), Value::Number(0x400 as f64));
2536            b.props.insert("SHOW_NOTATION".into(), Value::Number(0x800 as f64));
2537        }
2538        g.vars.insert("NodeFilter".into(), Value::Object(node_filter));
2539    }
2540
2541    // FinalizationRegistry(GC コールバック。AtmOS は GC 非搭載のため register は no-op)
2542    g.vars.insert(
2543        "FinalizationRegistry".into(),
2544        nv("FinalizationRegistry", finalization_registry_ctor),
2545    );
2546
2547    // fetch(同期HTTP GET を Promise/Response 互換で。data: URL も対応)。
2548    g.vars.insert("fetch".into(), nv("fetch", fetch));
2549    g.vars.insert("Request".into(), nv("Request", request_ctor));
2550    g.vars.insert("Headers".into(), nv("Headers", headers_ctor));
2551    // `Response` コンストラクタ自体が丸ごと未対応だった(これまで `fetch()`
2552    // 内部の `make_response`/`make_response_with_url` からしか生成できず、
2553    // Service Worker やテストコードでの `new Response(JSON.stringify(x),
2554    // {status:200})`/`Response.json(x)` という定番パターンが使えなかった)。
2555    {
2556        let response_ctor_val = nv("Response", response_ctor);
2557        if let Value::Object(o) = &response_ctor_val {
2558            let mut b = o.borrow_mut();
2559            b.props.insert("json".into(), nv("Response.json", response_json_static));
2560            b.props.insert("error".into(), nv("Response.error", response_error_static));
2561            b.props
2562                .insert("redirect".into(), nv("Response.redirect", response_redirect_static));
2563        }
2564        g.vars.insert("Response".into(), response_ctor_val);
2565    }
2566    // XMLHttpRequest(同期取得 + onload/onreadystatechange 発火)。
2567    g.vars
2568        .insert("XMLHttpRequest".into(), nv("XMLHttpRequest", xhr_ctor));
2569
2570    // RegExp コンストラクタ
2571    let regexp_ctor_v = nv("RegExp", regexp_ctor);
2572    if let Value::Object(rc) = &regexp_ctor_v {
2573        rc.borrow_mut()
2574            .props
2575            .insert("escape".into(), nv("RegExp.escape", regexp_escape_static));
2576        // 【2026-10-19】`RegExp.prototype` のメソッド。実体(`/x/.exec`)は `regexp_get` が
2577        // 同じ関数を返すが、プロトタイプから直接読む形(XRegExp の
2578        // `{exec: RegExp.prototype.exec, …}`、YouTube の kevlar_base)が undefined だった。
2579        let proto = Obj::plain();
2580        {
2581            let mut pb = proto.borrow_mut();
2582            let methods: [(&str, NativeFn); 9] = [
2583                ("test", re_test),
2584                ("exec", re_exec),
2585                ("toString", re_to_string),
2586                ("compile", re_compile),
2587                ("Symbol(Symbol.match)", re_symbol_match),
2588                ("Symbol(Symbol.replace)", re_symbol_replace),
2589                ("Symbol(Symbol.search)", re_symbol_search),
2590                ("Symbol(Symbol.split)", re_symbol_split),
2591                ("Symbol(Symbol.matchAll)", re_symbol_match_all),
2592            ];
2593            for (name, f) in methods {
2594                pb.props.insert(name.into(), nv(name, f));
2595            }
2596            pb.props.insert("constructor".into(), regexp_ctor_v.clone());
2597        }
2598        rc.borrow_mut().props.insert("prototype".into(), Value::Object(proto));
2599    }
2600    g.vars.insert("RegExp".into(), regexp_ctor_v);
2601
2602    // WeakMap / WeakSet / WeakRef(Rc ベース近似実装: キーはオブジェクト参照で識別)
2603    g.vars.insert("WeakMap".into(), nv("WeakMap", weakmap_ctor));
2604    g.vars.insert("WeakSet".into(), nv("WeakSet", weakset_ctor));
2605    g.vars.insert("WeakRef".into(), nv("WeakRef", weakref_ctor));
2606
2607    // Symbol(グローバル `Symbol()` 関数: 文字列ラベルを付与した一意値を返す)
2608    {
2609        let sym_fn = nv("Symbol", symbol_fn);
2610        if let Value::Object(sf) = &sym_fn {
2611            sf.borrow_mut().props.insert("for".into(), nv("Symbol.for", symbol_for));
2612            sf.borrow_mut().props.insert("keyFor".into(), nv("Symbol.keyFor", symbol_key_for));
2613            sf.borrow_mut().props.insert("iterator".into(), Value::str("Symbol(Symbol.iterator)"));
2614            sf.borrow_mut().props.insert("toPrimitive".into(), Value::str("Symbol(Symbol.toPrimitive)"));
2615            sf.borrow_mut().props.insert("hasInstance".into(), Value::str("Symbol(Symbol.hasInstance)"));
2616            sf.borrow_mut().props.insert("toStringTag".into(), Value::str("Symbol(Symbol.toStringTag)"));
2617            sf.borrow_mut().props.insert("asyncIterator".into(), Value::str("Symbol(Symbol.asyncIterator)"));
2618            // `Array.prototype.concat` が参照する well-known symbol。定数自体が丸ごと
2619            // 未登録だったため `Symbol.isConcatSpreadable` が常に `undefined` になり、
2620            // `arr[Symbol.isConcatSpreadable] = ...` が実質 `arr["undefined"] = ...` という
2621            // 無関係なプロパティ代入に化けていた。
2622            sf.borrow_mut()
2623                .props
2624                .insert("isConcatSpreadable".into(), Value::str("Symbol(Symbol.isConcatSpreadable)"));
2625            // `Symbol.species`/`unscopables`/`match`/`replace`/`search`/`split` も同じ理由
2626            // (定数自体が未登録だと `obj[Symbol.species] = ...` 等が `obj["undefined"] = ...`
2627            // という無関係なプロパティ代入に化ける)で追加登録する。派生コンストラクタ選択
2628            // (`species`)やカスタムマッチャープロトコル(`match`/`replace`/`search`/`split`。
2629            // `String.prototype` 側のメソッドはこれらを見ずに常に組み込み `RegExp` 実装へ
2630            // 委譲する簡略実装のまま)、`with` 文の `unscopables` フィルタは非対応のままだが、
2631            // 定数自体は仕様どおり存在させ、キー衝突による静かな破壊だけは防ぐ。
2632            for name in [
2633                "species",
2634                "unscopables",
2635                "match",
2636                "matchAll",
2637                "replace",
2638                "search",
2639                "split",
2640            ] {
2641                sf.borrow_mut()
2642                    .props
2643                    .insert(String::from(name), Value::str(format!("Symbol(Symbol.{})", name)));
2644            }
2645            // Explicit Resource Management(`using`/`await using` 宣言、ES2023→ES2026ベースライン)。
2646            sf.borrow_mut().props.insert("dispose".into(), Value::str("Symbol(Symbol.dispose)"));
2647            sf.borrow_mut().props.insert("asyncDispose".into(), Value::str("Symbol(Symbol.asyncDispose)"));
2648        }
2649        g.vars.insert("Symbol".into(), sym_fn);
2650
2651        // 各組み込みコンストラクタ関数への Symbol(Symbol.species) アクセサ配線。
2652        // `Symbol.species`は仕様上ゲッターアクセサ(`this`を返すだけ)であり、
2653        // 呼び出し可能な関数値をそのまま`props`にデータプロパティとして
2654        // 差し込んでしまうと、`Array[Symbol.species]`はそのゲッター関数
2655        // オブジェクト自体を返してしまい(呼び出されない)、`=== Array`が
2656        // 常に`false`になるバグだった(2026-07-18 発見・修正)。他の
2657        // アクセサ登録箇所と同じ`accessors`マップへ`Accessor{get,set}`として
2658        // 登録する必要がある。
2659        use super::value::Accessor;
2660        let species_fn = nv("get [Symbol.species]", symbol_species_getter);
2661        for ctor_name in [
2662            "Array", "RegExp", "Promise", "Map", "Set", "ArrayBuffer",
2663            "Uint8Array", "Int8Array", "Uint8ClampedArray", "Int16Array",
2664            "Uint16Array", "Int32Array", "Uint32Array", "Float16Array", "Float32Array",
2665            "Float64Array", "BigInt64Array", "BigUint64Array",
2666        ] {
2667            if let Some(Value::Object(ctor_obj)) = g.vars.get(ctor_name).cloned() {
2668                ctor_obj.borrow_mut().accessors.insert(
2669                    "Symbol(Symbol.species)".into(),
2670                    Accessor { get: Some(species_fn.clone()), set: None },
2671                );
2672            }
2673        }
2674    }
2675
2676    // 定数
2677    g.vars.insert("NaN".into(), Value::Number(f64::NAN));
2678    g.vars
2679        .insert("Infinity".into(), Value::Number(f64::INFINITY));
2680    g.vars.insert("undefined".into(), Value::Undefined);
2681    // globalThis は window オブジェクトを指す(location/history/fetch 等にアクセス可能)。
2682    let global_this = g.vars.get("window").cloned().unwrap_or(Value::Undefined);
2683    g.vars.insert("globalThis".into(), global_this);
2684    // `self`/`top`/`parent`/`frames`(丸ごと未対応だった。`globalThis`は
2685    // 実装済みだったのに、同じ「windowオブジェクト自身を指す別名」仲間の
2686    // うちこれら4つだけ登録が漏れていた。2026-07-16 発見・実装)。
2687    // `self`はワーカー/メインスレッド両対応コードで`typeof self !==
2688    // 'undefined'`という定番の環境判定に使われる。`top`/`parent`/`frames`は
2689    // 本来フレーム階層を表すが、この処理系にはiframeの独立JSレルムが無く
2690    // 常に単一フレームのため、仕様上「フレームが無い最上位ウィンドウでは
2691    // top/parent/framesはいずれも自分自身」という規定どおり`window`自身を
2692    // 指す(`window.top === window`という定番のフレーム内判定イディオムが
2693    // 正しく`true`になる)。
2694    // 【2026-09-05】グローバルにはあるのに `window.` 経由で読めないものを塞ぐ。
2695    //
2696    // `window` は個別に用意したプロパティしか持たず、`navigator` などは
2697    // 別のグローバル変数として登録されているだけだった。
2698    // ブラウザでは同じ物を両方から読めるので、`window.navigator` が
2699    // `undefined` になる。jQuery 1.8.2 は
2700    // `p.uaMatch(a.navigator.userAgent)`(`a` は window)と書くため、
2701    // ここで初期化が止まっていた。
2702    //
2703    // 既に `window` に入っているものは上書きしない(個別に用意した実装を
2704    // 壊さないため)。
2705    if let Some(Value::Object(w)) = g.vars.get("window").cloned() {
2706        for name in WINDOW_MIRRORED_GLOBALS {
2707            if w.borrow().props.contains_key(*name) {
2708                continue;
2709            }
2710            if let Some(v) = g.vars.get(*name).cloned() {
2711                w.borrow_mut()
2712                    .props
2713                    .insert(alloc::string::String::from(*name), v);
2714            }
2715        }
2716    }
2717    // 【2026-10-17】組込みのコンストラクタの多くは `.prototype` を持たない(メソッドは実体から
2718    // 動的に解決する簡略実装)。`typeof X.prototype.m !== "function" && (X.prototype.m = ...)`
2719    // というポリフィルの定番判定が `undefined` の読み取りで落ちるため、大文字で始まる
2720    // 呼び出し可能なグローバルに `prototype` が無ければ空の prototype(`constructor` 付き)を補う。
2721    {
2722        let ctors: alloc::vec::Vec<(alloc::string::String, Rc<RefCell<Obj>>)> = g
2723            .vars
2724            .iter()
2725            .filter(|(k, _)| k.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
2726            .filter_map(|(k, v)| match v {
2727                Value::Object(o) if o.borrow().is_callable() && !o.borrow().props.contains_key("prototype") => {
2728                    Some((k.clone(), o.clone()))
2729                }
2730                _ => None,
2731            })
2732            .collect();
2733        for (_name, ctor) in ctors {
2734            let proto = Obj::plain();
2735            proto.borrow_mut().props.insert("constructor".into(), Value::Object(ctor.clone()));
2736            ctor.borrow_mut().props.insert("prototype".into(), Value::Object(proto));
2737        }
2738    }
2739    let window_ref = g.vars.get("window").cloned().unwrap_or(Value::Undefined);
2740    g.vars.insert("self".into(), window_ref.clone());
2741    g.vars.insert("top".into(), window_ref.clone());
2742    g.vars.insert("parent".into(), window_ref.clone());
2743    g.vars.insert("frames".into(), window_ref);
2744}
2745
2746
2747// ===== 分割サブモジュール(2026-07-16 リファクタ フェーズ2) =====
2748// builtins.rs が肥大化したため、install() 以降の各セクションを builtins/ 配下へ
2749// 機械的に切り出した。ロジックは不変。`builtins::<name>` の外部参照パスを保つため
2750// ここで pub(crate) use で全て再エクスポートする。詳細は walkthrough.md 参照。
2751mod dom;
2752mod console_math_json;
2753mod object_array;
2754mod props_methods;
2755mod collections;
2756mod promise;
2757mod web_forms;
2758mod encoding;
2759mod extra_methods;
2760mod typed_array;
2761mod text_clone;
2762mod extra_static;
2763mod dom_document;
2764mod observers;
2765mod events;
2766mod window_api;
2767mod static_classlist;
2768mod timers_gen;
2769mod weak_symbol;
2770mod regexp;
2771mod dom_forward;
2772pub(crate) use dom_forward::{named_node_map_get, named_node_map_item};
2773mod date;
2774mod errors_misc;
2775mod intl;
2776mod crypto_subtle;
2777mod streams;
2778mod mse;
2779mod worker;
2780
2781pub(crate) use dom::*;
2782pub(crate) use console_math_json::*;
2783pub(crate) use object_array::*;
2784pub(crate) use props_methods::*;
2785pub(crate) use collections::*;
2786pub(crate) use promise::*;
2787pub(crate) use web_forms::*;
2788pub(crate) use encoding::*;
2789pub(crate) use extra_methods::*;
2790pub(crate) use typed_array::*;
2791pub(crate) use text_clone::*;
2792pub(crate) use extra_static::*;
2793pub(crate) use dom_document::*;
2794pub(crate) use observers::*;
2795pub(crate) use events::*;
2796pub(crate) use window_api::*;
2797pub(crate) use static_classlist::*;
2798pub(crate) use timers_gen::*;
2799pub(crate) use weak_symbol::*;
2800pub(crate) use regexp::*;
2801pub(crate) use date::*;
2802pub(crate) use errors_misc::*;
2803pub(crate) use intl::*;
2804pub(crate) use crypto_subtle::*;
2805pub(crate) use streams::*;
2806pub(crate) use mse::*;
2807pub(crate) use worker::*;
2808
2809