summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-09-02 03:14:05 +0900
committertanaka takashi <isazaka@gmail.com>2026-09-02 03:14:05 +0900
commit82bb55f66071b452e3bc7adeb09eafb5003de11f (patch)
tree01d48a68273594fc2ed480d837724562dd6fb05d /doc
parentf5439335e1d18ee577e2b7c1e67f7844f9f235ac (diff)
feat: PDF Viewer translation via context menu and two-mode Action Popupv0.1.0
Add Firefox built-in PDF Viewer support using only public WebExtensions APIs: a selection context menu ("選択したテキストを翻訳") starts a translation and the toolbar Action Popup shows the streaming result. No content-script injection into the privileged PDF Viewer, no PDF.js internals, no separate windows, no custom viewer (Specifications.md §4.6). Specification alignment (Phase 1) - Specifications.md: new requirements PDF-01..04 (PDF selection translation), CTX-01..03 (context menu translation), POP-01..06 (popup two modes); SEL-06 updated to route PDF through the menus API; section 13.4 adds a 14-item manual PDF verification checklist; section 15 removes "Firefox内蔵PDF Viewerへの対応" from out-of-scope - Design.md: architecture diagram with the menus path, menu-translation component, menus permission, popup translation-result state machine (idle/starting/streaming/completed/error/cancelled), shared translation state (§9.2), context-menu integration phase, test plan and API audit checklist items (menus permission scope, no PDF Viewer internals) - Security.md: menus permission restricted to selection-context translation; PDF Viewer treated as privileged browser UI (no injection, no wrappedJSObject/cloneInto/exportFunction, no internal DOM access); menus.OnClickData.selectionText treated as untrusted input passing the same validation pipeline; popup result rendering stays textContent-only Background (Phase 2) - translation-service.js (new): the single translation pipeline shared by every entry point - selection validation (type/trim/empty/control chars/max length), prompt building, Ollama request, session/abort, and the shared translation state (requestId, sourceText, translatedText, status, model, error, startedAt, source). "source" is metadata only; a PDF-specific translation engine does not exist - menu-translation.js (new): validates menus.OnClickData.selectionText and starts a sink-less translation observed via GET_POPUP_STATE - message-router.js: routes port translations through the shared service; new runtime messages GET_POPUP_STATE and REGENERATE_LAST; popup-state port handler broadcasts live state (initial push + every chunk/status change) so an open popup shows streaming progress in real time - index.js: registers the selection context menu (contexts: ["selection"] only, no menus.onShown) and calls browser.action.openPopup() synchronously inside the click user gesture, catching failures so a popup-open problem never breaks the translation itself - manifest.json: permissions now ["storage", "menus"] - settings-service.js: __resetCacheForTests hook for test isolation Popup two-mode UI (Phase 3) - popup.html/js/css: mode nav (設定 / 翻訳). Translation mode shows status, model, streaming text (textContent only), user-safe error, and copy (Clipboard API only, no execCommand fallback), regenerate (new requestId reusing the stored source text) and a settings shortcut. Closing the popup never cancels the request; reopening shows the latest text and continues receiving chunks live. Initial mode: active (starting/streaming) translation wins, otherwise the last tab the user picked is restored (storage.local.popupMode, default settings) Fresh-install model auto-selection - translation-service.js ensureModelConfigured(): when storage.model is empty, pick the first model from the running Ollama and persist it before translating, mirroring the popup/options auto-select. A user who never opened any settings UI can translate immediately; when Ollama is unreachable a clear OLLAMA_UNAVAILABLE message is shown User-safe error details - detailForUser(): strips URLs (no internal host names echoed to the UI), caps length at 120 chars, drops full JSON response bodies, and hides meaningless low-level network errors so the popover/popup shows the base message plus at most a short, safe detail Tests: 85 passed, 0 failed - selectionText validation (untrusted input pipeline) - menu selection -> translation -> popup state streaming/completed - empty selection rejected without starting a request - streaming status transitions observed through GET_POPUP_STATE - Ollama unreachable surfaces a user-safe error into popup state - newer request replaces older state (abort semantics) - regenerateLast reuses source text with a fresh requestId - popup-state port receives live broadcasts; wrong port names ignored - fresh-install auto-select persists the first Ollama model - error detail sanitization (URL stripping, length cap, JSON drop) - popup/options smoke tests updated for the two-mode markup - setupRouter resets the shared settings cache for test isolation Docs: README documents the two-mode popup and PDF flow; Testing-Results records automated results and the manual PDF checklist (to be executed). No git-side surprises: this commit was created explicitly on request (requirement 18 of the PDF task had deferred commits until now).
Diffstat (limited to 'doc')
-rw-r--r--doc/Design.md142
-rw-r--r--doc/Security.md32
-rw-r--r--doc/Specifications.md60
-rw-r--r--doc/Testing-Results.md27
4 files changed, 229 insertions, 32 deletions
diff --git a/doc/Design.md b/doc/Design.md
index 20de3e5..bc7f1ff 100644
--- a/doc/Design.md
+++ b/doc/Design.md
@@ -2,29 +2,32 @@
## 1. 設計概要
-本拡張は、Webページとの接点をContent Scriptに限定し、権限を伴うOllama通信と設定管理をBackgroundへ分離する。
+本拡張は、内容の翻訳接点をContent Script、権限の Ollama comunicación y administración de configuración を Background 内に分離する。Firefox内蔵PDF Viewerなどの content script 非対応ページおよび通常Webページの両方で、selectionベースの翻訳を context menuから実行できるようにする。
```text
-Web Page
- │
- │ selection
- ▼
-Content Script
- │ runtime.Port
- ▼
-Background
- │ fetch / HTTP
- ▼
-Ollama
- │ streaming
- ▼
-Background
- │ runtime.Port
- ▼
-Content Script
- │
- ▼
-Shadow DOM Popover
+Web Page / PDF Viewer
+ │
+ │ selection
+ ▼
+ selection経由
+ │
+ ├─ Content Script (通常ページのみ: 翻訳アイコン → Shadow DOM Popover)
+ ▼ │
+ ├─ menus API (任意ページ: PDF Viewer含むselection → context menu → Action Popup)
+ │ │
+ ▼ ▼
+ Background
+ │ fetch / HTTP
+ ▼
+ Ollama
+ │ streaming
+ ▼
+ Background + TranslationSession
+ │ runtime.Port / popup state
+ ▼
+ UI
+ ├─ Content Script: Shadow DOM Popover
+ └─ Action Popup: 翻訳結果モード
```
設計原則:
@@ -33,7 +36,7 @@ Shadow DOM Popover
- Ollama通信はBackgroundのみ(Ollama REST API + Fetch API + NDJSON streaming)。
- UI文字列はHTMLとして評価しない。
- 翻訳要求は常に1-shot。
-- 同時翻訳は1件に制限。
+- 同時翻訳は1件に制限(新規要求で旧要求abort)。
- 設定値はUIだけでなくBackgroundでも検証する。
- Ollama接続先は初期版ではlocalhost / 127.0.0.1 + 任意ポートのみ許可する。
@@ -47,11 +50,12 @@ Shadow DOM Popover
| `content/action-button` | 翻訳アイコン表示/非表示 |
| `content/translation-popover` | 翻訳結果UI、stream描画、copy/regenerate/close |
| `content/port-client` | Backgroundとの長期接続 |
-| `background/message-router` | Content Scriptからのrequest受信・validation |
+| `background/message-router` | Content Scriptからのrequest受信・validation(popup state 共有) |
+| `background/menu-controller` | context menu登録・selectionText取得・popup開き・selection経由翻訳 |
| `background/translation-session` | requestId、AbortController、active stream管理 |
| `background/ollama-client` | Ollama REST API呼び出し(fetch + NDJSON streaming) |
| `background/settings-service` | storage読書き、default、validation |
-| `popup` | 有効/無効、簡易設定、Options起動 |
+| `popup` | 設定UI + 翻訳結果モード(streaming表示、copy/regenerate、error表示) |
| `options` | 全設定UI |
| `shared/settings-schema` | 設定型、既定値、範囲 |
| `shared/messages` | runtime message型定義 |
@@ -113,7 +117,7 @@ Shadow DOM Popover
"manifest_version": 3,
"name": "Selected Text Translator",
"version": "0.1.0",
- "permissions": ["storage"],
+ "permissions": ["storage", "menus"],
"host_permissions": ["http://localhost/*", "http://127.0.0.1/*"],
"background": {
"scripts": ["background.js"],
@@ -143,7 +147,7 @@ Shadow DOM Popover
- `scripting` 権限は初期版では付与しない。
- `tabs` 権限は付与しない。
- localhost以外のhost permissionは初期版では付与しない。
-- Firefoxのmatch patternはポート番号指定に対応していない(Firefox bug 1362809 / 1468162)。`http://localhost/*` および `http://127.0.0.1/*` は任意ポートに一致するため、実際に通信してよい接続先はアプリ側Host validatorで `http://localhost:<port>` / `http://127.0.0.1:<port>` のみに限定する。
+- `menus` permission は selection context の翻訳のためのみに使用する。
---
@@ -250,6 +254,41 @@ ERROR
- Abortされた旧requestのchunkはrequestId不一致なら無視。
- Error時は再生成可能な状態に戻す。
+### 7.4 Action Popup 翻訳結果モード
+
+Pdf viewerやcontext menu経由翻訳では Action Popup が翻訳結果を表示する。状態機械と表示内容はPopoverと同様。
+
+状態:
+
+```text
+IDLE
+STARTING
+STREAMING
+COMPLETED
+ERROR
+CANCELLED
+```
+
+* `starting`: 翻訳 request を backgroundへ送信し、まだ started/chunk を受け取っていない状態
+* `streaming`: chunk受信中
+* `completed`: chunk完了(最後のchunkはdone:trueと推定)
+* `error`: ollamaエラーまたは request中止
+* `cancelled`: ユーザー cancel(request abort または新規 request abort)
+
+表示内容:
+
+| 要素 | 内容 |
+|---|---|
+| Header | 翻訳結果 |
+| TranslatedText | streaming翻訳本文 |
+| Meta | Model名 / Ollama |
+| Footer | コピー / 再生成 / 状態表示 / 「設定へ戻る」 |
+
+* popupが streaming中に開かれた場合でも、その時点での translation textを表示し、以降のchunkを継続受信する。
+* popupが閉じても requestを自動cancelしない。
+* 新しい翻訳request開始時に古いrequestは abortされる(既存方針)。
+* request sourceは backgroundで metadataとして識別してよい(例: `"content"` / `"context-menu"`)。ただし Ollama request生成処理は共通(prompt、settings、options生成)。
+
---
## 8. Runtime通信設計
@@ -365,6 +404,29 @@ Ollama streaming開始
### 9.1 古いchunk対策
受信chunkごとにactive requestIdを確認し、不一致なら破棄する。
+### 9.2 Translation state 共有(popup連携)
+
+Backgroundは context menu / content script いずれの経由でも translation stateを保持し、popupが開いた時に最新状態を取得できるようにする。
+
+```js
+// module internal state (background)
+translationState = {
+ requestId, // string
+ sourceText, // string (validated)
+ translatedText, // accumulated chunk text
+ status, // idle | starting | streaming | completed | error | cancelled
+ model, // settings.model when started
+ error, // error message (or null)
+ startedAt, // epoch ms
+ source // 'content' | 'context-menu'
+}
+```
+
+* popupからの `GET_POPUP_STATE` runtime messageでこのstateを返す。
+* `onChunk` ごとに state を更新(translatedText累積、status=streaming)。
+* completed/error/cancelledでもstateを更新する。popupが開き直されたときに最終状態を表示できる。
+* 新しい翻訳request開始時には state を初期化(古いrequestはabort)。
+
---
## 10. OllamaClient設計
@@ -742,6 +804,13 @@ BackgroundのOllamaClientからモデル一覧取得APIを呼び、Settings UIã�
- chat stream
- Abort
+### Phase 4.5: Context Menu 翻訳(PDF Viewer対応)
+- menus permission 追加
+- menu-controller.js(selection context menu 登録、selectionText取得、popup open)
+- message-routerへ request 生成共通化(port / menu / popup 経由の request が同じ設定・prompt・options生成を利用)
+- backgroundに translation state 共有(requestId/sourceText/translatedText/status/model/error)
+- Action Popup 翻訳結果モード表示
+
### Phase 5: Settings
- storage.local
- Basic settings
@@ -784,6 +853,8 @@ BackgroundのOllamaClientからモデル一覧取得APIを呼び、Settings UIã�
| prompt builder | fixed + user prompt結合 |
| message validation | malformed message拒否 |
| length limit | 境界値 |
+| selectionText validation | context menu selectionText の型/trim/空/最大長 |
+| popup state | status遷移、requestId/sourceText/translatedText/model/error更新 |
### 23.2 Integration
@@ -797,6 +868,17 @@ Content request
→ complete
```
+Context menu flow:
+
+```text
+menus selection (selectionText)
+→ validation
+→ translation request
+→ mock Ollama stream
+→ TranslationSession
+→ popup result state更新
+```
+
Abortテスト:
```text
@@ -814,6 +896,7 @@ B chunks only displayed
- 選択文字列にHTMLタグ表現を含むページ
- CSSが強く指定されたページ
- スクロール位置が深いページ
+- Firefox内蔵PDF Viewer(context menu翻訳)
確認:
- 選択位置近傍UI
@@ -825,6 +908,11 @@ B chunks only displayed
- close
- settings persistence
- Ollama down/up
+- context menu表示(通常ページ / PDF Viewer)
+- context menu翻訳→popup streaming表示
+- popup copy / regenerate
+- 新規翻訳時の旧request cancel
+- PDF上のHTML/script風文字列が実行されないこと
---
@@ -848,6 +936,8 @@ B chunks only displayed
- [ ] LLM出力への`innerHTML`なし
- [ ] 不要permissionsなし
- [ ] iframe初期対応なし
+- [ ] `menus` permission は selection context translationのみに使用
+- [ ] PDF Viewer内部DOM / PDF.js内部オブジェクトへのアクセスなし
---
diff --git a/doc/Security.md b/doc/Security.md
index 956f169..66dbde0 100644
--- a/doc/Security.md
+++ b/doc/Security.md
@@ -196,6 +196,7 @@ Use modern APIs where required:
| Extension storage | `storage.local` |
| Long-lived extension communication | `runtime.connect()` / `runtime.Port` |
| One-shot extension messages | `runtime.sendMessage()` |
+| Selection context menu translation | `menus` (selection context only) |
| Dynamic script injection, only if needed | `scripting.executeScript()` |
### 5.4 APIs / Patterns to Avoid
@@ -234,6 +235,7 @@ Expected initial permission set:
```text
storage
+menus
```
Expected host permission:
@@ -249,6 +251,8 @@ or the minimum equivalent required by the final Ollama-host design.
Page access MAY require content-script match patterns for HTTP/HTTPS pages because translation is initiated by selecting page text.
+The `menus` permission is used exclusively for the selection-context translation entry point (context menu "選択したテキストを翻訳"). It MUST NOT be used for any other purpose (no page action items, no tab items, no bookmark items, no launcher items).
+
Do not add broad privileged permissions for convenience.
Every newly introduced permission MUST be justified in code review.
@@ -260,11 +264,11 @@ Every newly introduced permission MUST be justified in code review.
The system has three primary trust zones.
```text
-Untrusted Web Page
+Untrusted Web Page / PDF Viewer selection
|
- | selected text
+ | selected text (content script or menus.selectionText)
v
-Content Script
+Content Script / menu-controller (background)
|
| validated extension messages
v
@@ -324,6 +328,25 @@ The background component owns privileged application operations:
- request cancellation;
- Ollama response parsing.
+### 7.4 Firefox PDF Viewer and Context Menu Input
+
+Firefox's built-in PDF Viewer is privileged browser UI:
+
+- The extension MUST NOT attempt to inject content scripts into it.
+- The extension MUST NOT access its internal DOM, PDF.js internal objects,
+ `wrappedJSObject`, `cloneInto`, `exportFunction`, or any Firefox-internal
+ URI/API to work around the injection restriction.
+- PDF translation is provided exclusively through the public `menus` API.
+
+`menus.OnClickData.selectionText` is UNTRUSTED input:
+
+- it MUST pass the same validation pipeline as content-script selections
+ (string type check, trim, empty rejection, maximum length, control
+ character handling) before being sent to Ollama;
+- it MUST NOT be treated as trusted control input or rendered as HTML;
+- the translation result MUST be rendered in the Action Popup with
+ `textContent`-style plain-text rendering only.
+
---
## 8. DOM and XSS Rules
@@ -743,6 +766,9 @@ Before declaring implementation complete, verify all of the following.
- [ ] No deprecated WebExtensions API used when a supported current alternative exists.
- [ ] Permissions are minimal.
- [ ] No unnecessary `tabs`, `scripting`, `webRequest`, `cookies`, or `nativeMessaging` permission.
+- [ ] `menus` permission used only for selection-context translation.
+- [ ] No content-script injection or internal DOM access into the Firefox PDF Viewer.
+- [ ] `menus.OnClickData.selectionText` validated before use.
- [ ] Firefox behavior manually verified in `about:debugging`.
### DOM / Page Boundary
diff --git a/doc/Specifications.md b/doc/Specifications.md
index ac6600d..617ddfd 100644
--- a/doc/Specifications.md
+++ b/doc/Specifications.md
@@ -57,7 +57,7 @@ Firefox上でユーザーがWebページ内の文字列を選択した際、選æ
| SEL-03 | 選択解除時は翻訳アイコンを非表示にすること |
| SEL-04 | 選択範囲の位置を取得し、その近傍に翻訳アイコンを表示すること |
| SEL-05 | 初期版ではトップレベル文書のみ対象とし、iframe内は対象外とすること |
-| SEL-06 | Firefox内部ページ、Reader View、view-source、内蔵PDF Viewer等、Content Scriptを利用できないページは対象外とすること |
+| SEL-06 | Firefox内部ページ、Reader View、view-source等、Content Scriptを利用できないページは対象外とすること。Firefox内蔵PDF Viewerはcontent scriptでなく menus APIで対応する(PDF-01) |
### 4.2 翻訳アイコン
@@ -104,6 +104,40 @@ Firefox上でユーザーがWebページ内の文字列を選択した際、選æ
| EN-01 | 拡張機能の翻訳機能を設定画面から有効/無効に切り替えられること |
| EN-02 | 無効時は選択文字列に対して翻訳アイコンを表示しないこと |
+### 4.6 PDF Selection Translation
+
+Firefox内蔵PDF Viewer上でテキストを選択し、右クリックで context menu の「選択したテキストを翻訳」を実行すると翻訳を開始できること。
+
+| ID | 要件 |
+|---|---|
+| PDF-01 | Firefox内蔵PDF Viewerでのみ、context menu経由で翻訳を開始できること |
+| PDF-02 | context menu翻訳の結果は Action Popup に streaming 表示すること |
+| PDF-03 | PDF Viewer への content script注入・translation popover表示は対象外 |
+| PDF-04 | 独自PDF Viewer・PDF.js同梱・外部変換サービスの利用は対象外 |
+
+### 4.7 Context Menu Translation
+
+通常Webページでも同じ context menu操作を使用可能とすること。
+
+| ID | 要件 |
+|---|---|
+| CTX-01 | 選択文字列の `menus.OnClickData.selectionText` を取得し translationを実行できること |
+| CTX-02 | 通常ページとFirefox PDF Viewerで同じ操作を利用できること |
+| CTX-03 | `menus` permission は selection context translationのみに使用すること |
+
+### 4.8 Popup 2モード化
+
+Action Popupに「設定モード」と「翻訳結果モード」を実装すること。
+
+| ID | 要件 |
+|---|---|
+| POP-01 | Action Popupが設定UIと翻訳結果UIの2モードを切り替えられること |
+| POP-02 | 翻訳結果モードはコピー・再生成・エラー表示・status表示を提供すること |
+| POP-03 | popupが閉じられた場合でも翻訳requestを自動cancelしないこと |
+| POP-04 | 新しい翻訳request開始時に進行中 requestを abortしてもよい |
+| POP-05 | popup 開いた時に最新の translation stateを取得できること |
+| POP-06 | streaming中にpopupが開かれた場合でも、その時点までのtranslation textを表示し、その後のchunkを継続受信できること |
+
---
## 5. 設定要件
@@ -352,6 +386,27 @@ Firefox上でユーザーがWebページ内の文字列を選択した際、選æ
13. ページ内のHTMLらしき文字列やscript文字列を選択してもDOM実行されない。
14. LLM出力にHTML文字列が含まれてもHTMLとして解釈されない。
+### 13.4 PDF Viewer実機動作確認
+
+以下の操作を実Firefox + 実Ollamaで確認すること。
+
+| # | 確認項目 |
+|---|---|
+| 1 | 通常Webページで文字列を選択する |
+| 2 | context menuに「選択したテキストを翻訳」が表示される |
+| 3 | menuからtranslationを開始できる |
+| 4 | Firefox内蔵PDF ViewerでPDFを開く |
+| 5 | PDF内のテキストを選択する |
+| 6 | context menuに翻訳項目が表示される |
+| 7 | `selectionText` が取得される |
+| 8 | Action Popupが開く |
+| 9 | Ollama streaming結果が逐次表示される |
+| 10 | Copyが動く |
+| 11 | Regenerateが動く |
+| 12 | Ollama停止時に安全なエラー表示になる |
+| 13 | HTML/script風のPDF文字列を選択しても実行されない |
+| 14 | 新しい翻訳開始時に古いrequestが正しくcancelされる |
+
---
## 14. 受け入れ条件
@@ -379,7 +434,6 @@ Firefox上でユーザーがWebページ内の文字列を選択した際、選æ
- Chrome/Edge完全互換
- iframe内翻訳
-- Firefox内蔵PDF Viewerへの対応
- 翻訳履歴管理
- チャット機能
- 複数同時翻訳
@@ -389,3 +443,5 @@ Firefox上でユーザーがWebページ内の文字列を選択した際、選æ
- Markdown/HTMLリッチレンダリング
- 自動翻訳(選択しただけでLLM実行)
- `tfs_z` / `mirostat*`
+- Firefox内蔵PDF Viewerへのページ内translation icon / popover表示(content script注入不可のため)
+- 独自PDF Viewer・外部PDF変換サービス
diff --git a/doc/Testing-Results.md b/doc/Testing-Results.md
index b2cf153..a9abf8a 100644
--- a/doc/Testing-Results.md
+++ b/doc/Testing-Results.md
@@ -6,7 +6,7 @@
実行環境: Firefox系ブラウザ(Geckoエンジン)で `tests/test.html` を実行。npm / Node.js 不使用の自作ランナー。
-**結果: 71 passed, 0 failed**
+**結果: 85 passed, 0 failed**
### カバレッジ
@@ -28,6 +28,10 @@
| toExtensionError | 2 | AbortError→REQUEST_ABORTED、デフォルトコード |
| TranslationSession | 2 | 新規要求で旧要求Abort+旧portへの通知、isActive/clear |
| settings service | 3 | 保存/復元ラウンドトリップ、不正値フォールバック、onChangedキャッシュ更新 |
+| selectionText validation | 1 | **context menu選択文字列のuntrusted validation**(型/trim/空/control文字/最大長) |
+| context menu flow + popup state | 11 | **menus選択→翻訳→popup state streaming/completed**、空選択拒否、streaming状態遷移、Ollama停止時error、新規翻訳で旧state置換、regenerateLast、**popup-state portライブ配信**、誤名port拒否、**初期状態のモデル自動選択(未設定→先頭モデルを自動永続化)**、Ollama停止時のOLLAMA_UNAVAILABLE報告、エラー詳細のユーザー安全化(URL除去・長さ制限・JSON除去) |
+| UI smoke (popup) | 1 | popup.js実DOM実行、2モードUI初期化、settings mode初期表示 |
+| UI smoke (options) | 1 | options.js実DOM実行、言語/モデル/host反映 |
| message router: port flow | 5 | started/chunk/completed伝送、**A→B新規要求でAがabort+stale chunk廃棄**、不正メッセージ破棄、OLLAMA_UNAVAILABLE、INVALID_SETTINGS |
| message router: listModels | 2 | 正常系、非信頼sender拒否 |
@@ -52,6 +56,27 @@
| 13 | ページ内のHTMLらしき文字列やscript文字列を選択してもDOM実行されない | ☐ 未実施 |
| 14 | LLM出力にHTML文字列が含まれてもHTMLとして解釈されない | ☐ 未実施 |
+## 2.1 PDF Viewer実機動作確認(context menu翻訳)
+
+以下はSpecifications.md §13.4の14項目です。実Firefox + 実Ollamaで確認してください。
+
+| # | 確認項目 | 結果 |
+|---|---|---|
+| 1 | 通常Webページで文字列を選択する | ☐ 未実施 |
+| 2 | context menuに「選択したテキストを翻訳」が表示される | ☐ 未実施 |
+| 3 | menuからtranslationを開始できる | ☐ 未実施 |
+| 4 | Firefox内蔵PDF ViewerでPDFを開く | ☐ 未実施 |
+| 5 | PDF内のテキストを選択する | ☐ 未実施 |
+| 6 | context menuに翻訳項目が表示される | ☐ 未実施 |
+| 7 | `selectionText` が取得される | ☐ 未実施 |
+| 8 | Action Popupが開く | ☐ 未実施 |
+| 9 | Ollama streaming結果が逐次表示される | ☐ 未実施 |
+| 10 | Copyが動く | ☐ 未実施 |
+| 11 | Regenerateが動く | ☐ 未実施 |
+| 12 | Ollama停止時に安全なエラー表示になる | ☐ 未実施 |
+| 13 | HTML/script風のPDF文字列を選択しても実行されない | ☐ 未実施 |
+| 14 | 新しい翻訳開始時に古いrequestが正しくcancelされる | ☐ 未実施 |
+
## 3. セキュリティチェック
| 項目 | 結果 |