fix(dom): match hidden code styles case-insensitively (#5640)

## Summary

- normalize casing and whitespace before applying the existing `display:
none` filter for `<code>` elements
- prevent hidden code payloads from entering clean Markdown when inline
CSS uses valid case or whitespace variants
- add real-browser regression coverage for lowercase, mixed-case,
tab-separated, and `!important` declarations

Fixes #5637

## Testing

- `uv run pytest tests/ci/test_html_serializer_style.py -q` — 4 passed
- `uv run pytest tests/ci/test_markdown_extractor.py
tests/ci/test_markdown_chunking.py -q` — 38 passed
- `uv run pre-commit run --files
browser_use/dom/serializer/html_serializer.py
tests/ci/test_html_serializer_style.py` — all hooks passed

Each regression case verifies that Chrome computes the element as
`display: none` and that `extract_clean_markdown` omits the hidden
payload while preserving visible content.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes hidden code payloads from leaking into clean Markdown when inline
`style` uses valid case or whitespace variants like `Display: None` or
`DISPLAY:\tNONE`. Fixes #5637.

- Normalizes style casing and whitespace before applying the existing
`display: none` filter.
- Adds regression tests for lower-case, mixed-case, tab-separated, and
`!important` declarations.

<sup>Written for commit c9b3e5507a.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5640?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Magnus Müller
2026-09-04 23:36:45 -07:00
committed by GitHub
2 changed files with 30 additions and 1 deletions
@@ -74,7 +74,8 @@ class HTMLSerializer:
if tag_name == 'code' and node.attributes:
style = node.attributes.get('style', '')
# Check if element is hidden (display:none) - likely JSON data
if 'display:none' in style.replace(' ', '') or 'display: none' in style:
normalized_style = ''.join(style.lower().split())
if 'display:none' in normalized_style:
return ''
# Also check for bpr-guid IDs (LinkedIn's JSON data pattern)
element_id = node.attributes.get('id', '')
+28
View File
@@ -0,0 +1,28 @@
from urllib.parse import quote
import pytest
from browser_use.dom.markdown_extractor import extract_clean_markdown
@pytest.mark.parametrize(
'style',
[
'display: none',
'Display: None',
'DISPLAY:\tNONE',
'color: red; display : none !important',
],
)
async def test_hidden_code_inline_style_is_case_insensitive(browser_session, style: str):
html = f'<main>visible control</main><code id="snippet" style="{style}">hidden state payload</code>'
await browser_session.navigate_to('data:text/html,' + quote(html))
page = await browser_session.get_current_page()
assert page is not None
computed_display = await page.evaluate("() => getComputedStyle(document.querySelector('#snippet')).display")
markdown, _ = await extract_clean_markdown(browser_session=browser_session)
assert computed_display == 'none'
assert 'visible control' in markdown
assert 'hidden state payload' not in markdown