KENTEM TechBlog

建設業のDXを実現するKENTEMの技術ブログです。

WXTのすゝめ ~俺だけのブラウザ拡張機能を作りたい~

目次

はじめに

こんにちは!今年新卒で入社したK.Mです!私は昨年末に開催されたKENTEM TECH CONF 2024に出場し、そこで自作のブラウザ拡張機能について紹介しました。今回はその内容を踏まえて、拡張機能の作り方についてご紹介したいと思います!

拡張機能の作り方

拡張機能を作るには、設定ファイルであるmanifest.jsonが必要です。また、必要に応じてJSファイルやHTMLファイルが必要です。

manifest.json

 manifest.jsonは以下のような構成になっています。

{
  "manifest_version": 3,
  "name": "青いヘッダー拡張機能",
  "version": "1.0",
  "description": "すべてのウェブページに水色のヘッダーを追加します",
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ],
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["storage", "activeTab", "tabs"]
}

各キーについて、

  • manifest_version : 設定ファイルがどのバージョンに準拠しているかを設定(基本的には3でOK)
  • name : 拡張機能の名前
  • version : 拡張機能のバージョン
  • description : 拡張機能の概要
  • action : ツールバーに表示される拡張機能のアイコンや、クリックした際に表示するポップアップの内容
  • content_script : ウェブページに挿入するスクリプト
  • background : バックグラウンドで動作するスクリプト
  • permissions : 作成した拡張機能に与える権限を設定(ストレージを使ったり、開いているタブの情報を取得したりする際に必要)

続いて、機能実装を司るactioncontent_scriptbackgroundの3つについて説明します。

action

manifest.jsonのところで軽く触れましたが、こちらはツールバーに表示される画面を設定するものです。具体的には、

  • ツールバーに表示するアイコン画像
  • ツールバーのアイコンをクリックしたときに表示するポップアップ

を設定するものとなっています。先程表示したmanifest.jsonでは、

  "action": {
    "default_popup": "popup.html"
  }

となっていますが、これはアイコンクリック時にpopup.htmlが表示されるように指定しています。例えば、以下のようなpopup.htmlを実装すると、

<!DOCTYPE html>
<html5>
<head>
  <meta charset="utf-8">
  <title>青いヘッダー拡張機能</title>
  <style>
    body {
      width: 300px;
      padding: 20px;
      font-family: sans-serif;
      text-align: center;
    }
    h1 {
      color: #4ba3c7;
    }
    button {
      background-color: #4ba3c7;
      color: white;
      border: none;
      padding: 8px 16px;
      border-radius: 4px;
      cursor: pointer;
      margin-top: 10px;
    }
    button:hover {
      background-color: #3a8baa;
    }
  </style>
</head>
<body>
  <h1>ようこそ!</h1>
  <p>この拡張機能はすべてのウェブページに水色のヘッダーを追加します。</p>
</body>
</html5>

このような形でポップアップを表示することができます。(アイコンは設定していないので名前の「青」が表示されています。)
今回はスタイルをHTMLファイルにそのまま記述していますが、CSSファイルを使ってスタイルを整えたり、JSファイルを追加したりすることもできる他、後述するWXTを用いることで、ReactやVueを使って実装することもできます。

content_script

content_scriptは表示しているWebページに挿入するスクリプトを設定します。これを設定することで、WebページDOMを操作することができます。また、どのWebページにどのようなスクリプトを挿入するのか、といった細かい制御も可能になっています。

  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ],

先程のmanifest.jsonで指定していた内容として、

  • matches : どのWebページに適用するのか(今回は全てのWebページ)
  • js : 実際に挿入するスクリプト(content.jsを挿入する)

といった内容になっています。 実際にcontent.jsを以下の様に実装すると、

// 新しいdiv要素を作成
const newDiv = document.createElement('div');

// divの内容を設定
newDiv.textContent = '追加したdom'; // テキストを追加
newDiv.style.backgroundColor = 'lightblue'; // スタイルを設定

// ページの先頭に追加
document.body.insertBefore(newDiv, document.body.firstChild);

テックブログのホームに水色のDOMを挿し込むことに成功しました。

background

 backgroundは拡張機能の裏側で常に動作するスクリプトを設定します。先程のcontent_scriptは表示しているWebページのみで実行されるものになりますが、backgroundは表示しているWebページに関係なく常に起動し続けるものとなっています。

  "background": {
    "service_worker": "background.js"
  }

backgroundは上記の様に、基本的にはservice_workerにスクリプトを記述したファイルを指定するのみとなっています。(バージョン2では色々なフィールド値があったようですが、バージョン3で統合されたようです)
試しにbackground.jsを以下の様に実装してみます。

// アクティブなタブが変更されたときにURLをログに記録
chrome.tabs.onActivated.addListener(async (activeInfo) => {
  try {
    const tab = await chrome.tabs.get(activeInfo.tabId);
    console.log("アクティブタブのURL:", tab.url);
  } catch (error) {
    console.error("エラーが発生しました:", error);
  }
});

// タブが更新されたときにURLをログに記録
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  // タブの読み込みが完了したときのみログを記録
  if (changeInfo.status === 'complete') {
    console.log("更新されたタブのURL:", tab.url);
  }
});

// 拡張機能が初めて起動したときに現在のアクティブタブのURLをログに記録
chrome.runtime.onInstalled.addListener(async () => {
  try {
    const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
    if (tabs[0]) {
      console.log("現在のアクティブタブのURL:", tabs[0].url);
    }
  } catch (error) {
    console.error("エラーが発生しました:", error);
  }
});

その後リロードボタンを押すと、以下の様にコンソールに更新したURLを表示することができました。

続いてタブを何回か移動してみると、このようにアクティブタブのURLが表示されることが確認できます。

一点注意点ですが、background.jsはポップアップを表示しなくても起動していますが、コンソールの値を確認するときはポップアップのHTMLに対してディベロッパーツールを立ち上げる必要がありますので、挙動を確認したい際にはご注意ください。

WXTを使った拡張機能開発

さて、ここまで拡張機能の基本的な作り方を説明しました。先程の拡張機能は特にツールを使わずに一つずつ実装して作成しましたが、このやり方は

  • デバッグがやりにくい(挙動を確認するのに拡張機能管理画面で毎回読み込む必要がある)
  • JavaScriptで記述する必要がある
  • manifest.jsonの管理が煩雑

といったデメリットがあります。 このようなデメリットを解消するのがWXTです。
WXTは、ブラウザ拡張機能を簡単に開発・ビルドすることができるビルドツールです。
WXTを使うメリットとしては、

  • ビルドツールとして有名なViteとほとんど同じように作ることができる
  • JavaScriptでなくTypeScriptで実装を進めることができ、そのための環境構築も簡単
  • npm run devで拡張機能をリアルタイムでリロードしながら確認できる
  • ビルド時にmanifest.jsonを作成してくれるので開発時に意識する必要がなくなる

等々、1から作るよりもはるかに楽に拡張機能を実装することができます。
WXTを使った拡張機能開発は、先に述べたように基本的にviteと同じ感覚で作っていけますので、以下のコマンドを実行します。

npm create wxt@latest

その後プロジェクト名を設定し、使用する言語とフレームワークを選択すれば、プロジェクトが出来上がります。
ここで一度、出来上がったプロジェクトを見てみましょう。デバッグを行うには

npm run dev

のコマンドを実行することで、デバッグ用のChromeが立ち上がります。立ち上がったChromeのツールバーにあるパズルのピースマークのアイコンを押すと、、、
Viteを使っている方は親の顔と同じくらい見たことある画面が出てきました(笑)
あとはentrypointディレクトリにあるpopup.htmlcontnet.tsbackground.tsにそれぞれ必要な機能を実装していけば拡張機能を作っていくことができます!

作成できる拡張機能の例

最後に、WXTを使って作成した私の拡張機能について紹介したいと思います!
今回作成した拡張機能は以下のようなフローとなっています。 機能としては、

  • popupでキャラクターを設定
  • Web上の英文をなぞったら、指定したキャラクターが和訳と構文の解説をしてくれる

といったものとなっています。実際に使ってみるとこんな感じです。 (今回解説の最後に一句読んでくれる俳句教師に構文解説をお願いしましたが、あんまり上手じゃないですね・・・笑)

まとめ

今回はブラウザの拡張機能の作り方について簡単に紹介しました。WXTを使わずに1から実装していくと大変ですが、WXTを使うことでそこそこの機能の拡張機能を簡単に作ることができました(先程紹介した拡張機能は、バックエンド含め概ね2週間程度で実装することができました)
WXTはモダンなWebアプリ開発手法と同様に拡張機能を作ることができるので、Webエンジニアの方はぜひ自分だけの拡張機能を作ってみてください!

おわりに

KENTEMでは、様々な拠点でエンジニアを大募集しています! 建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。 recruit.kentem.jp career.kentem.jp