たくさんの自由帳

大遅刻 Manifest V3 移行メモ

投稿日 : | 0 日前

文字数(だいたい) : 2740

どうもこんばんわ。

2024年06月、Manifest V2廃止がついに始まったらしい?
始まってから移行するのかよって感じですが。

というわけで、今回はこのManifest V3移行をやります。

チェックリストを用意してくれているので、これにそってやってみる。

https://developer.chrome.com/docs/extensions/develop/migrate/checklist?hl=ja

manifest.json 編

manifest_version を 3 にする

{
    "name": "URLDecodeCopy",
    "version": "1.2",
    "manifest_version": 2,

manifest_version3にします。また、審査に出す際にはバージョンも上げないといけないので、ついでに上げておくと良いかも

{
    "name": "URLDecodeCopy",
    "version": "1.3",
    "manifest_version": 3,

permissions と host_permissions

拡張機能の権限(クリップボードとか)と、どのサイトで動かすか(拡張機能を動かすサイトのドメインとか)が一つのpermissionsでしたが、
V3では分離されました。

{
    "name": "URLDecodeCopy",
    "version": "1.3",
    "manifest_version": 3,
    "description": "URLエンコードされたURLをURLデコードしてコピーします。",
    "permissions": [
        "activeTab",
        "http://*/*",
        "https://*/*",
        "contextMenus",
        "clipboardWrite"
    ],

分離して、こうですね。

{
    "name": "URLDecodeCopy",
    "version": "1.3",
    "manifest_version": 3,
    "description": "URLエンコードされたURLをURLデコードしてコピーします。",
    "permissions": [
        "activeTab",
        "contextMenus",
        "clipboardWrite"
    ],
    "host_permissions": [
        "http://*/*",
        "https://*/*"
    ],

Service Worker 移行編

Service Workerなので、documentとかwindowとかのDOMには触れないらしい。
WebExtensions API叩くコードとDOM触るコードが明確に分かれた?

manifest.json を直す

v2がこれで、

    "background": {
        "scripts": [
            "background.js"
        ]
    }

V3だとこうです。

    "background": {
        "service_worker": "background.js",
        "type": "module"
    }

とりあえず入れてみる

chrome://extensionsを開いて、読み込んでみます。
わんちゃんコード側の修正なしとかにならないかな()()

リロードを押して、、やっぱエラーなりますよね

Imgur

ダメだった

ちゃんとV3で動くように直します。

Unchecked runtime.lastError: Extensions using event pages or Service Workers must pass an id parameter to chrome.contextMenus.create

直した

chrome.contextMenus.createへ渡すオブジェクトにidキーが無いかららしい。
https://developer.chrome.com/docs/extensions/reference/api/contextMenus?hl=ja#type-CreateProperties

別に拡張機能の中でユニークであればいいってことかな。

+ const ID_CURRENT_PAGE = "url_decode_copy_current_page"
 
 
 chrome.contextMenus.create({
+    "id": ID_CURRENT_PAGE,

クリックイベントも治す

次はこれ

Unchecked runtime.lastError: Extensions using event pages or Service Workers cannot pass an onclick parameter to chrome.contextMenus.create. Instead, use the chrome.contextMenus.onClicked event.

"onclick"が使えなくなったらしい。
chrome.contextMenus.onClicked.addListenerでメニューを押したときのコールバック関数がもらえるのでこれで。
switchでメニューごとに分岐します。

// メニューを押した時
chrome.contextMenus.onClicked.addListener((info, tab) => {
    switch (info.menuItemId) {
        case ID_CURRENT_PAGE:
            // 今のページの URL デコード
            copy(decodeURI(tab.url))
            break
 
        case ID_ANCHOR_TAG:
            // リンク先のページの URL デコード
            copy(decodeURI(info.linkUrl))
            break
    }
})

DOM にアクセスできない

documentを拡張機能のService Workerから使おうとすると怒られます。

Error in event handler: ReferenceError: document is not defined at copy

本来はクリップボードにコピーするブラウザJSAPIがあるのでそれを使うべきなのですが、
Chromeの拡張機能だとそのAPI使えないんですよね。

仕方なくexecCommand('copy')を使っているのですが、それにはDOM操作が必要で、、、
ちなみにManifest V3でもDOM操作しか無いらしい。ServiceWorkerでは触れないのでコンテンツスクリプト??とかいう奴にお願いするといいらしい。

コンテンツスクリプト

DOM 触りたい場合(documentとかwindowとか)はこのJSを経由する必要がある。

https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts

manifest.jsonに書くものだと思ってたんですけど、実行中に動的にコンテンツスクリプトを追加できるとか書いてある。
今回はたかだかコピーしたいだけなのでmanifest.jsonに書きます。

manifest.jsonを開いてcontent_scriptの項目を足します。

    "background": {
        "service_worker": "background.js",
        "type": "module"
    },
    "content_scripts": [
        {
            "matches": [
                "<all_urls>"
            ],
            "js": [
                "content-script.js"
            ]
        }
    ]
}

次にcontent-script.jsmanifest.jsonと同じフォルダへ追加します。

Imgur

バックグラウンドスクリプトのServiceWorkerと、DOM を操作するコンテンツスクリプトのやり取りはこれです。
https://developer.chrome.com/docs/extensions/develop/concepts/messaging

ServiceWorkercontent-scriptの場合、送る側のServiceWorkerはこう

function sendCopyEvent(copy) {
    (async () => {
        const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true })
        await chrome.tabs.sendMessage(tab.id, { copyValue: copy })
    })();
}

受け取る側のcontent-scriptはこう

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    const copyValue = request.copyValue
    copy(copyValue)
})

でも動かない。manifest.jsonに書いたよ?
Webページをリロードしたらcontent-script.jsが読み込まれました。お騒がせしました。

Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.

content-script.js、ブラウザで動くのでまあなんというか、
console.logすればちゃんとF12のコンソールに出てきます。

権限を削った

contextMenusしか使ってないわ。
clipboardWritenavigator.clipboardAPIを使うときだけらしいし、
activeTabonClicked.addListener()のコールバック関数使う分には特に書いてないので、使ってなさそう。

     "manifest_version": 3,
     "description": "URLエンコードされたURLをURLデコードしてコピーします。",
     "permissions": [
-        "activeTab",
-        "contextMenus",
-        "clipboardWrite"
+        "contextMenus"
     ],
     "host_permissions": [
         "http://*/*",

公開する

なんか久しぶりに開いたら赤い文字でびっくり
プライバシーについて追加で聞かれる程度だった、とりあえず審査に出してみる。→通りました!!!

Imgur

差分

https://github.com/takusan23/URLDecodeCopy

おわりに

TypeScriptじゃないので、せめてもの救い//@ts-checkを使ってます。ありがとう。