mirror of
https://github.com/HeyPuter/browser.js.git
synced 2026-11-13 18:33:58 -10:00
develop
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a482b5ef2c | replace prettier with oxcfmt | ||
|
|
93495e9477 |
[core] rewrite the element and attribute layer (#112)
* [core] intercept setAttributeNode so detached Attr writes are rewritten
Carved out of the IDL interceptor branch. `Element.prototype.
setAttributeNode` was intercepted with an empty body, so
const attr = document.createAttribute("src");
attr.value = "https://origin.example/x.js";
script.setAttributeNode(attr);
reached no rewriter at all: `dom/attr.ts` only routes `Attr.value` back
through `setAttribute` for an Attr that already has an `ownerElement`,
and a freshly created one does not. Whether the resulting load stayed
inside the proxy came down to whether the service worker happened to be
controlling the document.
Rewritten in place on the node rather than replayed through
`setAttribute`, so that `el.getAttributeNode(name) === attr` and
`attr.ownerElement` both keep holding.
Covered by `adversarial/attribute-node-bypass.ts`.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01U6ry3tx3Z9nWXwLTJBRDMk
* rewrite element 2
Full rewrite of the element/attribute layer onto the IDL interceptors.
Scramjet rewrites content attributes: `src` on an image is a proxy URL
in the live DOM, `nonce` is gone entirely, `style` has been through the
CSS rewriter. None of that may be visible to the page, so every
rewritten attribute is now *mirrored* - the value the page wrote is kept
alongside it in `scramjet-attr-<name>`, and every read answers out of
the mirror.
The rules are the same ones the HTML rewriter applies while parsing, so
an attribute written by script ends up in exactly the state it would
have been in had it come down in the markup. Everything that touches an
attribute goes through one `attributeAccess` path: this module's
`Element` members, `dom/attr.ts` for `Attr` and `NamedNodeMap`,
`dom/reflect.ts` for the reflected IDL attributes. Nothing reads
`scramjet-attr-` by hand any more.
New files: `dom/attr.ts`, `dom/node.ts`, `dom/reflect.ts`,
`dom/markup.ts`, `dom/frames.ts`. `CharacterData` moves to `dom/node.ts`,
keeping the arity declarations - without them `node.appendData()`
rewrote and appended the string "undefined" where the native owes the
page a TypeError.
Supersedes the `setAttributeNode` interception from the previous commit
with one that also covers `setAttributeNodeNS`.
Drops the stale `SetHTMLUnsafeOptions: object` placeholder from
`webidl.ts`: this branch models the dictionary properly in
`global.d.ts`, so keeping both declared it twice.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01U6ry3tx3Z9nWXwLTJBRDMk
* [core] fix the element layer's regressions and remaining escapes
Review fixes for the element/attribute rewrite, each covered by
runway/src/tests/adversarial/element-layer.ts (24 of its 28 tests fail
without this commit).
Regressions against develop:
- script.textContent / innerText ran unrewritten: Blink defines both again
on HTMLScriptElement.prototype (Trusted Types sinks), shadowing the Node
and HTMLElement interceptors. Intercepted there too.
- A relative <base href> broke every URL getter: the native base still
started with the prefix and unrewrote to a relative string. Resolved from
the base element and the fallback base URL instead.
- setAttributeNode with an Attr the element already owns rewrote the
rewritten value into the mirror. An owned Attr now goes straight to the
native (no-op or InUseAttributeError).
- Appending to a parsed script rewrote it twice and duplicated the text:
existing Text children are now recorded before any mutation.
- removeAttributeNS / removeNamedItemNS left a stripped attribute's mirror.
Escapes:
- setHTMLUnsafe, setHTML (style), insertAdjacentHTML and outerHTML into a
script or style, Range.insertNode, Range.surroundContents and moveBefore
all landed raw text in a script.
- SVG script href (and xlink:href under any prefix) was never rewritten.
- box.instanceof honoured a page-defined Symbol.hasInstance, and decided
both text blanking and the markup rewriter's foreign context. Now
OrdinaryHasInstance, with native brand/namespace checks at those sites.
- meta refresh content set by script was never rewritten; it is now a rule
in htmlRules, re-run when http-equiv changes.
Correctness:
- Text blanked for an insertion is restored if the native throws.
- Text inserted into a connected, not-yet-started script now runs: Blink
does not prepare a script on a data write, so the script is re-prepared
with a native insert/remove once the rewritten program is in place.
- normalize() on an ancestor keeps its scripts' text; the source is
recorded before the write that can run the script.
- nonce is the [[CryptographicNonce]] slot, including on MathML.
- The style mirror follows CSSOM and typed OM writes; iframe.sandbox is a
real DOMTokenList over a detached stand-in; NamedNodeMap methods keep
their identity; insertAdjacentText throws SyntaxError on a bad position.
- textContent no longer walks subtrees with no script or style in them.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fp1ZD3JFmSXkxjonJv6BMV
* [core] reach natives through client.native in the element layer
The element layer captured descriptors out of `client.nativeStore` and
called them with `Reflect_apply`, in all six of its modules (attr,
element, fragments, markup, node, reflect) - the only DOM code in the
tree that does. Everything goes through `new client.native.X(obj)` now,
the same as the rest of client/, and the comment in element.ts arguing
for the snapshotted descriptors is gone.
Also stops `getSVGDocument` answering through `this.contentDocument`,
which the page can shadow, in favour of the getter's own steps.
No behaviour change: runway (no WPT) is identical before and after.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fp1ZD3JFmSXkxjonJv6BMV
* [core] make the attribute and text layers classes owned by the client
Both layers were module-level `WeakMap`s keyed on the client, filled
lazily by `attributeAccess(client)` / `textAccess(client)` with a record
of closures over that client - global state with a lifetime nothing
tied to the client, reachable by anything that imported the module.
They are now `AttributeLayer` (client/attributes.ts) and `TextLayer`
(client/text.ts), constructed in the `ScramjetClient` constructor as
`client.attributes` and `client.text`. They live outside client/dom/
because every module there is loaded as an interceptor.
The pure name helpers (`isInternalAttribute`, `mirrorAttributeName`, ...)
move with the attribute layer and stay free functions, holding no state.
`insertAttributeNode` becomes `AttributeLayer.insertNode`, and attr.ts's
`attrValue` / `setAttrValue` become `visibleValue` / `setVisibleValue`,
so nothing reaches into another interceptor module for shared logic.
No behaviour change: runway (no WPT) matches the previous run, bar the
live-site site-youtube-search test, which passes on its own.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fp1ZD3JFmSXkxjonJv6BMV
* elementlayer tests
* fix failing html tests
* vendor htmlparser2
* another review round
* more review + insert proxy finally
* more review 2
* some bullshit
* naiive attribute selector rewriter
* small perf improvements
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
|
||
|
|
684e46cfb2 |
[core] add client.Intercept and the WebIDL type machinery (#102)
* [core] add client.Intercept and the WebIDL type machinery Groundwork for replacing the RawProxy/RawTrap interceptors with declarative, IDL-typed ones. No API is migrated here - this lands the machinery and the differential tests that cover it, so the per-API rewrites can land in parallel on top. - `client/webidl.ts`: the IDL type language. Parses signature strings into types at compile time, and compiles them into argument validators and brand checkers at runtime. `Arguments`, `Returns`, `Type` and `Constructor` annotate an interceptor member; unresolved or malformed type names surface as a TS error on the annotation. - `client.Intercept`: takes a class whose members carry those annotations, splices it onto the native prototype chain, and wires each member through a trampoline so `error.stack` and `Function.prototype.toString` keep reporting the native shape. - `client/nativeerror.ts` + `shared/error.ts`: identify frames from the client bundle by a frame taken inside it rather than by file name, so stack cleaning holds however the embedder serves the bundle. This is what makes `cleanErrors` safe to turn on by default - with it off, every intercepted member leaves `scramjet.js` frames in any stack a page reads. - `shared/snapshot.ts`: more pinned natives, all additive. - `client/shared/origin.ts` + `client/helpers.ts`: the origin model the per-API rewrites build on. Replaces `dom/origin.ts` and `dom/protocol.ts`. - `tools/eslint/brand-check-plugin.mjs`: flags interceptor members whose IDL annotation and body disagree about brand checking. Wired in at "warn" - it will go to "error" once the APIs are migrated. - `tools/prettier/intercept-hug.mjs`: keeps the class argument of `client.Intercept(...)` hugged, so the migrations don't each reformat their own file. - `allowFailedIntercepts` is gone: an interceptor that fails now throws rather than silently calling through with unvalidated input. Three adversarial tests cover the machinery rather than any one API - `interceptor-nativeness` (the shape of the patches), `webidl-this` (brand checks and `this` handling) and `interface-object-identity`. Verified against origin/develop: rspack builds all bundles; tsc goes 43 -> 32 errors over the same file set; the types build goes 7 -> 4 erroring files; eslint goes 276 -> 261 problems. All remaining failures are pre-existing. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01U6ry3tx3Z9nWXwLTJBRDMk * clean up comments * small fixes * nits * preverse __proto__ --------- |