🗒️

Chrome拡張にコンテキストメニューを追加する

2023/09/12に公開

Chrome拡張にコンテキストメニューを追加する

趣味がChrome拡張を作る人みたいになっています。毎回書いている気がしますが、Chrome拡張はフロントエンドの勉強を行うのにお手軽な素材です。そして、日々手を動かすネタを考えています。

タイトルの通り、 「そういえばChrome拡張ってコンテキストメニューにコマンド追加出来るよなぁ」 って思ったのでちょっとやってみました。

参考にしたページ

https://qiita.com/FrogApp/items/cd64894721a0e4723047

manifest.json に追加

permissions のリストの中に contextMenus を追加する。

  "permissions": [
    "activeTab",
    "clipboardWrite",
+   "contextMenus",
    "scripting",
    "storage",
    "tabs",
    "webRequest"
  ],

https://developer.chrome.com/docs/extensions/reference/contextMenus/

ServiceWorkerにコンテキストメニューを追加する処理を追加

参考にさせていただいたページの通り、ServiceWorkerのコードに以下のようなコードを追加しました。

background.ts

chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: 'copy_all_urls',
    title: 'ページ内のリンクをクリップボードにコピーする',
  });
});

このコードを追加するとコンテキストメニューにアイテムが追加される。

https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onInstalled

拡張機能が最初にインストールされた時や、新しいバージョンへ更新された時、ブラウザーが新しいバージョンへ更新された時に発火します。

MSDNのマニュアルのとおり、Chrome拡張の更新が行われたときに発火するイベントを使ってコンテキストメニューを追加する。

コンテキストメニューのクリックイベントを追加する

今度はコンテキストメニューのクリックイベントを追加する。

background.ts

chrome.contextMenus.onClicked.addListener((info, tab) => {
    if (info.menuItemId === 'copy_all_urls') {
        chrome.scripting.executeScript({
            target: { tabId: tab.id },
            func: copyAllUrlInPage,
        });
    }
});

manifest.jsonpermissions に予め scripting を追加しておく必要がある。
追加されていないと chrome.scripting.executeScript が権限がないため以下のエラーが発生する。

Error in event handler: TypeError: Cannot read properties of undefined (reading 'executeScript')

onClicked.addListener の第1引数 OnClickData に含まれている menuItemId には追加したコンテキストメニューのIDが設定される。
そのため、今回のように1件であれば条件分岐する必要は無いが、複数件追加した場合にはIDに応じて条件分岐する必要がある。

chrome.scripting.executeScript の引数 func で予め定義しておいた関数を呼び出す形になる。

https://developer.chrome.com/docs/extensions/reference/scripting/#runtime-functions

ここでは chrome.scripting.executeScript の引数 target に指定している通り、任意のタブで実行するため ContextMenu のスクリプトは定義自体は ServiceWorker 内で定義するものの、実行するコードの領域としては ContentsScript 内になる。

また、ContextMenuのスクリプトは独立して実行されるようで、メソッドや変数のスコープはContextMenu内で閉じてしまうようだ。そのため、ContextMenu外で定義された変数や関数といったものは使えない。

ここら辺はなんとか重複しないコードの実装が出来れば嬉しいんだが…。

感想

思ったよりも簡単にコンテキストメニューの追加を行えることがわかった。
ただ、実際に複雑な実装を行うのであればメッセージパッシングを行ってServiceWorkerに処理を委譲したりしないといけなさそうなので設計が肝になりそう。

最終的な追加差分
https://github.com/satoshi-nishinaka/chrome-extension-study/pull/46

Discussion