Skip to content

fix(fetch): preserve rendered document content - #814

Closed
lanyue-llk wants to merge 5 commits into
mainfrom
codex/fetch-content-audit
Closed

lanyue-llk wants to merge 5 commits into
mainfrom
codex/fetch-content-audit

Conversation

@lanyue-llk

@lanyue-llk lanyue-llk commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

目的

这次修改来自 WebMainBench 545 页逐页内容审计。它基于 PR #785 已有的解析暂停修复,继续处理成功返回中仍然存在的共享内容缺陷。实现落在样式级联、页面内容筛选和 HTML-to-Markdown 层,没有按网址或 case ID 写特例。

已冻结的历史测量

提交 39169d705、二进制 SHA-256 8fb71de9…d60d 的认证运行结果为:

这些数字只描述上述冻结提交。当前 HEAD a825e2e07 继续修正复审发现的组合回归,没有把历史成绩当作当前 HEAD 的重新验收结果。报告保留原版、PR #785 修复版和内容修复版三个独立方法。

根因与修改

  • 将 <font color> 等 HTML presentation hint 纳入 Stylo 正常级联,让 author CSS 覆盖遗留属性;零对比度过滤只处理能够可靠判断的纯文本叶节点,遇到背景图或替换元素时保留内容。
  • 只从结构化 ARIA、链接关系和 inline handler 中明确的 document.getElementById / document.querySelector('#id') 建立 disclosure 关系;普通事件字符串不再解封同名隐藏节点。
  • tooltip fallback 进入正常链接转换流程;标题判空同时认可有效资源、图片替代文本和可访问标签。
  • lazy data-srcset 无法产生安全 URL 时继续尝试标准 srcset,并按文档 base 逐候选解析。
  • HTML fallback 将被引用目标的身份与节点标签渲染分离,覆盖 code/pre、表格、MathML、表单和媒体中的特殊节点。
  • 保留此前对序列化状态、表格/表单关系、代码与数学边界、相对资源和文本 box 边界的共享修复。

解析器暂停恢复属于 PR #785 的既有修复,本 PR 没有撤销其 ownership 设计;本次新增修复集中在内容导出的正确性。

当前 HEAD 验证

  • cargo test -p moli-html2md --test regressions:65 passed。
  • cargo test -p moli-renderer-v8 markdown_uses_live_visibility_and_preserves_disclosure_content:1 passed,8,314 filtered out。
  • cargo clippy -p moli-html2md -p moli-renderer-v8 --all-targets --offline -- -D warnings:通过。
  • cargo clippy -p moli-selector --all-targets --offline -- -D warnings:通过。
  • cargo fmt --all、git diff --check:通过。

按本轮约定未在本地重复运行仓库全量测试或重新测量 545 页质量分;完整矩阵由 PR CI 执行。

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Spider Bench A/B

✅ All benchmark browser service runs completed; results are informational.

Public HEAD: 184 / 240 rows; 38 / 48 sites produced rows; 10 unexpected empty sites.

✅ Deterministic fixture contract is clean.

Common ancestor 5ab69e4aac44 → HEAD 0b4c17664b7c; benchmark order: head-first; workflow: success.

Public 48-site run · informational

Metric Base HEAD Δ
Run status success success —
Extracted rows / contract 189 / 240 184 / 240 -5 (-2.65%)
Contract fill 78.75% 76.67% -2.08 pp (-2.64%)
Sites with rows 39 / 48 38 / 48 -1 (-2.56%)
Sites meeting row contract 37 / 48 36 / 48 -1 (-2.70%)
Unexpected empty sites 9 10 +1 (+11.11%)
Partial-row sites 2 2 0 (0.00%)
Site outcomes extracted: 38; HTTP error + rows: 1; empty: 6; HTTP error + empty: 1; snapshot error + empty: 2 extracted: 36; HTTP error + rows: 2; empty: 7; HTTP error + empty: 1; snapshot error + empty: 2 —
Observed duration 52.37 s 55.48 s +3.10 s (+5.92%)
Estimated CPU 49.04 s 51.57 s +2.52 s (+5.14%)
Peak PSS 282.47 MiB 272.21 MiB -10.26 MiB (-3.63%)
Peak RSS 286.10 MiB 276.35 MiB -9.75 MiB (-3.41%)
Peak threads 27 26 -1 (-3.70%)
Mismatched sites 0 0 0
Timeout with items 0 0 0
Category rows Base HEAD Δ
news 35 / 40 35 / 40 0
stocks 20 / 40 20 / 40 0
tech 35 / 40 35 / 40 0
sports 32 / 40 27 / 40 -5
games 37 / 40 37 / 40 0
life 30 / 40 30 / 40 0

Public-site content, timing, and memory are noisy. This single A/B run reports evidence only; site outcome counts explain missing rows without treating them as a deterministic regression.

CPU and memory timelines

These are bounded 40-point views of the same complete process-tree samples used by the HTML report. CPU uses 100% per occupied logical core; memory lines are RSS first and PSS second when complete PSS samples are available.

Base

xychart-beta
    title "Base CPU"
    x-axis "Elapsed seconds" [1.535, 2.537, 3.038, 4.539, 6.042, 7.544, 9.046, 10.047, 11.548, 13.051, 14.553, 16.055, 17.055, 17.556, 19.056, 20.559, 22.061, 23.561, 25.064, 26.565, 27.566, 29.067, 30.569, 32.07, 33.571, 35.074, 36.575, 38.076, 39.578, 41.08, 42.582, 44.085, 45.086, 46.588, 47.589, 48.089, 49.591, 51.093]
    y-axis "CPU percent" 0 --> 200
    line [45.91, 105.86, 111.78, 103.89, 159.64, 143.8, 110, 181.75, 147.86, 97.82, 111.94, 5.99, 0, 4, 2, 125.77, 75.98, 18, 141.68, 127.89, 151.81, 111.92, 123.76, 55.99, 109.86, 143.75, 111.66, 47.95, 69.94, 127.76, 17.97, 0, 95.9, 131.8, 183.82, 19.98, 99.97, 121.85]
Loading
xychart-beta
    title "Base memory: RSS then PSS"
    x-axis "Elapsed seconds" [0.034, 1.535, 2.537, 3.038, 4.539, 6.042, 7.544, 9.046, 10.047, 11.548, 13.051, 14.553, 16.055, 17.055, 17.556, 19.056, 20.559, 22.061, 23.561, 25.064, 26.565, 27.566, 29.067, 30.569, 32.07, 33.571, 35.074, 36.575, 38.076, 39.578, 41.08, 42.582, 44.085, 45.086, 46.588, 47.589, 48.089, 49.591, 51.093, 52.373]
    y-axis "Memory MiB" 0 --> 500
    line [12.42, 99.61, 115.44, 114.94, 141.75, 147.05, 159.84, 178.86, 164.84, 153.61, 163.07, 170.1, 150.11, 143.43, 143.57, 144.03, 154.2, 155.21, 138.25, 168.89, 162.69, 163.62, 286.1, 208.82, 160.56, 176.74, 179.59, 148.41, 155.14, 156.86, 170.55, 162.5, 155.18, 186.69, 167.08, 159.28, 158.44, 159.59, 170.25, 0]
    line [9.19, 96.1, 111.94, 111.36, 138.27, 143.55, 156.26, 175.25, 161.14, 150.01, 158.82, 166.69, 146.51, 139.83, 139.97, 140.43, 152.18, 151.61, 134.65, 165.8, 159.27, 160.03, 282.47, 205.19, 156.96, 173.33, 176.08, 144.81, 151.54, 153.26, 166.94, 158.89, 151.58, 183.03, 163.48, 155.67, 154.68, 155.99, 166.66, 0]
Loading

HEAD

xychart-beta
    title "HEAD CPU"
    x-axis "Elapsed seconds" [1.035, 1.535, 3.036, 4.538, 6.041, 7.542, 9.543, 11.044, 12.545, 14.047, 15.549, 17.05, 18.553, 20.055, 21.558, 23.06, 24.562, 26.064, 28.065, 28.566, 29.567, 31.069, 32.569, 34.072, 35.573, 37.074, 38.576, 40.077, 41.578, 43.081, 44.582, 46.585, 48.087, 49.588, 51.09, 52.592, 54.094, 55.096]
    y-axis "CPU percent" 0 --> 500
    line [0, 36.01, 111.88, 137.84, 153.74, 133.95, 107.99, 187.52, 141.92, 119.95, 147.62, 33.93, 7.98, 67.84, 0, 21.95, 149.96, 101.95, 99.91, 107.82, 135.97, 101.92, 134.13, 151.57, 115.97, 23.99, 81.98, 9.98, 89.97, 59.95, 4, 0, 111.88, 147.83, 41.97, 99.94, 119.81, 195.78]
Loading
xychart-beta
    title "HEAD memory: RSS then PSS"
    x-axis "Elapsed seconds" [0.036, 1.035, 1.535, 3.036, 4.538, 6.041, 7.542, 9.543, 11.044, 12.545, 14.047, 15.549, 17.05, 18.553, 20.055, 21.558, 23.06, 24.562, 26.064, 28.065, 28.566, 29.567, 31.069, 32.569, 34.072, 35.573, 37.074, 38.576, 40.077, 41.578, 43.081, 44.582, 46.585, 48.087, 49.588, 51.09, 52.592, 54.094, 55.096, 55.475]
    y-axis "Memory MiB" 0 --> 500
    line [12.3, 53.68, 88.6, 118.7, 133.08, 155.9, 178.43, 158.35, 182.13, 159.77, 174.18, 179.4, 166.96, 144.12, 154.11, 155.05, 143.12, 163.54, 159.51, 230.67, 276.35, 195.63, 227.96, 167.05, 173.14, 174.81, 142.5, 145.66, 151.77, 161.83, 171.77, 160.54, 151.84, 175.77, 157.64, 156.05, 158.38, 168.88, 181.25, 0]
    line [9.11, 50.21, 85.12, 115.25, 129.76, 152.44, 174.98, 154.76, 178.55, 156.19, 170.59, 176.33, 163.37, 140.53, 150.53, 151.46, 139.53, 159.5, 155.9, 227.12, 272.21, 192.08, 224.49, 163.47, 169.33, 171.24, 138.92, 142.08, 148.19, 158.3, 168.19, 156.96, 148.26, 171.91, 154.06, 152.47, 154.79, 165.31, 177.67, 0]
Loading

Deterministic fixture · required contract

The fixture exercises 15 routes: 8 are expected to emit rows and 7 intentionally exercise empty, loading, or timeout behavior.

Metric Base HEAD Δ
Run status success success —
Extracted rows / contract 40 / 40 40 / 40 0 (0.00%)
Contract fill 100.00% 100.00% 0.00 pp (0.00%)
Sites with rows 8 / 15 8 / 15 0 (0.00%)
Sites meeting row contract 15 / 15 15 / 15 0 (0.00%)
Unexpected empty sites 0 0 0
Partial-row sites 0 0 0
Site outcomes extracted: 7; HTTP error + rows: 1; empty: 3; timeout + empty: 4 extracted: 7; HTTP error + rows: 1; empty: 3; timeout + empty: 4 —
Observed duration 19.90 s 19.90 s +0.00 s (+0.02%)
Estimated CPU 0.41 s 0.42 s +0.01 s (+2.43%)
Peak PSS 58.39 MiB 57.68 MiB -0.71 MiB (-1.22%)
Peak RSS 61.88 MiB 61.13 MiB -0.75 MiB (-1.21%)
Peak threads 18 18 0 (0.00%)
Mismatched sites 0 0 0
Timeout with items 0 0 0

Full HTML, JSON, CSV, logs, and page snapshots: workflow run and full spider-bench-results artifact.

The public-web A/B is informational. The exact HEAD fixture contract runs as a separate required CI check; timing and public-site content deltas remain non-blocking.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Sequential Navigation Soak A/B

✅ HEAD completed the 200-navigation resilience and memory observation.

One browser process, one target, and one CDP session navigate CSDN → SegmentFault → Huaban → example.com repeatedly. This run issued 200 Page.navigate commands per binary.

Common ancestor 5ab69e4aac → HEAD 0b4c17664b; benchmark order: head-first; workflow: success.

Session resilience

Metric Base HEAD
Attempted / planned 200 / 200 200 / 200
Direct observable pass 186 / 200 200 / 200
Failures / recovered 14 / 14 0 / 0
Recovery failures 0 0
Lifecycle order violations 0 0
Network order violations 0 0
Wall time 488.6 s 290.3 s

A public-page failure is reported separately from an unrecoverable session. The soak requires all navigation attempts, zero failed recovery, zero lifecycle/network ordering violations, and complete resource evidence.

Process-tree memory

Metric Base HEAD Δ
Boundary samples 200 200 0
Boundary RSS samples 200 200 0
Boundary PSS samples 200 200 0
Periodic peak RSS 228.4 MiB 220.5 MiB -7.8 MiB
Periodic peak PSS 228.8 MiB 216.9 MiB -11.9 MiB
RSS · first 10 avg 169.1 MiB 167.8 MiB -1.3 MiB
RSS · last 10 avg 141.7 MiB 157.0 MiB +15.2 MiB
RSS · last − first window -27.4 MiB -10.9 MiB +16.5 MiB
RSS · warm slope / 100 nav -10.2 MiB +0.4 MiB +10.6 MiB
PSS · first 10 avg 166.2 MiB 164.5 MiB -1.8 MiB
PSS · last 10 avg 140.1 MiB 154.2 MiB +14.1 MiB
PSS · last − first window -26.2 MiB -10.3 MiB +15.9 MiB
PSS · warm slope / 100 nav -10.0 MiB +1.0 MiB +10.9 MiB
Peak file descriptors 64 65 1
Peak threads 31 31 0

HEAD memory by 50-navigation quarter

HEAD range Avg RSS Final RSS Peak RSS Avg PSS Final PSS Peak FD
1–50 159.0 MiB 152.4 MiB 219.6 MiB 155.5 MiB 148.9 MiB 58
51–100 156.0 MiB 137.0 MiB 173.7 MiB 152.5 MiB 133.5 MiB 53
101–150 156.9 MiB 154.5 MiB 176.3 MiB 154.0 MiB 151.6 MiB 53
151–200 157.1 MiB 136.9 MiB 174.9 MiB 154.2 MiB 134.1 MiB 54

Raw reports and per-navigation boundary samples: workflow run and full sequential-navigation-soak-results artifact.

Public-site timing and memory are observational. Use the A/B deltas and trend shape as evidence, not as a deterministic performance threshold.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

CI Regression Report

Source CI run · source state at render: in_progress · artifacts: 6/6

Check Status Signal
Release regression ✅ HEAD/base failures 0/0; raw binary +0.059598%
Frontend differential ✅ 1,020/1,020 cases matched; 0 issues
Agent episodes ✅ 8/8 Moli episodes passed; 0 failures
Runtime/CDP contracts ✅ 26 contract cases; 0 failures
CDP smoke ✅ 48/48 groups passed; 535 scenarios
WebMainBench ✅ 545/545 completed; 0 unexpected failures
Release regression — ✅ HEAD/base failures 0/0; raw binary +0.059598%

Package and image size

Metric Base HEAD Delta Delta %
Raw binary 146.47 MiB 146.56 MiB +89.39 KiB +0.059598%
Stripped binary 100.52 MiB 100.61 MiB +84.00 KiB +0.081604%
gzip binary 50.13 MiB 50.18 MiB +42.99 KiB +0.083731%
Rootfs 153.38 MiB 153.47 MiB +90.00 KiB +0.057303%
gzip rootfs 58.91 MiB 58.96 MiB +45.82 KiB +0.075951%

Startup latency and PSS

Case Base p50 HEAD p50 Delta Base PSS HEAD PSS Delta
Serve ready 27.23 ms 27.09 ms −0.14 ms 4.86 MiB 4.86 MiB −1.00 KiB
CDP first page 50.92 ms 49.27 ms −1.65 ms 4.57 MiB 4.86 MiB +301.00 KiB
CDP warm page 13.67 ms 12.82 ms −0.85 ms 49.33 MiB 49.54 MiB +218.00 KiB
CLI about:blank 14.33 ms 13.91 ms −0.43 ms 4.32 MiB 4.31 MiB −17.00 KiB
CLI local JS 17.63 ms 17.45 ms −0.18 ms 3.71 MiB 4.07 MiB +376.00 KiB

Concurrency matrix

Concurrency Base p50 HEAD p50 Delta Base PSS HEAD PSS Delta HEAD failures Unstable B/H
1 17.15 ms 15.44 ms −1.72 ms 849.00 KiB 833.00 KiB −16.00 KiB 0 0/0
5 19.37 ms 18.87 ms −0.50 ms 27.08 MiB 22.37 MiB −4.71 MiB 0 0/0
10 18.41 ms 20.02 ms +1.61 ms 17.86 MiB 12.60 MiB −5.26 MiB 0 0/0
25 20.17 ms 19.55 ms −0.62 ms 12.91 MiB 12.88 MiB −22.00 KiB 0 0/0
100 19.02 ms 18.57 ms −0.45 ms 13.70 MiB 26.93 MiB +13.23 MiB 0 0/0
Frontend differential — ✅ 1,020/1,020 cases matched; 0 issues
Match DOM mismatch Diagnostic mismatch Moli error Reference error Infrastructure error Mismatched frames Duration
1,020 0 0 0 0 0 0 6m 23.01s
Agent episodes · Moli vs Chromium — ✅ 8/8 Moli episodes passed; 0 failures
Fast contract Episodes Passed Assertions Ready p50 Episode p50
Moli 8 8 47/47 3.81 ms 24.65 ms
Chromium 8 8 47/47 287.89 ms 81.83 ms
Operation p50 Chromium Moli Delta Delta %
navigate 40.88 ms 6.66 ms −34.23 ms −83.72%
observe 0.79 ms 0.89 ms +0.10 ms +12.48%
fill 3.68 ms 3.11 ms −0.58 ms −15.62%
click 5.20 ms 3.29 ms −1.91 ms −36.65%
Idle-resource episode Peak RSS Peak PSS Average CPU Peak CPU Peak processes Sampler
Moli 63.68 MiB 60.98 MiB 0.63% 10.02% 1 ✅
Chromium 1286.84 MiB 332.03 MiB 1.81% 89.94% 13 ⚠️ PSS partial
Runtime and CDP session contracts — ✅ 26 contract cases; 0 failures
Suite Cases Failures Median case p50 Median case p95 Median peak PSS p50
Synthetic fetch 13 0 17.37 ms 18.07 ms 688.00 KiB
Long-lived CDP session 13 0 19.31 ms 19.57 ms —
CDP smoke — ✅ 48/48 groups passed; 535 scenarios

Workers: 4 · cumulative group time: 147.74 s · failed groups: 0

Slowest group Scenarios Duration Status
target-lifecycle 24 16.91 s ✅
multi-page 45 11.86 s ✅
chromium-cdp 56 11.05 s ✅
dom-input 27 9.63 s ✅
layout-screenshot 5 7.61 s ✅
WebMainBench · 545 pages — ✅ 545/545 completed; 0 unexpected failures
Completed Successful Expected DNS failure Unexpected failures Panics Crashes Timeouts Empty output
545/545 544 1 0 0 0 0 0

Frozen HTML · Linux · --wait done · 45 seconds per page · no retries. Content quality is evaluated separately.

Diagnostics: webmainbench-results — every page's Markdown, stderr, and exit status. Source run and artifacts

All artifact fields are parsed by the trusted default-branch renderer; missing or invalid inputs remain visible as unavailable.

@lanyue-llk lanyue-llk left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:需要修改后再评审。针对 923999da47ac0b6ac43d750b89c235672e6eb667,由主审和 3 个子审并行检查需求、转换结构、渲染可见性与质量证据,并做本地复测。本次提交 9 条可复现的行级问题。

原问题有大量真实依据,不能说这是“只为跑通测试”的整体改动:HEAD 原有 moli-html2md 335 个测试全部通过;把 HEAD 的 59 个 regressions 用例放回共同基线 5ab69e4aac44 后,35 个失败、24 个通过。表格、数学、表单、媒体和代码边界的多项修复因此具备可重复的基线失败/HEAD 成功证据。复杂表格保留 HTML、MathML 保留结构的方向合理,改动确实位于共享层。

不过,共享层里加入了一些根据 class、tooltip 属性、旧颜色属性和有限披露标记猜测内容语义的规则;这些规则有实际反例,不能用 335 个绿测或调用成功率覆盖。

PR 所列需求 审查结果
可见性与加载遮罩 原有 renderer 正向回归通过;新增 CSS 颜色覆盖、aria-hidden 非活动面板用例失败,真实冻结页还丢失普通 JS 展开的详情。
隐藏状态与非正文过滤 过滤序列化状态、隐藏副本和复制按钮有合理用途;但 display:none 并不等于“无正文价值”,需要统一保留策略。
表格、表单、控件关系 原始问题和多数修复成立;class=quote 的通配规则抢占原生结构分支,实测列表变成引用段落。
代码、公式与边界 结构保留方案总体合理;代码块内部的被引用锚点仍丢失。代码评分下降的主因及一项真实损失见下。
链接、锚点和媒体 tooltip 提前返回吞掉真实链接;图片标题被判空;ID 重复解码;srcset 最终 URL 未按文档 base 解析。

代码分下降已经追到具体原因。 对 PR785 修复版与本次内容修复版的认证逐页结果比较,21 页代码分下降、1 页上升;可评分分母从 97 变为 100。下降的 21 页中,16 页在重新提取 HTML pre/Markdown fence 并忽略空白后,文本逐字符相同。官方代码评分器不提取 HTML pre,这些页面仅改变表示形式就被记成代码缺失,约占负向分值总和的 80.9%。剩余 5 页分别涉及隐藏 textarea 副本、白底白字混淆数字、复制按钮、增加 search 文本,以及 mcubed 页真实的可展开比赛详情丢失;最后一项已在本 PR 精确 HEAD 的 CI Markdown 中确认并单独评论。既不应把 80.69%→61.11% 解读成等量代码损坏,也不能把所有下降解释成评分器问题。

需求描述还需收紧到本 PR 的实际范围。 五类问题已分类,但描述中的“本 PR 修复样式表阻塞、document.write 外部脚本和 DOM 回调暂停状态”与本次 25 文件差异不符,那些属于继承的解析器修复。请明确继承项与本次新增项,并为 604 条观察、38 页/53 条断言、545 页质量测量补上可访问的证据与复测入口。还应加入上述代码分归因,而不是只列下降数字。

验证边界和复测结果:

  • cargo test -p moli-html2md --offline:原有 335 个测试通过。
  • 同一份 HEAD regressions 测试在真实基线:35 失败/24 通过;在 HEAD:59 通过。
  • 独立新增 tooltip、图片标题、quote 列表 3 个行为测试:真实基线 3/3 通过,HEAD 3/3 失败。
  • 独立新增百分号 ID、代码行目标 2 个断言:HEAD 均失败。
  • PageVm renderer 模块:PR 原有用例通过;新增 font CSS、inactive tab、相对 srcset 用例均失败。另测到相对位移标签被改为 sup,作为启发式规则需补反例的附加证据,未单列阻断项。
  • 已读取 精确 HEAD CI 的检查结果并下载 WebMainBench 原始输出;545 页 gate 检查的是执行稳定性,实际为 544 success + 1 expected DNS failure,不是正文质量验收。

建议保留已证明有效的结构化转换方案,优先修正上述 9 个具体行为问题,收敛启发式规则并补上反例,再复跑内容验收。每条行级评论均附最小输入或冻结页标识、实际错误输出和修正方向。

Comment thread moli-html2md/src/machine.rs Outdated
Comment on lines +233 to +234
self.writer().text(value);
return;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] tooltip 不能替换可见子内容并吞掉链接

输入 <a href="/download" data-toggle="tooltip" data-original-title="Download the latest version">Download</a>,当前输出只剩 Download the latest version;可见标签和 /download 都丢了。基线同一行为测试通过,PR HEAD 失败。data-original-title 是附加说明,不是替代整个元素子树的语义依据;这里提前 return 也绕过了正常链接转换。请优先保留实际子内容和链接,只在确实没有可读内容时采用明确的 fallback,并加上「tooltip 与可见标签不同」的反例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。tooltip 现在优先转换真实子树和链接,只在子树确实没有可输出内容时才采用 data-original-title;新增了可见标签与 tooltip 不同的回归场景。

Comment on lines +387 to +389
if !subtree_has_content(self.dom, node, remaining) {
self.writer().boundary(2);
return;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 空标题判定不能删除只有图片的标题

输入 <h1><a href="/home"><img src="/logo.svg"></a></h1><p>Article</p>,HEAD 输出只有 Article;基线保留图片和链接。subtree_has_content 仅认可非空文本或非空 alt,把有 src 但缺失/为空 alt 的可见图片判成空内容,导致整个标题提前丢弃。这不只是去掉空 #,而是删掉资源与导航。请根据实际转换结果判空,或覆盖所有能够输出内容的节点类型;补无 alt/空 alt 图片以及可访问标签的标题用例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。标题判空现在把具有实际资源的图片和叶节点 aria-label 视为内容,覆盖无 alt 图片及可访问标签场景。

Comment thread moli-html2md/src/machine.rs Outdated
Comment on lines +412 to +415
_ if quote_container(self.dom.attribute(node, "class")) => {
self.capture();
self.tasks.push(Task::EndQuote);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] class 引用启发式不能抢占原生列表和表格语义

<ul class="quote"><li>First</li><li>Second</li></ul> 在基线是列表,HEAD 变成 > First\n>\n> Second,重新解析后没有任何 <ul>/<li>。这个通配 guard 排在 ul/ol/li/table 分支之前,普通 class 名一旦命中就跳过原生结构处理;例如 table.quote 也会丢单元格关系。class 不足以覆盖元素自身语义。最小修正是让原生结构分支优先,只对可证明的普通容器附加引用语义;应覆盖列表、表格与普通 div 的正反例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。原生列表和表格分支优先,quote class 只作用于 div、section、article、aside 等普通容器;正反例均已覆盖。

Comment thread moli-html2md/src/machine.rs Outdated
Comment on lines +265 to +270
if let Some(id) = self.dom.attribute(node, attribute)
&& self.anchor_targets.contains(&crate::anchors::normalize(id))
{
let id = crate::anchors::normalize(id);
if self.emitted_anchors.insert(id.clone()) {
self.writer().inline_html(&crate::anchors::markup(&id));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] fragment 解码后应与原始 ID 匹配

实测 <a href="#x%2520y">Go</a><p id="x%20y">Target</p> 输出 [Go](#x%2520y) 和 Target,却不输出目标锚点。referenced() 已把 URL fragment 解码为 x%20y;这里又对 DOM ID 解码为 x y,两者无法匹配。对 ID 解码后再去重还会合并本来不同的目标。请只解码 URL fragment,匹配、去重和输出都保留 DOM ID 原值,并同步修正 anchors::contains。这是新锚点功能的缺口,已用独立断言复现。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。只解码 URL fragment,DOM id/name 在匹配、去重和输出时保留原值;anchors::contains 同步修正。

Comment on lines 358 to +362
"code" | "pre" => {
if subtree_has_link(self.dom, node, self.options.max_depth - depth) {
let html =
crate::html_table::render(self.dom, node, depth, self.options.max_depth);
if tag == "pre" {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 保留代码内部被外部链接引用的锚点

输入 <a href="#L1">Line 1</a><pre><code><span id="L1">let x = 1;</span> <span id="L2">let y = 2;</span></code></pre>,HEAD 保留了 Line 1 链接,但输出普通代码围栏并丢弃 L1,因此链接无目标。当前只以「代码内部是否有链接」决定 HTML 回退;代码行号链接常常来自代码外部。请像 table 分支一样检查被引用的目标,保留代码内容及目标位置,并补外部引用 code/pre 内部 ID 的场景。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。code/pre 除内部链接外,也会检查外部 fragment 是否引用其内部目标;有目标时保留 HTML 结构和锚点。

Comment on lines +180 to +183
let foreground = match Dom::node_kind(dom, node) {
NodeKind::Element("font") => Dom::attribute(dom, node, "color")
.and_then(css_color)
.or_else(|| values.get(10).and_then(|value| css_color(value))),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 零对比度过滤必须服从 CSS 级联后的实际颜色

实测 <p>Before <font color="white" style="color:black">VISIBLE</font> after</p>:PageVm 中 getComputedStyle(font).color 确认返回 rgb(0, 0, 0),Markdown 却丢弃 VISIBLE。这里优先采用旧 color 属性,且 page_dump.rs 构造 styles 时把 foreground 槽填为空串,并未读取计算颜色。于是正常黑字被错当白底白字删除。请使用实际计算前景色;对于无法可靠确定背景/对比度的情况保留内容,并补「旧 font 属性被内联/外部 CSS 覆盖」反例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。page dump 读取 getComputedStyle 的实际 color,并按稳定槽位传入可见性判断;不再让旧 font color 属性覆盖 CSS 级联结果。

Comment on lines +205 to +207
&& !document_root)
&& !disclosure)
|| aria_hidden

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] disclosure 保留策略需要覆盖带 aria-hidden 的非活动 tabpanel

实测 <button role="tab" aria-controls="specs">Specs</button><section id="specs" role="tabpanel" aria-hidden="true" style="display:none">Technical specifications</section>,HEAD 只输出 Specs,完整正文消失。上面已将该节点识别成 disclosure,但这里 || aria_hidden 无条件覆盖保留例外;非活动面板同时设置 display:none 和 aria-hidden 是正常页面状态。本 PR/README 明确保留 inactive tabs 和可展开内容,请统一 display、opacity、ARIA 分支对已确认 disclosure 的处理,并保留普通 aria-hidden 装饰/UI 不输出的反例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。经 ARIA 或控件关系确认的 disclosure 即使 aria-hidden=true 也保留,普通 aria-hidden 内容仍过滤。

Comment on lines +257 to +259
NodeKind::Element("img") => {
&["src", "data-original", "data-src", "data-lazy-src"]
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 对 srcset 选中的最终资源也应用文档 base

PageVm 文档 URL 为 https://example.com/docs/,输入 <img srcset="small.png 1x, large.png 2x" alt="Responsive">,实测输出 ![Responsive](large.png),而不是 ![Responsive](https://example.com/docs/large.png)。这里仅解析 src/data-*,随后 converter 从未解析的 srcset 选候选;导出的 Markdown 在其他位置使用时会访问错误地址。请统一解析最终选中的 URL,不能把整串 srcset 当单个 URL join;补只有相对 srcset、显式 base 与 data-srcset 的场景。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。srcset 和 data-srcset 逐候选解析并分别应用文档 base,再由转换器选择候选;没有把整串 srcset 当成单个 URL。

Comment on lines +199 to +206
if ((values.first().is_some_and(|value| value == "none")
|| values
.get(2)
.is_some_and(|value| value.parse::<f32>() == Ok(0.0))
&& !lazy_media
&& !animated
&& !document_root)
&& !disclosure)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 不要把正常点击展开的正文当成非内容删除

WebMainBench 冻结页 8798f93a-240c-46d7-a8d1-a5fb011279fc(mcubed 比赛历史)真实包含 list1 至 list91 的比赛详情,初始由 hideallids() 设置 display:none,页面按钮通过 showPageElement(document.getElementById('list1')) 恢复 display:block。旧认证输出保留详情;本 PR 的精确 HEAD CI 输出 artifact 中该页 Markdown 已缺 WINNER、1987/04/07、1986/10/05 等原有详情。这是实质正文丢失,不是代码围栏改 HTML 导致的评分差异。disclosure 收集只覆盖 ARIA 和特定 href/data-target/截断段落模式,未识别的 display:none 子树在这里全部删除。请明确内容 dump 的保留策略,对无法证明是隐藏 UI 的正文采用保守保留,而非继续加某个站点/函数名特例;把此冻结页及普通 JS 展开场景列为回归用例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 0b4c176 修复。实现不依赖站点或函数名:任意 inline 事件处理器中引用的隐藏 DOM id 都作为可展开正文目标保留;同时只解析处理器中的字符串目标,避免对所有隐藏节点逐一搜索。

@lanyue-llk

Copy link
Copy Markdown
Contributor Author

本轮 review 处理

9 条 inline 意见均为可复现的共享语义缺陷,已在 0b4c17664 修复并逐条回复:

  • tooltip 不再替换真实子树和链接;
  • 图片或可访问标签构成的标题不再被当作空标题;
  • quote class 不再抢占列表、表格等原生语义;
  • URL fragment 只解码一次,DOM ID 保留原值;
  • code/pre 内被外部链接引用的锚点得到保留;
  • 零对比度判断采用 CSS 级联后的计算前景色;
  • 已确认的 disclosure 不再被 aria-hidden 二次删除;
  • srcset / data-srcset 按候选应用文档 base;
  • inline 点击处理器引用的隐藏目标作为通用 disclosure 保留,不依赖站点、函数名或具体 case ID。

验证:

  • cargo test -p moli-html2md --test regressions:63 passed;
  • CARGO_TARGET_DIR=/private/tmp/moli-review-814/target cargo test -p moli-renderer-v8 markdown_uses_live_visibility_and_preserves_disclosure_content:1 passed,8,314 filtered out;
  • cargo fmt --all 和 git diff --check 通过。

按本次约定未在本地重复执行全量测试,完整矩阵交由 PR CI。

@lanyue-llk lanyue-llk left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审结论:0b4c17664b7c12499882c36a1ebaaf69c0f4cfe6 仍需要修改。本轮主审和 3 个子审分别复测转换、渲染、真实页证据与 CI,确认 7 类行为问题,另有一个必需检查阻塞;详细输入、实际输出和修正方向见行级评论。

先确认修复成果:上轮的 9 个直接复现已得到处理,包括可见标签 tooltip 链接、带 src 的图片标题、quote 列表、百分号 ID、span 代码行锚点、CSS 覆盖 font、ARIA 非活动 tab、相对 srcset,以及 mcubed 页面。mcubed 的 91 个展开区域已全部恢复,与冻结输入逐段对比,忽略空白的文本均一致。本轮问题来自组合场景、修复不完整和新引入的回归,不是重复提交旧结论。

类别 本轮确认问题
数据正确性 <font color=white> 干扰数字重新混入真实数据;测试反而改成保留 hidden suffix。两个冻结页已出现数值污染。
可见性判定 只比较文字颜色与纯色背景,误删黑色渐变上的白字,也误删继承相同文字颜色的图片。
展开正文识别 任意事件字符串与 ID 同名就解封隐藏内容;querySelector/addEventListener 的实际展开行为又识别不到。
标题 无有效 src 但有 alt 的图片标题从上一版正常输出变成空串。
媒体回退 空或不可用 data-srcset 遮住有效 srcset,图片退化成纯 alt 文字。
锚点 HTML fallback 的 mark、input、math、video 等路径仍丢被引用 ID。
tooltip 空链接使用 fallback 标签时仍直接 return,导致 href 丢失。

方案判断:仍存在按已见样本补规则的问题。 把任意 JS 字符串当成 disclosure 关系,并没有建立可靠的语义依据;扩大零对比度删除范围,也没有确认背景绘制和替换元素的适用边界。需要修正这些共享规则,而不是继续增加站点、标签或函数名特例。标题和媒体的新回归也说明,正例修复没有配套验证 fallback 的反方向。

真实页面证据: 最新 CI 的 webmainbench-results 绑定本次 HEAD。7311d034-ea36-4a4c-9f59-96da9b81ea61 从 2000 012·5 变成 2000 01 582·5,是原 HTML 中白色干扰数字 58 被重新导出;另一页也出现 8. 被污染成 648.。这与评分器是否识别 HTML 代码块无关,是可直接观察到的内容错误。

复测结果:

  • 原有 moli-html2md 339 个测试通过;上轮独立 converter 直接复现也已通过。
  • 新增标题 alt 场景:上一版通过,本版失败;新增两个 data-srcset fallback 场景同样上一版通过、本版失败。
  • 新增 tooltip fallback 与 4 个特殊节点锚点断言失败。
  • PageVm 模块:原测试和上轮 font/tab/srcset 独立测试共 4 个通过;新增事件误识别、渐变正文、图片、querySelector 展开、事件监听器展开共 5 个失败。监听器用例实际点击后可见,确认不是无效页面脚本。
  • 本地 cargo clippy -p moli-html2md --all-targets --offline -- -D warnings 退出 101,与 CI Clippy 失败 一致:machine.rs:266–272 的嵌套 if 触发 collapsible_if。应合并条件,不应禁用 lint。

PR 描述仍写旧 HEAD 923999da4 和“18 项检查全部通过”,与当前状态不符;仍将继承的 parser 暂停修复混入本 PR 范围。所列质量分绑定更早的 39169d705,应明确是历史测量,不能当作最新修复已验收的成绩。最新 545 页 gate 的 544 success + 1 expected failure 依旧只证明执行稳定性。

建议先修正上述行为问题与 Clippy,恢复被改掉的负向断言,再进行新 HEAD 的内容验收。

Comment thread moli-html2md/src/machine.rs Outdated
Comment on lines +709 to +711
NodeKind::Element("img") if crate::media::source(dom, node).is_some() => {
return true;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 标题判空仍需认可无可用 src 的图片 alt 文本

这次将 img 判定从非空 alt 替换成 media::source().is_some(),引入了相反方向的丢内容。<h1><img alt="Product manual"></h1> 在上一版 923999da4 输出标题文本,在本版输出空串;无效/被过滤 src + 非空 alt 同样失败。正常 img 转换在没有资源时会输出 alt,但这里先把整个标题丢弃了。已用同一行为测试对照上一版通过、本版失败。请保留 src 或 alt 任一能够输出内容的情况,最好统一判空与实际节点转换的契约,而不是每次只增加一个已知节点反例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正确,已在 a825e2e 修复。标题判空现在与图片实际转换契约一致:有效资源或非空 alt 任一存在都保留标题;新增无 src 与不安全 src 的反向测试。

Comment thread moli-html2md/src/machine.rs Outdated
Comment on lines +233 to +235
{
self.writer().text(value);
return;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] tooltip fallback 仍然绕过链接转换

有可见标签的旧复现已经修好,但 <a href="/download" data-toggle="tooltip" data-original-title="Download"></a> 仍只输出 Download,/download 被吞掉。fallback 应提供链接标签,而不应该在链接样式、目标锚点等语义处理之前直接 return。请把 fallback 内容送入正常元素转换流程,并覆盖空链接/图标链接和普通 span 两类场景;本轮独立断言确认仍失败。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正确,已在 a825e2e 修复。tooltip fallback 不再提前 return,而是在正常元素语义建立后作为内容写入,因此空链接仍保留 href;普通 span fallback 继续成立。

Comment thread moli-html2md/src/media.rs Outdated
Comment on lines +14 to +17
dom.attribute(node, "data-srcset")
.or_else(|| dom.attribute(node, "srcset"))
.and_then(largest_srcset_candidate)
.filter(|value| safe_url(value, true))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 不可用的 data-srcset 不应遮蔽有效 srcset

新增的优先级在解析/验证前使用 or_else:只要 data-srcset 属性存在,就不再尝试 srcset。<img alt="Chart" data-srcset="" srcset="/chart.png 1x"> 当前只输出 Chart;data-srcset="javascript:bad() 1x" 也一样。两例在上一版都保留 ![Chart](/chart.png),本版均红测。请分别解析并验证每个候选来源,在 lazy 来源无法产生可用 URL 时再回退到 srcset,补空值/无效值反例。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正确,已在 a825e2e 修复。data-srcset 和 srcset 现在分别解析、验证,前者为空或不安全时会继续回退到后者;两个反向场景已加入回归。

Comment on lines +116 to +123
NodeKind::Element("math") => {
output.push_str(&crate::mathml::render(dom, node, limit - depth))
}
NodeKind::Element("input") => {
if let Some(value) = crate::form::input_text(dom, node) {
escape(&value, &mut output);
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] HTML 回退路径仍会删除非白名单及特殊节点的锚点

上轮 span 代码行用例已修好,但目标换成 <mark id="L1"> 就再次丢失:<a href="#L1">Line 1</a><pre><code><mark id="L1">let x = 1;</mark></code></pre> 输出链接与 <pre><code>let x = 1;</code></pre>,没有 L1。相同根因也出现在表格内的 input id=email、math id=eq1、video id=video1:这些特殊分支或非白名单标签没有统一保留目标身份。4 个组合场景均已红测。请把被引用目标的保留与“是否保留原标签/如何渲染其内容”解耦,而不是继续扩大标签白名单来逐例修复。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正确,已在 a825e2e 修复。HTML fallback 先独立保留被引用目标,再决定是否保留原标签;非白名单 mark 及 input、math、video 等特殊渲染路径共用同一规则。

"#).unwrap();
for strip_css in [false, true] {
let output = page.render_page_dump(options(strip_css));
for kept in ["Visible body", "Restored child", "Panel content", "ARIA panel content", "Disclosure content", "Closed details content", "Followers 2", "Score 31", "Accessible helper", "Faded text", "Animated article text", "Visible value hidden suffix 8", "CSS override VISIBLE", "White on black stays visible", "![Article photo](https://example.test/article.jpg)"] {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 不应将白字干扰内容重新泄入正文改成预期行为

这里把 hidden suffix 从应省略改为应保留,放过了真实数据回归。最新精确 HEAD 的 545 页 CI artifact 中,case 7311d034-ea36-4a4c-9f59-96da9b81ea61 的源码 2000 01<font color="white"> 58</font>2·5 从上一版 2000 012·5 变成 2000 01 582·5;case 811694dc… 也从 2007 01 8. 变成 2007 01 648.。这是干扰数字混入数值,不是格式差异。根因是切换到 computed color 时没有补齐旧 font color 的 presentation hint;黑底白字导航也因被计算成黑字而消失。请在共享样式层正确处理 legacy color 与 CSS 级联,恢复“无 CSS 覆盖时白字应隐藏”和“CSS 覆盖为黑字时应保留”两个反向测试;不要恢复无条件优先读取旧属性,也不要通过改断言接受污染。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正确,已在 a825e2e 从根因修复。font color 进入 Stylo presentation hint 级联,未被 CSS 覆盖时计算为白色,inline 或 stylesheet author CSS 均能覆盖;属性变更也会触发重新计算。测试恢复为省略 hidden suffix,并覆盖两种 CSS 覆盖方向。

Comment on lines +193 to +202
let foreground = values.get(10).and_then(|value| css_color(value));
let zero_contrast_leaf = !has_element_child(dom, node)
&& foreground
.filter(|color| color.3 > 0.99)
.zip(effective_backgrounds.get(&node).copied())
.is_some_and(|(foreground, background)| {
background.3 > 0.99
&& (foreground.0, foreground.1, foreground.2)
== (background.0, background.1, background.2)
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 不能仅凭文字颜色和纯色背景删除整个叶元素

新增全节点 computed color 后,两个正常场景都被导出为空串:<p style="color:white;background-image:linear-gradient(black,black)">Visible gradient text</p>;<div style="color:white;background:white"><img src="/photo.png" alt="Photo"></div>。第一个背景实际是黑色渐变,算法只读 background-color 并回退白色;第二个 img 的可见内容与继承的文字 color 无关,却被当成零对比度文字整体删除。两个 PageVm 场景均已实测。请把适用范围限定为能可靠证明不可见的文字,对图片等替换元素以及背景图片/渐变等无法由当前模型判定的场景保留内容。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正确,已在 a825e2e 修复。零对比度删除限定为无元素子节点且含真实文本的叶节点,并在 background-image 存在时保守保留;渐变白字和继承白色的图片均有回归测试。

Comment on lines +86 to +88
for literal in quoted_literals(handler) {
if let Some((&id, _)) = targets.get_key_value(literal) {
disclosures.insert(id);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 任意事件字符串不代表可展开正文关系

这次 mcubed 的旧例已恢复,但新规则把所有 on* 处理器中的引号字符串当作目标 ID,没有检查它是否操作可见性。实测 <button onclick="console.log('tracking')">Save</button><section id="tracking" style="display:none">Hidden telemetry state</section> 输出 Save 加 Hidden telemetry state,一个普通日志参数就让隐藏区进入正文。这里仍是按局部特征猜测语义,不能作为通用 disclosure 识别。请去除任意字符串匹配的豁免,明确内容 dump 的保留策略与可证明的结构化关系,补“普通事件参数与 hidden ID 同名”的反例,避免修复展开内容时再次泄入隐藏 UI。

反方向也已实测:改用 onclick="document.querySelector('#history').style.display='block'",或通过 addEventListener 注册同样的展开行为,导出前的正文仍被删除;后者测试实际点击后正文可见。不能通过给这几种字符串再加分支修复,应让相同行为遵守一致的内容保留策略。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

前半条正确,后半条要求超出静态 dump 可可靠证明的范围。a825e2e07 已删除任意字符串匹配,只识别代码字符串之外明确的 document.getElementById(...) 和 document.querySelector("#id");普通日志参数及日志字符串不再解封节点,mcubed 和 querySelector 展开仍保留。任意 addEventListener 回调可能依赖闭包、运行状态和用户操作,静态 DOM 无法可靠推出其可见性效果;执行所有监听器会改变页面并泄入隐藏 UI,因此未加入这一推断。

@lanyue-llk

Copy link
Copy Markdown
Contributor Author

最新复审处理

本轮 7 类行为意见中,标题、tooltip、媒体回退、HTML fallback 锚点、legacy color 级联、零对比度范围和任意事件字符串误识别均为正确问题,已在 a825e2e07 修复。修复落在共享转换、Stylo presentation hint 和可见性规则,没有站点或 case 特判。

addEventListener 后任意脚本行为的静态推断没有采纳。监听器可能依赖闭包、异步状态和用户操作;执行监听器会改变页面,扫描源码也不能可靠证明最终可见性。当前边界保留结构化 ARIA/链接关系,以及 inline handler 中代码字符串之外明确的标准 DOM ID 查询;普通日志参数和日志字符串均有负向测试。

测试按功能场景覆盖正反方向:

  • HTML 转换:65/65,包括有效/无效资源回退、普通与特殊节点锚点、空/非空 tooltip 和资源/替代文本标题。
  • renderer:1/1 聚合场景,包括遗留属性、inline/stylesheet 覆盖、属性变更、渐变、替换元素、有效 DOM 目标与同名日志字符串。
  • Clippy:moli-html2md、moli-renderer-v8、moli-selector 的 all-targets 均通过 -D warnings。
  • cargo fmt --all、git diff --check 通过。

PR 描述已改为区分 39169d705 的历史测量与当前 HEAD,并移除旧 HEAD、旧 CI 全绿和分支状态声明。

@lanyue-llk

Copy link
Copy Markdown
Contributor Author

#814 与当前 main 已产生历史冲突。修复已按原提交顺序移植到 main 0a985e0,并在最新主线上保留 object presentation hint、hidden-until-found 和本 PR 的内容修复;目标回归与 Clippy 已通过。可合并版本转至 #919,#814 保留原始测量与 review 讨论。

@lanyue-llk lanyue-llk closed this Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant