mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 09:35:59 -04:00
Apply fragment parse-mode to DOMParser
Closes the DOMParser gap left as a follow-up in the previous review-fix commit. DOMParser.parseFromString built its target Document via the frame's parser without touching `_parse_mode`, so `Build.created` → `linkAddedCallback` → `loadExternalStylesheet` saw `_parse_mode == .document` and fetched/registered sheets on the LIVE frame document for every stylesheet link in the parsed string. Bracket both the text/html and XML branches with the same fragment parse-mode `parseHtmlAsChildren` uses. The existing gate in `loadExternalStylesheet` already short-circuits on .fragment, so no change is needed there. Side benefits: parser-emitted scripts in DOMParser content stop reaching `scriptAddedCallback` against the live frame, default-script injection skips DOMParser content, and mutation observers on the live document no longer fan out for parsed nodes — all of which match what DOMParser should do per spec. Regression test extended to cover the DOMParser path alongside the existing innerHTML case. Refs #2343
This commit is contained in:
@@ -125,22 +125,28 @@
|
||||
{
|
||||
// Stylesheet links parsed via innerHTML / outerHTML /
|
||||
// insertAdjacentHTML / Range.createContextualFragment / <template>
|
||||
// content must NOT trigger a network fetch or register a sheet on
|
||||
// the live document — the owning subtree may never be attached.
|
||||
// Regression test for the fragment-mode bypass caught in code review.
|
||||
// content / DOMParser.parseFromString must NOT trigger a network
|
||||
// fetch or register a sheet on the live document — the owning
|
||||
// subtree may never be attached or belongs to a different Document.
|
||||
//
|
||||
// Assertion is synchronous on purpose: parseHtmlAsChildren runs
|
||||
// inline, so any unintended sheet registration would be visible
|
||||
// immediately. Deferring to testing.onload would race against the
|
||||
// async tests above that legitimately add sheets.
|
||||
//
|
||||
// DOMParser.parseFromString is a separate case (parses with
|
||||
// `_parse_mode = .document` into a *different* Document) and is not
|
||||
// covered by the same gate — tracked as a follow-up.
|
||||
// Assertion is synchronous on purpose: both parse paths run inline,
|
||||
// so any unintended sheet registration would be visible immediately.
|
||||
// Deferring to testing.onload would race against the async tests
|
||||
// above that legitimately add sheets.
|
||||
const before = document.styleSheets.length;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = '<link rel="stylesheet" href="/styles/visibility.css">';
|
||||
testing.expectEqual(before, document.styleSheets.length);
|
||||
|
||||
const parsed = new DOMParser().parseFromString(
|
||||
'<html><head><link rel="stylesheet" href="/styles/visibility2.css"></head><body></body></html>',
|
||||
'text/html'
|
||||
);
|
||||
// Parsed link exists in the new document...
|
||||
testing.expectEqual(1, parsed.querySelectorAll('link').length);
|
||||
// ...but no sheet on the live document.
|
||||
testing.expectEqual(before, document.styleSheets.length);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -53,6 +53,17 @@ pub fn parseFromString(
|
||||
const arena = try frame.getArena(.medium, "DOMParser.parseFromString");
|
||||
defer frame.releaseArena(arena);
|
||||
|
||||
// DOMParser builds a detached Document. Borrow the same fragment
|
||||
// parse-mode that `parseHtmlAsChildren` uses so frame-side hooks
|
||||
// triggered from `Build.created` / `nodeIsReady` (external stylesheet
|
||||
// fetches, script execution, mutation-observer fan-out, default-script
|
||||
// injection) treat the parsed nodes as detached and skip
|
||||
// side effects on the live document. The frame's `_parse_mode` is
|
||||
// restored on exit.
|
||||
const previous_parse_mode = frame._parse_mode;
|
||||
frame._parse_mode = .fragment;
|
||||
defer frame._parse_mode = previous_parse_mode;
|
||||
|
||||
return switch (target_mime) {
|
||||
.@"text/html" => {
|
||||
// Create a new HTMLDocument
|
||||
|
||||
Reference in New Issue
Block a user