Skip to content

Commit df9c26c

Browse files
authored
make iframe depth variable (#3026)
Auto-generated PR for: make iframe depth variable <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Made iframe processing limits configurable and removed hard-coded caps. Defaults now allow up to 100 iframes and 5 levels of depth to capture more content while still preventing crashes. - **New Features** - Added BrowserProfile.max_iframes and BrowserProfile.max_iframe_depth. - Threaded settings through BrowserSession and DOM watchdog to DomService. - DomService uses these values to cap snapshot documents and recursion, with warnings when limiting. <!-- End of auto-generated description by cubic. -->
2 parents edd5ee8 + 744dd53 commit df9c26c

File tree

4 files changed

+25
-9
lines changed

4 files changed

+25
-9
lines changed

browser_use/browser/profile.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,15 @@ class BrowserProfile(BrowserConnectArgs, BrowserLaunchPersistentContextArgs, Bro
590590
default=True,
591591
description='Enable cross-origin iframe support (OOPIF/Out-of-Process iframes). When False, only same-origin frames are processed to avoid complexity and hanging.',
592592
)
593+
max_iframes: int = Field(
594+
default=100,
595+
description='Maximum number of iframe documents to process to prevent crashes.',
596+
)
597+
max_iframe_depth: int = Field(
598+
ge=0,
599+
default=5,
600+
description='Maximum depth for cross-origin iframe recursion (default: 5 levels deep).',
601+
)
593602

594603
# --- Page load/wait timings ---
595604

browser_use/browser/session.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ def __init__(
270270
cross_origin_iframes: bool | None = None,
271271
highlight_elements: bool | None = None,
272272
paint_order_filtering: bool | None = None,
273+
# Iframe processing limits
274+
max_iframes: int | None = None,
275+
max_iframe_depth: int | None = None,
273276
):
274277
# Following the same pattern as AgentSettings in service.py
275278
# Only pass non-None values to avoid validation errors

browser_use/browser/watchdogs/dom_watchdog.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ async def _build_dom_tree_without_highlights(self, previous_state: SerializedDOM
361361
logger=self.logger,
362362
cross_origin_iframes=self.browser_session.browser_profile.cross_origin_iframes,
363363
paint_order_filtering=self.browser_session.browser_profile.paint_order_filtering,
364+
max_iframes=self.browser_session.browser_profile.max_iframes,
365+
max_iframe_depth=self.browser_session.browser_profile.max_iframe_depth,
364366
)
365367

366368
# Get serialized DOM tree using the service

browser_use/dom/service.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@
2727
if TYPE_CHECKING:
2828
from browser_use.browser.session import BrowserSession
2929

30-
# Global limits to prevent iframe explosion
31-
MAX_TOTAL_IFRAMES = 3 # Maximum number of iframe documents to process (very conservative to prevent explosions)
32-
MAX_IFRAME_DEPTH = 1 # Maximum depth for cross-origin iframe recursion (only 1 level deep)
30+
# Note: iframe limits are now configurable via BrowserProfile.max_iframes and BrowserProfile.max_iframe_depth
3331

3432

3533
class DomService:
@@ -49,11 +47,15 @@ def __init__(
4947
logger: logging.Logger | None = None,
5048
cross_origin_iframes: bool = False,
5149
paint_order_filtering: bool = True,
50+
max_iframes: int = 100,
51+
max_iframe_depth: int = 5,
5252
):
5353
self.browser_session = browser_session
5454
self.logger = logger or browser_session.logger
5555
self.cross_origin_iframes = cross_origin_iframes
5656
self.paint_order_filtering = paint_order_filtering
57+
self.max_iframes = max_iframes
58+
self.max_iframe_depth = max_iframe_depth
5759

5860
async def __aenter__(self):
5961
return self
@@ -419,12 +421,12 @@ def create_dom_tree_request():
419421
# DEBUG: Log snapshot info and limit documents to prevent explosion
420422
if snapshot and 'documents' in snapshot:
421423
original_doc_count = len(snapshot['documents'])
422-
# Limit to MAX_TOTAL_IFRAMES documents to prevent iframe explosion
423-
if original_doc_count > MAX_TOTAL_IFRAMES:
424+
# Limit to max_iframes documents to prevent iframe explosion
425+
if original_doc_count > self.max_iframes:
424426
self.logger.warning(
425-
f'⚠️ Limiting processing of {original_doc_count} iframes on page to only first {MAX_TOTAL_IFRAMES} to prevent crashes!'
427+
f'⚠️ Limiting processing of {original_doc_count} iframes on page to only first {self.max_iframes} to prevent crashes!'
426428
)
427-
snapshot['documents'] = snapshot['documents'][:MAX_TOTAL_IFRAMES]
429+
snapshot['documents'] = snapshot['documents'][: self.max_iframes]
428430

429431
total_nodes = sum(len(doc.get('nodes', [])) for doc in snapshot['documents'])
430432
self.logger.debug(f'🔍 DEBUG: Snapshot contains {len(snapshot["documents"])} frames with {total_nodes} total nodes')
@@ -641,9 +643,9 @@ async def _construct_enhanced_node(
641643
self.cross_origin_iframes and node['nodeName'].upper() == 'IFRAME' and node.get('contentDocument', None) is None
642644
): # None meaning there is no content
643645
# Check iframe depth to prevent infinite recursion
644-
if iframe_depth >= MAX_IFRAME_DEPTH:
646+
if iframe_depth >= self.max_iframe_depth:
645647
self.logger.debug(
646-
f'Skipping iframe at depth {iframe_depth} to prevent infinite recursion (max depth: {MAX_IFRAME_DEPTH})'
648+
f'Skipping iframe at depth {iframe_depth} to prevent infinite recursion (max depth: {self.max_iframe_depth})'
647649
)
648650
else:
649651
# Check if iframe is visible and large enough (>= 200px in both dimensions)

0 commit comments

Comments
 (0)