Spaces:
Sleeping
Sleeping
File size: 8,271 Bytes
d7b3d84 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
#!/usr/bin/env python3
"""Test frame hierarchy for any URL passed as argument."""
import asyncio
import sys
from browser_use.browser import BrowserSession
from browser_use.browser.events import BrowserStartEvent
from browser_use.browser.profile import BrowserProfile
async def analyze_frame_hierarchy(url):
"""Analyze and display complete frame hierarchy for a URL."""
profile = BrowserProfile(headless=True, user_data_dir=None)
session = BrowserSession(browser_profile=profile)
try:
print('π Starting browser...')
await session.on_BrowserStartEvent(BrowserStartEvent())
print(f'π Navigating to: {url}')
await session._cdp_navigate(url)
await asyncio.sleep(3)
print('\n' + '=' * 80)
print('FRAME HIERARCHY ANALYSIS')
print('=' * 80)
# Get all targets
targets = await session.cdp_client.send.Target.getTargets()
all_targets = targets.get('targetInfos', [])
# Separate by type
page_targets = [t for t in all_targets if t.get('type') == 'page']
iframe_targets = [t for t in all_targets if t.get('type') == 'iframe']
print('\nπ Target Summary:')
print(f' Total targets: {len(all_targets)}')
print(f' Page targets: {len(page_targets)}')
print(f' Iframe targets (OOPIFs): {len(iframe_targets)}')
# Show all targets
print('\nπ All Targets:')
for i, target in enumerate(all_targets):
t_type = target.get('type')
t_url = target.get('url', 'none')
t_id = target.get('targetId', 'unknown')
if t_type in ['page', 'iframe']:
print(f'\n [{i + 1}] Type: {t_type}')
print(f' URL: {t_url}')
print(f' Target ID: {t_id[:30]}...')
print(f' Attached: {target.get("attached", False)}')
# Get main page frame tree
main_target = next((t for t in page_targets if url in t.get('url', '')), page_targets[0] if page_targets else None)
if main_target:
print('\nπ Main Page Frame Tree:')
print(f' Target: {main_target["url"]}')
print(f' Target ID: {main_target["targetId"][:30]}...')
s = await session.cdp_client.send.Target.attachToTarget(params={'targetId': main_target['targetId'], 'flatten': True})
sid = s['sessionId']
try:
await session.cdp_client.send.Page.enable(session_id=sid)
tree = await session.cdp_client.send.Page.getFrameTree(session_id=sid)
print('\n Frame Tree Structure:')
def print_tree(node, indent=0, parent_id=None):
frame = node['frame']
frame_id = frame.get('id', 'unknown')
frame_url = frame.get('url', 'none')
prefix = ' ' * indent + ('ββ ' if indent > 0 else '')
print(f'{prefix}Frame: {frame_url}')
print(f'{" " * (indent + 1)}ID: {frame_id[:30]}...')
if parent_id:
print(f'{" " * (indent + 1)}Parent: {parent_id[:30]}...')
# Check cross-origin status
cross_origin = frame.get('crossOriginIsolatedContextType', 'unknown')
if cross_origin != 'NotIsolated':
print(f'{" " * (indent + 1)}β οΈ Cross-Origin: {cross_origin}')
# Process children
for child in node.get('childFrames', []):
print_tree(child, indent + 1, frame_id)
print_tree(tree['frameTree'])
finally:
await session.cdp_client.send.Target.detachFromTarget(params={'sessionId': sid})
# Show iframe target trees
if iframe_targets:
print('\nπΈ OOPIF Target Frame Trees:')
for iframe_target in iframe_targets:
print(f'\n OOPIF Target: {iframe_target["url"]}')
print(f' Target ID: {iframe_target["targetId"][:30]}...')
s = await session.cdp_client.send.Target.attachToTarget(
params={'targetId': iframe_target['targetId'], 'flatten': True}
)
sid = s['sessionId']
try:
await session.cdp_client.send.Page.enable(session_id=sid)
tree = await session.cdp_client.send.Page.getFrameTree(session_id=sid)
frame = tree['frameTree']['frame']
print(f' Frame ID: {frame.get("id", "unknown")[:30]}...')
print(f' Frame URL: {frame.get("url", "none")}')
print(' β οΈ This frame runs in a separate process (OOPIF)')
except Exception as e:
print(f' Error: {e}')
finally:
await session.cdp_client.send.Target.detachFromTarget(params={'sessionId': sid})
# Now show unified view from get_all_frames
print('\n' + '=' * 80)
print('UNIFIED FRAME HIERARCHY (get_all_frames method)')
print('=' * 80)
all_frames, target_sessions = await session.get_all_frames()
# Clean up sessions
for tid, sess_id in target_sessions.items():
try:
await session.cdp_client.send.Target.detachFromTarget(params={'sessionId': sess_id})
except Exception:
pass
print('\nπ Frame Statistics:')
print(f' Total frames discovered: {len(all_frames)}')
# Separate root and child frames
root_frames = []
child_frames = []
for frame_id, frame_info in all_frames.items():
if not frame_info.get('parentFrameId'):
root_frames.append((frame_id, frame_info))
else:
child_frames.append((frame_id, frame_info))
print(f' Root frames: {len(root_frames)}')
print(f' Child frames: {len(child_frames)}')
# Display all frames with details
print('\nπ All Frames:')
for i, (frame_id, frame_info) in enumerate(all_frames.items()):
url = frame_info.get('url', 'none')
parent = frame_info.get('parentFrameId')
target_id = frame_info.get('frameTargetId', 'unknown')
is_cross = frame_info.get('isCrossOrigin', False)
print(f'\n [{i + 1}] Frame URL: {url}')
print(f' Frame ID: {frame_id[:30]}...')
print(f' Parent Frame ID: {parent[:30] + "..." if parent else "None (ROOT)"}')
print(f' Target ID: {target_id[:30]}...')
print(f' Cross-Origin: {is_cross}')
# Highlight problems
if not parent and 'v0-simple-landing' in url:
print(' β PROBLEM: Cross-origin frame incorrectly marked as root!')
elif not parent and url != 'about:blank' and url not in ['chrome://newtab/', 'about:blank']:
# Check if this should be the main frame
if any(url in t.get('url', '') for t in page_targets):
print(' β
Correctly identified as root frame')
if is_cross:
print(' πΈ This is a cross-origin frame (OOPIF)')
# Show parent-child relationships
print('\nπ³ Frame Relationships:')
# Build a tree structure
def print_frame_tree(frame_id, frame_info, indent=0, visited=None):
if visited is None:
visited = set()
if frame_id in visited:
return
visited.add(frame_id)
url = frame_info.get('url', 'none')
prefix = ' ' * indent + ('ββ ' if indent > 0 else '')
print(f'{prefix}{url[:60]}...')
print(f'{" " * (indent + 1)}[{frame_id[:20]}...]')
# Find children
for child_id, child_info in all_frames.items():
if child_info.get('parentFrameId') == frame_id:
print_frame_tree(child_id, child_info, indent + 1, visited)
# Print trees starting from roots
for frame_id, frame_info in root_frames:
print('\n Tree starting from root:')
print_frame_tree(frame_id, frame_info)
print('\n' + '=' * 80)
print('β
Analysis complete!')
print('=' * 80)
except Exception as e:
print(f'β Error: {e}')
import traceback
traceback.print_exc()
finally:
# Stop the CDP client first before killing the browser
print('\nπ Shutting down...')
# Close CDP connection first while browser is still alive
if session._cdp_client_root:
try:
await session._cdp_client_root.stop()
except Exception:
pass # Ignore errors if already disconnected
# Then stop the browser process
from browser_use.browser.events import BrowserStopEvent
stop_event = session.event_bus.dispatch(BrowserStopEvent())
try:
await asyncio.wait_for(stop_event, timeout=2.0)
except TimeoutError:
print('β οΈ Browser stop timed out')
def main():
if len(sys.argv) != 2:
print('Usage: python test_frame_hierarchy.py <URL>')
print('\nExample URLs to test:')
print(' https://v0-website-with-clickable-elements.vercel.app/nested-iframe')
print(' https://v0-website-with-clickable-elements.vercel.app/cross-origin')
print(' https://v0-website-with-clickable-elements.vercel.app/shadow-dom')
sys.exit(1)
url = sys.argv[1]
asyncio.run(analyze_frame_hierarchy(url))
# Ensure clean exit
print('β
Script completed')
sys.exit(0)
if __name__ == '__main__':
main()
|