Chrome DevTools Protocol(CDP)技術調査レポート — 仕様の全体像と Playwright との関係
発行日: 2026-09-09 テーマ: Chrome DevTools Protocol(CDP)の仕組み・バージョン体系・接続方法・全ドメインの整理と、Playwright / Puppeteer / WebDriver BiDi との関係 一次情報: chromedevtools.github.io/devtools-protocol(プロトコルビューア)、ChromeDevTools/devtools-protocol の
json/browser_protocol.json/json/js_protocol.json(npmdevtools-protocol0.0.1693794 相当、2026-09-09 取得)
TL;DR
- CDP は Chromium 系ブラウザを外部から制御・観測するための JSON メッセージプロトコル。DevTools(開発者ツール)の UI 自体がこのプロトコルでブラウザと会話しており、Puppeteer・Playwright(Chromium 部分)・Selenium の DevTools API・Chrome DevTools MCP など、Chromium を自動化するツールはほぼすべて CDP の上に載っている。
- 構造は ドメイン → コマンド / イベント / 型 の 3 層。トランスポートは WebSocket(
--remote-debugging-port)または パイプ(--remote-debugging-pipe)で、メッセージは{id, method, params, sessionId}の JSON-RPC 風。 - 2026-09 時点の tip-of-tree は 58 ドメイン(ブラウザ側 52 + V8 側 6)・661 コマンド・231 イベント・605 型。ただし experimental でない「安定」ドメインは 17 だけ(
Page/Network/Runtime/Target/Fetch/Emulation/Input/DOM/Debuggerなど)で、それ以外はいつ変わっても文句を言えない。 - バージョンは tot(最新・非互換上等)/1.3(Chrome 64 で固定した安定サブセット)/1.2/v8(Node.js 用) の 4 系統。後方互換の保証はなく、ツール側(Puppeteer / Playwright)が差分を吸収している。
- Chrome 136 以降、
--remote-debugging-port/--remote-debugging-pipeは既定プロファイルには効かず、--user-data-dirで別ディレクトリを指定しないと無視される(Cookie 窃取対策)。既存 Chrome にconnectOverCDPする運用は影響を受ける。 - Playwright と CDP の関係は 3 層で理解するとよい。
- 内部実装: Chromium ドライバは CDP そのもの(
Page.enable→Target.setAutoAttach→Runtime.addBinding→Page.createIsolatedWorld…)。Firefox は独自の Juggler、WebKit は パッチ済み WebKit Inspector Protocol を使うため、CDP は 3 ブラウザ中 1 つでしかない。これが「Playwright はブラウザにパッチを当てて配布する」理由。 - 公開 API:
context.newCDPSession(page)/browser.newBrowserCDPSession()で 生の CDP に降りられる(Chromium 限定)。chromium.connectOverCDP(url)で既存の Chrome に接続できるが、公式は「Playwright 独自プロトコルのconnect()より忠実度が低い」と明言。 - 将来: W3C WebDriver BiDi への移行は「実験段階」。Playwright は BiDi 版 Firefox チャンネル(
moz-firefox系)とテスト基盤を持つが、仕様の穴(レスポンスボディ取得・ポップアップのビューポート設定等)が多く、当面は CDP + Juggler + WebKit の 3 本立てが続く。Chrome 側の BiDi 実装(chromium-bidi)自体が CDP の上に載る変換層なので、CDP が消える見込みは無い。
- 内部実装: Chromium ドライバは CDP そのもの(
1. CDP とは何か — 位置づけ
CDP(Chrome DevTools Protocol)は、Chromium / Chrome / Edge / Brave などの Blink + V8 ベースのブラウザ内部(ページ・DOM・ネットワーク・JS ランタイム・ストレージ・エミュレーション…)を、外部プロセスから計測・操作するための RPC プロトコルである。
もともとは Chrome DevTools(F12 で開く開発者ツール)のフロントエンドとバックエンドの通信路として作られたもので、その通信路を 外部にも開放したのがリモートデバッグ機能である。したがって DevTools でできることは、原理的にすべて CDP で外部から再現できる。
| 利用者 | CDP との関係 |
|---|---|
| Chrome DevTools フロントエンド | CDP の第一の(そして最大の)クライアント。devtools-frontend リポジトリは CDP をそのまま話す |
| Puppeteer | Google 製。Chrome では CDP が既定プロトコル。Firefox では WebDriver BiDi が既定 |
| Playwright | Chromium ドライバは CDP。Firefox は Juggler、WebKit は独自プロトコル(後述) |
| Selenium 4 | WebDriver(HTTP)に加えて DevTools API で CDP を直接叩けた。CDP API は非推奨で 5.0 で削除予定、Firefox 向け CDP は 4.29 で削除済み |
Chrome DevTools MCP(chrome-devtools-mcp) |
AI エージェント向け MCP サーバー。Puppeteer 経由で CDP を使う |
Node.js --inspect |
V8 Inspector が CDP の v8 サブセット(Runtime / Debugger / Profiler / HeapProfiler)を実装。Chrome の chrome://inspect や VS Code のデバッガはこれに接続する |
| Lighthouse / Web Vitals 計測ツール | Tracing / Performance / Network ドメインで計測 |
2. プロトコルの基本構造
2.1 ドメイン・コマンド・イベント・型
仕様は ドメイン(Domain) 単位で分割され、各ドメインが次の 3 種類を持つ。
| 要素 | 方向 | 例 |
|---|---|---|
| Command(メソッド) | クライアント → ブラウザ。id 付きで送り、同じ id の応答が返る |
Page.navigate, Runtime.evaluate, Network.enable |
| Event | ブラウザ → クライアント。id なしで非同期に飛んでくる。多くは <Domain>.enable を呼ばないと流れない |
Page.loadEventFired, Network.responseReceived, Runtime.consoleAPICalled |
| Type | コマンド/イベントの引数・戻り値で使う構造体・列挙・ID 型 | Network.Request, Runtime.RemoteObject, DOM.NodeId |
各ドメイン・コマンド・イベント・フィールドには experimental(変更・削除されうる)と deprecated(代替あり、将来削除)のフラグが付く。プロトコルビューアでは experimental が赤、deprecated が打ち消し線で表示される。ドメイン間には dependencies(例: CSS は DOM と Page に依存)もある。
2.2 メッセージ形式
WebSocket 上を流れるのは 1 行 1 メッセージの JSON。JSON-RPC 2.0 に似ているが jsonrpc フィールドは無い。
// クライアント → ブラウザ(コマンド)
{ "id": 1, "method": "Runtime.evaluate", "params": { "expression": "1 + 1" }, "sessionId": "ABCD…" }
// ブラウザ → クライアント(応答。id が一致)
{ "id": 1, "result": { "result": { "type": "number", "value": 2 } }, "sessionId": "ABCD…" }
// ブラウザ → クライアント(イベント。id なし)
{ "method": "Page.loadEventFired", "params": { "timestamp": 12345.678 }, "sessionId": "ABCD…" }
// エラー応答
{ "id": 2, "error": { "code": -32601, "message": "'Foo.bar' wasn't found" } }
idは セッションごとに一意であればよい整数。sessionIdは後述のターゲット/セッション機構で、どのページ(ターゲット)に対する命令かを示す。ブラウザ全体のセッション(root)では省略できる。
2.3 定義ファイルの実体(PDL → JSON → TypeScript)
- 正本は Chromium ソースツリー内の PDL(Protocol Definition Language) ファイル:
third_party/blink/public/devtools_protocol/browser_protocol.pdlと V8 のinclude/js_protocol.pdl。 - これを JSON 化したものが
ChromeDevTools/devtools-protocolリポジトリのjson/browser_protocol.json/json/js_protocol.json。Chromium のリビジョンごとに自動ロールされ、npm パッケージdevtools-protocolの版番号(例:0.0.1693794)は Chromium のリビジョン番号そのもの。 - 同リポジトリの
types/protocol.d.ts(型)、types/protocol-mapping.d.ts(メソッド名 → 引数/戻り値の対応)、types/protocol-proxy-api.d.ts(DomainApi形式)が TypeScript 定義として配布される。Puppeteer はこれを直接依存に持つ。 - 起動中のブラウザ自身からも
http://localhost:9222/json/protocolでそのバイナリが実装している版の JSON を取得できる。Playwright はこのエンドポイントからprotocol.d.tsを生成している(後述 7.1)。
3. バージョン体系と安定性
プロトコルビューアの左上で切り替えられる 4 系統。
| 系統 | 内容 | 用途 |
|---|---|---|
| tot(latest / tip-of-tree) | 最新 Chromium の全能力。頻繁に変わり、いつ壊れてもおかしくないが、能力は完全 | Puppeteer / Playwright など「ブラウザと一緒にロールする」ツール |
| 1.3(stable) | Chrome 64 の時点でタグ付けした安定サブセット。experimental を除いた部分。ビューアの 1-3/ |
長期間動かす自前クライアント。ただし 2018 年時点の凍結なので新機能は無い |
| 1.2(stable、旧) | Chrome 54 時点の旧安定版。歴史的参照用 | — |
| v8(v8-inspector) | V8 Inspector が実装するサブセット。Node.js の --inspect がこれ |
Node デバッガ・プロファイラ |
公式の但し書きは明確で、「tot は後方互換を保証しない。安定版はサブセットである」という立場。実務上の含意は次のとおり。
- 「安定」と書かれているのは ドメイン単位のフラグであり、安定ドメインの中にも experimental なコマンド・フィールドが多数ある(例:
Pageは 63 コマンド中 38 が experimental、Emulationは 49 中 33)。 - ブラウザの版とクライアントの版を揃えるのが基本。Chrome for Testing(版固定のバイナリ配布)はこの目的で作られた。
- 自前で CDP を叩くなら、依存するメソッドが experimental かどうかをビューアで確認し、Puppeteer / Playwright の該当実装をリファレンスとして読むのが早い(彼らが毎リリースで追随作業をしている)。
4. 接続方法(トランスポートとエンドポイント)
4.1 起動フラグ
# WebSocket(ポート指定。0 なら空きポートを自動選択)
chrome --remote-debugging-port=9222 --user-data-dir=/tmp/cdp-profile
# パイプ(fd 3/4 を使う。ポートを開けないので安全。Puppeteer/Playwright の既定はこちら)
chrome --remote-debugging-pipe --user-data-dir=/tmp/cdp-profile
起動すると stderr に DevTools listening on ws://127.0.0.1:9222/devtools/browser/<uuid> が出力され、同じ URL がプロファイル内の DevToolsActivePort ファイルにも書かれる。
Chrome 136 からの変更(重要): 既定のユーザーデータディレクトリに対しては
--remote-debugging-port/--remote-debugging-pipeが無視されるようになった。App-Bound Encryption 導入後、リモートデバッグ経由で Cookie を抜くマルウェアが増えたための対策で、--user-data-dirで非標準ディレクトリを明示しないとリモートデバッグは有効にならない。「普段使いの Chrome にそのままconnectOverCDPする」使い方はこれで塞がれた(2026-08 には認証済みセッション乗っ取りに DevTools を悪用する手法も報じられており、ポート公開は依然として攻撃面である)。
4.2 HTTP ディスカバリエンドポイント
| パス | 内容 |
|---|---|
GET /json/version |
ブラウザ名・User-Agent・webSocketDebuggerUrl(ブラウザターゲットの WS URL) |
GET /json または /json/list |
開いているページ等のターゲット一覧(id, type, url, webSocketDebuggerUrl, devtoolsFrontendUrl) |
GET /json/protocol |
このバイナリが実装するプロトコル定義 JSON |
PUT /json/new?<url> |
新しいタブを開く |
GET /json/activate/<id> / /json/close/<id> |
タブのアクティブ化/クローズ |
Playwright の connectOverCDP('http://localhost:9222') は内部で /json/version を叩いて webSocketDebuggerUrl を得ている。WebSocket URL を直接渡すこともできる。
4.3 ターゲットとセッション
CDP の理解で一番つまずくのがここ。
- ターゲット(Target) = 制御対象の単位。
browser(常に 1 つ存在)、page、iframe(別プロセスの OOPIF)、worker、service_worker、shared_worker、background_page(拡張)など。 - セッション(Session) = ターゲットに attach してできる会話の文脈。
Target.attachToTarget({targetId, flatten: true})でsessionIdを得て、以後のメッセージにsessionIdを付けて送る。 - WebSocket を
/devtools/browser/<uuid>に張ると ルート(ブラウザ)セッションが暗黙に作られる。/devtools/page/<id>に直接張ればそのページのセッションになる(古いスタイル)。 flatten: trueが現在の標準。旧方式(Target.sendMessageToTargetでメッセージを入れ子にする)は非推奨。Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true, flatten: true})を使うと、新しく生まれた子ターゲット(iframe・worker・ポップアップ)に自動で attach し、初期化が済むまで実行を止めておける。Puppeteer / Playwright はこれで「ページが動き出す前にスクリプト注入・ネットワーク傍受を仕込む」を実現している。- 親セッションを閉じると子セッションも閉じる。
WebSocket ──── root(browser) session
├─ Target.attachToTarget → page session A(sessionId: …)
│ └─ setAutoAttach → iframe session(OOPIF)
│ └─ setAutoAttach → worker session
└─ Target.attachToTarget → page session B
複数クライアントの同時接続は Chrome 63 以降サポートされている(DevTools を開いたまま Puppeteer が動く)。ただし同じドメインの enable 状態はセッションごとに独立で、一方が Emulation を変えると他方にも影響するようなブラウザグローバルな副作用を持つコマンドもある。
5. 全ドメイン一覧(tot、2026-09-09 時点)
browser_protocol.json(version 1.3 表記、52 ドメイン)と js_protocol.json(6 ドメイン)から集計。数字は コマンド数 / イベント数 / 型数。「安定」は experimental・deprecated のいずれでもないドメイン。
5.1 安定ドメイン(17)
| ドメイン | cmd / ev / type | 役割 | 主なコマンド・イベント |
|---|---|---|---|
| Browser | 22 / 2 / 10 | ブラウザ本体の管理(バージョン、ウィンドウ、権限、ダウンロード) | getVersion, grantPermissions, setDownloadBehavior, close |
| DOM | 54 / 19 / 18 | DOM の読み書き。ノードはミラーオブジェクト(NodeId)で扱う |
getDocument, querySelector, getBoxModel, resolveNode, childNodeInserted |
| DOMDebugger | 10 / 0 / 3 | DOM 変更・イベント・XHR へのブレークポイント | setDOMBreakpoint, setEventListenerBreakpoint, getEventListeners |
| Emulation | 49 / 2 / 21 | 端末・ビューポート・UA・ロケール・タイムゾーン・位置情報・メディア・CPU スロットリング等のエミュレーション | setDeviceMetricsOverride, setUserAgentOverride, setTimezoneOverride, setGeolocationOverride, setCPUThrottlingRate |
| Fetch | 9 / 2 / 6 | ネットワーク層をクライアントで差し替える(リクエスト傍受・改変・偽応答・認証応答) | enable, requestPaused, continueRequest, fulfillRequest, failRequest, authRequired |
| IO | 3 / 0 / 1 | 大きな出力(トレース・PDF・レスポンス本文)のストリーム読み出し | read, close, resolveBlob |
| Input | 13 / 1 / 6 | マウス・キーボード・タッチ・ドラッグイベントの合成 | dispatchMouseEvent, dispatchKeyEvent, dispatchTouchEvent, insertText |
| Log | 5 / 1 / 2 | ブラウザログ(ネットワークエラー、セキュリティ警告等) | enable, entryAdded |
| Network | 35 / 43 / 89 | HTTP/WS/データ URL 等の通信の追跡。イベントが最も多いドメイン | enable, setExtraHTTPHeaders, getResponseBody, setCookies, requestWillBeSent, responseReceived, loadingFinished |
| Page | 63 / 28 / 58 | ページのライフサイクル・ナビゲーション・スクリーンショット・PDF・スクリプト注入・ダイアログ | navigate, reload, captureScreenshot, printToPDF, addScriptToEvaluateOnNewDocument, createIsolatedWorld, setLifecycleEventsEnabled, loadEventFired, frameNavigated, javascriptDialogOpening |
| Performance | 4 / 1 / 1 | ランタイムメトリクス(レイアウト数、JS ヒープ等) | enable, getMetrics |
| Security | 5 / 3 / 10 | 証明書エラー・セキュリティ状態 | setIgnoreCertificateErrors, securityStateChanged |
| Target | 19 / 7 / 7 | ターゲット発見と attach(4.3 参照)。ブラウザコンテキスト(シークレット相当の隔離環境)の作成もここ | getTargets, attachToTarget, setAutoAttach, createTarget, createBrowserContext, attachedToTarget, targetCreated |
| Tracing | 6 / 3 / 6 | Chrome トレース(Performance パネルの生データ)の記録 | start, end, dataCollected, tracingComplete |
| Debugger(js) | 33 / 5 / 13 | JS デバッガ:ブレークポイント、ステップ実行、スコープ、ソースマップ | enable, setBreakpointByUrl, pause, resume, stepInto, evaluateOnCallFrame, paused, scriptParsed |
| Profiler(js) | 9 / 3 / 6 | CPU プロファイル、コードカバレッジ | start, stop, startPreciseCoverage, takePreciseCoverage |
| Runtime(js) | 23 / 8 / 23 | JS の遠隔評価とミラーオブジェクト。CDP で最も使うドメイン | evaluate, callFunctionOn, getProperties, addBinding, releaseObject, executionContextCreated, consoleAPICalled, exceptionThrown, bindingCalled |
5.2 Experimental ドメイン(39)
「いつでも変わりうる」扱いだが、実務では普通に使われているものも多い(CSS / Overlay / Storage / Accessibility など)。
DOM・スタイル・レンダリング
| ドメイン | cmd / ev / type | 役割 |
|---|---|---|
| CSS | 39 / 6 / 46 | スタイルシート・ルール・算出スタイルの読み書き(Elements パネルの Styles) |
| Accessibility | 8 / 2 / 10 | アクセシビリティツリーの取得(getFullAXTree, queryAXTree) |
| Animation | 10 / 4 / 5 | CSS/Web Animations の列挙・再生速度変更 |
| DOMSnapshot | 4 / 0 / 15 | DOM + レイアウト + スタイルを一括スナップショット(captureSnapshot) |
| LayerTree | 9 / 2 / 7 | コンポジタレイヤーの検査 |
| Overlay | 31 / 6 / 23 | ページ上へのハイライト描画(要素ハイライト、グリッド・Flex オーバーレイ、検査モード) |
| EventBreakpoints | 3 / 0 / 0 | ネイティブ側イベント(DOM 以外)へのブレークポイント |
| Inspector | 2 / 4 / 0 | インスペクタ自身の状態(detached, targetCrashed) |
ネットワーク・ストレージ
| ドメイン | cmd / ev / type | 役割 |
|---|---|---|
| Storage | 23 / 6 / 8 | Cookie / ストレージクォータ / Trust Token / Shared Storage / Attribution Reporting などの横断操作 |
| CacheStorage | 5 / 0 / 6 | Cache API の中身 |
| DOMStorage | 6 / 4 / 3 | localStorage / sessionStorage |
| IndexedDB | 9 / 0 / 7 | IndexedDB の列挙・読み取り・削除 |
| FileSystem | 1 / 0 / 3 | OPFS 等のディレクトリ取得 |
| ServiceWorker | 12 / 3 / 11 | Service Worker の登録・更新・停止・push/sync の発火 |
| BackgroundService | 4 / 2 / 3 | Background Fetch / Sync / Push 等の記録 |
| Preload | 2 / 6 / 12 | Speculation Rules(prefetch / prerender)の状態 |
| Tethering | 2 / 1 / 0 | ポートバインディング(リモートデバイス向け) |
JS ランタイム・パフォーマンス
| ドメイン | cmd / ev / type | 役割 |
|---|---|---|
| HeapProfiler(js) | 12 / 5 / 4 | ヒープスナップショット、アロケーション追跡 |
| Memory | 11 / 0 / 5 | DOM カウンタ、メモリサンプリング、圧力通知の模擬 |
| PerformanceTimeline | 1 / 1 / 4 | LCP / Layout Shift 等の Performance Timeline イベント |
| Media | 2 / 5 / 8 | メディア要素・プレイヤーの詳細ログ |
| WebAudio | 3 / 13 / 13 | Web Audio グラフの検査 |
| HeadlessExperimental | 3 / 0 / 1 | ヘッドレス専用(beginFrame による決定的レンダリング) |
エミュレーション・デバイス系テスト
| ドメイン | cmd / ev / type | 役割 |
|---|---|---|
| WebAuthn | 13 / 4 / 6 | 仮想認証器を作って WebAuthn / Passkey をテスト |
| BluetoothEmulation | 15 / 3 / 9 | 仮想 Bluetooth デバイスで Web Bluetooth をテスト |
| SmartCardEmulation | 12 / 14 / 9 | Web Smart Card API のエミュレーション |
| DeviceAccess | 4 / 1 / 3 | デバイス選択ダイアログ(USB/HID 等)の操作 |
| DeviceOrientation | 2 / 0 / 0 | 端末の向きの上書き |
| FedCm | 7 / 2 / 5 | FedCM(Federated Credential Management)ダイアログの操作 |
| DigitalCredentials | 1 / 0 / 1 | Digital Credentials API の自動化 |
| Autofill | 4 / 1 / 7 | 自動入力のトリガーと住所データ設定 |
| Cast | 6 / 2 / 1 | Cast / Presentation API / Remote Playback |
ブラウザ・システム・その他
| ドメイン | cmd / ev / type | 役割 |
|---|---|---|
| Audits | 4 / 1 / 67 | Issues パネル相当(CSP 違反、Cookie 問題、Quirks 等)。型数が多いのは issue 種別ごとの詳細型があるため |
| Extensions | 8 / 0 / 2 | 拡張機能のロード・ストレージ操作 |
| PWA | 7 / 0 / 3 | PWA のインストール・起動・ファイルハンドラ |
| SystemInfo | 3 / 0 / 8 | GPU / プロセス情報 |
| Ads | 2 / 0 / 3 | 広告関連メトリクス |
| CrashReportContext | 1 / 0 / 1 | CrashReportContext API の状態 |
| WebMCP | 4 / 4 / 4 | WebMCP(ページをエージェント向けツール化する提案)の検査。本サイトの WebMCP 調査レポート 参照 |
5.3 Deprecated ドメイン(2)
| ドメイン | 代替 |
|---|---|
| Console(js) | Runtime.consoleAPICalled と Log |
| Schema(js) | /json/protocol エンドポイント |
6. 代表的なユースケースとコマンドの組み合わせ
| やりたいこと | 使うコマンド・イベント |
|---|---|
| JS を実行して値を取る | Runtime.evaluate({expression, returnByValue: true, awaitPromise: true})。DOM 要素など非シリアライズ値は RemoteObject.objectId で参照し、Runtime.callFunctionOn で操作、Runtime.releaseObject で解放 |
| ページ遷移して完了を待つ | Page.enable → Page.setLifecycleEventsEnabled({enabled: true}) → Page.navigate → Page.lifecycleEvent(DOMContentLoaded / load / networkIdle)や Page.loadEventFired を待つ |
| 新規ドキュメントごとにスクリプト注入 | Page.addScriptToEvaluateOnNewDocument(worldName を渡すと隔離ワールドで実行) |
| ページ JS と隔離された実行環境 | Page.createIsolatedWorld({frameId, worldName}) → 返る executionContextId で Runtime.evaluate |
| ページから Node 側へコールバック | Runtime.addBinding({name}) → ページ側 window.name(payload) → Runtime.bindingCalled |
| リクエストの傍受・書き換え・モック | Fetch.enable({patterns}) → Fetch.requestPaused → continueRequest / fulfillRequest / failRequest。認証は handleAuthChallenge |
| 通信の記録 | Network.enable → requestWillBeSent / responseReceived / loadingFinished → Network.getResponseBody |
| スクリーンショット / PDF | Page.captureScreenshot({format, clip, captureBeyondViewport}) / Page.printToPDF(大きい場合は transferMode: 'ReturnAsStream' + IO.read) |
| モバイル端末エミュレーション | Emulation.setDeviceMetricsOverride + setUserAgentOverride + setTouchEmulationEnabled |
| ダイアログ処理 | Page.javascriptDialogOpening → Page.handleJavaScriptDialog({accept, promptText}) |
| 入力の合成 | Input.dispatchMouseEvent(mouseMoved → mousePressed → mouseReleased)、Input.dispatchKeyEvent(keyDown / keyUp)、Input.insertText |
| カバレッジ | Profiler.enable → startPreciseCoverage → takePreciseCoverage。CSS は CSS.startRuleUsageTracking |
| パフォーマンストレース | Tracing.start({categories, transferMode: 'ReturnAsStream'}) → Tracing.end → tracingComplete → IO.read |
| Passkey のテスト | WebAuthn.enable → addVirtualAuthenticator → addCredential |
7. Playwright との関係
7.1 内部実装: CDP は 3 ブラウザ中 1 つのドライバ
Playwright の売りは Chromium / Firefox / WebKit を 同じ API で動かせることだが、その裏側でブラウザごとに話すプロトコルは異なる。
| ブラウザ | プロトコル | 供給形態 | 型定義の生成元 |
|---|---|---|---|
| Chromium(Chrome / Edge 含む) | CDP(tot) | 上流 Chromium をそのままビルド(Chrome N+1 相当を先行提供)。browser_patches/ に chromium ディレクトリは無い |
起動中ブラウザの http://localhost:9339/json/protocol から server/chromium/protocol.d.ts を生成 |
| Firefox | Juggler(Playwright 独自) | browser_patches/firefox/ のパッチを当てた自前ビルド。ブランド版 Firefox では動かない |
omni.ja 内の chrome/juggler/content/protocol/Protocol.js から生成 |
| WebKit | WebKit Inspector Protocol(パッチ拡張) | browser_patches/webkit/ のパッチを当てた自前ビルド。Safari では動かない |
ビルド同梱の protocol.json から生成 |
つまり **Playwright は「CDP を全ブラウザに移植した」のではなく、「CDP 相当の能力(自動 attach、隔離ワールド、ネットワーク傍受、ライフサイクルイベント…)を Firefox と WebKit に自前パッチで持ち込み、上位の Playwright プロトコル(protocol.yml)で 3 つを抽象化した」**という構造である。Playwright のクライアント(Node / Python / Java / .NET)が話しているのはこの Playwright プロトコルであり、CDP ではない。
Chromium ドライバ(server/chromium/crPage.ts)がページ初期化時に発行する CDP コマンドは、CDP の「正しい使い方」の実例として参考になる。
Page.enable
Page.getFrameTree
Log.enable
Runtime.enable ← 実行コンテキストの列挙(後述の検知問題の発生源)
Network.enable ← crNetworkManager 経由
Target.setAutoAttach ← OOPIF / worker への自動 attach
Page.setLifecycleEventsEnabled
Page.addScriptToEvaluateOnNewDocument
Page.createIsolatedWorld ← utility world(Playwright の内部スクリプト用)
Runtime.addBinding ← ページ → Playwright のコールバック
Emulation.setFocusEmulationEnabled / setTouchEmulationEnabled / setUserAgentOverride
Emulation.setLocaleOverride / setTimezoneOverride / setGeolocationOverride
Page.setBypassCSP / Security.setIgnoreCertificateErrors / Page.setInterceptFileChooserDialog
Runtime.runIfWaitingForDebugger ← 初期化完了後に実行再開
page.route() は Fetch ドメイン、page.screenshot() は Page.getLayoutMetrics + Page.captureScreenshot、page.mouse.* / keyboard.* は Input.dispatch* で実装されている。
7.2 公開 API: 生の CDP に降りる手段(Chromium 限定)
Playwright の高レベル API で足りない時のために、CDP を直接叩く窓が用意されている。
// ページ(ターゲット)単位のセッション
const client = await page.context().newCDPSession(page);
await client.send('Animation.enable');
client.on('Animation.animationCreated', () => console.log('Animation created!'));
const { playbackRate } = await client.send('Animation.getPlaybackRate');
await client.send('Animation.setPlaybackRate', { playbackRate: playbackRate / 2 });
await client.detach();
// ブラウザ単位のセッション(Target / Browser / SystemInfo など)
const browserSession = await browser.newBrowserCDPSession();
CDPSession.send(method, params)/.on(event, handler)/.detach()が基本。1.59 でevent(全イベント)とcloseイベントが追加。- 引数・戻り値は
devtools-protocol由来の型で補完が効く。 - Firefox / WebKit では使えない(
newCDPSessionは Chromium 以外で例外)。クロスブラウザ性を捨てる覚悟が要る。 - Playwright 自身が同じターゲットで
Runtime/Network/Fetchを enable 済みなので、自前セッションでFetch.enableを重ねると Playwright のroute()と競合しうる。CDP 直叩きは「Playwright が触らないドメイン」(Animation,CSS,Accessibility,Tracing,Profiler,WebAuthn,Emulation.setCPUThrottlingRate等)に限るのが安全。
7.3 公開 API: 既存の Chrome に接続する connectOverCDP
const browser = await chromium.connectOverCDP('http://localhost:9222'); // または ws://…/devtools/browser/<id>
const context = browser.contexts()[0]; // 既定コンテキスト
const page = context.pages()[0];
| オプション | 追加版 | 内容 |
|---|---|---|
headers |
— | 接続時の追加 HTTP ヘッダ |
timeout / slowMo |
— | 接続タイムアウト(既定 30 秒)/操作遅延 |
isLocal |
1.47 | 同一ホストで動いている前提の最適化(ファイル系操作) |
noDefaults |
1.60 | Playwright が既存コンテキストに施す既定の上書き(ビューポート等)を無効化 |
artifactsDir |
1.61 | トレース・ダウンロード等の保存先 |
公式ドキュメントが明記している注意点は 3 つ。
- Chromium 系のみ。
- Playwright 独自プロトコル経由の
browserType.connect()(launchServerと対)より忠実度が大きく劣る。高度な用途はconnect()を推奨。 - Playwright は特定の起動引数に依存しているため、別途起動したブラウザに接続すると一部機能が壊れうる。
用途は「ユーザーがログイン済みの Chrome を使い回す」「リモートのヘッドレス Chrome サービス(Browserless 等)に繋ぐ」「Electron / CEF アプリのデバッグポートに繋ぐ」など。Chrome 136 以降は既定プロファイルに対してポートが開かないため、前者は --user-data-dir で専用プロファイルを作る運用に変える必要がある。
7.4 WebDriver BiDi との関係 — Playwright はまだ CDP に留まる
W3C WebDriver BiDi は CDP と同じ「WebSocket 上の双方向 JSON」モデルを ブラウザ横断の標準として定義し直したもの。Chrome / Edge / Firefox がネイティブ実装し、Puppeteer は Firefox で BiDi を既定に、Selenium は CDP API を非推奨にして BiDi へ移行中である。
Playwright の立場は次の通り。
- 2024-09 にメンテナ(Yury Semikhatsky)が issue #32577「Current limitations blocking Playwright’s WebDriver BiDi adoption」 を公開。実験実装でのテスト通過率は Chromium 61%・Firefox 38% で、仕様側の欠落として ポップアップ/新規タブ読み込み前のビューポート設定、コンテキスト単位のプロキシ・証明書設定、レスポンスボディ取得、
resourceType、ダウンロードのメタデータ、ロケール/タイムゾーン/UA エミュレーション、追加 HTTP ヘッダ、コンテキスト単位の preload script、オフラインモード、JS 無効化などを列挙。 - リポジトリには
tests/bidi/と、BiDi 経由でシステムの Firefox を動かすmoz-firefox/moz-firefox-beta/moz-firefox-nightlyチャンネル(_createBidiFirefoxChannel)が存在する。BIDI_FFPATH/BIDI_CRPATHでバイナリを差し替えてテストする。 - Mozilla 側は Bugzilla のメタバグ 1917540 で Playwright 向け高優先 API(ユーザーコンテキスト設定、位置情報等のエミュレーション)を順次実装中。
- 公式の説明は「BiDi 対応は実験的。プロトコルが Playwright の全機能を賄えるほど成熟した時点で、将来のバージョンで自動的に移行するだろう」。
一方 Chrome 側の BiDi 実装 chromium-bidi は「BiDi コマンドを CDP コマンドに変換する JavaScript 層(Mapper)」としてブラウザ内で動く。つまり Chrome では BiDi の下に CDP がある構造で、CDP が廃止される見込みはない。Chrome チームも「BiDi は CDP を置き換えるものではなく、CDP は低レベル・Chromium 固有のデバッグ用途に最適化され続ける」と説明している。
| CDP | WebDriver BiDi | |
|---|---|---|
| 策定 | Chromium プロジェクト | W3C |
| 対応ブラウザ | Chromium 系(Firefox は 129 で CDP 実装を削除) | Chrome / Edge / Firefox(WebKit は未) |
| 互換性 | 後方互換なし(tot) | 仕様として安定化を目指す |
| 能力 | 全部(DevTools と同等) | 自動化に必要な部分集合。低レベル機能は goog:cdp.sendCommand 拡張で CDP に逃がす |
| Puppeteer | Chrome の既定 | Firefox の既定。Chrome は protocol: 'webDriverBiDi' で opt-in |
| Playwright | Chromium の実装 | 実験段階(moz-firefox チャンネル、tests/bidi) |
| Selenium | 4.x で非推奨、5.0 で削除予定 | 4.27〜4.30 で network / script / log / input を整備 |
7.5 副作用としての「検知」問題
CDP を有効にしていること自体はページからは見えないが、Playwright / Puppeteer が既定で呼ぶ Runtime.enable は、実行コンテキストの列挙時に V8 側の挙動(エラーオブジェクトのプレビュー生成など)を変えるため、ページ内 JS から観測できることが知られている。アンチボット製品はこれを「DevTools クライアントが attach している」シグナルとして使う。
これに対して、Runtime.enable を呼ばずに隔離ワールドで動く Patchright(Playwright の派生)や rebrowser-patches といったプロジェクトがあり、V8 側も 2025-05 にエラープレビューの挙動を変更するなど、いたちごっこになっている。テスト自動化の文脈では無関係だが、「Playwright を使うと CDP の痕跡がページに漏れる」という事実は把握しておく価値がある。
8. AI エージェントと CDP — Chrome DevTools MCP
2025 年に Google が公開した chrome-devtools-mcp は、Claude Code や Gemini CLI などのコーディングエージェントに「動いている Chrome を操作・検査させる」MCP サーバーで、Puppeteer(= CDP)を土台にしている。提供するツールはナビゲーション、スクリーンショット、コンソール/ネットワークの読み取り、パフォーマンストレース(Tracing)と Insights の抽出など、CDP のドメインをほぼそのまま MCP ツールに写した形になっている。
Playwright 側の同種ツールは @playwright/mcp で、こちらは Playwright API(アクセシビリティツリーベースのスナップショット)を土台にしており、Chromium では最終的に CDP に落ちる。「エージェントにブラウザを触らせる」文脈でも、Chromium 系の最下層は CDPである。
9. 実務上の注意点
- experimental を前提にする: 安定 17 ドメインの中でも experimental コマンドが多い。ブラウザ更新で壊れた時に追える体制(Puppeteer / Playwright のソースを読む、
/json/protocolで差分を確認する)が要る。 - ブラウザ版を固定する: Chrome for Testing か Playwright / Puppeteer 同梱ブラウザを使い、CDP クライアントとブラウザを同時にロールする。
- ポートを外に出さない:
--remote-debugging-portはローカルの全プロセスから到達可能で、認証は無い。--remote-debugging-pipeを優先し、ポートが必要なら127.0.0.1に限定し--remote-allow-originsで WebSocket の Origin を絞る。Chrome 136 の変更は「既定プロファイルは守る」という意思表示であり、専用プロファイルであっても中の Cookie は同様に抜ける。 enableの重複と副作用: 同じターゲットに複数クライアントが付く場合、EmulationやNetwork.setExtraHTTPHeadersのようなグローバル状態を持つコマンドは互いに上書きする。RemoteObjectのリーク:Runtime.evaluateでreturnByValue: falseの結果を受け取り続けると、releaseObject/releaseObjectGroupを呼ばない限りブラウザ側で GC されない。- 大きな出力は IO ストリーム:
Page.printToPDF/Tracing/ 大きなレスポンス本文はtransferMode: 'ReturnAsStream'+IO.readで。JSON メッセージに base64 で載せると WebSocket フレームが巨大になる。 - Playwright で CDP を直叩きするのは最後の手段: 使うなら Playwright が enable していないドメインに限定し、Chromium 限定であることをテストの前提として明記する。
10. まとめ
- CDP は「DevTools がブラウザと話す言葉」を外部に開いたもので、Chromium 自動化ツールの共通基盤。58 ドメイン・661 コマンドに及ぶが、安定と言えるのは 17 ドメインで、残りは tot 追随が前提。
- 接続は WebSocket またはパイプ、
{id, method, params, sessionId}の JSON。ターゲット/セッションとTarget.setAutoAttachの理解が肝。Chrome 136 以降は--user-data-dir必須。 - Playwright にとって CDP は Chromium ドライバの実装言語であり、Firefox(Juggler)・WebKit(パッチ済み Inspector Protocol)とは別物。ユーザーは
newCDPSessionとconnectOverCDPで CDP に降りられるが、いずれも Chromium 限定で、後者は公式に「connect()より低忠実度」。 - 標準化の流れは WebDriver BiDi だが、Playwright は仕様の欠落を理由に実験段階に留まり、Chrome の BiDi 実装自体が CDP 上の変換層である以上、CDP は当面「Chromium の全能力に触れる唯一の道」であり続ける。
参考リンク
- Chrome DevTools Protocol Viewer(tot / 1-3 / 1-2 / v8)
- ChromeDevTools/devtools-protocol(
json/browser_protocol.json,json/js_protocol.json,types/) - Getting Started with CDP (aslushnikov) — ターゲット/セッション/flatten の解説
- Changes to remote debugging switches to improve security (Chrome for Developers) — Chrome 136 の
--user-data-dir必須化 - WebDriver BiDi production-ready in Firefox, Chrome and Puppeteer (Chrome for Developers)
- GoogleChromeLabs/chromium-bidi — BiDi → CDP Mapper
- Puppeteer: WebDriver BiDi support
- Playwright: CDPSession / BrowserType.connectOverCDP / Browsers / Release notes
- Playwright ソース:
browser_patches/(firefox / webkit のみ)、utils/protocol-types-generator、packages/playwright-core/src/server/chromium/crPage.ts、tests/bidi/README.md - microsoft/playwright #32577: Current limitations blocking Playwright’s WebDriver BiDi adoption
- Bugzilla 1917540: [meta] Support WebDriver BiDi in Playwright
- Selenium: Removing ChromeDevTools Support For Firefox / Selenium WebDriver BiDi
- ChromeDevTools/chrome-devtools-mcp
- Detecting CDP in the wild: the Runtime.enable leak (crawlex)