Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions crawl4ai/browser_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ def build_browser_flags(config: BrowserConfig) -> List[str]:
"--force-color-profile=srgb",
"--mute-audio",
"--disable-background-timer-throttling",
# Memory-saving flags: disable unused Chrome features
"--disable-features=OptimizationHints,MediaRouter,DialMediaRouteProvider",
# Memory-saving flags: disable unused Chrome features.
# Do not disable OptimizationHints — it SEGV_ACCERRs Chrome-for-Testing
# under --headless=new on macOS arm64 (issue #2239).
"--disable-features=MediaRouter,DialMediaRouteProvider",
"--disable-component-update",
"--disable-domain-reliability",
]
Expand Down Expand Up @@ -1102,8 +1104,10 @@ def _build_browser_args(self) -> dict:
"--force-color-profile=srgb",
"--mute-audio",
"--disable-background-timer-throttling",
# Memory-saving flags: disable unused Chrome features
"--disable-features=OptimizationHints,MediaRouter,DialMediaRouteProvider",
# Memory-saving flags: disable unused Chrome features.
# Do not disable OptimizationHints — it SEGV_ACCERRs Chrome-for-Testing
# under --headless=new on macOS arm64 (issue #2239).
"--disable-features=MediaRouter,DialMediaRouteProvider",
"--disable-component-update",
"--disable-domain-reliability",
# "--single-process",
Expand Down
20 changes: 14 additions & 6 deletions tests/async/test_browser_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,11 @@ async def test_memory_saving_flags_applied(test_server):
assert any("max-old-space-size" in a for a in args_list), (
"memory_saving_mode=True should add V8 heap cap"
)
# Always-on flags should be present regardless
assert any("OptimizationHints" in a for a in args_list)
# Always-on flags should be present regardless.
# OptimizationHints must stay off the disable-list (issue #2239).
assert any("MediaRouter" in a for a in args_list)
assert any("DialMediaRouteProvider" in a for a in args_list)
assert not any("OptimizationHints" in a for a in args_list)

async with AsyncWebCrawler(config=config_off) as crawler:
bm = _bm(crawler)
Expand All @@ -201,7 +204,9 @@ async def test_memory_saving_flags_applied(test_server):
"memory_saving_mode=False should NOT add V8 heap cap"
)
# Always-on flags should still be there
assert any("OptimizationHints" in a for a in args_list)
assert any("MediaRouter" in a for a in args_list)
assert any("DialMediaRouteProvider" in a for a in args_list)
assert not any("OptimizationHints" in a for a in args_list)


# ===========================================================================
Expand All @@ -210,15 +215,18 @@ async def test_memory_saving_flags_applied(test_server):

@pytest.mark.asyncio
async def test_always_on_flags_present(test_server):
"""The 3 always-on memory flags should appear in _build_browser_args
even with default BrowserConfig."""
"""The always-on memory flags should appear in _build_browser_args
even with default BrowserConfig. OptimizationHints must not be disabled
(issue #2239 / Chrome-for-Testing SEGV_ACCERR under --headless=new)."""
config = BrowserConfig(headless=True, verbose=False)
async with AsyncWebCrawler(config=config) as crawler:
browser_args = _bm(crawler)._build_browser_args()
args_list = browser_args.get("args", browser_args) if isinstance(browser_args, dict) else browser_args
assert any("disable-component-update" in a for a in args_list)
assert any("disable-domain-reliability" in a for a in args_list)
assert any("OptimizationHints" in a for a in args_list)
assert any("MediaRouter" in a for a in args_list)
assert any("DialMediaRouteProvider" in a for a in args_list)
assert not any("OptimizationHints" in a for a in args_list)


# ===========================================================================
Expand Down
45 changes: 45 additions & 0 deletions tests/regression/test_reg_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,51 @@ async def test_browser_context_manager(local_server):
# If we get here without exception, cleanup succeeded


# ---------------------------------------------------------------------------
# Launch flags
# ---------------------------------------------------------------------------


def _disable_feature_names(args):
"""Every name listed in any --disable-features switch (not last-wins)."""
names = []
for arg in args:
if arg.startswith("--disable-features="):
names.extend(
name.strip()
for name in arg.split("=", 1)[1].split(",")
if name.strip()
)
return names


def test_disable_features_omits_optimization_hints():
"""Issue #2239: OptimizationHints in --disable-features crashes
Chrome-for-Testing under --headless=new on macOS arm64 (SEGV_ACCERR).

MediaRouter and DialMediaRouteProvider must remain. #2219 merges
repeated --disable-features lists and previously asserted
OptimizationHints as a default that must survive; after this fix the
expected defaults are MediaRouter and DialMediaRouteProvider only.
"""
from crawl4ai.browser_manager import BrowserManager, ManagedBrowser

for light_mode in (False, True):
config = BrowserConfig(headless=True, light_mode=light_mode)
for flags in (
ManagedBrowser.build_browser_flags(config),
BrowserManager(browser_config=config)._build_browser_args()["args"],
):
names = _disable_feature_names(flags)
assert "OptimizationHints" not in names, (
f"light_mode={light_mode}: OptimizationHints must not be disabled"
)
for expected in ("MediaRouter", "DialMediaRouteProvider"):
assert expected in names, (
f"light_mode={light_mode} lost {expected}"
)


# ---------------------------------------------------------------------------
# Viewport configuration
# ---------------------------------------------------------------------------
Expand Down
59 changes: 59 additions & 0 deletions tests/unit/test_browser_disable_features.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Issue #2239: OptimizationHints must not appear in built --disable-features.

Chrome-for-Testing 151+ SEGV_ACCERRs under --headless=new on macOS arm64 when
OptimizationHints is disabled. MediaRouter and DialMediaRouteProvider stay.

These are flag-construction checks only — no browser or network required.

#2219 merges repeated --disable-features switches and currently expects
OptimizationHints to survive that merge. After this change the surviving
defaults are MediaRouter and DialMediaRouteProvider only.
"""

from crawl4ai import BrowserConfig
from crawl4ai.browser_manager import BrowserManager, ManagedBrowser


def _disable_feature_names(args):
"""Every name listed in any --disable-features switch (not last-wins)."""
names = []
for arg in args:
if arg.startswith("--disable-features="):
names.extend(
name.strip()
for name in arg.split("=", 1)[1].split(",")
if name.strip()
)
return names


def test_build_browser_flags_omits_optimization_hints():
config = BrowserConfig(headless=True)
names = _disable_feature_names(ManagedBrowser.build_browser_flags(config))
assert "OptimizationHints" not in names
assert "MediaRouter" in names
assert "DialMediaRouteProvider" in names


def test_build_browser_args_omits_optimization_hints():
config = BrowserConfig(headless=True)
args = BrowserManager(browser_config=config)._build_browser_args()["args"]
names = _disable_feature_names(args)
assert "OptimizationHints" not in names
assert "MediaRouter" in names
assert "DialMediaRouteProvider" in names


def test_light_mode_still_omits_optimization_hints():
"""light_mode appends BROWSER_DISABLE_OPTIONS; OptimizationHints must
not reappear there either. Aligns with #2219's merge expectations:
MediaRouter / DialMediaRouteProvider remain the crawl4ai defaults."""
config = BrowserConfig(headless=True, light_mode=True)
for flags in (
ManagedBrowser.build_browser_flags(config),
BrowserManager(browser_config=config)._build_browser_args()["args"],
):
names = _disable_feature_names(flags)
assert "OptimizationHints" not in names
assert "MediaRouter" in names
assert "DialMediaRouteProvider" in names