Skip to content

필사 모드: ブラウザ拡張機能開発 2026 — Manifest V3 / Plasmo / WXT / Side Panel API / Safari Web Extensions 徹底ガイド

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

プロローグ — MV3 はついに本物になった

ブラウザ拡張機能の開発者は、5 年間「Manifest V3 はもうすぐ強制されますよ」と言われ続けた。毎回延期された。2022 年 1 月。2023 年 1 月。2024 年 1 月。そして 2024 年 6 月、Chrome はついにその約束を守った。

2024 年 6 月以降、Chrome 安定版で MV2 拡張機能は無効化された。Chrome Web Store への MV2 新規登録は、その 1 年前にすでに塞がれていた。2025 年にはエンタープライズ向けの逃げ道(`ExtensionManifestV2Availability` ポリシー)も段階的に廃止され、2026 年 5 月時点で MV2 は事実上、博物館行きの過去のものだ。

この変化の重みは、単に「マニフェストのバージョンが上がった」だけではない。

- **バックグラウンドページはサービスワーカーになった。** 常駐していたページが、イベントに応じて眠ったり目を覚ましたりするワーカーになった。

- **blocking webRequest は declarativeNetRequest に置き換わった。** uBlock Origin は Chrome から去り、代わりに uBlock Origin Lite が登場した。

- **ホスト権限はランタイムオプトインがデフォルトになった。** 「すべてのサイトへのアクセス」はもはや、インストール時に自動承認されない。

- **Side Panel API・Offscreen Documents API・Scripting API・declarativeNetRequest** — これらの新 API は、バックグラウンドページ時代には不可能だったことを可能にする。

同時に、拡張機能の開発ツールも進化した。**Plasmo** は React を一等市民として迎え入れ、**WXT** は Vite ベースで軽くて速い代替を持ち込んだ。**vite-plugin-web-extension** はもっとミニマルな道を提供する。本稿はその全体像を見ていく。

第 1 章 · 2026 年のブラウザ拡張機能 — MV3 時代の風景

まずは全体像から。

Browser Extension API

|

+-------------------+-------------------+

| | |

Chrome (Blink) Firefox (Gecko) Safari (WebKit)

| | |

manifest_version: 3 manifest_version: 3 manifest_version: 3

service_worker background.scripts background.scripts

(event page) (persistent: false)

| | |

declarativeNetRequest webRequest blocking declarativeNetRequest

(MV2 互換を維持) (限定的な webRequest)

| | |

chrome.* namespace browser.* + chrome.* browser.* (Promise)

Side Panel API sidebarAction 一部未サポート

Offscreen Documents backgroundScripts 一部未サポート

この 1 枚の図が、2026 年の拡張機能開発の本質を捉えている。

- **Chrome(および Edge・Brave・Opera のような Chromium 派生)**: MV3 のみ。blocking webRequest なし。

- **Firefox**: MV3 をサポートしつつ、blocking webRequest を一部維持(特に広告ブロッカーのため)。バックグラウンドはイベントページモデル(休眠/起動)。

- **Safari**: WebKit ベース。iOS/iPadOS でも拡張機能が動く(Safari 15+、iOS 15+)。一部 API は未サポート。

3 ブラウザは **WebExtensions** 標準(W3C Browser Extensions Community Group)を共有するが、細部で分岐する。「一度書いてどこでも動く」のスローガンは半分しか真実ではない。

拡張機能の 4 大コンポーネント

+----------------------------------------------------+

| 拡張機能パッケージ (.crx / .xpi / .pkg) |

+----------------------------------------------------+

| manifest.json (メタデータ + 権限) |

| |

| +-----------------+ +-----------------+ |

| | Service Worker | | Content Script | |

| | (background) |<-->| (ページに注入) | |

| +-----------------+ +-----------------+ |

| ^ ^ |

| | | |

| v v |

| +-----------------+ +-----------------+ |

| | Popup / UI | | Side Panel | |

| | (browser_action)| | (Chrome 114+) | |

| +-----------------+ +-----------------+ |

| |

| Offscreen Documents (DOM が必要な作業に) |

+----------------------------------------------------+

各コンポーネントは別々の実行コンテキスト。通信はメッセージパッシング(`chrome.runtime.sendMessage` 等)で行う。

第 2 章 · Manifest V3 — 2024 年 6 月の強制適用以降

最も基本的な manifest.json(Chrome MV3)

{

"manifest_version": 3,

"name": "My Extension",

"version": "1.0.0",

"description": "A simple MV3 extension",

"icons": {

"16": "icons/16.png",

"48": "icons/48.png",

"128": "icons/128.png"

},

"action": {

"default_popup": "popup.html",

"default_icon": "icons/16.png"

},

"background": {

"service_worker": "background.js",

"type": "module"

},

"content_scripts": [

{

"matches": ["https://*.example.com/*"],

"js": ["content.js"]

}

],

"permissions": ["storage", "tabs", "sidePanel"],

"host_permissions": ["https://api.example.com/*"],

"side_panel": {

"default_path": "sidepanel.html"

}

}

MV2 から MV3 への主要な差分

| 項目 | MV2 | MV3 |

| --- | --- | --- |

| Background | 常駐ページ / イベントページ | Service Worker(Firefox はイベントページ) |

| webRequest blocking | 可能 | declarativeNetRequest に置換(Firefox は一部維持) |

| ホスト権限 | インストール時に自動付与 | ランタイムオプトイン可能(`activeTab`/`optional_host_permissions`) |

| リモートコード | 許可 | 禁止(すべての JS をパッケージに同梱) |

| Content Security Policy | オブジェクト形式 | より厳格なオブジェクト形式 |

| Action API | `browser_action` + `page_action` | 統合された `action` |

| Promise API | 一部のみ | すべての chrome.* API が Promise 対応 |

リモートコード禁止は大きな変化だ。広告ブロッカーがリモートのフィルタリストを動的に評価できなくなり、分析・実験ツールがクラウドから JS を取得して実行するパターンはすべて書き直しを迫られた。

CRX フォーマット — 拡張機能はどう梱包されるか

Chrome 拡張機能は `.crx` ファイルとして配布される。これは本質的に **ヘッダ付きの ZIP ファイル**だ。

[CRX3 Magic: "Cr24"] [Version: 3] [Header Length] [Header (ProtoBuf)]

+- RSA 署名(開発者鍵)

+- ECDSA 署名(Google またはセルフ)

[拡張機能ファイルの ZIP アーカイブ]

署名鍵は拡張機能の **ID を決定する**。同じ鍵で署名すれば同じ ID、別の鍵なら別の ID。Web Store に登録すれば、Google がその鍵を管理する。

第 3 章 · Service Worker — バックグラウンドページの後継

MV3 の核心は、バックグラウンドコンテキストのモデルが変わったことだ。

Service Worker のライフサイクル

[イベント発生] -> [ワーカー起動] -> [ハンドラ実行] -> [アイドル検出]

|

v

[30 秒後に終了]

Service Worker は **休眠できる**。約 30 秒のアイドルでブラウザが終了させる。次のイベントで再び起きる。

これが意味すること:

- **グローバル変数に状態を持てない。** ワーカーが死ぬと消える。`chrome.storage.session` や `chrome.storage.local` を使う。

- **タイマーは生き残らない。** `setTimeout`/`setInterval` の代わりに `chrome.alarms` を使う。

- **WebSocket を永続的に保てない。** 切断される。必要なら Offscreen Document で保持する。

- **DOM がない。** ワーカーコンテキストでは DOM API が使えない。パースが必要なら Offscreen Document。

典型的な Service Worker パターン

// background.ts

chrome.runtime.onInstalled.addListener(() => {

console.log('Extension installed')

chrome.storage.local.set({ installedAt: Date.now() })

})

chrome.action.onClicked.addListener(async (tab) => {

if (!tab.id) return

await chrome.scripting.executeScript({

target: { tabId: tab.id },

func: () => alert('Hello from extension!'),

})

})

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {

if (msg.type === 'PING') {

sendResponse({ type: 'PONG', timestamp: Date.now() })

}

return true // 非同期応答のため

})

// 定期作業は alarms で

chrome.alarms.create('hourly-sync', { periodInMinutes: 60 })

chrome.alarms.onAlarm.addListener((alarm) => {

if (alarm.name === 'hourly-sync') {

syncData()

}

})

async function syncData() {

const res = await fetch('https://api.example.com/sync')

const data = await res.json()

await chrome.storage.local.set({ lastSync: data })

}

keepAlive アンチパターン

初期の頃、ワーカー終了に戸惑った開発者は `setInterval(() => fetch('/ping'), 20000)` でワーカーを強制的に生かそうとした。2026 年には明らかなアンチパターンだ。Chrome はそうしたワーカーも結局終了させるし、ポリシー上も推奨されない。正攻法は **状態を storage に保存し、イベントごとに起きて処理する**ことだ。

第 4 章 · declarativeNetRequest — 広告ブロックへの影響

MV3 で最も議論を呼んだ変更。blocking webRequest を declarativeNetRequest(DNR)に置き換え。

なぜ変えたのか

Chrome チームの公式見解: **性能とプライバシー**。blocking webRequest はすべてのネットワークリクエストを拡張機能ワーカーに同期評価のためルーティングしていた。それが遅く、拡張機能が全トラフィックを覗ける権限が広すぎた。

反対派の見解: その権力を拡張機能から奪い **宣言的ルール**に閉じ込めたということは、複雑な広告ブロックロジック(特にコスメティックフィルタや動的ブロック)が弱体化したという意味だ。

DNR の基本

{

"manifest_version": 3,

"name": "Simple Ad Blocker",

"version": "1.0.0",

"permissions": ["declarativeNetRequest"],

"host_permissions": ["<all_urls>"],

"declarative_net_request": {

"rule_resources": [

{

"id": "ruleset_1",

"enabled": true,

"path": "rules.json"

}

]

}

}

[

{

"id": 1,

"priority": 1,

"action": { "type": "block" },

"condition": {

"urlFilter": "||doubleclick.net^",

"resourceTypes": ["script", "image", "sub_frame"]

}

},

{

"id": 2,

"priority": 1,

"action": { "type": "redirect", "redirect": { "url": "https://example.com/blocked" } },

"condition": {

"urlFilter": "||tracker.com^",

"resourceTypes": ["main_frame"]

}

}

]

ルールは **宣言的**。拡張機能はリクエストごとに判断せず、ブラウザがルールセットを参照して決める。

制約

- **静的ルールの上限**: 単一拡張機能で 30,000 個。有効化されたすべての拡張機能の合計で 330,000 個。uBlock Origin が使っていた 100 万件超の EasyList ルールは全部入らない。

- **動的ルール**: 5,000 個。ユーザーが追加できるルール。

- **セッションルール**: 5,000 個。ブラウザ終了時に消える。

- **正規表現ルール**: マッチ時間制限が厳格。

uBlock Origin の作者 Raymond Hill は、Chrome では **uBlock Origin Lite** という MV3 互換版を別途運用しているが、「MV2 版 uBO ほどの性能は出せない」と明記している。Firefox では今もフル機能の uBO が動く(Mozilla が blocking webRequest を維持する選択をした)。

第 5 章 · Side Panel API(Chrome 114+)

2023 年 6 月の Chrome 114 で導入された Side Panel API は、拡張機能の UX を根本的に変えた。ポップアップがクリックで閉じる揮発性 UI だとすると、Side Panel は **ブラウザ横に永続する UI** だ。

基本

{

"manifest_version": 3,

"name": "Side Panel Demo",

"version": "1.0.0",

"permissions": ["sidePanel"],

"side_panel": {

"default_path": "sidepanel.html"

},

"action": {

"default_title": "Open Side Panel"

}

}

// background.ts

chrome.runtime.onInstalled.addListener(() => {

chrome.sidePanel

.setPanelBehavior({ openPanelOnActionClick: true })

.catch((error) => console.error(error))

})

// 特定のタブだけサイドパネルを有効化

chrome.tabs.onUpdated.addListener(async (tabId, info, tab) => {

if (!tab.url) return

const url = new URL(tab.url)

if (url.origin === 'https://www.example.com') {

await chrome.sidePanel.setOptions({

tabId,

path: 'sidepanel-example.html',

enabled: true,

})

} else {

await chrome.sidePanel.setOptions({

tabId,

enabled: false,

})

}

})

Side Panel の強み

- **永続性**: タブを切り替えても同じパネルが残る(タブ別にすることも可能)。

- **広さ**: ポップアップは 800x600 程度が限界だが、サイドパネルはもっと広い。

- **対話性**: ユーザーが作業を続ける間、パネルが見え続ける。

- **AI アシスタントとの相性**: チャット UI がサイドパネルに自然に収まる。

Edge もサイドパネルをサポート(Edge の Copilot がまさにこの構造)。Firefox は MV2 時代から `sidebarAction` という似た API を持っていて、MV3 でも維持。Safari は一部バージョンでサポート。

第 6 章 · Offscreen Documents + Scripting API

Offscreen Documents — DOM が必要なとき

Service Worker には DOM がない。だが拡張機能はときどき本当に DOM が必要だ — HTML パース、クリップボードアクセス、`<audio>` 再生、`DOMParser`、WebRTC など。

解決策: **Offscreen Document**。ユーザーに見えない HTML ページをバックグラウンドで立ち上げられる。

{

"permissions": ["offscreen"]

}

// background.ts

async function ensureOffscreenDocument() {

const existing = await chrome.runtime.getContexts({

contextTypes: ['OFFSCREEN_DOCUMENT'],

documentUrls: [chrome.runtime.getURL('offscreen.html')],

})

if (existing.length > 0) return

await chrome.offscreen.createDocument({

url: 'offscreen.html',

reasons: ['DOM_PARSER'],

justification: 'Parse HTML for content extraction',

})

}

chrome.action.onClicked.addListener(async () => {

await ensureOffscreenDocument()

const response = await chrome.runtime.sendMessage({

target: 'offscreen',

type: 'PARSE_HTML',

data: '<html>...</html>',

})

console.log('parsed:', response)

})

<!-- offscreen.html -->

<!doctype html>

// offscreen.ts

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {

if (message.target !== 'offscreen') return

if (message.type === 'PARSE_HTML') {

const parser = new DOMParser()

const doc = parser.parseFromString(message.data, 'text/html')

const title = doc.querySelector('title')?.textContent ?? ''

sendResponse({ title })

}

})

`reasons` は固定 enum から選ぶ: `AUDIO_PLAYBACK`、`CLIPBOARD`、`DOM_PARSER`、`DOM_SCRAPING`、`IFRAME_SCRIPTING`、`LOCAL_STORAGE`、`TESTING`、`USER_MEDIA`、`WEB_RTC`、`BLOBS`、`WORKERS`。

Scripting API — コンテンツスクリプトの未来

MV2 の `chrome.tabs.executeScript` は MV3 の `chrome.scripting.executeScript` に置き換わった。

// 関数の注入

await chrome.scripting.executeScript({

target: { tabId: 123 },

func: (color) => {

document.body.style.backgroundColor = color

},

args: ['lightblue'],

})

// ファイルの注入

await chrome.scripting.executeScript({

target: { tabId: 123, allFrames: true },

files: ['content.js'],

})

// CSS の挿入

await chrome.scripting.insertCSS({

target: { tabId: 123 },

css: 'body { filter: invert(1); }',

})

// コンテンツスクリプトの動的登録

await chrome.scripting.registerContentScripts([

{

id: 'dynamic-script',

matches: ['https://*.example.com/*'],

js: ['dynamic.js'],

runAt: 'document_idle',

},

])

`scripting` 権限 + ホスト権限(または `activeTab`)の組み合わせが必要。

第 7 章 · Plasmo — React ベースのフレームワーク

伝統的な拡張機能開発は、manifest を手書きし、webpack/rollup を設定し、ホットリロードを自分で組み上げる… 退屈だった。Plasmo はそれら全部を **Next.js のように** ほどく。

30 秒セットアップ

pnpm create plasmo my-extension

cd my-extension

pnpm dev

my-extension/

├── popup.tsx # 自動でポップアップになる

├── options.tsx # 自動でオプションページ

├── background.ts # 自動でサービスワーカー

├── contents/

│ └── plasmo.ts # コンテンツスクリプト

├── sidepanel.tsx # サイドパネル

└── package.json # マニフェストの一部

manifest.json を直接書く必要はない。ファイル名とディレクトリの規約から決まる。メタデータは package.json に書く。

{

"name": "my-extension",

"displayName": "My Extension",

"version": "1.0.0",

"description": "...",

"manifest": {

"permissions": ["storage", "tabs"],

"host_permissions": ["https://*.example.com/*"]

}

}

Plasmo の強み

- **React 一等市民**: `popup.tsx` がそのまま React コンポーネント。

- **HMR**: 拡張機能のコードを編集すると自動リロード。

- **Content Script UI**: `contents/` に React コンポーネントを置けばページに注入される UI を React で書ける。

- **Storage Hook**: `@plasmohq/storage` の `useStorage` フック。

- **Messaging**: 型安全なメッセージング。

- **クロスブラウザ**: Chrome / Firefox / Edge ビルドを分離。

Content Script UI の例

// contents/inline.tsx

export const config: PlasmoCSConfig = {

matches: ['https://*.example.com/*'],

}

export default function ContentUI() {

const [count, setCount] = useState(0)

return (

)

}

これが自動でページに注入されて動く。Shadow DOM 隔離もオプションで可能。

Plasmo の弱み

- 抽象が厚いので、manifest を細かく制御したいときに不自由なときがある。

- ビルド成果物が重くなる傾向(React ランタイム同梱)。

- かつて活発だった BrowserStack(親会社)のサポートが減ったとの観測。とはいえ OSS コミュニティは健在。

第 8 章 · WXT — Vite ベースの新鋭

Plasmo が「React/Next.js」アナロジーなら、**WXT** は「Vite/Nuxt」のそれだ。より軽く、より速く、より自由。2023 年の登場以降、2025-2026 年に急成長。

基本構造

pnpm dlx wxt@latest init my-extension

cd my-extension

pnpm dev

my-extension/

├── entrypoints/

│ ├── background.ts

│ ├── content.ts

│ ├── popup/

│ │ └── index.html

│ └── options/

├── wxt.config.ts

└── package.json

// wxt.config.ts

export default defineConfig({

manifest: {

permissions: ['storage', 'tabs'],

host_permissions: ['https://*.example.com/*'],

},

modules: ['@wxt-dev/module-react'],

})

// entrypoints/background.ts

export default defineBackground(() => {

console.log('Hello from background!')

browser.runtime.onMessage.addListener((msg) => {

return Promise.resolve({ pong: true })

})

})

WXT の強み

- **Vite ベース**: ビルドが速く、ESM フレンドリー。

- **フレームワーク非依存**: React モジュール、Vue モジュール、Svelte もある。

- **自動 import**: Nuxt スタイルの自動 imports — `browser`、`defineBackground`、`defineContentScript` がグローバルとして自動で入ってくる。

- **クロスブラウザ**: 一度書いて Chrome/Firefox/Safari ビルドを分離。

- **Web-ext 統合**: 開発モードでブラウザを自動起動しホットリロード。

WXT vs Plasmo 一行比較

| 項目 | Plasmo | WXT |

| --- | --- | --- |

| 基盤 | Parcel/Next.js 風 | Vite/Nuxt 風 |

| フレームワーク | React 中心(Vue/Svelte も可) | モジュールで対等 |

| Manifest 制御 | 規約優先 | 直接記述可能 |

| 学習曲線 | 低(規約に従えばよい) | 中(設定は明示的) |

| コミュニティ | 2022-2024 活発 → 停滞 | 2024-2026 急成長 |

| 推奨シナリオ | React 中心の素早いプロトタイプ | 長期運用・多フレームワーク・精密制御 |

2026 年時点で新規スタートなら **WXT が優勢**というのが多数意見。Plasmo もまだ良いが、勢いは WXT に移った。

第 9 章 · vite-plugin-web-extension — ミニマル選択肢

フレームワークの重さが負担なら、**vite-plugin-web-extension** が答えかもしれない。Vite プラグイン 1 つで拡張機能のビルドを解決する。

pnpm add -D vite vite-plugin-web-extension

// vite.config.ts

export default defineConfig({

plugins: [

webExtension({

manifest: path.resolve('manifest.json'),

additionalInputs: {

html: ['sidepanel.html'],

},

}),

],

})

`manifest.json` を直接書き、Vite がエントリを自動発見してビルドする。規約による自動化はないが、**Vite のすべてをそのまま使える。**

選択基準

- 「フレームワーク依存なしで軽く」→ vite-plugin-web-extension

- 「Vite エコシステム + ある程度の自動化」→ WXT

- 「React 中心で素早く」→ Plasmo

- 「フレームワーク不使用の素のまま」→ webpack/Rollup 直接(現在は非推奨)

第 10 章 · Firefox MV3 — 一部 MV2 を維持

Mozilla は Chrome の MV3 推進に微妙な反対の立場だった。結果として Firefox MV3 は Chrome MV3 に似ているが、部分的に異なる。

Firefox MV3 の差別点

- **Background**: Service Worker ではなく **イベントページ**モデル。休眠可能だが DOM を持てる。

- **blocking webRequest**: 維持。Firefox で uBlock Origin がフル機能で動く理由。

- **ホスト権限**: Chrome 同様、ランタイムオプトイン可能。

- **API 名前空間**: `browser.*`(Promise ベース)が標準。`chrome.*` も互換のため動く。

同じコードを両方で動かす

// 共通ヘルパー

const api = (typeof browser !== 'undefined' ? browser : chrome) as typeof browser

async function getCookie(url: string, name: string) {

return api.cookies.get({ url, name })

}

`webextension-polyfill` ライブラリを使えば `chrome.*` も Promise を返すようにできる。

await browser.storage.local.set({ key: 'value' })

Plasmo と WXT はこの polyfill または同等の抽象を内蔵している。

manifest の差分

Firefox は `manifest.json` に `browser_specific_settings` を要求することがある。

{

"manifest_version": 3,

"name": "Cross-Browser Ext",

"version": "1.0.0",

"background": {

"scripts": ["background.js"],

"type": "module"

},

"browser_specific_settings": {

"gecko": {

"id": "myext@example.com",

"strict_min_version": "115.0"

}

}

}

WXT はこの分岐を自動で処理してくれる。

第 11 章 · Safari Web Extensions — Mac/iOS

Apple は 2020 年の Safari 14 で Web Extensions を受け入れた。2021 年の iOS 15 から **iPhone/iPad でも** 拡張機能が可能になった。これは小さな出来事ではない — モバイル拡張機能エコシステムの事実上初の例だ。

Safari Web Extension の特徴

- **Xcode プロジェクトでラップ**: Web 拡張機能コードを Xcode アプリコンテナに入れてビルド。App Store 経由で配布。

- **`browser.*` API を使用**: Mozilla 互換の名前空間。

- **一部 API は未サポート**: `chrome.identity`、`chrome.sidePanel`、`chrome.declarativeNetRequest` の一部、`chrome.offscreen` などが未対応/制限。

- **iOS 制約**: モバイルでは権限モデルがより厳格。ユーザーの明示的な有効化が必要。

変換ツール — safari-web-extension-converter

既存の Chrome/Firefox 拡張機能を Safari に移植するとき:

xcrun safari-web-extension-converter /path/to/extension

このコマンドが Xcode プロジェクトの足場を作る。manifest を読み、互換のない API を警告で知らせる。

配布

- **macOS**: Safari Extensions Gallery は 2022 年に閉鎖。今はすべての Safari 拡張機能が **App Store** 経由で配布。

- **iOS**: 同じく App Store。

- **Apple Developer Program** メンバーシップが必要(年 $99)。

この参入障壁が、Safari 拡張機能のエコシステムが Chrome ほど豊かでない理由。だが iOS で拡張機能が可能な唯一の道なので、ますます重要になる。

第 12 章 · Edge Add-ons — Microsoft

Edge は Chromium ベースなので Chrome 拡張機能がほぼそのまま動く。だが Microsoft は **Edge Add-ons** という独自ストアを運営している。

Edge に拡張機能を載せる

1. Microsoft Partner Center に開発者アカウント登録(無料)。

2. 同じ .zip パッケージをアップロード。

3. レビュー。

Chrome 拡張機能との互換性

- **ほぼ 100%**: Chrome MV3 拡張機能は Edge でほぼそのまま動く。

- **Edge 特化 API**: 一部の `chrome.*` の代わりに `edge.*` として公開される API があるが、大部分は同じ。

- **サイドバー**: Edge は Sidebar API を積極活用(Copilot 等)。

Edge でだけ可能なこと

- **強制インストールポリシー**: エンタープライズ環境で IT 管理者が強制配布可能。

- **Bing Chat / Copilot 統合**: マイクロソフトのサービスとの統合 API。

- **Edge Workspaces**: ワークスペースに紐づく拡張機能。

Edge のシェアは世界で 5-7% 程度と小さく見えるが、**エンタープライズ環境では Chrome に劣らず重要**。B2B 拡張機能なら Edge ストア登録は必須。

第 13 章 · AI 連携拡張機能 — Side Panel + LLM

2026 年の拡張機能開発で最も興味深い流れは **AI 連携**だ。ChatGPT ブラウザ拡張機能、Perplexity、Glasp、Compose AI、Wisedocs といったツールがみな同じパターンを使う。

典型的な AI 拡張機能アーキテクチャ

[Web Page]

| (ユーザーアクション: テキスト選択、ページ要約リクエスト)

v

[Content Script] -- ページコンテキスト抽出 -->

|

v

[Service Worker] -- LLM API 呼び出し(Claude / OpenAI / 自前) -->

|

v

[Side Panel] -- 結果をストリーミング表示 -->

|

v

[ユーザー]

Side Panel + ストリーミング UI

// sidepanel/App.tsx (WXT + React)

export default function SidePanelApp() {

const [messages, setMessages] = useState<{ role: 'user' | 'assistant'; content: string }[]>([])

const [input, setInput] = useState('')

async function send() {

const userMsg = { role: 'user' as const, content: input }

setMessages((m) => [...m, userMsg])

setInput('')

const response = await chrome.runtime.sendMessage({

type: 'ASK_LLM',

messages: [...messages, userMsg],

})

setMessages((m) => [...m, { role: 'assistant', content: response.text }])

}

return (

{messages.map((m, i) => (

{m.content}

))}

value={input}

onChange={(e) => setInput(e.target.value)}

onKeyDown={(e) => e.key === 'Enter' && send()}

className="flex-1 border p-2"

/>

Send

)

}

// background.ts

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {

if (msg.type === 'ASK_LLM') {

callLLM(msg.messages).then((text) => sendResponse({ text }))

return true // async

}

})

async function callLLM(messages: any[]) {

const apiKey = (await chrome.storage.local.get('apiKey')).apiKey

const res = await fetch('https://api.anthropic.com/v1/messages', {

method: 'POST',

headers: {

'x-api-key': apiKey,

'anthropic-version': '2023-06-01',

'content-type': 'application/json',

},

body: JSON.stringify({

model: 'claude-opus-4-7',

max_tokens: 1024,

messages,

}),

})

const data = await res.json()

return data.content[0].text

}

ページコンテキストの活用

// content.ts

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {

if (msg.type === 'GET_PAGE_TEXT') {

sendResponse({

title: document.title,

url: location.href,

text: document.body.innerText.slice(0, 50000),

})

}

})

// sidepanel: 現在のタブのテキストを取得して要約リクエスト

async function summarizeCurrentPage() {

const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })

if (!tab.id) return

const pageData = await chrome.tabs.sendMessage(tab.id, { type: 'GET_PAGE_TEXT' })

// ... LLM 呼び出し

}

Chrome Built-in AI(Gemini Nano)

2024 年から Chrome は **オンデバイス Gemini Nano** を拡張機能で使えるよう提供を開始した(Origin Trial → GA)。`chrome.ai` という名前空間が段階的に解放されており、2026 年 5 月時点で一部 API(prompt、summarize、translate)が安定チャネル。

// 可用性確認

const availability = await ai.languageModel.availability()

if (availability === 'available') {

const session = await ai.languageModel.create()

const response = await session.prompt('Summarize: ...')

}

長所: ローカル実行、無料、プライバシー。短所: モデルが小さいので精緻な作業には限界。

ビジネスモデル

AI 拡張機能のよくあるビジネスモデル 4 つ。

1. **BYOK(Bring Your Own Key)**: ユーザーが自分の API キーを入れる。開発者にトラフィックコストがかからない。

2. **無料枠 + 有料**: 一定回数まで無料、以降サブスク。

3. **純粋 SaaS**: 拡張機能はクライアント、認証/課金は自前バックエンド。

4. **オンデバイス**: Chrome Built-in AI または自前の量子化モデルでコスト 0。

第 14 章 · 韓国 / 日本 — Kakao、Naver、pixiv

韓国

- **Kakao 拡張機能**: KakaoTalk シェア、Daum 検索、Kakao Bank 認証など Kakao エコシステムと連携するユーティリティ拡張機能が多い。Kakao は公式に拡張機能 SDK を出してはいないが、Kakao API と OAuth を拡張機能から使う事例が豊富。

- **Naver 拡張機能**: Naver 検索結果の強化、Naver Cloud、Naver 辞書、Naver Shopping 価格比較(Signal、Danawa のような比較ショッピング拡張機能)が代表的。

- **Toss / Karrot**: 決済・中古取引領域で社内向け拡張機能を運用する事例が多い。

- **翻訳器**: Papago、Naver 翻訳の拡張機能。モバイル優勢の韓国で、デスクトップ翻訳は今も有効な市場。

- **広告ブロック**: AdBlock Plus の EasyList Korea が活発。

日本

- **pixiv 拡張機能**: pixiv 作品ダウンローダ、タグ強化、イラストビューア補助といった非公式 OSS が多数。ただし pixiv ToS と衝突する可能性が常にあるので慎重に。

- **Niconico 拡張機能**: 動画ダウンロード補助、コメント強化、ビューア改善など。

- **Mercari / Rakuma**: 中古マーケットプレイスの価格追跡拡張機能。

- **Rakuten 拡張機能**: 楽天ポイントの自動計算、クーポン自動適用。

- **日本語学習**: Rikaikun、10ten Japanese Reader、Yomichan(現在は Yomitan としてフォーク) — 日本語学習者には事実上必携。

- **AdGuard**: 日本語 EasyList Japan のメンテナンスが活発。

Kakao/Naver OAuth in 拡張機能

// chrome.identity.launchWebAuthFlow による OAuth

async function loginWithKakao() {

const clientId = 'YOUR_KAKAO_REST_API_KEY'

const redirectUri = chrome.identity.getRedirectURL()

const authUrl =

`https://kauth.kakao.com/oauth/authorize?` +

`client_id=${clientId}&` +

`redirect_uri=${encodeURIComponent(redirectUri)}&` +

`response_type=code`

const responseUrl = await chrome.identity.launchWebAuthFlow({

url: authUrl,

interactive: true,

})

const code = new URL(responseUrl!).searchParams.get('code')

// code をバックエンドに送ってトークン交換

return code

}

`chrome.identity.getRedirectURL()` が返す URL を Kakao 開発者コンソールの Redirect URI に登録する必要がある。形式は `https://<extension-id>.chromiumapp.org/`。

韓国・日本の拡張機能開発のリアル

- 決済モジュール(Kakao Pay、Naver Pay、PayPay)を拡張機能から直接呼ぶのはほぼ不可能。バックエンド経由が必須。

- インアプリブラウザ(KakaoTalk、Line の内部ブラウザ)は拡張機能をサポートしない。モバイルで拡張機能を狙うなら iOS の Safari Web Extension がほぼ唯一の選択肢。

- 日本は保守的な市場なので、拡張機能の採用率は韓国・米国より低い傾向。代わりに一度入れると長く使う。

第 15 章 · 誰が何を選ぶべきか — 個人 / チーム / クロスブラウザ / AI 連携

個人開発者 + 素早いプロトタイプ

- **推奨スタック**: Plasmo または WXT。

- **理由**: HMR、規約、ボイラープレート最小化。1 週間以内に使える拡張機能を出せる。

- **ターゲットブラウザ**: Chrome 優先、うまくいけば Firefox/Edge へ拡張。

- **注意点**: Web Store レビュー時間(平均 1-7 日)を見込む。

小規模チーム + 長期プロダクト

- **推奨スタック**: WXT。

- **理由**: Vite ベースでビルドが速く、TS フレンドリー、マルチブラウザサポートが自然。

- **推奨**: Storybook や Chromatic で UI 回帰テスト。E2E は Playwright の拡張機能モード(BrowserContext に拡張機能をロード)。

クロスブラウザ優先

- **推奨スタック**: WXT + `webextension-polyfill`。

- **ビルドマトリクス**: Chrome MV3、Firefox MV3、Edge MV3、Safari(別 Xcode プロジェクト)。

- **注意点**: declarativeNetRequest 使用時は Firefox では webRequest にフォールバック分岐が必要なことがある。

AI 連携拡張機能

- **UI**: Side Panel 優先。ポップアップは短いアクション専用。

- **LLM**: BYOK モデルから始めて、ユーザー数が増えたら自前バックエンドを検討。

- **オンデバイス**: Chrome Built-in AI(Gemini Nano)も選択肢。無料という点が強い。

- **コンテキスト抽出**: content script でページテキスト + メタデータを取得 → Service Worker から LLM 呼び出し → Side Panel でストリーミング表示。

広告ブロック / コンテンツフィルタリング

- **MV3 Chrome**: DNR の上限内で可能。フル機能は難しい。

- **Firefox**: blocking webRequest でフル機能可能。uBlock Origin が Firefox でまだ生きている理由。

- **推奨**: Firefox をメインターゲット、Chrome は Lite 版で分岐。

エンタープライズ / B2B

- **Chrome**: エンタープライズポリシーで強制配布可能(`force_install`)。

- **Edge**: 同じ方式。Microsoft 365 環境で自然。

- **推奨**: 会社ドメインの SAML SSO 連携が可能なバックエンドを構築。拡張機能はクライアント役。

第 16 章 · 拡張機能セキュリティ — 何に気をつけるか

ホスト権限は最小に

{

"permissions": ["activeTab"],

"optional_host_permissions": ["https://*.example.com/*"]

}

`<all_urls>` はレビューで strict に見られる。`activeTab` を優先し、広範な権限が本当に必要なら `optional_host_permissions` でランタイム要求。

CSP を厳格に

{

"content_security_policy": {

"extension_pages": "script-src 'self'; object-src 'self'"

}

}

`unsafe-eval`、`unsafe-inline` は MV3 で禁止。外部 CDN スクリプトも禁止(すべての JS をパッケージに同梱しなければならない)。

externally_connectable

Web ページが拡張機能にメッセージを送れるようにするには:

{

"externally_connectable": {

"matches": ["https://yoursite.com/*"]

}

}

これを通じて自社サイトが拡張機能と通信するパターンが可能だが、ドメインを狭く取らないとセキュリティホールになる。

XSS 防御

- content script でページ DOM に何かを注入する際は `textContent` を使い、`innerHTML` は避ける。

- React を使えば基本的に安全だが、`dangerouslySetInnerHTML` は慎重に。

- Trusted Types の適用を検討。

個人情報

- API キーは `chrome.storage.local` に保存しつつ、可能なら BYOK またはバックエンド経由のトークン交換。

- ページコンテンツを外部に送るときはユーザーに明示的に伝え、オプトインさせる。

- Chrome Web Store のデータ使用開示ポリシーを必ず守る — 違反すると拡張機能がブロックされる。

第 17 章 · Chrome Web Store ポリシー — 通すコツ

よくある却下理由

1. **権限過多**: `<all_urls>` または広範なホスト権限の正当性不足。

2. **単一目的違反**: 1 拡張機能は明確な単一目的。「複数ユーティリティを束ねた」は却下事由。

3. **コンテンツポリシー**: 広告挿入、検索結果操作、アフィリエイト横取りは strict に禁止。

4. **ユーザーデータポリシー**: データ収集時に privacy policy URL 必須、明示的な開示。

5. **ソースコード難読化**: 意図的な難読化は禁止。minify は OK だが readable でなければならない。

6. **決済回避**: Chrome Web Store Payments を使わず外部決済で迂回するのは制限。

レビュー時間

- **新規登録**: 平均 1-7 日、長いと 2-3 週間。

- **更新**: 通常 1-2 日。

- **センシティブな権限(`<all_urls>`、`tabs`、`downloads`)があると延びる**。

ベータチャネル活用

`unlisted` または `private` 配布でベータテスターに先に配布した後、public に切り替え。レビューはもう一度通る。

ユーザーデータ開示(Privacy Practices)

Chrome Web Store は次を明示的に開示するよう要求:

- どんなユーザーデータを扱うか

- なぜ扱うか

- どう保護するか

- どこに送信するか

虚偽の開示は即時のブロック事由。

第 18 章 · テストと CI/CD

ユニットテスト

// Vitest でヘルパー関数をテスト

global.chrome = {

storage: {

local: {

set: vi.fn(),

get: vi.fn().mockResolvedValue({}),

},

},

alarms: {

create: vi.fn(),

onAlarm: { addListener: vi.fn() },

},

} as any

describe('syncData', () => {

it('saves data to storage', async () => {

await syncData()

expect(chrome.storage.local.set).toHaveBeenCalled()

})

})

E2E — Playwright with Extension

test('extension loads', async () => {

const pathToExtension = path.resolve(__dirname, '../.output/chrome-mv3')

const context = await chromium.launchPersistentContext('', {

headless: false,

args: [

`--disable-extensions-except=${pathToExtension}`,

`--load-extension=${pathToExtension}`,

],

})

const page = await context.newPage()

await page.goto('https://example.com')

// content script が注入されたか確認

const injected = await page.evaluate(() => (window as any).__MY_EXT_LOADED__)

expect(injected).toBe(true)

await context.close()

})

CI/CD

.github/workflows/release.yml

name: Build and Release

on:

push:

tags: ['v*']

jobs:

build:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: pnpm/action-setup@v3

- uses: actions/setup-node@v4

with:

node-version: 20

cache: 'pnpm'

- run: pnpm install

- run: pnpm test

- run: pnpm build --target chrome-mv3

- run: pnpm build --target firefox-mv3

- uses: actions/upload-artifact@v4

with:

name: extensions

path: .output/

publish-chrome:

needs: build

runs-on: ubuntu-latest

steps:

- uses: actions/download-artifact@v4

- name: Upload to Chrome Web Store

uses: PlasmoHQ/bpp@v3

with:

keys: $TEST_KEYS_PLACEHOLDER

chrome-file: extensions/chrome-mv3.zip

`PlasmoHQ/bpp`(Browser Plugin Publisher)アクションが Chrome/Firefox/Edge への自動アップロードをサポート。

第 19 章 · よくある落とし穴

「Service Worker が死んで作業が途切れる」

- 原因: 30 秒のアイドル後に終了。

- 対処: 状態を `chrome.storage` に保存。長時間作業は `chrome.alarms` か `chrome.notifications` で進捗表示。

「WebSocket が切れる」

- 原因: Service Worker 終了。

- 対処: Offscreen Document に WebSocket を置き、ワーカーとメッセージパッシング。

「content script からページの変数にアクセスできない」

- 原因: Content Script は **隔離コンテキスト(isolated world)** で実行。ページの `window` は見えない。

- 対処: `chrome.scripting.executeScript` で `world: 'MAIN'` オプション、または script タグ注入。

「Chrome では動くが Firefox では動かない」

- 原因: API 差分(特に DNR 制限、ホスト権限モデル)。

- 対処: WXT/Plasmo のクロスブラウザビルド、`webextension-polyfill`、分岐コード。

「レビューで却下された — 権限が過剰」

- 原因: `<all_urls>` または広範なホスト権限。

- 対処: `activeTab` 優先、本当に必要なドメインだけ明示。

「拡張機能が突然無効化された — ポリシー違反」

- 原因: データ使用開示の欠落、単一目的違反など。

- 対処: Chrome Web Store 開発者ダッシュボードで違反事由を確認、修正後に再提出。

第 20 章 · エピローグ — 拡張機能は死んだのか、生きているのか

2020 年代初頭には「PWA が拡張機能を置き換える」という言説があった。2026 年現在、その予測は半分しか当たっていない。PWA は拡張機能の一部の領域(インストール型 Web アプリ)を取ったが、**ブラウザのすべてのページとインタラクションする能力**は拡張機能だけが持つ。だから拡張機能は死なない。

むしろ MV3 の強制適用と AI の隆盛が **拡張機能のルネサンス**をもたらした。ChatGPT、Claude、Perplexity の拡張機能は数百万ユーザーを集め、毎日新しい AI 補助拡張機能がリリースされる。Side Panel API はチャット UI のためのキャンバスになり、ページコンテキストを自由に LLM に送れることは拡張機能だけの強みだ。

2026 年の拡張機能開発の推奨を一行に圧縮すれば:

> **WXT か Plasmo で始めよ。Manifest V3、Service Worker、Side Panel API を身につけよ。Chrome を最初に、Firefox/Edge は自動ビルドで、Safari は市場があれば別プロジェクトで。AI 連携なら Side Panel + LLM API から。権限は最小に、レビューポリシーは正確に、ユーザーデータ開示は正直に。**

これが 2026 年 5 月時点での正解。1 年後にはまた変わる — それが拡張機能の世界だ。

参考文献 / References

- Chrome Extensions Manifest V3 — [https://developer.chrome.com/docs/extensions/mv3/intro/](https://developer.chrome.com/docs/extensions/mv3/intro/)

- MV2 to MV3 Migration — [https://developer.chrome.com/docs/extensions/migrating/](https://developer.chrome.com/docs/extensions/migrating/)

- Service Workers in Extensions — [https://developer.chrome.com/docs/extensions/develop/concepts/service-workers](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers)

- declarativeNetRequest API — [https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest](https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest)

- Side Panel API — [https://developer.chrome.com/docs/extensions/reference/api/sidePanel](https://developer.chrome.com/docs/extensions/reference/api/sidePanel)

- Offscreen Documents API — [https://developer.chrome.com/docs/extensions/reference/api/offscreen](https://developer.chrome.com/docs/extensions/reference/api/offscreen)

- Scripting API — [https://developer.chrome.com/docs/extensions/reference/api/scripting](https://developer.chrome.com/docs/extensions/reference/api/scripting)

- Chrome Built-in AI — [https://developer.chrome.com/docs/ai/built-in](https://developer.chrome.com/docs/ai/built-in)

- Plasmo — [https://docs.plasmo.com/](https://docs.plasmo.com/)

- WXT — [https://wxt.dev/](https://wxt.dev/)

- vite-plugin-web-extension — [https://github.com/aklinker1/vite-plugin-web-extension](https://github.com/aklinker1/vite-plugin-web-extension)

- Mozilla WebExtensions — [https://extensionworkshop.com/](https://extensionworkshop.com/)

- Firefox MV3 — [https://extensionworkshop.com/documentation/develop/manifest-v3-migration-guide/](https://extensionworkshop.com/documentation/develop/manifest-v3-migration-guide/)

- Safari Web Extensions — [https://developer.apple.com/documentation/safariservices/safari_web_extensions](https://developer.apple.com/documentation/safariservices/safari_web_extensions)

- Edge Add-ons — [https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/](https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/)

- webextension-polyfill — [https://github.com/mozilla/webextension-polyfill](https://github.com/mozilla/webextension-polyfill)

- uBlock Origin Lite — [https://github.com/uBlockOrigin/uBOL-home](https://github.com/uBlockOrigin/uBOL-home)

- Chrome Web Store Developer Program Policies — [https://developer.chrome.com/docs/webstore/program-policies/](https://developer.chrome.com/docs/webstore/program-policies/)

- PlasmoHQ BPP (Browser Plugin Publisher) — [https://github.com/PlasmoHQ/bpp](https://github.com/PlasmoHQ/bpp)

- Yomitan (formerly Yomichan) — [https://github.com/yomidevs/yomitan](https://github.com/yomidevs/yomitan)

현재 단락 (1/739)

ブラウザ拡張機能の開発者は、5 年間「Manifest V3 はもうすぐ強制されますよ」と言われ続けた。毎回延期された。2022 年 1 月。2023 年 1 月。2024 年 1 月。そして 202...

작성 글자: 0원문 글자: 27,763작성 단락: 0/739