feat(sync): 同步 upstream 改动并升级到 0.16.3-fork.5

This commit is contained in:
leeguooooo
2026-03-09 12:00:13 +09:00
parent 3cbc284076
commit 356e2f5f39
46 changed files with 3542 additions and 218 deletions
+314
View File
@@ -0,0 +1,314 @@
import type { BenchmarkCommand, Scenario } from "./scenarios.js";
function generateArticlePage(): string {
const paragraphs = Array.from({ length: 30 }, (_, index) => {
const words = Array.from(
{ length: 40 + (index % 5) * 10 },
(_, wordIndex) =>
[
"the",
"quick",
"browser",
"engine",
"renders",
"content",
"across",
"multiple",
"layout",
"passes",
"while",
"handling",
"style",
"recalculations",
"and",
"DOM",
"mutations",
][wordIndex % 17],
).join(" ");
return `<p class="article-p">${words}</p>`;
});
const comments = Array.from(
{ length: 40 },
(_, index) =>
`<div class="comment" data-id="${index}">` +
`<div class="comment-header"><span class="author">User ${index}</span><time>2025-01-${String((index % 28) + 1).padStart(2, "0")}</time></div>` +
`<div class="comment-body"><p>This is comment number ${index + 1} with some discussion text.</p></div>` +
'<div class="comment-actions"><button class="reply-btn">Reply</button><button class="like-btn">Like</button></div>' +
"</div>",
);
const sidebar = Array.from(
{ length: 20 },
(_, index) =>
`<li class="sidebar-item"><a href="#section-${index}">Related Article ${index + 1}: A Longer Title Here</a></li>`,
);
return [
"<html><head><title>Benchmark Article</title>",
"<style>",
"body{font-family:system-ui;margin:0;padding:0;display:grid;grid-template-columns:1fr 300px;gap:20px;max-width:1200px;margin:0 auto}",
".article{padding:20px}.sidebar{padding:20px;border-left:1px solid #ddd}",
".comment{border:1px solid #eee;padding:12px;margin:8px 0;border-radius:4px}",
".comment-header{display:flex;justify-content:space-between;font-size:14px;color:#666}",
".nav{display:flex;gap:16px;padding:12px 20px;background:#f5f5f5;grid-column:1/-1}",
".tag{display:inline-block;padding:2px 8px;background:#e0e7ff;border-radius:12px;font-size:12px;margin:2px}",
"</style></head><body>",
`<nav class="nav">${Array.from({ length: 8 }, (_, index) => `<a href="#nav-${index}">Section ${index + 1}</a>`).join("")}</nav>`,
'<div class="article">',
"<h1>Understanding Modern Browser Engine Architecture</h1>",
'<div class="meta"><span class="author">Dr. Smith</span> | <time>2025-03-15</time> | <span>15 min read</span></div>',
`<div class="tags">${Array.from({ length: 6 }, (_, index) => `<span class="tag">tag-${index + 1}</span>`).join("")}</div>`,
"<h2>Introduction</h2>",
...paragraphs.slice(0, 5),
"<h2>Core Concepts</h2>",
...paragraphs.slice(5, 12),
'<blockquote>"Performance is not just about speed, it is about efficiency." - Anonymous</blockquote>',
"<h2>Implementation Details</h2>",
...paragraphs.slice(12, 20),
"<h3>Subsection A</h3>",
...paragraphs.slice(20, 25),
"<h3>Subsection B</h3>",
...paragraphs.slice(25),
"<h2>Comments</h2>",
'<div class="comments">',
...comments,
"</div></div>",
'<div class="sidebar">',
"<h3>Related Articles</h3>",
`<ul>${sidebar.join("")}</ul>`,
"<h3>Archives</h3>",
`<ul>${Array.from({ length: 12 }, (_, index) => `<li><a href="#month-${index}">Month ${index + 1}, 2025</a></li>`).join("")}</ul>`,
"</div>",
"</body></html>",
].join("");
}
function generateDataTablePage(): string {
const headerCells = ["ID", "Name", "Email", "Department", "Role", "Status", "Joined", "Last Active"];
const header = `<tr>${headerCells.map((cell) => `<th>${cell}</th>`).join("")}</tr>`;
const rows = Array.from({ length: 200 }, (_, index) => {
const department = ["Engineering", "Design", "Marketing", "Sales", "Support"][index % 5];
const role = ["Admin", "Manager", "Member", "Viewer"][index % 4];
const status = ["Active", "Inactive", "Pending"][index % 3];
return (
`<tr data-row="${index}">` +
`<td>${index + 1}</td>` +
`<td><a href="#user-${index}">User ${index + 1}</a></td>` +
`<td>user${index + 1}@example.com</td>` +
`<td>${department}</td>` +
`<td><span class="badge badge-${role.toLowerCase()}">${role}</span></td>` +
`<td><span class="status status-${status.toLowerCase()}">${status}</span></td>` +
`<td>2024-${String((index % 12) + 1).padStart(2, "0")}-${String((index % 28) + 1).padStart(2, "0")}</td>` +
`<td>${index % 3 === 0 ? "Today" : index % 3 === 1 ? "Yesterday" : "Last week"}</td>` +
"</tr>"
);
});
return [
"<html><head><title>Benchmark Table</title>",
"<style>",
"body{font-family:system-ui;margin:20px}",
"table{width:100%;border-collapse:collapse}",
"th,td{padding:8px 12px;border:1px solid #ddd;text-align:left}",
"th{background:#f5f5f5;font-weight:600;position:sticky;top:0}",
"tr:nth-child(even){background:#fafafa}",
".badge{padding:2px 8px;border-radius:4px;font-size:12px}",
".toolbar{display:flex;gap:12px;margin-bottom:16px;align-items:center}",
"input,select,button{padding:6px 12px;border:1px solid #ccc;border-radius:4px}",
"</style></head><body>",
"<h1>User Management Dashboard</h1>",
'<div class="toolbar">',
'<input id="search" type="text" placeholder="Search users...">',
'<select id="dept-filter"><option value="">All Departments</option><option value="eng">Engineering</option><option value="des">Design</option></select>',
'<select id="status-filter"><option value="">All Statuses</option><option value="active">Active</option><option value="inactive">Inactive</option></select>',
'<button id="add-user">Add User</button>',
'<span id="count">Showing 200 users</span>',
"</div>",
`<table><thead>${header}</thead><tbody>${rows.join("")}</tbody></table>`,
'<div class="pagination">',
...Array.from({ length: 10 }, (_, index) => `<button class="page-btn" data-page="${index + 1}">${index + 1}</button>`),
"</div>",
"</body></html>",
].join("");
}
function generateNestedPage(): string {
function nest(depth: number, breadth: number, prefix: string): string {
if (depth === 0) {
return `<span class="leaf" data-path="${prefix}">Leaf node at ${prefix}</span>`;
}
const children = Array.from(
{ length: breadth },
(_, index) =>
`<div class="node depth-${depth}" data-depth="${depth}" data-idx="${index}">` +
`<div class="node-header"><strong>Section ${prefix}.${index + 1}</strong> <em>(depth ${depth})</em></div>` +
`<div class="node-content">${nest(depth - 1, Math.max(2, breadth - 1), `${prefix}.${index + 1}`)}</div>` +
"</div>",
);
return children.join("");
}
return [
"<html><head><title>Benchmark Nested</title>",
"<style>",
"body{font-family:system-ui;margin:20px}",
".node{border-left:2px solid #ddd;padding-left:16px;margin:4px 0}",
".node-header{padding:4px 0;cursor:pointer}",
".leaf{display:block;padding:2px 8px;background:#f0f9ff;margin:2px 0;border-radius:2px}",
"</style></head><body>",
"<h1>Deeply Nested Document Structure</h1>",
nest(7, 3, "root"),
"</body></html>",
].join("");
}
function generateDashboardPage(): string {
const cards = Array.from(
{ length: 12 },
(_, index) =>
`<div class="card" data-card="${index}">` +
`<div class="card-title">Metric ${index + 1}</div>` +
`<div class="card-value">${Math.floor(Math.random() * 10000)}</div>` +
`<div class="card-trend ${index % 2 === 0 ? "up" : "down"}">${index % 2 === 0 ? "+" : "-"}${(Math.random() * 20).toFixed(1)}%</div>` +
"</div>",
);
const chartBars = Array.from({ length: 24 }, (_, index) => {
const height = 20 + ((index * 7 + 13) % 80);
return `<div class="bar" style="height:${height}%" data-hour="${index}"><span class="bar-label">${String(index).padStart(2, "0")}:00</span></div>`;
});
const logRows = Array.from({ length: 100 }, (_, index) => {
const level = ["INFO", "WARN", "ERROR", "DEBUG"][index % 4];
return (
`<tr class="log-${level.toLowerCase()}" data-log="${index}">` +
`<td>${new Date(2025, 0, 1, index % 24, index % 60).toISOString()}</td>` +
`<td><span class="level level-${level.toLowerCase()}">${level}</span></td>` +
`<td>Service ${["auth", "api", "worker", "cache", "db"][index % 5]}</td>` +
`<td>Log message number ${index + 1}: operation completed in ${(Math.random() * 1000).toFixed(0)}ms</td>` +
"</tr>"
);
});
return [
"<html><head><title>Benchmark Dashboard</title>",
"<style>",
"body{font-family:system-ui;margin:0;background:#f5f5f5}",
".header{background:#1a1a2e;color:white;padding:12px 24px;display:flex;justify-content:space-between;align-items:center}",
".grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;padding:24px}",
".card{background:white;padding:20px;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.1)}",
".card-value{font-size:28px;font-weight:700;margin:8px 0}",
".card-trend.up{color:#16a34a}.card-trend.down{color:#dc2626}",
".chart-area{background:white;margin:0 24px;padding:20px;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.1)}",
".bars{display:flex;align-items:flex-end;gap:4px;height:200px}",
".bar{background:#3b82f6;flex:1;border-radius:2px 2px 0 0;position:relative;min-width:8px}",
".log-table{margin:24px;background:white;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.1);overflow:hidden}",
"table{width:100%;border-collapse:collapse;font-size:13px}",
"th,td{padding:6px 12px;border-bottom:1px solid #eee;text-align:left}",
"th{background:#f9fafb;font-weight:600}",
".tabs{display:flex;gap:0;margin:24px 24px 0}",
".tab{padding:8px 20px;background:#e5e7eb;cursor:pointer;border-radius:6px 6px 0 0}",
".tab.active{background:white}",
"</style></head><body>",
'<div class="header"><h1>Operations Dashboard</h1><div><input id="dash-search" placeholder="Search..." type="text"><button id="refresh">Refresh</button></div></div>',
`<div class="grid">${cards.join("")}</div>`,
'<div class="tabs"><div class="tab active">Hourly</div><div class="tab">Daily</div><div class="tab">Weekly</div></div>',
`<div class="chart-area"><h3>Request Volume</h3><div class="bars">${chartBars.join("")}</div></div>`,
'<div class="log-table">',
"<h3 style='padding:16px 12px 0'>Recent Logs</h3>",
`<table><thead><tr><th>Timestamp</th><th>Level</th><th>Service</th><th>Message</th></tr></thead><tbody>${logRows.join("")}</tbody></table>`,
"</div>",
"</body></html>",
].join("");
}
const ARTICLE_HTML = generateArticlePage();
const TABLE_HTML = generateDataTablePage();
const NESTED_HTML = generateNestedPage();
const DASHBOARD_HTML = generateDashboardPage();
function injectCmd(id: string, html: string): BenchmarkCommand {
return {
action: "evaluate",
id,
script: `document.open(); document.write(${JSON.stringify(html)}); document.close(); 'ok'`,
};
}
function setupPage(html: string, tag: string): BenchmarkCommand[] {
return [
{ action: "navigate", id: `${tag}-nav`, url: "about:blank", waitUntil: "domcontentloaded" },
injectCmd(`${tag}-inject`, html),
];
}
export const engineScenarios: Scenario[] = [
{
commands: [{ action: "snapshot", id: "snap" }],
description: "Snapshot a realistic article page (~800 DOM nodes, 30 paragraphs, 40 comments)",
name: "article-snapshot",
setup: setupPage(ARTICLE_HTML, "art"),
},
{
commands: [{ action: "snapshot", id: "snap" }],
description: "Snapshot a data table with 200 rows and 8 columns",
name: "table-snapshot",
setup: setupPage(TABLE_HTML, "tbl"),
},
{
commands: [{ action: "snapshot", id: "snap" }],
description: "Snapshot a deeply nested DOM tree (7 levels, ~3000 nodes)",
name: "nested-snapshot",
setup: setupPage(NESTED_HTML, "nest"),
},
{
commands: [{ action: "snapshot", id: "snap" }],
description: "Snapshot an operations dashboard with cards, chart, and 100 log rows",
name: "dashboard-snap",
setup: setupPage(DASHBOARD_HTML, "dash"),
},
{
commands: [injectCmd("ai-write", ARTICLE_HTML)],
description: "Write a full article page into the DOM (measures parse + layout)",
name: "article-inject",
setup: [{ action: "navigate", id: "ai-nav", url: "about:blank", waitUntil: "domcontentloaded" }],
},
{
commands: [
{
action: "evaluate",
id: "query",
script: "document.querySelectorAll('tr[data-row]').length + ' rows, ' + document.querySelectorAll('td').length + ' cells'",
},
],
description: "Evaluate a querySelectorAll across a large table",
name: "table-query",
setup: setupPage(TABLE_HTML, "tq"),
},
{
commands: [
{ action: "snapshot", id: "dw-snap" },
{ action: "fill", id: "dw-fill", selector: "#dash-search", value: "error logs" },
{ action: "click", id: "dw-click", selector: "#refresh" },
{ action: "evaluate", id: "dw-eval", script: "document.querySelectorAll('.card').length + ' cards'" },
{ action: "screenshot", id: "dw-ss" },
],
description: "Full agent workflow on complex dashboard: snapshot, click, fill, eval, screenshot",
name: "dashboard-workflow",
setup: setupPage(DASHBOARD_HTML, "dw"),
},
{
commands: [
{
action: "evaluate",
id: "walk",
script: "(function(){let c=0;const w=n=>{c++;for(const ch of n.children)w(ch);};w(document.body);return c+' nodes';})()",
},
],
description: "Recursive DOM traversal via evaluate on deeply nested tree",
name: "nested-eval",
setup: setupPage(NESTED_HTML, "ne"),
},
];
+222
View File
@@ -0,0 +1,222 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Understanding Modern Browser Engine Architecture</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #1a1a2e; background: #fff; }
.nav { display: flex; align-items: center; gap: 24px; padding: 12px 24px; background: #1a1a2e; color: #fff; position: sticky; top: 0; z-index: 100; }
.nav a { color: #94a3b8; text-decoration: none; font-size: 14px; transition: color 0.2s; }
.nav a:hover { color: #fff; }
.layout { display: grid; grid-template-columns: 1fr 320px; gap: 40px; max-width: 1200px; margin: 0 auto; padding: 40px 24px; }
.article { min-width: 0; }
.article h1 { font-size: 2.2rem; line-height: 1.2; margin-bottom: 16px; }
.meta { display: flex; gap: 16px; color: #64748b; font-size: 14px; margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid #e2e8f0; }
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 32px; }
.tag { display: inline-block; padding: 4px 12px; background: #e0e7ff; color: #3730a3; border-radius: 16px; font-size: 12px; font-weight: 500; }
.article h2 { font-size: 1.5rem; margin: 32px 0 16px; padding-top: 24px; border-top: 1px solid #f1f5f9; }
.article h3 { font-size: 1.2rem; margin: 24px 0 12px; }
.article p { margin-bottom: 16px; color: #374151; }
.article blockquote { margin: 24px 0; padding: 16px 24px; border-left: 4px solid #6366f1; background: #f8fafc; font-style: italic; color: #475569; border-radius: 0 8px 8px 0; }
.article pre { background: #1e293b; color: #e2e8f0; padding: 16px 20px; border-radius: 8px; overflow-x: auto; margin: 16px 0; font-size: 14px; line-height: 1.5; }
.article code { font-family: 'SF Mono', 'Fira Code', monospace; }
.article img { max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0; }
.figure { margin: 24px 0; text-align: center; }
.figure figcaption { font-size: 13px; color: #64748b; margin-top: 8px; }
.comments { margin-top: 40px; }
.comments h2 { border-top: 2px solid #e2e8f0; }
.comment { padding: 16px; margin: 12px 0; border: 1px solid #e2e8f0; border-radius: 8px; transition: box-shadow 0.2s; }
.comment:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
.comment-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.comment-author { font-weight: 600; font-size: 14px; }
.comment-date { font-size: 12px; color: #94a3b8; }
.comment-body { font-size: 14px; color: #475569; }
.comment-actions { display: flex; gap: 12px; margin-top: 8px; }
.comment-actions button { background: none; border: none; color: #6366f1; font-size: 13px; cursor: pointer; padding: 2px 0; }
.sidebar { position: sticky; top: 72px; align-self: start; }
.sidebar section { margin-bottom: 32px; }
.sidebar h3 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b; margin-bottom: 12px; }
.sidebar ul { list-style: none; }
.sidebar li { margin-bottom: 8px; }
.sidebar a { color: #3730a3; text-decoration: none; font-size: 14px; }
.sidebar a:hover { text-decoration: underline; }
.toc a { display: block; padding: 4px 0; border-left: 2px solid transparent; padding-left: 12px; }
.toc a:hover { border-left-color: #6366f1; }
.sidebar .widget { background: #f8fafc; padding: 16px; border-radius: 8px; }
@media (max-width: 768px) { .layout { grid-template-columns: 1fr; } .sidebar { display: none; } }
</style>
</head>
<body>
<nav class="nav">
<strong style="font-size:18px;color:#fff">TechBlog</strong>
<a href="#">Home</a><a href="#">Articles</a><a href="#">Tutorials</a><a href="#">About</a>
<a href="#">Open Source</a><a href="#">Newsletter</a><a href="#">Contact</a>
<div style="flex:1"></div>
<a href="#">Sign In</a>
</nav>
<div class="layout">
<main class="article">
<h1>Understanding Modern Browser Engine Architecture</h1>
<div class="meta">
<span>By <strong>Dr. Alexandra Chen</strong></span>
<span>March 15, 2025</span>
<span>18 min read</span>
<span>2,847 views</span>
</div>
<div class="tags">
<span class="tag">Browser Engines</span><span class="tag">Performance</span>
<span class="tag">Web Standards</span><span class="tag">Rendering</span>
<span class="tag">Architecture</span><span class="tag">Open Source</span>
</div>
<p>Modern browser engines are among the most complex pieces of software ever created. They must parse HTML, CSS, and JavaScript, construct a DOM tree, compute styles, perform layout calculations, paint pixels, and composite layers, all within milliseconds to maintain smooth rendering.</p>
<p>This article explores the architecture of modern browser engines, examining how they process web content from raw bytes to rendered pixels on screen. We trace the critical rendering path, review optimization strategies, and explain why certain patterns lead to better performance.</p>
<h2>The Critical Rendering Path</h2>
<p>When a browser receives an HTML document, it begins a multi-stage pipeline known as the critical rendering path. Each stage transforms the document into progressively more structured representations until pixels are painted on screen.</p>
<p>The first stage involves parsing the HTML into a Document Object Model (DOM). The parser processes tokens sequentially, building a tree structure that represents the document hierarchy. During this phase, the parser may encounter external resources like stylesheets and scripts that can block further processing.</p>
<p>CSS parsing happens in parallel where possible. The browser constructs the CSS Object Model (CSSOM), which represents all style rules that apply to the document. This includes user-agent styles, author styles, and inline styles.</p>
<p>Once both the DOM and CSSOM are available, the browser combines them into a render tree. This tree contains only the elements that will be visible on screen. Elements with <code>display: none</code> are excluded, while pseudo-elements like <code>::before</code> and <code>::after</code> are added.</p>
<p>Layout, also called reflow, calculates the exact position and size of each element in the render tree. This is one of the most computationally expensive operations in the rendering pipeline because changes to one element can cascade through the rest of the tree.</p>
<blockquote>"The fastest code is code that does not run. The fastest layout is layout that does not need to happen."</blockquote>
<h2>DOM Construction and Tree Building</h2>
<p>The DOM is a tree-structured representation of the HTML document. Each node corresponds to an element, text node, comment, or other construct in the HTML. The tree preserves hierarchical relationships between elements, allowing efficient traversal and manipulation.</p>
<p>Modern parsers handle malformed HTML gracefully through error recovery algorithms specified in the HTML standard. This includes automatic closing of unclosed tags, adoption of misplaced elements, and reconstruction of formatting element lists.</p>
<p>Shadow DOM introduces additional complexity by creating encapsulated subtrees that can have their own scoped styles and behavior. Custom elements use shadow roots to attach shadow trees that are rendered in place of the element's regular children.</p>
<h3>Incremental DOM Updates</h3>
<p>When JavaScript modifies the DOM, the browser must determine which parts of the rendering pipeline need to be re-executed. Modern engines use fine-grained invalidation to minimize the work required. A change to an element's text content may only require a repaint, while changing its width could trigger a full relayout.</p>
<p>Mutation observers provide a way for JavaScript to respond to DOM changes without polling. The browser batches mutations and delivers them asynchronously, allowing multiple changes to be processed efficiently in a single callback.</p>
<h3>Memory Management</h3>
<p>DOM nodes are garbage collected when no longer reachable. Detached DOM trees, subtrees removed from the document but still referenced by JavaScript, are a common source of memory leaks in web applications.</p>
<p>Browser engines use string interning for common values, node pools for rapid allocation, and lazy initialization of rarely accessed properties to minimize memory overhead.</p>
<h2>Style Resolution and Cascade</h2>
<p>CSS style resolution involves matching each element against all applicable style rules and computing the final value for every CSS property. With thousands of rules and millions of elements on complex pages, this process must be highly optimized.</p>
<p>Modern engines use fast prefilters to eliminate rules that cannot match an element, reducing the number of full selector matches required. Selector matching proceeds right-to-left, starting from the key selector and working backward through ancestors.</p>
<p>The cascade algorithm resolves conflicts between competing declarations by considering origin, specificity, and source order. Custom properties add another layer of complexity because they must be resolved during the cascade before they can be used in property values.</p>
<pre><code>.data-grid tr:nth-child(even) td {
background-color: #f8fafc;
padding: 8px 12px;
font-size: 14px;
border-bottom: 1px solid #e2e8f0;
}</code></pre>
<h2>Layout Algorithms</h2>
<p>Layout converts the styled render tree into positioned boxes with concrete pixel dimensions. Different layout modes, including block, inline, flex, grid, and table, each use their own algorithm for determining element sizes and positions.</p>
<p>Flexbox layout involves multiple passes: computing the flex basis of each item, distributing free space according to flex-grow and flex-shrink factors, and then positioning items along the cross axis. This makes flex layout more expensive than simple block layout.</p>
<p>Grid layout is even more complex, supporting both explicit and implicit grid definitions, named areas, auto-placement, and spanning. The placement algorithm must resolve conflicts between explicitly placed and auto-placed items while respecting sizing constraints.</p>
<h2>Paint and Compositing</h2>
<p>After layout, the browser paints the visual representation of each element. This includes drawing backgrounds, borders, text, images, shadows, and other effects in the correct stacking order defined by the z-index property and stacking context rules.</p>
<p>Modern browsers use a layered compositing architecture. Elements that change frequently, such as animations, scrolling regions, and video, are promoted to their own compositing layers. These layers can be updated independently and combined on the GPU.</p>
<p>The compositor thread operates independently from the main thread, allowing smooth scrolling and animations even when JavaScript is executing. Touch events and scroll gestures are often handled directly by the compositor.</p>
<h2>JavaScript Engine Integration</h2>
<p>The JavaScript engine is tightly integrated with the browser rendering pipeline. Script execution can trigger style recalculation, layout, and paint through DOM manipulation and CSSOM access. The browser must balance script execution with maintaining smooth rendering.</p>
<p>Modern engines use just-in-time compilation to achieve near-native performance for hot code paths. The compilation pipeline typically includes an interpreter for initial execution, a baseline compiler for warm functions, and an optimizing compiler for hot functions.</p>
<h2>Conclusion</h2>
<p>Browser engines represent decades of engineering effort to make the web fast, secure, and compatible. Understanding their architecture helps web developers write code that works with the browser rather than against it.</p>
<p>As the web platform continues to evolve with new APIs, layout modes, and rendering capabilities, browser engines must adapt while maintaining backwards compatibility with billions of existing web pages.</p>
<div class="comments">
<h2>Comments (50)</h2>
<script>
(function() {
var container = document.querySelector('.comments');
var names = ['Alex Morgan', 'Jamie Rivera', 'Sam Patel', 'Taylor Kim', 'Jordan Lee',
'Casey Wu', 'Riley Chen', 'Morgan Davis', 'Avery Singh', 'Quinn Zhao'];
for (var i = 0; i < 50; i++) {
var div = document.createElement('div');
div.className = 'comment';
div.dataset.id = i;
var d = new Date(2025, 2, 15 - Math.floor(i / 5));
div.innerHTML = '<div class="comment-header"><span class="comment-author">' + names[i % 10] +
'</span><span class="comment-date">' + d.toLocaleDateString() + '</span></div>' +
'<div class="comment-body"><p>' + (i % 3 === 0 ?
'Great article. The section on compositing layers was particularly useful.' :
i % 3 === 1 ?
'Thanks for the detailed breakdown. The invalidation notes were helpful.' :
'This clarified several misconceptions I had about GPU acceleration.') +
'</p></div><div class="comment-actions"><button>Reply</button><button>Like (' + (Math.floor(Math.random() * 30)) + ')</button></div>';
container.appendChild(div);
}
})();
</script>
</div>
</main>
<aside class="sidebar">
<section>
<h3>Table of Contents</h3>
<ul class="toc">
<li><a href="#crp">The Critical Rendering Path</a></li>
<li><a href="#dom">DOM Construction and Tree Building</a></li>
<li><a href="#styles">Style Resolution and Cascade</a></li>
<li><a href="#layout">Layout Algorithms</a></li>
<li><a href="#paint">Paint and Compositing</a></li>
<li><a href="#js">JavaScript Engine Integration</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</section>
<section>
<h3>Related Articles</h3>
<ul>
<li><a href="#">How V8 Optimizes JavaScript Execution</a></li>
<li><a href="#">CSS Grid Layout: A Complete Guide</a></li>
<li><a href="#">Web Performance Metrics That Matter</a></li>
<li><a href="#">Understanding the Event Loop</a></li>
<li><a href="#">Debugging Layout Thrashing</a></li>
<li><a href="#">Service Workers and Caching Strategies</a></li>
<li><a href="#">WebAssembly Beyond JavaScript</a></li>
<li><a href="#">Rendering Performance Case Studies</a></li>
<li><a href="#">Accessibility Tree Deep Dive</a></li>
<li><a href="#">Cross-Browser Compatibility Patterns</a></li>
</ul>
</section>
<section class="widget">
<h3>Newsletter</h3>
<p style="font-size:13px;color:#475569;margin-bottom:8px">Get weekly browser engineering insights.</p>
<input type="email" placeholder="you@example.com" style="width:100%;padding:8px;border:1px solid #d1d5db;border-radius:4px;margin-bottom:8px">
<button style="width:100%;padding:8px;background:#6366f1;color:#fff;border:none;border-radius:4px;cursor:pointer">Subscribe</button>
</section>
</aside>
</div>
</body>
</html>
+234
View File
@@ -0,0 +1,234 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Operations Dashboard</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f1f5f9; color: #0f172a; }
.header { background: #0f172a; color: #fff; padding: 12px 24px; display: flex; justify-content: space-between; align-items: center; }
.header h1 { font-size: 18px; }
.header-actions { display: flex; gap: 12px; align-items: center; }
.header input { padding: 6px 12px; border: 1px solid #334155; background: #1e293b; color: #fff; border-radius: 6px; font-size: 13px; width: 200px; }
.header button { padding: 6px 16px; background: #6366f1; color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; }
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; padding: 24px; }
.card { background: #fff; padding: 20px; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.card-label { font-size: 13px; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; }
.card-value { font-size: 32px; font-weight: 700; margin: 8px 0 4px; }
.card-trend { font-size: 14px; font-weight: 500; }
.card-trend.up { color: #16a34a; }
.card-trend.down { color: #dc2626; }
.card-sparkline { height: 40px; display: flex; align-items: flex-end; gap: 2px; margin-top: 8px; }
.card-sparkline .bar { flex: 1; background: #e0e7ff; border-radius: 2px; min-width: 3px; }
.card-sparkline .bar:last-child { background: #6366f1; }
.section { margin: 0 24px 24px; background: #fff; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); overflow: hidden; }
.section-header { padding: 16px 20px; border-bottom: 1px solid #f1f5f9; display: flex; justify-content: space-between; align-items: center; }
.section-header h2 { font-size: 16px; }
.tabs { display: flex; gap: 0; }
.tab { padding: 6px 16px; font-size: 13px; border: 1px solid #e2e8f0; background: #fff; cursor: pointer; }
.tab:first-child { border-radius: 6px 0 0 6px; }
.tab:last-child { border-radius: 0 6px 6px 0; }
.tab.active { background: #6366f1; color: #fff; border-color: #6366f1; }
.chart { padding: 20px; height: 240px; display: flex; align-items: flex-end; gap: 4px; }
.chart .bar { flex: 1; background: #6366f1; border-radius: 4px 4px 0 0; position: relative; min-width: 6px; }
.chart .bar-label { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); font-size: 10px; color: #94a3b8; white-space: nowrap; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th { background: #f8fafc; padding: 10px 16px; text-align: left; font-weight: 600; color: #475569; border-bottom: 1px solid #e2e8f0; }
tbody td { padding: 8px 16px; border-bottom: 1px solid #f1f5f9; }
tbody tr:hover { background: #f8fafc; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
.badge-info { background: #dbeafe; color: #1d4ed8; }
.badge-warn { background: #fef3c7; color: #b45309; }
.badge-error { background: #fee2e2; color: #dc2626; }
.badge-debug { background: #f1f5f9; color: #475569; }
.badge-active { background: #dcfce7; color: #166534; }
.badge-inactive { background: #f1f5f9; color: #64748b; }
.badge-pending { background: #fef3c7; color: #b45309; }
.pagination { display: flex; justify-content: center; gap: 4px; padding: 16px; }
.pagination button { width: 32px; height: 32px; border: 1px solid #e2e8f0; background: #fff; border-radius: 6px; cursor: pointer; font-size: 13px; }
.pagination button.active { background: #6366f1; color: #fff; border-color: #6366f1; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 0 24px 24px; }
@media (max-width: 1024px) { .grid { grid-template-columns: repeat(2, 1fr); } .two-col { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<div class="header">
<h1>Operations Dashboard</h1>
<div class="header-actions">
<input id="search" type="text" placeholder="Search...">
<button id="refresh">Refresh</button>
<button style="background:#334155">Export</button>
</div>
</div>
<div class="grid" id="metrics-grid"></div>
<div class="section">
<div class="section-header">
<h2>Request Volume</h2>
<div class="tabs">
<div class="tab active">Hourly</div>
<div class="tab">Daily</div>
<div class="tab">Weekly</div>
</div>
</div>
<div class="chart" id="chart"></div>
</div>
<div class="two-col">
<div class="section" style="margin:0">
<div class="section-header"><h2>Top Endpoints</h2></div>
<table>
<thead><tr><th>Endpoint</th><th>Requests</th><th>Avg Latency</th><th>Error Rate</th></tr></thead>
<tbody id="endpoints-table"></tbody>
</table>
</div>
<div class="section" style="margin:0">
<div class="section-header"><h2>Active Alerts</h2></div>
<table>
<thead><tr><th>Alert</th><th>Severity</th><th>Service</th><th>Since</th></tr></thead>
<tbody id="alerts-table"></tbody>
</table>
</div>
</div>
<div class="section">
<div class="section-header">
<h2>Recent Logs</h2>
<div class="tabs">
<div class="tab active">All</div>
<div class="tab">Errors</div>
<div class="tab">Warnings</div>
</div>
</div>
<table>
<thead><tr><th>Timestamp</th><th>Level</th><th>Service</th><th>Message</th><th>Duration</th></tr></thead>
<tbody id="logs-table"></tbody>
</table>
<div class="pagination" id="pagination"></div>
</div>
<div class="section">
<div class="section-header"><h2>Service Status</h2></div>
<table>
<thead><tr><th>Service</th><th>Status</th><th>Uptime</th><th>CPU</th><th>Memory</th><th>Requests/min</th><th>Error Rate</th><th>Last Deploy</th></tr></thead>
<tbody id="services-table"></tbody>
</table>
</div>
<script>
(function() {
var metrics = [
{ label: 'Total Requests', value: '1,284,392', trend: '+12.5%', up: true },
{ label: 'Avg Response Time', value: '142ms', trend: '-8.3%', up: true },
{ label: 'Error Rate', value: '0.42%', trend: '+0.12%', up: false },
{ label: 'Active Users', value: '3,847', trend: '+5.1%', up: true },
{ label: 'Throughput', value: '892/s', trend: '+3.7%', up: true },
{ label: 'P99 Latency', value: '487ms', trend: '+15ms', up: false },
{ label: 'CPU Usage', value: '67%', trend: '-2.4%', up: true },
{ label: 'Memory Usage', value: '4.2GB', trend: '+180MB', up: false },
{ label: 'Cache Hit Rate', value: '94.7%', trend: '+1.2%', up: true },
{ label: 'Queue Depth', value: '234', trend: '-45', up: true },
{ label: 'Open Connections', value: '12,483', trend: '+892', up: false },
{ label: 'Deployments Today', value: '7', trend: '+2', up: true }
];
var grid = document.getElementById('metrics-grid');
metrics.forEach(function(metric) {
var sparkBars = '';
for (var index = 0; index < 12; index++) {
var height = 20 + Math.floor(Math.random() * 80);
sparkBars += '<div class="bar" style="height:' + height + '%"></div>';
}
grid.innerHTML += '<div class="card"><div class="card-label">' + metric.label +
'</div><div class="card-value">' + metric.value +
'</div><div class="card-trend ' + (metric.up ? 'up' : 'down') + '">' +
(metric.up ? '+' : '') + metric.trend +
'</div><div class="card-sparkline">' + sparkBars + '</div></div>';
});
var chart = document.getElementById('chart');
for (var hour = 0; hour < 24; hour++) {
var height = 15 + ((hour * 17 + 7) % 85);
var bar = document.createElement('div');
bar.className = 'bar';
bar.style.height = height + '%';
bar.innerHTML = '<span class="bar-label">' + String(hour).padStart(2, '0') + ':00</span>';
chart.appendChild(bar);
}
var endpoints = document.getElementById('endpoints-table');
var paths = ['/api/users', '/api/auth', '/api/products', '/api/orders', '/api/search',
'/api/analytics', '/api/notifications', '/api/payments', '/api/inventory', '/api/reports'];
paths.forEach(function(pathName, index) {
endpoints.innerHTML += '<tr><td><code>' + pathName + '</code></td><td>' +
(50000 - index * 3000) + '</td><td>' + (45 + index * 12) + 'ms</td><td>' +
(0.1 + index * 0.08).toFixed(2) + '%</td></tr>';
});
var alerts = document.getElementById('alerts-table');
var alertData = [
['High error rate on /api/payments', 'error', 'payments'],
['Memory usage above 85%', 'warn', 'api-gateway'],
['Slow queries detected', 'warn', 'database'],
['SSL certificate expiring in 7 days', 'info', 'infrastructure'],
['Disk usage above 80%', 'warn', 'storage'],
['Connection pool exhaustion', 'error', 'database']
];
alertData.forEach(function(alert, index) {
var badge = alert[1] === 'error' ? 'badge-error' : alert[1] === 'warn' ? 'badge-warn' : 'badge-info';
alerts.innerHTML += '<tr><td>' + alert[0] + '</td><td><span class="badge ' + badge + '">' +
alert[1].toUpperCase() + '</span></td><td>' + alert[2] + '</td><td>' + (index * 15 + 5) + 'm ago</td></tr>';
});
var logs = document.getElementById('logs-table');
var services = ['auth', 'api-gateway', 'worker', 'cache', 'database', 'payments', 'search', 'notifications'];
var levels = ['INFO', 'WARN', 'ERROR', 'DEBUG'];
var messages = [
'Request processed successfully',
'Connection pool running low',
'Failed to connect to upstream service',
'Cache miss for key session:',
'Query execution exceeded threshold',
'Payment webhook received',
'Search index rebuild started',
'Rate limit applied to client'
];
for (var i = 0; i < 160; i++) {
var level = levels[i % 4];
var badge = level === 'ERROR' ? 'badge-error' : level === 'WARN' ? 'badge-warn' :
level === 'DEBUG' ? 'badge-debug' : 'badge-info';
var ts = new Date(2025, 2, 15, 23 - Math.floor(i / 8), 59 - (i % 60));
logs.innerHTML += '<tr><td style="white-space:nowrap">' + ts.toISOString().replace('T', ' ').substring(0, 19) +
'</td><td><span class="badge ' + badge + '">' + level +
'</span></td><td>' + services[i % 8] +
'</td><td>' + messages[i % 8] + ' #' + (i + 1) +
'</td><td>' + (Math.floor(Math.random() * 500) + 10) + 'ms</td></tr>';
}
var pagination = document.getElementById('pagination');
for (var page = 1; page <= 10; page++) {
pagination.innerHTML += '<button class="' + (page === 1 ? 'active' : '') + '">' + page + '</button>';
}
var servicesTable = document.getElementById('services-table');
var serviceNames = ['api-gateway', 'auth-service', 'payment-processor', 'search-engine',
'notification-hub', 'analytics-pipeline', 'cache-layer', 'worker-pool'];
serviceNames.forEach(function(name, index) {
var status = index < 6 ? 'active' : index === 6 ? 'pending' : 'inactive';
var badge = status === 'active' ? 'badge-active' : status === 'pending' ? 'badge-pending' : 'badge-inactive';
servicesTable.innerHTML += '<tr><td><strong>' + name + '</strong></td>' +
'<td><span class="badge ' + badge + '">' + status + '</span></td>' +
'<td>' + (99.9 - index * 0.05).toFixed(2) + '%</td>' +
'<td>' + (30 + index * 5) + '%</td>' +
'<td>' + (512 + index * 128) + 'MB</td>' +
'<td>' + (2000 - index * 150) + '</td>' +
'<td>' + (0.1 + index * 0.04).toFixed(2) + '%</td>' +
'<td>' + (index + 1) + 'h ago</td></tr>';
});
})();
</script>
</body>
</html>
+176
View File
@@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TechStore - Electronics & Gadgets</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #fff; color: #0f172a; }
.topbar { background: #0f172a; color: #94a3b8; font-size: 12px; padding: 6px 24px; display: flex; justify-content: space-between; }
.navbar { display: flex; align-items: center; gap: 24px; padding: 12px 24px; border-bottom: 1px solid #e2e8f0; position: sticky; top: 0; background: #fff; z-index: 100; }
.navbar .logo { font-size: 22px; font-weight: 800; color: #6366f1; }
.navbar .search { flex: 1; max-width: 500px; position: relative; }
.navbar .search input { width: 100%; padding: 10px 16px; border: 2px solid #e2e8f0; border-radius: 8px; font-size: 14px; }
.nav-actions { display: flex; gap: 16px; align-items: center; }
.nav-actions button { background: none; border: none; font-size: 14px; cursor: pointer; color: #475569; }
.cart-badge { background: #6366f1; color: #fff; font-size: 11px; padding: 2px 6px; border-radius: 10px; margin-left: 4px; }
.categories { display: flex; gap: 0; padding: 0 24px; border-bottom: 1px solid #f1f5f9; overflow-x: auto; }
.categories a { padding: 10px 16px; font-size: 13px; color: #64748b; text-decoration: none; white-space: nowrap; border-bottom: 2px solid transparent; }
.categories a.active { color: #6366f1; border-bottom-color: #6366f1; }
.hero { background: linear-gradient(135deg, #312e81, #6366f1); color: #fff; padding: 60px 24px; text-align: center; }
.hero h2 { font-size: 2.5rem; margin-bottom: 12px; }
.hero p { font-size: 18px; opacity: 0.9; margin-bottom: 24px; }
.hero button { padding: 12px 32px; background: #fff; color: #6366f1; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; }
.container { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
.section-title { font-size: 1.5rem; font-weight: 700; margin: 32px 0 16px; display: flex; justify-content: space-between; align-items: center; }
.section-title a { font-size: 14px; color: #6366f1; text-decoration: none; }
.product-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 32px; }
.product { border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; transition: box-shadow 0.2s, transform 0.2s; }
.product:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.1); transform: translateY(-2px); }
.product-img { height: 200px; display: flex; align-items: center; justify-content: center; font-size: 48px; }
.product-info { padding: 16px; }
.product-brand { font-size: 12px; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; }
.product-name { font-size: 15px; font-weight: 600; margin: 4px 0 8px; line-height: 1.3; }
.product-price { font-size: 20px; font-weight: 700; color: #0f172a; }
.product-original { font-size: 14px; color: #94a3b8; text-decoration: line-through; margin-left: 8px; }
.product-rating { display: flex; align-items: center; gap: 4px; margin-top: 8px; font-size: 13px; color: #64748b; }
.stars { color: #f59e0b; }
.product-actions { display: flex; gap: 8px; margin-top: 12px; }
.product-actions button { flex: 1; padding: 8px; border: none; border-radius: 6px; font-size: 13px; cursor: pointer; }
.btn-primary { background: #6366f1; color: #fff; }
.btn-secondary { background: #f1f5f9; color: #475569; }
.filters { display: flex; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; }
.filter { padding: 6px 16px; border: 1px solid #e2e8f0; border-radius: 20px; font-size: 13px; background: #fff; cursor: pointer; }
.filter.active { background: #6366f1; color: #fff; border-color: #6366f1; }
.deals-banner { background: #fef3c7; border: 1px solid #fbbf24; border-radius: 12px; padding: 20px 24px; margin: 24px 0; display: flex; justify-content: space-between; align-items: center; }
.deals-banner h3 { color: #b45309; }
.reviews { margin: 24px 0; }
.review { padding: 16px; border-bottom: 1px solid #f1f5f9; }
.review-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.review-author { font-weight: 600; font-size: 14px; }
.review-date { font-size: 12px; color: #94a3b8; }
.review-body { font-size: 14px; color: #475569; }
.footer { background: #0f172a; color: #94a3b8; padding: 48px 24px; margin-top: 48px; }
.footer-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; max-width: 1280px; margin: 0 auto; }
.footer h4 { color: #fff; margin-bottom: 16px; font-size: 14px; }
.footer a { display: block; color: #94a3b8; text-decoration: none; font-size: 13px; margin-bottom: 8px; }
.footer-bottom { border-top: 1px solid #1e293b; padding-top: 24px; margin-top: 32px; text-align: center; font-size: 13px; max-width: 1280px; margin-left: auto; margin-right: auto; }
</style>
</head>
<body>
<div class="topbar">
<span>Free shipping on orders over $99</span>
<span>Customer Service: 1-800-TECH | Track Order | Help</span>
</div>
<nav class="navbar">
<div class="logo">TechStore</div>
<div class="search"><input type="text" placeholder="Search products, brands, categories..."></div>
<div class="nav-actions">
<button>Account</button>
<button>Wishlist</button>
<button>Cart <span class="cart-badge">3</span></button>
</div>
</nav>
<div class="categories">
<a href="#" class="active">All</a><a href="#">Laptops</a><a href="#">Phones</a>
<a href="#">Tablets</a><a href="#">Audio</a><a href="#">Cameras</a>
<a href="#">Monitors</a><a href="#">Storage</a><a href="#">Networking</a>
<a href="#">Accessories</a><a href="#">Deals</a><a href="#">New Arrivals</a>
</div>
<div class="hero">
<h2>Spring Tech Sale</h2>
<p>Up to 40% off on selected electronics. Limited time offer.</p>
<button>Shop Now</button>
</div>
<div class="container">
<div class="deals-banner">
<div><h3>Flash Deals - Ends in 04:32:17</h3><p style="font-size:13px;color:#92400e">Extra 15% off with code SPRING15</p></div>
<button class="btn-primary" style="padding:10px 24px;border-radius:8px;border:none;cursor:pointer">View All Deals</button>
</div>
<div class="section-title"><span>Featured Products</span><a href="#">View All</a></div>
<div class="filters">
<span class="filter active">All</span><span class="filter">Under $100</span>
<span class="filter">$100 - $500</span><span class="filter">$500+</span>
<span class="filter">Top Rated</span><span class="filter">New</span>
</div>
<div class="product-grid" id="featured-grid"></div>
<div class="section-title"><span>Best Sellers</span><a href="#">View All</a></div>
<div class="product-grid" id="bestsellers-grid"></div>
<div class="section-title"><span>New Arrivals</span><a href="#">View All</a></div>
<div class="product-grid" id="newarrivals-grid"></div>
<div class="section-title"><span>Customer Reviews</span></div>
<div class="reviews" id="reviews"></div>
</div>
<footer class="footer">
<div class="footer-grid">
<div><h4>Shop</h4><a href="#">Laptops</a><a href="#">Phones</a><a href="#">Tablets</a><a href="#">Audio</a></div>
<div><h4>Support</h4><a href="#">Help Center</a><a href="#">Returns</a><a href="#">Warranty</a><a href="#">Contact Us</a></div>
<div><h4>Company</h4><a href="#">About Us</a><a href="#">Careers</a><a href="#">Press</a><a href="#">Blog</a></div>
<div><h4>Connect</h4><a href="#">Newsletter</a><a href="#">Social Media</a><a href="#">Affiliate Program</a><a href="#">Developer API</a></div>
</div>
<div class="footer-bottom">2025 TechStore Inc. All rights reserved. | Privacy Policy | Terms of Service | Cookie Settings</div>
</footer>
<script>
(function() {
var brands = ['Apple', 'Samsung', 'Sony', 'Bose', 'Dell', 'Lenovo', 'LG', 'ASUS', 'Logitech', 'Canon'];
var categories = ['Laptop', 'Phone', 'Tablet', 'Headphones', 'Camera', 'Monitor', 'Speaker', 'Keyboard'];
var colors = ['#dbeafe', '#fce7f3', '#d1fae5', '#fef3c7', '#e0e7ff', '#f1f5f9', '#fef2f2', '#f0fdf4'];
function makeProduct(i) {
var brand = brands[i % brands.length];
var category = categories[i % categories.length];
var price = 49 + (i * 73) % 1500;
var original = Math.round(price * 1.25);
var rating = (3.5 + (i % 15) * 0.1).toFixed(1);
var reviews = 50 + (i * 37) % 2000;
var stars = '';
for (var s = 0; s < 5; s++) stars += s < Math.round(parseFloat(rating)) ? '*' : ' ';
return '<div class="product"><div class="product-img" style="background:' + colors[i % 8] + '">' +
category.charAt(0).toUpperCase() + '</div><div class="product-info">' +
'<div class="product-brand">' + brand + '</div>' +
'<div class="product-name">' + brand + ' ' + category + ' Pro ' + (2024 + (i % 3)) + ' Edition</div>' +
'<div><span class="product-price">$' + price + '</span><span class="product-original">$' + original + '</span></div>' +
'<div class="product-rating"><span class="stars">' + stars + '</span> ' + rating + ' (' + reviews + ')</div>' +
'<div class="product-actions"><button class="btn-primary">Add to Cart</button><button class="btn-secondary">Compare</button></div>' +
'</div></div>';
}
var featured = document.getElementById('featured-grid');
for (var i = 0; i < 16; i++) featured.innerHTML += makeProduct(i);
var bestsellers = document.getElementById('bestsellers-grid');
for (var j = 16; j < 32; j++) bestsellers.innerHTML += makeProduct(j);
var newArrivals = document.getElementById('newarrivals-grid');
for (var k = 32; k < 48; k++) newArrivals.innerHTML += makeProduct(k);
var reviewsEl = document.getElementById('reviews');
var names = ['Alice M.', 'Bob K.', 'Carol S.', 'David L.', 'Eva R.', 'Frank W.'];
var reviewTexts = [
'Excellent product, arrived faster than expected. Build quality is outstanding.',
'Good value for money. Reliable device and easy to set up.',
'Battery life could be better, but performance is strong.',
'Amazing quality. This is my third purchase from this brand.'
];
for (var idx = 0; idx < 16; idx++) {
reviewsEl.innerHTML += '<div class="review"><div class="review-header"><div><span class="review-author">' +
names[idx % names.length] + '</span></div><span class="review-date">March ' + (15 - idx % 15) + ', 2025</span></div>' +
'<div class="review-body">' + reviewTexts[idx % 4] + '</div></div>';
}
})();
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
+113
View File
@@ -0,0 +1,113 @@
export interface BenchmarkCommand {
id: string;
action: string;
[key: string]: unknown;
}
export interface Scenario {
name: string;
description: string;
/** Commands to run once before measured iterations (e.g. navigate to a page). */
setup?: BenchmarkCommand[];
/** The commands whose total execution time is measured per iteration. */
commands: BenchmarkCommand[];
/** Commands to run once after measured iterations (e.g. cleanup). */
teardown?: BenchmarkCommand[];
}
const FORM_HTML = [
"<html><head><title>Bench</title></head><body>",
"<h1>Benchmark Page</h1>",
"<input id='name' type='text' placeholder='Name'>",
"<input id='email' type='email' placeholder='Email'>",
"<select id='color'><option value='red'>Red</option><option value='blue'>Blue</option></select>",
"<input id='agree' type='checkbox'>",
"<textarea id='bio' placeholder='Bio'></textarea>",
"<button id='submit'>Submit</button>",
"<p id='status'>Ready</p>",
"<a id='link' href='javascript:void(0)' onclick=\"document.getElementById('status').textContent='Clicked'\">Click me</a>",
"<ul>",
...Array.from({ length: 20 }, (_, i) => `<li class='item'>Item ${i + 1}</li>`),
"</ul>",
"</body></html>",
].join("");
const INJECT_FORM: BenchmarkCommand = {
id: "inject",
action: "evaluate",
script: `document.open(); document.write(${JSON.stringify(FORM_HTML)}); document.close(); 'ok'`,
};
const SETUP_PAGE: BenchmarkCommand[] = [
{ id: "setup-nav", action: "navigate", url: "about:blank", waitUntil: "domcontentloaded" },
INJECT_FORM,
];
export const scenarios: Scenario[] = [
{
name: "navigate",
description: "Page navigation (about:blank round-trip)",
commands: [
{ id: "nav", action: "navigate", url: "about:blank", waitUntil: "domcontentloaded" },
],
},
{
name: "snapshot",
description: "DOM snapshot (accessibility tree)",
setup: SETUP_PAGE,
commands: [{ id: "snap", action: "snapshot" }],
},
{
name: "screenshot",
description: "Screenshot capture",
setup: SETUP_PAGE,
commands: [{ id: "ss", action: "screenshot" }],
},
{
name: "evaluate",
description: "JavaScript evaluation",
setup: SETUP_PAGE,
commands: [
{
id: "eval",
action: "evaluate",
script: "document.title + ' ' + document.querySelectorAll('li').length",
},
],
},
{
name: "click",
description: "Element click interaction",
setup: SETUP_PAGE,
commands: [{ id: "clk", action: "click", selector: "#link" }],
},
{
name: "fill",
description: "Form field fill",
setup: SETUP_PAGE,
commands: [{ id: "fill", action: "fill", selector: "#name", value: "Benchmark User" }],
},
{
name: "tabs",
description: "Tab new + list + switch",
commands: [
{ id: "tnew", action: "tab_new", url: "about:blank" },
{ id: "tlist", action: "tab_list" },
{ id: "tswitch", action: "tab_switch", index: 0 },
],
teardown: [{ id: "tclose", action: "tab_close", index: 1 }],
},
{
name: "full-workflow",
description: "Realistic agent workflow: navigate, snapshot, click, fill, evaluate, screenshot",
commands: [
{ id: "w-nav", action: "navigate", url: "about:blank", waitUntil: "domcontentloaded" },
INJECT_FORM,
{ id: "w-snap", action: "snapshot" },
{ id: "w-click", action: "click", selector: "#link" },
{ id: "w-fill", action: "fill", selector: "#name", value: "Agent User" },
{ id: "w-eval", action: "evaluate", script: "document.getElementById('name').value" },
{ id: "w-ss", action: "screenshot" },
],
},
];