🧩

Google Gemini を Chrome 拡張のサイドバーに埋め込みたかった話:iframe ではじかれてから DNR にたどり着くまで

に公開

概要

Chrome 拡張のサイドバーに Google Gemini をそのまま表示したかった。最初は単純に iframe で読み込むつもりだったが、セキュリティヘッダーでブロックされていた。本記事では、その問題の構造と Declarative Net Request(DNR) による解決手順をまとめる。


1. 行き詰まった原因

根本的な問題は、Google Gemini 側が iframe 埋め込みを拒否していることだった。Gemini のレスポンスヘッダーには次のような項目が含まれている。

X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy-Report-Only: (省略)

これらのヘッダーがあると、ブラウザが自動的に iframe 表示を拒否する。そのため、どんな JavaScript を書いても HTML レベルでは回避できなかった。


2. 試したアプローチと失敗

Chrome 拡張として一般的な3つの方法を試したが、いずれも HTTP レイヤーの制約には対抗できなかった。

ステージ アプローチ 結果
Stage 1 別ウィンドウで Gemini を開き、認証済み状態で iframe に埋め込み ❌ サイドバー内で完結しない
Stage 2 offscreenDocument 経由で iframe を生成 ❌ ヘッダーでブロック
Stage 3 content script で DOM 操作 ❌ HTTP ヘッダーは操作不可

つまり、HTML や JavaScript では解決不能だった。


3. 解決の糸口:Declarative Net Request (DNR)

突破口となったのが、Chrome 拡張 API の Declarative Net Request (DNR) だった。これはブラウザが受け取る HTTP レスポンスを横取りし、ヘッダーを追加・削除できる仕組みである。

DNR を使い、Google Gemini からのレスポンスヘッダーのうち、ブロック要因を削除することで iframe 埋め込みが可能になった。


4. 実装手順

(1) rules.json を作成

Gemini からのレスポンスヘッダーを削除するルールを定義する。

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "responseHeaders": [
        { "header": "X-Frame-Options", "operation": "remove" },
        { "header": "Content-Security-Policy", "operation": "remove" },
        { "header": "Content-Security-Policy-Report-Only", "operation": "remove" }
      ]
    },
    "condition": {
      "urlFilter": "https://gemini.google.com/*",
      "resourceTypes": ["main_frame", "sub_frame"]
    }
  }
]

(2) manifest.json に DNR 関連設定を追加

{
  "manifest_version": 3,
  "name": "Gemini Sidebar",
  "version": "1.0",
  "permissions": [
    "declarativeNetRequest",
    "declarativeNetRequestWithHeadersModification"
  ],
  "host_permissions": ["https://gemini.google.com/*"],
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules.json"
      }
    ]
  },
  "side_panel": {
    "default_path": "side_panel/panel.html"
  }
}

(3) panel.html に iframe を配置

<iframe src="https://gemini.google.com/" width="100%" height="100%"></iframe>

これでサイドバー内に Gemini を直接表示できるようになる。


5. 動作確認

拡張を再読み込み後、Chrome のサイドパネルを開くと、Gemini がそのまま埋め込まれて表示される。JavaScript 側では特別な処理は不要で、iframe だけで完結する。


6. 学び

  • 問題の原因はコードではなく HTTP レイヤーのセキュリティヘッダー にあった。
  • content scriptoffscreenDocument は DOM にしかアクセスできず、ヘッダー操作はできない。
  • DNR によって、ブラウザレベルでレスポンスヘッダーを制御することが可能。
  • 必要なのは複雑なコードではなく、「どの層の制約かを正確に見極めること」だった。

7. 補足:API を使わなかった理由

Gemini API を利用すれば同様のことは可能だが、API 経由では従量課金が発生する。サイドバーで軽く使いたいユースケースでは、公式 UI の iframe 埋め込みの方が実用的だった。


まとめ

  • Google Gemini は X-Frame-OptionsCSP によって iframe 埋め込みを拒否している。
  • その制約は JavaScript ではなくブラウザのレスポンス制御層で対処する必要がある。
  • Declarative Net Request により、拡張レベルでブロックヘッダーを削除し、サイドバー埋め込みを実現できる。

Discussion