# agent にクロスオリジン iframe をクリックさせる：chrome-use がこの難題を突破した話

AI agent にブラウザをつなぐと、{最初|さいしょ}はだいたい{順調|じゅんちょう}です。ページを{開|ひら}く、{内容|ないよう}を{読|よ}む、{検索|けんさく}ボックスに{入力|にゅうりょく}する。{本当|ほんとう}に{人|ひと}を{詰|つ}まらせるのは、**クロスオリジン iframe の{中|なか}に{隠|かく}れたフォーム**です——Google Payments の{受取|うけとり}{情報|じょうほう}、さまざまな{決済|けっさい}コンポーネント、KYC コントロール。agent はその{中|なか}の{文字|もじ}を{読|よ}めるし、{値|あたい}も{入力|にゅうりょく}できる。**でも、あの「保存」ボタンだけはクリックできない**。{読|よ}めるのに、{完了|かんりょう}できない。

![agent が「{窓|まど}の{中|なか}の{窓|まど}」に{手|て}を{伸|の}ばしてクリックしようとするが、{空振|からぶ}りする](https://img.leeguoo.com/media/398a3962-c4fd-4f9a-bf8d-ae19aa2dd257/hero.png)

この記事は、この{壁|かべ}を{乗|の}り{越|こ}えた{過程|かてい}の{記録|きろく}です。{主役|しゅやく}は [chrome-use](https://github.com/leeguooooo/chrome-use)——Rust で{書|か}かれた、agent {向|む}けのブラウザ{自動化|じどうか} CLI です。Playwright も headless も{使|つか}わず、あなたが**{実際|じっさい}にログインしている**その Chrome を{直接|ちょくせつ}{操作|そうさ}します。

プロジェクトページ：https://chrome-use.leeguoo.com/

## なぜクロスオリジン iframe はこんなに{難|むずか}しいのか

{普通|ふつう}のページなら{簡単|かんたん}です。アクセシビリティツリーを{取得|しゅとく}し、{要素|ようそ}の{参照|さんしょう}を{取|と}り、クリックすれば{終|お}わりです。しかしクロスオリジン iframe（たとえば `adsense.google.com` のページに `payments.google.com` の iframe が{埋|う}め{込|こ}まれている{場合|ばあい}）では、{一度|いちど}に3つの{地雷|じらい}を{踏|ふ}みます。

1. **セレクタが{中|なか}に{入|はい}れない**。{同一|どういつ}オリジンポリシーのもとでは、{外側|そとがわ}のドキュメントで{実行|じっこう}する CSS セレクタや `eval` は、iframe {内部|ないぶ}の DOM に{一切|いっさい}{触|ふ}れられません。ここでは `document.querySelector` は{役|やく}に{立|た}ちません。
2. **スクロールが{外|はず}れる**。ページをスクロールしているつもりでも、{実際|じっさい}にスクロールできるのは iframe {内部|ないぶ}のスクロールコンテナです。wheel イベントは{外側|そとがわ}のドキュメントに{送|おく}られ、{中|なか}は{微動|びどう}だにしません——{目標|もくひょう}の{行|ぎょう}はいつまでも「{画面外|がめんがい}」にあり、{見|み}ることすらできません。
3. **{座標|ざひょう}を{推測|すいそく}してクリックするしかなくなる**。{前|まえ}の2つの{問題|もんだい}により、「スクリーンショット + ピクセル{座標|ざひょう}の{推測|すいそく}」へ{戻|もど}らざるを{得|え}ません。これはもっとも{不正確|ふせいかく}で、{隣|となり}のフィールドを{誤|あやま}ってクリックしやすい{方法|ほうほう}です。**{全体|ぜんたい}の{支払|しはら}い{情報|じょうほう}**を{変更|へんこう}するフォームでは、クリック{一回|いっかい}の{間違|まちが}いの{代償|だいしょう}は{小|ちい}さくありません。

## chrome-use の{基盤|きばん}：agent に{渡|わた}すのは HTML ではなく「{参照|さんしょう}」

{突破|とっぱ}{方法|ほうほう}を{話|はな}す{前|まえ}に、まず chrome-use の{基本|きほん}を{説明|せつめい}する{必要|ひつよう}があります。これは、「HTML をモデルに{食|た}べさせる」{派|は}との{根本的|こんぽんてき}な{違|ちが}いでもあります。

![{恐|おそ}ろしい{量|りょう}の HTML を @e1 @e2 @e3 のようなきれいな{参照|さんしょう}に{置|お}き{換|か}える](https://img.leeguoo.com/media/201c5b38-f3ff-4aec-ab93-b26c5d77f494/refs.png)

chrome-use はページのソースコードを agent に{投|な}げません。かわりに**アクセシビリティツリーのスナップショット**を{取得|しゅとく}し、{操作|そうさ}できる{各|かく}{要素|ようそ}に{短|みじか}い{参照|さんしょう}を{付|つ}けます。

```毎
- textbox "メール" [ref=e2]
- listbox "国/地域" [ref=e60]
- button "保存" [ref=e41]
```

agent はその{参照|さんしょう}を{直接|ちょくせつ}{操作|そうさ}します。`fill @e2 "..."`、`click @e41` のように。1ページはだいたい 200–400 token で{済|す}み、DOM ノイズで{画面|がめん}{全体|ぜんたい}が{埋|う}まることはありません。この{参照|さんしょう}{仕組|しく}みこそが、あとで iframe を{貫通|かんつう}できる{前提|ぜんてい}になります——スナップショットが iframe {内部|ないぶ}のノードを「{見|み}られる」なら、{参照|さんしょう}も{取得|しゅとく}できます。

## 3つの{壁|かべ}を{一|ひと}つずつ{越|こ}える

**{第一|だいいち}の{壁|かべ}: スナップショットに iframe {内部|ないぶ}を{見|み}せる。**
アクセシビリティツリーがクロスオリジン iframe を{貫通|かんつう}し、{内部|ないぶ}のノードにも{参照|さんしょう}を{付|つ}けて{持|も}ってこられるようにします。{修正|しゅうせい}すると、`snapshot` が{直接|ちょくせつ}こう{列挙|れっきょ}します。

```毎
- textbox "電話番号 (任意)" [ref=e59]
- listbox "国/地域コード：日本 (+81)" [ref=e60]
```

——セレクタが{入|はい}れない{場所|ばしょ}にも、{参照|さんしょう}は{入|はい}れます。

**{第二|だいに}の{壁|かべ}: スクロールを iframe のスクロールコンテナに{効|き}かせる。**
wheel を{外側|そとがわ}のドキュメントへまとめて{送|おく}るのをやめ、{本当|ほんとう}にスクロールすべきコンテナをスクロールします。これで{下|した}のフォーム{行|ぎょう}がようやく{視野|しや}に{入|はい}り、{参照|さんしょう}も{取|と}れるようになります。

**{第三|だいさん}の{壁|かべ}（もっとも{硬|かた}い）: クロスオリジン iframe {内|ない}の「{有効|ゆうこう}」な{送信|そうしん}ボタンをクリックしても{反応|はんのう}しない。**
この{段階|だんかい}がいちばん{苦|くる}しいところでした。なぜなら、**{見|み}た{目|め}にはすべて{正|ただ}しい**からです。

- {実際|じっさい}のキー{入力|にゅうりょく}で{番号|ばんごう}を{入|い}れた。`get value` で{読|よ}むと、たしかに{入|はい}っている。
- 「保存」ボタンは{有効|ゆうこう}になるべきタイミングで{有効|ゆうこう}になった（{正|ただ}しい{値|あたい}を{入|い}れる{前|まえ}は disabled で、{入力後|にゅうりょくご}に{現|あらわ}れる）。
- そして `click @e41`——フォームは{微動|びどう}だにしない。`find text "保存"`？クロスオリジンなので{取|と}れない。フォーカスして Enter / Space を{押|お}す？それでも{反応|はんのう}なし。

{合|あ}っているのに、どこもかしこも{合|あ}っていない。{根本|こんぽん}{原因|げんいん}は、クロスオリジン iframe {内|ない}の Material/フレームワーク{系|けい}ボタンが、**{合成|ごうせい}クリック**を{受|う}け{付|つ}けないことでした。さらに、`fill` は{入力|にゅうりょく}フィールドの{値|あたい}だけを{変|か}え、フレームワークが{必要|ひつよう}とする `input`/`change` イベントを{発火|はっか}していませんでした。そのためフォーム{側|がわ}は「{変化|へんか}していない」と{思|おも}い、保存ボタンは{無効|むこう}のまま、またはクリックしても{何|なに}も{起|お}きない{状態|じょうたい}になります。

{解決|かいけつ}は2つに{分|わ}かれます。{値|あたい}の{入力|にゅうりょく}は**{実際|じっさい}のキー{入力|にゅうりょく}**に{切|き}り{替|か}える（{各|かく}{文字|もじ}が{本物|ほんもの}のイベントを{発生|はっせい}させるので、フレームワークが{認識|にんしき}する）。クリックは、iframe {内|ない}のコンテンツノードに{対|たい}して、ただ `click()` を{被|かぶ}せるのではなく、**{本物|ほんもの}のマウス/キーボード{起動|きどう}**を{一式|いっしき}{送|おく}る{必要|ひつよう}があります。

## {仕上|しあ}げ: {中|なか}にクリックして、保存する

![パーティーハットをかぶった agent が iframe に{手|て}を{伸|の}ばし、SAVE を{押|お}すことに{成功|せいこう}し、{緑|みどり}のチェックと saved が{表示|ひょうじ}される](https://img.leeguoo.com/media/0b45b5f8-4e78-439c-8017-08335e823848/fixed.png)

3つの{壁|かべ}を{越|こ}えると、{全体|ぜんたい}の{流|なが}れがつながります。**{開|ひら}く → {目標|もくひょう}{行|ぎょう}までスクロール → スナップショットで{参照|さんしょう}を{取得|しゅとく} → {実際|じっさい}のキー{入力|にゅうりょく}で{値|あたい}を{入|い}れる → 保存を{押|お}す**。あの「{読|よ}めるのに、{完了|かんりょう}できない」{詰|つ}みは、ここで{終|お}わりです。

## {同|おな}じように agent ブラウザ{自動化|じどうか}をしている{人|ひと}へ、{血|ち}で{得|え}た{経験|けいけん}

- **まずアクセシビリティ{参照|さんしょう}を{使|つか}い、デフォルトでスクリーンショットの{座標|ざひょう}クリックに{逃|に}げない**。スナップショットが iframe を{見|み}られるなら、{参照|さんしょう}はピクセル{推測|すいそく}よりずっと{安定|あんてい}します。スクリーンショットは canvas/WebGL のように、本当に{構造|こうぞう}がない{場面|ばめん}に{残|のこ}します。
- **クロスオリジン iframe は{明確|めいかく}な{境界|きょうかい}です**。セレクタと `eval` はそこで{終|お}わりです。ツールに a11y ツリーを{貫通|かんつう}させるか、そうでなければ{座標|ざひょう}を{盲目的|もうもくてき}にクリックするしかありません。
- **「{入力|にゅうりょく}できるか」だけでなく、「{送信|そうしん}できるか」を{測|はか}る**。{値|あたい}が{入|はい}ったこと ≠ フレームワークが{受|う}け{取|と}ったこと、です。`fill` がイベントを{発火|はっか}しないような{落|お}とし{穴|あな}は、{実際|じっさい}に保存をクリックしてはじめて{露呈|ろてい}します。
- **{実際|じっさい}にログインしたブラウザを{使|つか}えるなら、headless は{避|さ}ける**。ログイン{状態|じょうたい}、cookie、{拡張機能|かくちょうきのう}がすべてそのままあり、{自動化|じどうか}フィンガープリントもありません——これが chrome-use が「あなた{自身|じしん}の Chrome を{操作|そうさ}する」{道|みち}を{選|えら}ぶ{理由|りゆう}でもあります。

## {試|ため}す

```bash
curl -fsSL https://raw.githubusercontent.com/leeguooooo/chrome-use/main/install.sh | sh
```

リポジトリは [github.com/leeguooooo/chrome-use](https://github.com/leeguooooo/chrome-use) にあります。私はずっと、このような「あなた{自身|じしん}のサブスクリプションを{使|つか}い、agent を{実際|じっさい}のブラウザ/{端末|たんまつ}につなぐ」ツールを{作|つく}っています。{進捗|しんちょく}は [X @leeguooooo](https://x.com/leeguooooo) に{投稿|とうこう}しています。

関連ドキュメント（英語）：[chrome-use · Forms & Clicking](https://chrome-use.leeguoo.com/en/interacting.html)、[インストール](https://chrome-use.leeguoo.com/en/install.html)。
