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

この記事では、この壁を攻略した過程を記録します。主役は chrome-use——Rust で書かれた、agent 向けのブラウザ自動化 CLI です。Playwright も headless も使わず、あなたが**実際にログインしている**その Chrome を直接操作します。
なぜクロスオリジン iframe はこんなに難しいのか
普通のページなら簡単です。アクセシビリティツリーを取得し、要素の参照を取り、クリックすれば終わりです。ところがクロスオリジン iframe、たとえば adsense.google.com のページに payments.google.com の iframe が埋め込まれているようなケースでは、一度に三つの地雷を踏みます。
- セレクタが中に入れない。同一オリジンポリシーのもとでは、外側のドキュメントで実行する CSS セレクタや
evalは、iframe 内部の DOM にまったく触れられません。ここではdocument.querySelectorは役に立ちません。 - スクロールが空振りする。ページをスクロールしているつもりでも、実際にスクロールすべきなのは iframe 内部のスクロールコンテナです。wheel イベントは外側のドキュメントに送られ、中はまったく動きません。目的の行はいつまでも「画面の外」で、見ることすらできません。
- 座標を勘でクリックするしかない。前の二つのせいで、「スクリーンショット + ピクセル座標の推測」に戻らざるを得ません。しかしこれはもっとも不正確で、隣のフィールドを誤ってクリックしやすい方法です。しかも変更するのが**全体の支払い情報**であるフォームなら、クリック一回の間違いの代償は小さくありません。
chrome-use の土台:agent に渡すのは HTML ではなく「参照」
突破方法の前に、まず基本の仕組みを説明します。ここが「HTML をモデルに食わせる」方式との根本的な違いでもあります。

chrome-use はページソースを agent に投げません。かわりにアクセシビリティツリーのスナップショットを取得し、各インタラクティブ要素に短い参照を付けます。
- textbox "メール" [ref=e2]
- listbox "<ruby>国<rt>くに</rt></ruby>/<ruby>地域<rt>ちいき</rt></ruby>" [ref=e60]
- button "<ruby>保存<rt>ほぞん</rt></ruby>" [ref=e41]
agent はその参照を直接操作します。fill @e2 "..."、click @e41 のように。一ページあたりおよそ 200–400 token で、DOM ノイズだらけの画面全体ではありません。この参照の仕組みこそが、あとで iframe を貫通できる前提になります。スナップショットが iframe 内のノードを「見られる」なら、参照も取れるからです。
三つの壁を、一つずつ越える
第一の壁: スナップショットに iframe 内のものを見せる。
アクセシビリティツリーがクロスオリジン iframe を貫通し、中のノードにも参照を付けて持ってこられるようにします。修正後、snapshot は直接こう列挙します。
- textbox "<ruby>電話番号<rt>でんわばんごう</rt></ruby> (<ruby>任意<rt>にんい</rt></ruby>)" [ref=e59]
- listbox "<ruby>国<rt>くに</rt></ruby>/<ruby>地域<rt>ちいき</rt></ruby>コード:<ruby>日本<rt>にほん</rt></ruby> (+81)" [ref=e60]
——セレクタが入れない場所にも、参照なら入れます。
第二の壁: スクロールを iframe のスクロールコンテナに効かせる。 wheel を外側のドキュメントへまとめて送るのをやめ、本当にスクロールすべきコンテナをスクロールします。これで下のフォーム行がようやく視野に入り、参照も取得できるようになります。
第三の壁(もっとも硬い): クロスオリジン iframe 内の「有効」な送信ボタンを押しても反応しない。 この段階がいちばん苦しいところです。なぜなら、見た目にはすべて正しいからです。
- 実際のキー入力で番号を入力できている。
get valueで読むと、たしかに入っている。 - 「保存」ボタンは点灯すべきタイミングで点灯している(正しい値を入れる前は disabled で、入力後に現れる)。
- そして
click @e41——フォームはまったく動かない。find text "<ruby>保存<rt>ほぞん</rt></ruby>"?クロスオリジンなので取れない。フォーカスして Enter / スペースを押す?それでも反応しない。
合っているのに、どこもかしこも合っていない。根本原因は、クロスオリジン iframe 内にある Material / フレームワーク系のボタンが、合成クリックを受け付けないことでした。さらに fill は入力ボックスの値だけを変更し、フレームワークが必要とする input / change イベントを発火しません。そのためフォーム自身は「変化していない」と思い、保存ボタンが無効のままだったり、クリックしてもクリックしていないのと同じになったりします。
解決策は二つに分かれます。値の入力は**実際のキー入力に切り替えること(各文字が本物のイベントを発生させて、フレームワークが認識します)。クリックについては、iframe 内のコンテンツノードに対して、click() を雑に投げるのではなく、本物のマウス / キーボード起動**を一式送ることです。
仕上げ: 中までクリックし、保存する

三つの壁を越えると、一連の流れがつながります。開く → 目的の行までスクロールする → スナップショットで参照を取る → 実際のキー入力で値を入れる → 保存を押す。あの「読めるのに、完了できない」という行き止まりは、ここで終わりです。
同じように agent のブラウザ自動化をしている人へ、痛みから得た経験
- まずアクセシビリティ参照を使い、デフォルトでスクリーンショットの座標クリックに頼らない。スナップショットが iframe を見られるようになったら、参照はピクセルの推測よりずっと安定します。スクリーンショットは canvas / WebGL のような、本当に構造がない場面に残しましょう。
- クロスオリジン iframe は明確な境界です。セレクタと
evalはここで終わりです。ツールに a11y ツリーを貫通させるか、さもなければ盲目的なクリックしか残りません。 - 「入力できるか」だけでなく、「送信できるか」を検証する。値が入ったこと ≠ フレームワークが受け取ったこと、です。
fillがイベントを発火しないという落とし穴は、実際に保存をクリックしてはじめて露呈します。 - 実際にログインしているブラウザを使えるなら、headless は避ける。ログイン状態、cookie、拡張機能がすべてそのままあり、自動化のフィンガープリントもありません。これが chrome-use が「あなた自身の Chrome を操作する」道を選んでいる理由でもあります。
試す
curl -fsSL https://raw.githubusercontent.com/leeguooooo/chrome-use/main/install.sh | sh
リポジトリは github.com/leeguooooo/chrome-use にあります。私はずっと、このような「あなた自身のサブスクリプションを使い、agent を実際のブラウザ / デバイスにつなぐ」ツールを作っており、進捗は X @leeguooooo に投稿しています。
コメント
コメントは即時公開されますが、ポリシー違反時は非表示になる場合があります。