Merge pull request #2487 from navidemad/feat/external-stylesheets-flag

Add --enable-external-stylesheets flag with fetch + parse
This commit is contained in:
Pierre Tachoire
2026-05-20 13:41:59 +02:00
committed by GitHub
12 changed files with 499 additions and 2 deletions

View File

@@ -100,6 +100,7 @@ const CommonOptions = .{
.{ .name = "storage_sqlite_path", .type = ?[:0]const u8 },
.{ .name = "disable_subframes", .type = bool },
.{ .name = "disable_workers", .type = bool },
.{ .name = "enable_external_stylesheets", .type = bool },
};
fn dumpValidator(_: Allocator, args: *std.process.ArgIterator) !?DumpFormat {
@@ -255,6 +256,13 @@ pub fn disableWorkers(self: *const Config) bool {
};
}
pub fn enableExternalStylesheets(self: *const Config) bool {
return switch (self.mode) {
inline .serve, .fetch, .mcp => |opts| opts.enable_external_stylesheets,
else => unreachable,
};
}
pub fn httpProxy(self: *const Config) ?[:0]const u8 {
return switch (self.mode) {
inline .serve, .fetch, .mcp => |opts| opts.http_proxy,

View File

@@ -52,6 +52,7 @@ const AbstractRange = @import("webapi/AbstractRange.zig");
const MutationObserver = @import("webapi/MutationObserver.zig");
const IntersectionObserver = @import("webapi/IntersectionObserver.zig");
const Worker = @import("webapi/Worker.zig");
const CSSStyleSheet = @import("webapi/css/CSSStyleSheet.zig");
const CustomElementDefinition = @import("webapi/CustomElementDefinition.zig");
const PageTransitionEvent = @import("webapi/event/PageTransitionEvent.zig");
const SubmitEvent = @import("webapi/event/SubmitEvent.zig");
@@ -1712,6 +1713,131 @@ pub fn queueLoad(self: *Frame, html: *Element.Html) !void {
}
}
// Hard cap on a single external stylesheet body. CSS rule storage is per-
// arena so a hostile sheet could otherwise inflate page memory; 2 MiB is
// well above anything seen on real sites (Tailwind's `preflight + utilities`
// build is ~400 KiB gzipped, ~3 MiB raw — at which point a site should be
// splitting by route anyway).
const MAX_STYLESHEET_BYTES: usize = 2 * 1024 * 1024;
// Synchronously fetch and parse an external `<link rel=stylesheet>`.
// href is passed in as an optimization since the [currently] only callsite has
// it, so why look it up again?
pub fn loadExternalStylesheet(self: *Frame, link: *Element.Html.Link, href: []const u8) !void {
if (self.isGoingAway() or href.len == 0) {
return;
}
const session = self._session;
// this feature is disabled by default, and can be turned on via a command
// line flag or via an CDP command
if (session.load_external_stylesheets == false) {
return self.queueLoad(link._proto);
}
// Fragment-parsed links (innerHTML, DOMParser, ...) may not be attached.
// TODO: this isn't correct in all cases. If the link is added into an
// attached node, I think we SHOULD load it.
if (self._parse_mode == .fragment) {
return;
}
const element = link.asElement();
const arena = try session.getArena(.medium, "Frame.loadExternalStylesheet");
defer session.releaseArena(arena);
const resolved = URL.resolve(arena, self.base(), href, .{ .encoding = self.charset }) catch |err| {
log.warn(.http, "external stylesheet resolve", .{ .err = err, .href = href });
try self.fireElementEvent(element, comptime .wrap("error"));
return;
};
const http_client = &session.browser.http_client;
var headers = try http_client.newHeaders();
try headers.add("Accept: text/css,*/*;q=0.1");
try self.headersForRequest(&headers);
// Set the script-manager `is_evaluating` flag for the same reason
// `ScriptManager.addFromElement` does: `syncRequest` pumps the CDP
// socket inline, so a `Target.closeTarget` / `Page.close` arriving
// mid-fetch would otherwise drive `Session.removePage` while this
// function still holds pointers to `self`. The check in
// `Session.removePage` (Session.zig:253) consults
// `frame.anyScriptEvaluating()`, which only sees this flag.
const sm = &self._script_manager.base;
const was_evaluating = sm.is_evaluating;
sm.is_evaluating = true;
defer sm.is_evaluating = was_evaluating;
var response = http_client.syncRequest(arena, .{
.url = resolved,
.method = .GET,
.frame_id = self._frame_id,
.loader_id = self._loader_id,
.headers = headers,
.cookie_jar = &session.cookie_jar,
.cookie_origin = self.url,
.resource_type = .stylesheet,
.notification = session.notification,
}) catch |err| {
log.warn(.http, "external stylesheet fetch", .{ .err = err, .url = resolved });
return self.fireElementEvent(element, comptime .wrap("error"));
};
defer response.deinit(arena);
if (response.status < 200 or response.status >= 300) {
log.info(.http, "external stylesheet status", .{ .status = response.status, .url = resolved });
return self.fireElementEvent(element, comptime .wrap("error"));
}
if (response.body.items.len > MAX_STYLESHEET_BYTES) {
log.warn(.http, "external stylesheet too large", .{
.bytes = response.body.items.len,
.max = MAX_STYLESHEET_BYTES,
.url = resolved,
});
return self.fireElementEvent(element, comptime .wrap("error"));
}
// Reuse the cached sheet on re-fetch (href mutation on a connected
// link) so `document.styleSheets` keeps a single entry per <link>
// instead of accumulating one per href change. On first load, create
// and register; on subsequent loads, replace content in place.
//
// First-load creation assigns `link._sheet` AFTER `sheets.add`
// succeeds so an OOM during registration doesn't cache an unregistered
// sheet (which would short-circuit every future re-fetch via the
// `orelse` branch, leaving the sheet permanently unreachable through
// `document.styleSheets`).
const sheet = link._sheet orelse blk: {
const new_sheet = try CSSStyleSheet.initWithOwner(element, self);
const sheets = try self.document.getStyleSheets(self);
try sheets.add(new_sheet, self);
link._sheet = new_sheet;
break :blk new_sheet;
};
// Parse first, only swap `_href` on success. `replaceSync` itself is
// not atomic (clears rules before the insert loop), so a mid-parse
// OOM still drops the old rules — full atomicity would require a
// scratch-list pattern in `CSSStyleSheet.replaceSync`. Keeping
// `_href` consistent with what the sheet actually contains is the
// minimum.
sheet.replaceSync(response.body.items, self) catch |err| {
log.warn(.http, "external stylesheet parse", .{ .err = err, .url = resolved });
return self.fireElementEvent(element, comptime .wrap("error"));
};
sheet._href = try self.arena.dupe(u8, resolved);
try self.fireElementEvent(element, comptime .wrap("load"));
}
fn fireElementEvent(self: *Frame, el: *Element, name: String) !void {
const event = try Event.initTrusted(name, .{}, self._page);
try self._event_manager.dispatch(el.asEventTarget(), event);
}
fn dispatchLoad(self: *Frame) !void {
const has_dom_load_listener = self._event_manager.has_dom_load_listener;
@@ -1724,8 +1850,7 @@ fn dispatchLoad(self: *Frame) !void {
for (to_process.items) |html_element| {
if (has_dom_load_listener or html_element.hasAttributeFunction(.onload, self)) {
const event = try Event.initTrusted(comptime .wrap("load"), .{}, self._page);
try self._event_manager.dispatch(html_element.asEventTarget(), event);
try self.fireElementEvent(html_element.asElement(), comptime .wrap("load"));
}
}
@@ -3045,6 +3170,20 @@ pub fn removeNode(self: *Frame, parent: *Node, child: *Node, opts: RemoveNodeOpt
style._sheet = null;
}
self._style_manager.sheetModified();
} else if (el.is(Element.Html.Link)) |link| {
// External stylesheet links registered via Frame.loadExternalStylesheet
// must be symmetrically deregistered on disconnect, or
// `document.styleSheets` accumulates phantom entries and the
// visibility cascade keeps honoring rules from removed links —
// exactly the SPA theme-switch pattern (append new sheet,
// remove old) the feature exists to serve.
if (link._sheet) |sheet| {
if (self.document._style_sheets) |sheets| {
sheets.remove(sheet);
}
link._sheet = null;
self._style_manager.sheetModified();
}
}
}
}

View File

@@ -977,6 +977,7 @@ pub const Request = struct {
xhr,
script,
fetch,
stylesheet,
// Allowed Values: Document, Stylesheet, Image, Media, Font, Script,
// TextTrack, XHR, Fetch, Prefetch, EventSource, WebSocket, Manifest,
@@ -988,6 +989,7 @@ pub const Request = struct {
.xhr => "XHR",
.script => "Script",
.fetch => "Fetch",
.stylesheet => "Stylesheet",
};
}
};

View File

@@ -91,6 +91,15 @@ subframe_loading_enabled: bool = true,
// session init; the LP.configureLoading CDP method can flip it per-session.
worker_loading_enabled: bool = true,
// Opt-in fetch of external <link rel=stylesheet> resources. Defaults to
// false to preserve the current rendering-free fast path: drivers that
// don't need accurate visibility checks pay nothing. Set from the
// `--enable-external-stylesheets` CLI flag at session init; the
// LP.configureLoading CDP method can flip it per-session. When true,
// `Link.linkAddedCallback` routes to `Frame.loadExternalStylesheet`
// (synchronous fetch + parse + register on `document.styleSheets`).
load_external_stylesheets: bool = false,
pub fn init(self: *Session, browser: *Browser, notification: *Notification) !void {
const allocator = browser.app.allocator;
const arena_pool = browser.arena_pool;
@@ -112,6 +121,7 @@ pub fn init(self: *Session, browser: *Browser, notification: *Notification) !voi
// CLI defaults; LP.configureLoading can flip these per-session.
.subframe_loading_enabled = !browser.app.config.disableSubframes(),
.worker_loading_enabled = !browser.app.config.disableWorkers(),
.load_external_stylesheets = browser.app.config.enableExternalStylesheets(),
};
}

View File

@@ -0,0 +1,185 @@
<!DOCTYPE html>
<head>
<script src="../testing.js"></script>
<link id="ext" rel="stylesheet" href="/styles/visibility.css">
</head>
<body>
<div id="visible">always visible</div>
<div id="hidden" class="ext-hide">hidden by external rule</div>
<script id="ext_stylesheet_registered">
{
// The parser-synchronous fetch in Frame.loadExternalStylesheet runs
// before this script executes, so document.styleSheets reflects the
// fetched sheet by the time we look.
testing.expectEqual(1, document.styleSheets.length);
const sheet = document.styleSheets[0];
testing.expectEqual(testing.ORIGIN + '/styles/visibility.css', sheet.href);
testing.expectEqual(1, sheet.cssRules.length);
testing.expectEqual('.ext-hide', sheet.cssRules[0].selectorText);
}
</script>
<script id="ext_stylesheet_cascade">
{
// External rule must reach StyleManager: a `.ext-hide` element should
// report checkVisibility() === false, exactly as if the rule were
// declared inline in a `<style>` block.
testing.expectTrue(document.getElementById('visible').checkVisibility());
testing.expectFalse(document.getElementById('hidden').checkVisibility());
}
</script>
<script id="ext_stylesheet_dynamic_load_event">
{
// Dynamically-inserted <link> fires `load` after the fetch completes.
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/visibility.css';
testing.async(async () => {
const fired = await new Promise(resolve => {
link.addEventListener('load', () => resolve(true));
link.addEventListener('error', () => resolve(false));
document.head.appendChild(link);
});
testing.expectEqual(true, fired);
});
testing.expectEqual(true, true);
}
</script>
<script id="ext_stylesheet_404_fires_error">
{
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/404.css';
testing.async(async () => {
const evt = await new Promise(resolve => {
link.addEventListener('load', () => resolve('load'));
link.addEventListener('error', () => resolve('error'));
document.head.appendChild(link);
});
testing.expectEqual('error', evt);
});
testing.expectEqual(true, true);
}
</script>
<script id="ext_stylesheet_oversize_fires_error">
{
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/oversize.css';
testing.async(async () => {
const evt = await new Promise(resolve => {
link.addEventListener('load', () => resolve('load'));
link.addEventListener('error', () => resolve('error'));
document.head.appendChild(link);
});
testing.expectEqual('error', evt);
});
testing.expectEqual(true, true);
}
</script>
<script id="ext_stylesheet_href_change_replaces">
{
// Mutating link.href on a connected stylesheet link must REPLACE the
// cached sheet's rules, not append a second entry to
// document.styleSheets. Regression test for the stale-sheet
// accumulation bug caught in code review.
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/visibility.css';
testing.async(async () => {
const baseline = document.styleSheets.length;
await new Promise(resolve => {
link.addEventListener('load', () => resolve(), { once: true });
document.head.appendChild(link);
});
const afterFirst = document.styleSheets.length;
testing.expectEqual(baseline + 1, afterFirst);
const second = await new Promise(resolve => {
link.addEventListener('load', () => resolve('load'), { once: true });
link.addEventListener('error', () => resolve('error'), { once: true });
link.href = '/styles/visibility2.css';
});
testing.expectEqual('load', second);
testing.expectEqual(afterFirst, document.styleSheets.length);
// New rules must be in effect after the swap. The reused sheet is
// the last registered one (the static head link registered first).
const sheet = document.styleSheets[afterFirst - 1];
testing.expectEqual(testing.ORIGIN + '/styles/visibility2.css', sheet.href);
testing.expectEqual(1, sheet.cssRules.length);
testing.expectEqual('.ext-hide-2', sheet.cssRules[0].selectorText);
});
testing.expectEqual(true, true);
}
</script>
<script id="ext_stylesheet_fragment_parse_skipped">
{
// Stylesheet links parsed via innerHTML / outerHTML /
// insertAdjacentHTML / Range.createContextualFragment / <template>
// 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: 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>
<script id="ext_stylesheet_disconnect_removes">
{
// Removing a connected stylesheet link must drop its sheet from
// document.styleSheets. Without symmetric deregistration the SPA
// theme-switch pattern (append new, remove old) would accumulate
// phantom entries and stale cascade rules.
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/visibility2.css';
testing.async(async () => {
const baseline = document.styleSheets.length;
await new Promise(resolve => {
link.addEventListener('load', () => resolve(), { once: true });
document.head.appendChild(link);
});
testing.expectEqual(baseline + 1, document.styleSheets.length);
link.remove();
testing.expectEqual(baseline, document.styleSheets.length);
// Re-appending must register fresh — the disconnect cleanup
// cleared link._sheet so the cached short-circuit doesn't apply.
await new Promise(resolve => {
link.addEventListener('load', () => resolve(), { once: true });
document.head.appendChild(link);
});
testing.expectEqual(baseline + 1, document.styleSheets.length);
});
testing.expectEqual(true, true);
}
</script>
</body>

View File

@@ -137,3 +137,19 @@
});
}
</script>
<script id="ext_stylesheet_disabled_default">
{
// Flag defaults off — a <link rel=stylesheet> must NOT register a sheet
// on document.styleSheets and must NOT touch the network. The synthetic
// load event still fires (asserted by other tests above).
const before = document.styleSheets.length;
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/visibility.css';
document.head.appendChild(link);
testing.onload(() => {
testing.expectEqual(before, document.styleSheets.length);
});
}
</script>

View File

@@ -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

View File

@@ -26,6 +26,12 @@ const HtmlElement = @import("../Html.zig");
const Link = @This();
_proto: *HtmlElement,
// Cached CSSStyleSheet for an external `rel=stylesheet` once
// `Frame.loadExternalStylesheet` has registered it. Re-fetches (href
// mutated on a connected link) reuse this sheet via `replaceSync` so the
// old rules are dropped instead of accumulating in `document.styleSheets`.
// Mirrors `Style._sheet`.
_sheet: ?*@import("../../css/CSSStyleSheet.zig") = null,
pub fn asElement(self: *Link) *Element {
return self._proto._proto;
@@ -114,6 +120,14 @@ pub fn linkAddedCallback(self: *Link, frame: *Frame) !void {
return;
}
// Opt-in fetch for `rel="stylesheet"` — drives `frame.loadExternalStylesheet`,
// which fires the load/error event itself. Other rels (preload,
// modulepreload) and the disabled case keep the rendering-free stub that
// fires a synthetic `load` event without touching the network.
if (std.mem.eql(u8, rel, "stylesheet")) {
return frame.loadExternalStylesheet(self, href);
}
try frame.queueLoad(self._proto);
}
@@ -143,7 +157,24 @@ pub const JsApi = struct {
}
};
// Parser-created <link> elements are void (no closing tag) so they never
// reach `Frame.nodeComplete`. Mirror `Image.Build.created` so static head
// links in HTML go through `linkAddedCallback` at element-create time,
// with attributes already populated by `populateElementAttributes`.
pub const Build = struct {
pub fn created(node: *Node, frame: *Frame) !void {
const self = node.as(Link);
return self.linkAddedCallback(frame);
}
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.Link" {
try testing.htmlRunner("element/html/link.html", .{});
}
test "WebApi: HTML.Link external stylesheet" {
const filter: testing.LogFilter = .init(&.{.http});
defer filter.deinit();
try testing.htmlRunner("css/external_stylesheet.html", .{ .load_external_stylesheets = true });
}

View File

@@ -209,6 +209,7 @@ pub fn requestIntercept(bc: *CDP.BrowserContext, intercept: *const Notification.
.xhr => "XHR",
.document => "Document",
.fetch => "Fetch",
.stylesheet => "Stylesheet",
},
.networkId = &id.toRequestId(transfer), // matches the Network REQ-ID
}, .{ .session_id = session_id });
@@ -453,6 +454,7 @@ pub fn requestAuthRequired(bc: *CDP.BrowserContext, intercept: *const Notificati
.xhr => "XHR",
.document => "Document",
.fetch => "Fetch",
.stylesheet => "Stylesheet",
},
.authChallenge = .{
.origin = "", // TODO get origin, could be the proxy address for example.

View File

@@ -68,11 +68,13 @@ fn configureLoading(cmd: *CDP.Command) !void {
const params = (try cmd.params(struct {
subFrame: ?bool = null,
worker: ?bool = null,
externalStylesheets: ?bool = null,
})) orelse return error.InvalidParams;
const bc = cmd.browser_context orelse return error.NoBrowserContext;
if (params.subFrame) |v| bc.session.subframe_loading_enabled = v;
if (params.worker) |v| bc.session.worker_loading_enabled = v;
if (params.externalStylesheets) |v| bc.session.load_external_stylesheets = v;
return cmd.sendResult(null, .{});
}
@@ -688,3 +690,36 @@ test "cdp.lp: configureLoading toggles subFrame and worker independently" {
try testing.expectEqual(true, bc.session.subframe_loading_enabled);
try testing.expectEqual(true, bc.session.worker_loading_enabled);
}
test "cdp.lp: configureLoading toggles externalStylesheets independently" {
var ctx = try testing.context();
defer ctx.deinit();
const bc = try ctx.loadBrowserContext(.{});
_ = try bc.session.createPage();
// Default is opt-in: off unless the CLI flag or CDP toggle enables it.
try testing.expectEqual(false, bc.session.load_external_stylesheets);
// Enable via CDP; the other two loading toggles stay at their defaults.
try ctx.processMessage(.{
.id = 1,
.method = "LP.configureLoading",
.params = .{ .externalStylesheets = true },
});
try ctx.expectSentResult(null, .{ .id = 1 });
try testing.expectEqual(true, bc.session.load_external_stylesheets);
try testing.expectEqual(true, bc.session.subframe_loading_enabled);
try testing.expectEqual(true, bc.session.worker_loading_enabled);
// Flip back off; partial params must not reset the other fields.
try ctx.processMessage(.{
.id = 2,
.method = "LP.configureLoading",
.params = .{ .externalStylesheets = false },
});
try ctx.expectSentResult(null, .{ .id = 2 });
try testing.expectEqual(false, bc.session.load_external_stylesheets);
try testing.expectEqual(true, bc.session.subframe_loading_enabled);
try testing.expectEqual(true, bc.session.worker_loading_enabled);
}

View File

@@ -157,6 +157,13 @@
\\ script fetch is initiated and its scope never runs.
\\ Defaults to false.
\\
\\--enable-external-stylesheets
\\ Fetch external <link rel=stylesheet> resources so
\\ their rules contribute to computed styles (and
\\ therefore to visibility checks like display,
\\ visibility, opacity, pointer-events).
\\ Defaults to false.
\\
\\--block-private-networks Block HTTP requests to private/internal IP
\\ addresses after DNS resolution.
\\ Defaults to false.

View File

@@ -341,6 +341,7 @@ const WEB_API_TEST_ROOT = "src/browser/tests/";
const HtmlRunnerOpts = struct {
timeout_ms: u32 = 2000,
inject_script: ?[]const u8 = null,
load_external_stylesheets: bool = false,
};
pub fn htmlRunner(comptime path: []const u8, opts: HtmlRunnerOpts) !void {
@@ -353,6 +354,9 @@ pub fn htmlRunner(comptime path: []const u8, opts: HtmlRunnerOpts) !void {
}
defer test_session.inject_scripts = &.{};
test_session.load_external_stylesheets = opts.load_external_stylesheets;
defer test_session.load_external_stylesheets = false;
const root = try std.fs.path.joinZ(arena_allocator, &.{ WEB_API_TEST_ROOT, path });
const stat = std.fs.cwd().statFile(root) catch |err| {
std.debug.print("Failed to stat file: '{s}'", .{root});
@@ -678,6 +682,53 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
});
}
if (std.mem.eql(u8, path, "/styles/visibility.css")) {
// Used by css/external_stylesheet.html — drives the visibility
// cascade through StyleManager via Frame.loadExternalStylesheet
// so a `.ext-hide` element is observable to checkVisibility().
return req.respond(".ext-hide { display: none; }", .{
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/css" },
},
});
}
if (std.mem.eql(u8, path, "/styles/visibility2.css")) {
// Second visibility sheet used by the href-change regression test:
// mutating link.href must replace the cached sheet's rules in place,
// not append a new entry to document.styleSheets.
return req.respond(".ext-hide-2 { display: none; }", .{
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/css" },
},
});
}
if (std.mem.eql(u8, path, "/styles/404.css")) {
return req.respond("/* unused */", .{
.status = .not_found,
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/css" },
},
});
}
if (std.mem.eql(u8, path, "/styles/oversize.css")) {
// Body that exceeds Frame.MAX_STYLESHEET_BYTES (2 MiB) — written as a
// long sequence of valid declarations so the response itself parses
// fine and the error path is exercised by the size cap, not by a
// CSS parse failure.
const chunk = ".pad { color: #abcdef; } "; // 25 bytes
const repeats = (2 * 1024 * 1024 / chunk.len) + 1024;
var body = try std.ArrayList(u8).initCapacity(arena_allocator, chunk.len * repeats);
for (0..repeats) |_| body.appendSliceAssumeCapacity(chunk);
return req.respond(body.items, .{
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/css" },
},
});
}
if (std.mem.eql(u8, path, "/echo_referer")) {
// Echo the request's Referer header back as HTML so tests can assert
// what Referer the navigation sent. Used by the cross-page Referer test.