Chrome Extensions をMV3に移行しなくちゃ、ね。


はじめに

MV3で何が変わるの、か?

Chromeオフィシャルサイトによると大まかには以下が変更点という、ことらしい。

機能の概要

  • サービスワーカーによるバックグラウンドページの置き換え。
  • ネットワークリクエストの変更は、declarativeNetRequestAPIで処理される。
  • リモートでホストされたコードは許可されず、パッケージに含まれているJavaScriptのみを実行可能。
  • 多くのメソッドにPromiseのサポートを追加。



えーっと、、とりあえずわかったふりをしつつ、、。

やってみよう

今回MV3に移行するExtensionsでは、バックグラウンド処理としてはコンテキストメニューのみ。ただ、formをDOM生成してPOST送信している。だが、DOMは扱えない、んだ。

どうするのか?

解決策としては2つ。

  1. 個人情報は扱ってなく、受信側もPOST・GET両方に対応させてるので、GET送信に変更。
  2. 新しくTabページを開いてPOST送信。


まずは、manifest.jsonの更新

manifest.json
{
-   "manifest_version": 2,
+   "manifest_version": 3,
    ...
-   "browser_action": {
+   "action": {
        "default_icon": {
            "16": "img/icon16.png",
            "32": "img/icon32.png",
            "48": "img/icon48.png",
            "128": "img/icon128.png"
        },
        "default_title": "__TITLE__",
        "default_popup": "popup.html"
    },
-   "content_scripts": [
-       {
-           "matches": [ "http://*/*", "https://*/*" ],
-           "js": [ "js/jquery-3.6.0.min.js", ... ]
-       }
-   ],
    "permissions" : [
        "activeTab",
-       "contextMenus",
+       "contextMenus"
-       "http://*/*",
-       "https://*/*"
    ],
+   "host_permissions": [
+       "http://*/*",
+       "https://*/*"
+   ],
    "background": {
-       "scripts": [ "background.js" ],
-       "persistent": false
+       "service_worker": "background.js"
    }
}

content_scriptsの理解が出来てなく、パッケージ内で使用してるjsを全て書いていたので削除しました。


バックグラウンド処理

GETで送信 に変更する場合
background.js
chrome.contextMenus.removeAll(function() {
    chrome.contextMenus.create({
        id: '__ID__',
        title: '__TITLE__',
        contexts: ['image', 'video'],
        type: 'normal'
    });
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
    if (info.menuItemId == '__ID__') {
-       var frm = document.createElement('form');
-       frm.setAttribute('method', 'POST');
-       frm.setAttribute('target', '_blank');
-       frm.setAttribute('action', '__URL__');
-       var req = document.createElement('input');
-       req.setAttribute('type', 'hidden');
-       req.setAttribute('name', 'img_url');
-       req.setAttribute('value', info.srcUrl);
-       frm.appendChild(req);
-       document.body.appendChild(frm);
-       frm.submit();
+       chrome.tabs.create({ url: '__URL__?img_url=' + info.srcUrl });
    }
});

GETメソッドで扱えるデータに上限があるので、今回はPOSTを選択。

POSTで送信 する場合
background.js
chrome.contextMenus.removeAll(function() {
    ...
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
    if (info.menuItemId == '__ID__') {
-       ...
+       postForm('__URL__', {'img_url': info.srcUrl});
+
+       function postForm(url, data) {
+           chrome.tabs.create(
+               { url: chrome.runtime.getURL('postform.html') },
+               function(tab) {
+                   var handler = function(tabId, changeInfo) {
+                       if(tabId == tab.id && changeInfo.status == "complete"){
+                           chrome.tabs.onUpdated.removeListener(handler);
+                           chrome.tabs.sendMessage(tabId, {url: url, data: data});
+                       }
+                   }
+                   chrome.tabs.onUpdated.addListener(handler);
+                   chrome.tabs.sendMessage(tab.id, {url: url, data: data});
+               }
+           );  
+       }
    }
});

新しくTabページを開き、ServiceWorkerとページ間でメッセージのやり取りが可能なので、開いたページ(postform.html)にdataを渡します。

postform.html
<html><body><script src="js/postform.js"></script></body></html>

開いたページではDOM操作が可能だが、インラインJavaScriptは書くことが出来ないので注意が必要です。

postform.js
var onMessageHandler = function(message){
  chrome.runtime.onMessage.removeListener(onMessageHandler);
  var frm = document.createElement('form');
  frm.setAttribute('method', 'post');
  frm.setAttribute('action', message.url);
  for(var key in message.data) {
    var req = document.createElement('input');
    req.setAttribute('type', 'hidden');
    req.setAttribute('name', key);
    req.setAttribute('value', message.data[key]);
    frm.appendChild(req);
  }
  document.body.appendChild(frm);
  frm.submit();
}

chrome.runtime.onMessage.addListener(onMessageHandler);

メッセージを受け取ったらformをDOM生成しPOST送信。

まとめ

バックグラウンドでDOMが扱えなくなった、んだ。どうにかDOMを扱うことは出来ないのか?、、fake-domのようなもので代替出来ないのか?、、、、試行錯誤し、、模索し、あきらめかけましたが、、新しく開いたページではDOM操作が可能という事だったので、MV2と同じ機能のままMV3に移行することが出来ました。

今回MV3に移行したChrome Extensions