附著與合成器層坐標(biāo)點擊)
Browser Harness 跨源 iframe 操控指南iframe_target 目標(biāo)附著與合成器層坐標(biāo)點擊【免費下載鏈接】browser-harnessBrowser Harness | Self-healing harness that enables LLMs to complete any task.項目地址: https://gitcode.com/gh_mirrors/br/browser-harness導(dǎo)讀本文聚焦 Browser Harnessbrowser-harness處理跨源 iframe 的核心方法論通過iframe_target(...)解析出 iframe 的 CDP Target借助js(..., target_id...)完成目標(biāo)附著target attachment并在 DOM 層面無法直接操作時改用合成器層compositor-level坐標(biāo)點擊。讀完本文你將掌握跨源 iframe 場景下何時走 DOM、何時走坐標(biāo)的取舍原則以及從 helpers.py 到真實領(lǐng)域技能CentileBrain、Shopify Admin的完整調(diào)用鏈。關(guān)聯(lián)文檔 interaction-skills/cross-origin-iframes.md 定義了三條鐵律聚焦iframe_target(...)、理解目標(biāo)附著、認(rèn)識到合成器層坐標(biāo)點擊在跨目標(biāo) DOM 工作受阻時更低摩擦。本文沿此骨架結(jié)合源碼縱深展開。為什么跨源 iframe 是獨立的關(guān)卡同源與跨源的本質(zhì)差異瀏覽器安全模型決定了對 iframe 的兩種截然不同的處理方式同源 iframe父頁面可以通過contentDocument/contentWindow直接穿透訪問內(nèi)部 DOM這也是 interaction-skills/iframes.md 講述的路徑——直接遍歷 frame 內(nèi)部文檔??缭?iframe父頁面的 JavaScript 無法觸碰其 DOMcontentDocument返回nullcontentWindow上的屬性訪問被SecurityError攔截。此時任何從父頁面 JS 注入查詢的方案都會失敗。Browser Harness 的答案不是繞過安全模型而是繞過父頁面把 iframe 當(dāng)作一個獨立的 CDP Target 來附著直接在目標(biāo)上下文中執(zhí)行 JavaScript。這正是iframe_target(...)存在的意義。設(shè)計約束不要發(fā)明跨 frame 的 JS 遍歷器項目主文檔 src/browser_harness/SKILL.md 的 Gotchas 一節(jié)明確要求 Agent Do not invent aRuntime.evaluatescroll replacement or a cross-frame JS walker。換句話說遇到跨源 iframe 時正確姿勢是使用框架內(nèi)置的iframe_target() target 附著機(jī)制而不是嘗試在父頁面寫一個遞歸遍歷所有 frame 的 JS 腳本這在跨源下本就行不通。iframe_target()從 CDP Target 列表中定位 iframe源碼實現(xiàn)iframe_target()定義在 src/browser_harness/helpers.pydef iframe_target(url_substr): First iframe target whose URL contains url_substr. Use with js(..., target_id...). for t in cdp(Target.getTargets)[targetInfos]: if t[type] iframe and url_substr in t.get(url, ): return t[targetId] return None其工作原理調(diào)用 CDP 命令Target.getTargets通過 cdp() 封裝返回result中的targetInfos。遍歷所有 target 信息篩選type iframe且 URL 中包含url_substr的目標(biāo)。返回第一個匹配的targetId無匹配時返回None。入?yún)⒃O(shè)計為什么用 URL 子串匹配跨源 iframe 沒有可用的 CSS 選擇器父頁面 DOM 訪問不到因此定位依據(jù)必須是瀏覽器層可見的屬性而 URL 是最穩(wěn)定、最具辨識度的特征。典型用法是傳 iframe 加載的應(yīng)用路徑片段例如CentileBrain 的 Shiny 應(yīng)用iframe_target(shinyapps.io/SV-MALE)—— 定位子皮層體積Subcortical Volume男性模態(tài)的 iframeShopify Admin 的嵌入應(yīng)用iframe_target(qa-pairs-app)—— 定位 Knowledge Base 應(yīng)用 iframe。返回值的兩種形態(tài)命中返回字符串形式的targetId可原樣傳給js(..., target_id...)未命中返回None。調(diào)用方應(yīng)據(jù)此判斷 iframe 尚未渲染完成或 URL 匹配錯誤再決定重試或放棄。目標(biāo)附著js(..., target_id...) 的完整生命周期調(diào)用鏈剖析js()定義在 src/browser_harness/helpers.py是目標(biāo)附著的核心入口def js(expression, target_idNone): Run JS in the attached tab (default) or inside an iframe target (via iframe_target()). sid cdp(Target.attachToTarget, targetIdtarget_id, flattenTrue)[sessionId] if target_id else None try: result _js_evaluate(expression, sid) except BaseException: if sid: try: _detach_iframe_session(sid) except BaseException: pass raise if sid: _detach_iframe_session(sid) return result一次完整的跨源 iframe JS 執(zhí)行包含四個階段附著attach調(diào)用Target.attachToTarget(targetIdtarget_id, flattenTrue)獲得一個專屬的sessionId。flattenTrue使該 session 的消息走扁平化協(xié)議與 daemon 的 IPC 模型兼容。這是目標(biāo)附著的字面含義——在瀏覽器內(nèi)部把執(zhí)行上下文切換到目標(biāo) iframe。求值evaluate_js_evaluate(expression, sid)內(nèi)部走 Runtime.evaluate帶session_idsid把腳本投遞到 iframe 的 JS 上下文。表達(dá)式先原樣求值若 Chrome 報 Illegal return statement則用(function(){...})()包裹后重試見 helpers.py因此document.title和const x 1; return x兩種寫法都可用。異常清理求值拋錯時仍會嘗試 detach避免 session 泄漏且 detach 失敗不會覆蓋原始求值錯誤。分離detach正常路徑也會調(diào)用_detach_iframe_session(sid)釋放 session。為什么每次調(diào)用都要 detach_detach_iframe_session()的文檔字符串helpers.py解釋了原因防止輪詢循環(huán)每次調(diào)用累積一個存活 session及其事件流。Agent 在 iframe 內(nèi)等待元素出現(xiàn)時常寫 15 次輪詢?nèi)舨患皶r分離session 會越積越多。實現(xiàn)上調(diào)用Target.detachFromTarget(sessionIdsid)并寬容處理 Chrome 已因 iframe 導(dǎo)航或關(guān)閉而丟棄 session 的情況no session with given id 視為正常不拋錯保證iframe 已不在時不是泄漏。落到真實調(diào)用形態(tài)在 agent-workspace/domain-skills/shopify-admin/polaris-inputs.md 的 add-FAQ 模式中附著后的典型用法是from helpers import iframe_target, js, type_text, page_info, screenshot tid iframe_target(qa-pairs-app) # 1. 解析 iframe target for _ in range(15): # 2. 輪詢等表單渲染完成 ready js((() { const i Array.from(document.querySelectorAll(input[typetext], input:not([type]))) .find(x { const r x.getBoundingClientRect(); return r.width 100; }); const t Array.from(document.querySelectorAll(textarea)) .find(x { const r x.getBoundingClientRect(); return r.width 100; }); if (i t) { i.focus(); return true; } return false; })(), target_idtid) # 3. 附著到 iframe 執(zhí)行 if ready: break time.sleep(0.3) type_text(question) # 4. 焦點已在 iframe 內(nèi)直接輸入注意第 3 步選擇器input、textarea是在iframe 自己的文檔里查詢的而不是父頁面——這正是目標(biāo)附著的價值。合成器層坐標(biāo)點擊跨目標(biāo) DOM 工作的低摩擦替代核心原理項目設(shè)計約束src/browser_harness/SKILL.md原話是Coordinate clicks default. CDP mouse events pass through iframes/shadow/cross-origin at the compositor level.即 CDP 的鼠標(biāo)事件是在合成器層派發(fā)的天然穿透 iframe、Shadow DOM、跨源邊界。當(dāng)目標(biāo)元素在跨源 iframe 內(nèi)、且無法或不便用 JS 操作時直接用頁面坐標(biāo)系計算坐標(biāo)并派發(fā)鼠標(biāo)事件就能物理點擊到 iframe 里的元素。實現(xiàn)click_at_xy()坐標(biāo)點擊的底層實現(xiàn)在 src/browser_harness/helpers.pydef click_at_xy(x, y, buttonleft, clicks1): cdp(Input.dispatchMouseEvent, typemousePressed, xx, yy, buttonbutton, clickCountclicks) cdp(Input.dispatchMouseEvent, typemouseReleased, xx, yy, buttonbutton, clickCountclicks)它連續(xù)派發(fā)mousePressed與mouseReleased兩個事件完成一次點擊可指定buttonleft/right/middle與clicks連擊次數(shù)。坐標(biāo)(x, y)為頁面視口坐標(biāo)CSS 像素。這里必須強(qiáng)調(diào) frame-local 與 page 坐標(biāo)的換算陷阱iframe 內(nèi)部元素調(diào)用getBoundingClientRect()得到的是相對于 iframe 視口的坐標(biāo)而click_at_xy()需要的是相對于頂層頁面的坐標(biāo)。跨源 iframe 在父頁面中的偏移可通過父頁面 JS 讀取 iframe 元素自身的位置父頁面能讀到 iframe 標(biāo)簽的布局信息獲得然后做加法換算。這也是 interaction-skills/iframes.md 要求keep the frame-local versus page-coordinate warning explicit的原因。何時坐標(biāo)點擊優(yōu)于跨目標(biāo) DOM 工作場景首選方案理由需要讀 iframe 內(nèi)部狀態(tài)值、文本、屬性js(..., target_idiframe_target(...))DOM 讀取必須附著到目標(biāo)上下文需要觸發(fā)框架事件React/Vue 受控組件js(target_id...)fill_input系輔助坐標(biāo)點擊不產(chǎn)生框架可見的合成事件只差點一下按鈕/鏈接無需讀取結(jié)果click_at_xy(x, y)合成器層穿透跨源邊界零 DOM 依賴、零 session 開銷元素在 Shadow DOM 深層且跨源坐標(biāo)點擊合成器層同樣穿透 shadow 邊界核心取舍需要讀就附著 target 走 DOM只需要點就坐標(biāo)點擊。前者每次調(diào)用都有 attach/detach 與 JS 求值成本后者只是兩次鼠標(biāo)事件派發(fā)因此更低摩擦lower-friction。真實場景復(fù)盤兩個跨源 iframe 實戰(zhàn)案例CentileBrain多模態(tài) Shiny iframe 切換agent-workspace/domain-skills/centilebrain/generate-estimates.md 記錄了一個教科書級場景https://centilebrain.org/#/model主頁面只是薄殼真正的工作界面是centilebrain-app.shinyapps.io/{SV|CT|SA}-{MALE|FEMALE}/三個模態(tài) × 兩種性別的Shiny iframe。上傳表單、Compute 按鈕、下載鏈接全部在 iframe 內(nèi)部且切換模態(tài) 換 iframe src。因此t iframe_target(shinyapps.io/SV-MALE) # 定位對應(yīng)模態(tài)的 iframe再配合upload_file(..., target_id...)這類 iframe 感知輔助函數(shù)完成任務(wù)。該文檔還警告了一個關(guān)鍵坑詳見下節(jié)。Shopify Admin嵌入應(yīng)用Embedded App面板Shopify 的嵌入式應(yīng)用同樣通過跨源 iframe 承載embedded-apps.md 與 knowledge-base.md 展示了標(biāo)準(zhǔn)套路tid iframe_target(qa-pairs-app)或online-store-web后一切針對面板內(nèi)部元素的操作都帶target_idtid執(zhí)行。實戰(zhàn)注意事項target_id 會隨導(dǎo)航失效CentileBrain 技能文檔明確指出模態(tài)切換后舊 target_id 立即失效。原因在于 iframe 導(dǎo)航會使其對應(yīng)的 CDP Target 被瀏覽器銷毀重建舊targetId指向已不存在的目標(biāo)。即便Target.getTargets短暫仍列出舊條目附著也會失敗。正確做法是點擊切換按鈕如CORTICAL THICKNESStime.sleep(2-3)等待新 iframe 加載重新調(diào)用iframe_target(shinyapps.io/CT-MALE)獲取新 target_id。未命中返回 None 的處理iframe_target()找不到匹配時返回None。此時若直接傳入js(..., target_idNone)會退化為在當(dāng)前已附著標(biāo)簽頁執(zhí)行 JS——這可能靜默執(zhí)行到錯誤上下文。因此在真實技能中應(yīng)顯式輪詢?nèi)?15 次 × 0.3s確認(rèn)tid非空再繼續(xù)。注意聚焦限制合成器層點擊依賴視口坐標(biāo)因此目標(biāo)元素必須先滾動進(jìn)視口參考 interaction-skills/scrolling.md。若頁面因隱藏而暫停background tab 的節(jié)流可臨時用cdp(Emulation.setFocusEmulationEnabled, enabledTrue)操作完成后在finally中關(guān)閉而不是把 Chrome 帶到前臺SKILL.md。小結(jié)跨源 iframe 的三個決策點定位一律用iframe_target(url_substr)按 URL 子串從Target.getTargets解析 targetId禁止嘗試跨 frame JS 遍歷SKILL.md 明確禁止。執(zhí)行需要讀寫 iframe 內(nèi)部 DOM 時用js(expression, target_idtid)附著執(zhí)行js()內(nèi)部自動完成 attach → evaluate → detach 的生命周期且 detach 是防泄漏的必要環(huán)節(jié)helpers.py。點擊只需觸發(fā)交互時優(yōu)先click_at_xy(x, y)走合成器層鼠標(biāo)事件它天然穿透跨源邊界務(wù)必把 frame 內(nèi)getBoundingClientRect()得到的局部坐標(biāo)換算為頂層頁面坐標(biāo)。掌握這三步Browser Harness 就能穩(wěn)定駕馭支付回調(diào)頁、嵌入式管理后臺、Shiny 數(shù)據(jù)分析面板等典型跨源 iframe 應(yīng)用——這是純 DOM 自動化工具難以企及的邊界?!久赓M下載鏈接】browser-harnessBrowser Harness | Self-healing harness that enables LLMs to complete any task.項目地址: https://gitcode.com/gh_mirrors/br/browser-harness創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考