雑多に技術メモと他色々

主に自分用な技術メモが多くなる気がする。他色々が書かれるかどうかは不明。

Chrome拡張機能をmanifest v3で開発した簡易記録とハマりポイントの解消方法

Chrome拡張機能プラグイン)を自作で作ってみたので、色々書く。

やってみたこと

  • 仕事用にChrome拡張機能を自作してみた。
  • manifest v2の解説が多いが、Deprecatedになっていくようなのでv3で作成。
  • ローカルで開発機能を作ってデバッグしながら開発。

やった結果

動かせたの?

  • とりあえず想定するものを動かすことはできた。
  • ハマりポイントも多かったので解消方法含めメモっておく。
  • ハマったポイントはプラグイン作成が初だから引っかかる課題も、v3特有の課題も。

開発した時の環境

  • Windows 10 Professional
  • 当然だがGoogle Chromeインストール済み
  • 開発環境としてVSCodeを導入済み
  • npm導入済み(今回はVSCodeプラグインの強制アップデートにだけ使った)
  • Dockerとdocker-composeは導入済み(拡張機能とは少し離れるが動画キャプチャ加工で使った)

ハマりポイントと解消方法の一覧

そもそも拡張機能を作るために必要な構成や作るべきファイルがわからない
  • コードの例をどこから引っ張ってきて開発すればいいかすら最初はわからなかった。
  • とりあえずChrome拡張機能の開発用サンプルがGithubに公開はされている。
    GitHub - GoogleChrome/chrome-extensions-samples: Chrome Extensions Samples
  • サンプルは本当に最小構成が多いので、あくまでこれはベースであってググりまくって開発する。
  • 書くものはJavascript、HTML、cssといったWebファイル相当なので、それを編集する環境があればある程度事足りる。
開発成果物のそれぞれの役割がよくわからない

ざっくり使ったものは自分の認識で取りまとめしておく。

用語 どんなもの
manifest.json 拡張機能管理の大元。これを作らないと始まらない。
拡張機能の基本情報や登録する機能、そのために使うファイルや属性を記載する。
manifestバージョンはv3への移行が進んでいて、2022年1月にmanifest v2の拡張機能は新規申請NGになった。
Chromeウェブストアへの申請が止まっただけで既存ツールや自作したものは動く。
2023年には動作サポートも切られるようで、基本的にこれから作るならv3で作るべき。
content_scripts 特定のWebサイトに対して追加機能を提供するための設定。任意のjsやcssファイルを追加、実行してページをカスタムしたり、ページ上の要素を取得したりすることができる。matchesにURLの正規表現を指定し、合致したWebサイトのみ適用されるように設定可能。
Javascript上で後述のservice_workerとやりとりすることで、Webページ上のデータを拡張機能に引き渡した処理や、クロスドメイン制約を超えたWeb通信なども実現可能。
service_worker バックグラウンドに常駐する機能。というかリスナ登録で特定イベントやJavascriptからの呼び出しで発火する機能を作成、登録するイメージ。service_workerの定義をJavascriptで記載して登録する。
chrome.runtime.sendMessageを通じてcontent_scriptsとデータ連携することが可能。こちらの機能はWebサイトではなくてChrome Extensionの世界で、拡張機能で許可設定した通信先であればクロスドメイン制約を受けずに通信が可能になる。*1
単独機能としては何でもできるが、外部のWebページにDOM書き換え、データ取得などの干渉はできないため、そこはcontent_scriptsに任せる必要がある。
options_page Chrome拡張機能のオプションを選択した際に開かれるページを設定することができる。ツールが利用する設定項目がある場合、ここでユーザに入力させてchrome.storage.localなどに保存する。
いい感じの設定画面になるようにHTML、cssJavascript作成してあげる(JavaScriptchrome機能操作をするところを除くと大体通常のWeb開発と同様でいける)。
host_permissions service_workerなどがここに指定したホストへのアクセス権を持つようになる。拡張機能自身はクロスドメイン制約を受けないものの、この設定がないと外部ホストへの通信処理は行えない。
また、この設定が必要なことによりユーザに拡張機能がアクセスする可能性のあるホストが明示されるようになる。
permissions ローカルストレージやブックマーク、履歴などのChromeブラウザ自身が持っているデータや、各種機能、システム情報へのアクセス権を設定する。
拡張機能は何でもできるが、このpermissionが与えられていないとできない操作は多い。
参考までにローカルストレージを利用する場合はstorageの定義が必要。
web_accessible_resources content_scriptsなどを通じて任意の(拡張機能利用が許可された)WebページからChrome拡張機能内のファイルへアクセス権を与える場合にアクセス可能な拡張機能内の対象フォルダ・ファイルを指定する。
後述するがcontent_scriptsを適用したWebページが拡張機能の持つ画像アイコンなどを取り込む場合は必要になる。
自作したChrome拡張機能の取り込み方法がわからない
  • Chrome拡張機能の管理画面を開き、デベロッパーモードをONにする。
  • パッケージ化されていない拡張機能を取り込むmanifest.jsonが置いてあるフォルダを選択。
ツールを修正した時の取り込み方法がよくわからない
  • 最初は削除して再インストールするしか無いと思っていたけどそんなことなかった。
  • これもChrome拡張機能の管理ページから更新すればツールが1から読み込まれる。
  • 自作したoptionsページの修正でも、manifestの修正でも、各種scriptの修正でもOKで全部リロードされる。
  • ただしcontent_scriptsが動作済のWebページは更新後にページリロードしないと正しくツールが動作しない。
service_workerが動作しない原因がわからず、デバッグもできない

content_scriptsからchrome.runtime.sendMessageを通じてbackground.js(service_worker)の処理を呼び出しても期待通りに動作せず、下記のようなエラーが表示される場合など。

Could not establish connection. Receiving end does not exist.`

動作しているはずのWebページ上で開発者ツールを見てもエラーログなどが出力されず原因が掴めない。
この場合、background.js 側でエラーが発生しているケースがあってログの見方は下記になる。

  • 拡張機能の管理メニューを開く。
  • [ビューを検証] の Service Worker を開く。
  • Chrome開発者機能のウィンドウが表示されて、そのコンソールに background.js のログやエラーが表示される。
  • content_scripts を動作させているページのコンソールなどには何も出力されないので注意。
content_scriptsに指定したcssの参照先画像(URL)にアクセスできない
  • jQuery UIを導入して拡張機能に組み込み、拡張機能を使うWebページ上に表示しようとした時に発生。
  • js, cssmanifest.jsonに指定したものをWebページ上で追加ロードできる。
    • この時cssから参照するアイコンが相対パスで定義されており、Webページ側のURLを参照して404エラーとなり表示崩れ。
    • アイコン参照を持つcssを自前で編集して、参照パスを相対から絶対パスに書き換えることで解決。
    • chrome-extension://__MSG_@@extension_id__/ で開始するパスに置換することで拡張機能内部を参照できる。
  • manifestには web_accessible_resources として格納先のパスをワイルドカード付きで指定。
    • 画像を images に格納していたら "resources": ["images/*"] のように定義。
拡張機能アイコンクリック時の動作が定義しても動かない

拡張機能のアイコンクリック時にポップアップではなくて特定の処理(タブで設定ページ開くなど)をやりたい場合。

  • NG: chrome.browserAction.onClicked.addListener をservice_workerに定義しても動作してくれない。
  • NG: chrome.pageAction.onClicked.addListener でも動かない。
  • OK: 結果的にmanifest v3ではchrome.action.onClicked.addListenerを定義するのが正しいらしい。
    • 使い方は色々ググったときにbrowserActionの例で出てきたものと一緒で動いた。
    • manifest仕様でもbrowser_actionpage_actionactionに統合されているとのこと。
service_workerで外部jsが使えない
  • 外部通信したいけどXMLHttpRequstめんどくさい。
  • jQueryでPOSTした方が楽そうだけどjsソース複数定義できないので動作しなさそう、どうしようとなった。
  • 結果的に外部js定義は使わずに実装し、通信処理はfetch使えばいいじゃんということにした。
    Fetch の使用 - Web API | MDN
  • 色々やりたい場合は{"type": "module"}を指定してESModuleとして作って諸々インポートしろってことらしい。(これは今回未実施)
自作した拡張機能用HTMLでonclick属性が動作しない
  • 元々拡張機能内のHTMLでは動作させられない仕様らしい。
  • 代わりにjQuery$('#id').on('click' ()=>{})等イベントリスナを仕込んで発火させた。
Local Storageから読み込んだ情報が想定外にObjectになった

元々Objectで取れる仕様だが作成中は勘違いしてた。(未登録でも中身が空のObjectになる)

  • chrome.storage.local.set({'key': 'value'}), ...といった感じでデータ登録した場合。
    • chrome.storage.local.get('key', (result)...の際にresultは'value'が直接入る(未登録ならnullなど)と思い込んでた。
    • 実際は{key: 'value'}相当のObjectが返却される。
    • 指定キーが登録されていたらそのキーを含むObjectをcallbackする仕様と思われる(get時に複数キーも指定できるし)。
    • ということで上記の例だとresult.keyと参照して初めて登録値が取り出せる。
JavascriptのOptional Chainがフォーマッタで崩れる
プラグインの説明用に動画キャプチャ取りたいけど取得方法が不明

README.mdに利用方法の動画キャプチャ取ってGitに上げておきたいケース。
その他説明用にファイルサイズ下げたりgif化するなど動画加工などを行う場合の方法。

動画キャプチャのとり方
  • Win+GXBoxソーシャルメニューが開き、その中で動画キャプチャが可能。
  • キャプチャを実行するとドライブ:\Users\username\Videos\Captures配下にファイルが登録される。
動画の加工方法概要

ffmpegを利用する。Windowsインストールしても良いけどDockerで実行する。
起動方法は今回下記のWebサイトを参考にした。
dockerでffmpegを構築しmkv形式をmp4形式に変換する - フリーランス 技術調査ブログ

ffmpeg起動用のDocker実行構成を作る

下記の通りdocker-compose.yamlを作り、同階層にinputフォルダを作って加工元ファイルを入れる。

version: '3'
services:
  ffmpeg:
    container_name:  ffmpeg
    image: jrottenberg/ffmpeg:4.1-alpine
    tty: true
    entrypoint:
      - "ash"
    volumes:
      - ./input:/srv/data

一応ffmpegのDockerイメージはDockerHubで最新バージョンを確認する。
Docker Hub

Docker起動して環境ログイン

alpineだとシェルはashなのでそちらを利用。

  • 起動
docker-compose up -d
  • ログイン
docker exec -it ffmpeg ash
キャプチャした動画を加工(圧縮)する

元々Windowsキャプチャした動画だとサイズが大きいので加工する。

  • 加工元ファイルの場所へ移動
cd srv/data
  • 動画加工実行
ffmpeg -i in.mp4 -vf scale=800:-1 -t 15 -r 3 out.gif
  • 指定オプション説明
    • -i in.mp4
      • 入力ファイルにin.mp4を指定
    • -vf scale=800:-1
    • -t 15
      • 動画を入力ファイルの開始から15秒までで切り出し。
      • 開始終了の切り出し時間を指定する場合は-ss-toでもう少し詳細に設定できる。
    • -r 3
      • フレームレートを3fpsに変更。とにかく軽くしたかったので。
    • out.gif
      • 出力ファイル名。指定拡張子に応じてffmpegが自動的にフォーマット変換してinputファイルと同じ場所に出力される。
      • Markdownファイルから画像参照するのが楽だったので今回はgifに変換。

*1:Web公開するようなツールはセキュリティ問題が出ないように作る必要があるし、ウェブストアのツール導入時も特にマイナーなものではデータが外部流出するように定義されてないかは注意が必要。