その他

自作Chrome拡張機能で定期処理を実行する方法[No110]

スポンサーリンク

Google Chromeで選択した時間間隔でプログラムを実行されるChrome拡張機能のプログラムを用意しました。また、開発の過程でいつくかの試作品もできましたので、何かの参考になればと思い、それらのプログラムのコードも紹介します。

プログラムの概要、実行イメージ

概ねの内容を記載します。今回は、一定時間ごとに実行する処理を"ブラウザのタブの更新"にして実装しています。

  • Chromeの拡張機能をクリックし、プルダウンから「更新間隔(秒)」を指定する。
  • 「start」ボタンをクリックすると、実行までのカウントダウンが始まる。「stop」ボタンをクリックすると、カウントダウンが停止する。拡張機能アイコンのところにカウントダウンの数字が表示される。
  • カウントダウンした数字が0になると、設定していた処理が実行される。

また、前述の内容では、Chromeの拡張機能はブラウザを起動するたびに、「start」ボタンを押さないとならない。そのため、ブラウザ起動などをトリガーに「start」ボタンの処理を行うようにする。

また、おまけとして、いろいろと行う。

試作

内容

この試作段階では、「start」「stop」ボタンのクリックでカウントダウンの開始、停止ができるかどうか目指しました。

問題点(改善点)

  • Chromeの拡張機能をクリックし、ポップアップ画面を開いた状態でのみ正常に動作します。ポップアップを閉じると、ボタンを押しても反応がありません。

ファイル構成

・manifest.json

・popup.html

・popup.js

・icon.png

ソースコード


{
  "name": "拡張機能_step01",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon.png"
    }
  },
  "permissions": [
    "tabs",
    "storage"
  ]
}

〇popup.html


<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>拡張機能_step01</title>
    <style>
      .center {
        min-width: 100px;
        display: grid;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        /* background-color: lightseagreen; */
      }

      h1 {
        font-size: 15px;
      }

      .text {
        font-size: auto;
        font-weight: bold;
        color: rgb(0, 0, 0);
      }

    </style>
    <script src="popup.js"></script>
  </head>
  <body>
    <h1>見出し1</h1>
    <div class="center">
      <label class="text" for="interval">更新間隔(秒):</label>
      <select id="interval">
        <option value="1">1秒</option>
        <option value="5">5秒</option>
        <option value="10">10秒</option>
        <option value="15" selected>15秒</option>
        <option value="30">30秒</option>
        <option value="45">45秒</option>
        <option value="60">60秒</option>
      </select>
      <br>
      <div class="center">
        <button id="start">Start</button>
        <button id="stop">Stop</button>
      </div>
      <br>
    </div>
  </body>
</html>

〇popup.js


// ポップアップが読み込まれた際のイベントリスナー
document.addEventListener('DOMContentLoaded', function () {
	console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_document.addEventListener" + "[start]");

  // 各要素の取得
	let startButton = document.getElementById('start');
	let stopButton = document.getElementById('stop');
	let intervalSelect = document.getElementById('interval');

	let countdownInterval;
	let secondsRemaining;

  // 「start」ボタンのクリックイベントリスナー
	startButton.addEventListener("click", startCountdown);
	
  // 「stop」ボタンのクリックイベントリスナー
	stopButton.addEventListener("click", stopCountdown);

	function startCountdown() {
		console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_startCountdown" + "[start]");

		secondsRemaining = parseInt(intervalSelect.value);
		countdownInterval = setInterval(updateBadgeText, 1000);
		
		// ボタンの状態を変更して有効・無効を設定
		startButton.disabled = true;
		stopButton.disabled = false;

		console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_startCountdown" + "[end]");
	};

	function stopCountdown() {
		console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_stopCountdown" + "[start]");

		clearInterval(countdownInterval);
		
		// ボタンの状態を変更して有効・無効を設定
		startButton.disabled = false;
		stopButton.disabled = true;
		chrome.action.setBadgeText({ text: "" });

		console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_stopCountdown" + "[end]");
	};

	function updateBadgeText() {
		console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_updateBadgeText" + "[start]");

		chrome.action.setBadgeText({ text: secondsRemaining.toString() });
		if (secondsRemaining === 0) {
			clearInterval(countdownInterval);
			startCountdown();
		} else {
			secondsRemaining--;
		}

		console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_updateBadgeText" + "[end]");
	}

	console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_document.addEventListener" + "[end]");
});

〇icon.png:用意する方法を紹介します。ペイントでプロパティを選択し、"単位"を"ピクセル"にして、画像サイズを16×16にして「OK」を選択する。枠内で画像を作成し、ファイル形式をPNGにして、ファイル名を「icon.png」にして、保存する。

スポンサーリンク

完成品1

内容

この段階では、バックグラウンドで動作するようにしている。

ファイル構成

  • manifest.json
  • popup.html
  • popup.js
  • background.js
  • icon.png

ソースコード

〇manifest.json


{
  "name": "拡張機能_step02",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "permissions": [
    "tabs",
    "storage",
    "contextMenus",
    "notifications",
    "alarms"
  ],
  "host_permissions": [
    "<all_urls>"
  ]
}

〇popup.html


<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>拡張機能_step02</title>
    <style>
      .center {
        min-width: 100px;
        display: grid;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        /* background-color: lightseagreen; */
      }

      h1 {
        font-size: 15px;
      }

      .text {
        font-size: auto;
        font-weight: bold;
        color: rgb(0, 0, 0);
      }

    </style>
    <script src="popup.js"></script>
  </head>
  <body>
    <h1>見出し1</h1>
    <div class="center">
      <label class="text" for="interval">更新間隔(秒):</label>
      <select id="interval">
        <option value="1">1秒</option>
        <option value="5">5秒</option>
        <option value="10">10秒</option>
        <option value="15" selected>15秒</option>
        <option value="30">30秒</option>
        <option value="45">45秒</option>
        <option value="60">60秒</option>
      </select>
      <br>
      <div class="center">
        <button id="start">Start</button>
        <button id="stop">Stop</button>
      </div>
      <br>
    </div>
  </body>
</html>

〇popup.js


// ポップアップが読み込まれた際のイベントリスナー
document.addEventListener('DOMContentLoaded', function () {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_document.addEventListener" + "[start]");

  // 各要素の取得
  let startButton = document.getElementById('start');
  let stopButton = document.getElementById('stop');
  let intervalSelect = document.getElementById('interval');

  // ボタンの状態を復元
  chrome.storage.local.get(["buttonStatus"], function (result) {
    console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_chrome.storage.local.get" + "[start]");

    // ローカルストレージから取得したボタンの状態に応じて、各ボタンの有効・無効を設定
    if (result.buttonStatus === 'start') {
      startButton.disabled = true;
      stopButton.disabled = false;
    } else if (result.buttonStatus === 'stop') {
      startButton.disabled = false;
      stopButton.disabled = true;
    }

    console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_chrome.storage.local.get" + "[end]");
  });

  // 「start」ボタンのクリックイベントリスナー
  startButton.addEventListener('click', function () {
    console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_startButton.addEventListener" + "[start]");

    // バックグラウンドスクリプトにメッセージを送信して、プログラムを開始するようリクエスト
    chrome.runtime.sendMessage({ action: 'startRun', reloadSeconds: parseInt(intervalSelect.value) });

    // ボタンの状態を変更して有効・無効を設定
    startButton.disabled = true;
    stopButton.disabled = false;

    // ボタンの状態を保存
    chrome.storage.local.set({ 'buttonStatus': "start" });

    console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_startButton.addEventListener" + "[end]");
  });

  // 「stop」ボタンのクリックイベントリスナー
  stopButton.addEventListener('click', function () {
    console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_stopButton.addEventListener" + "[start]");

    // バックグラウンドスクリプトにメッセージを送信して、プログラムを停止するようリクエスト
    chrome.runtime.sendMessage({ action: 'stopRun', reloadSeconds: parseInt(intervalSelect.value) });

    // ボタンの状態を変更して有効・無効を設定
    startButton.disabled = false;
    stopButton.disabled = true;

    // ボタンの状態を保存
    chrome.storage.local.set({ 'buttonStatus': "stop" });

    console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_stopButton.addEventListener" + "[end]");
  });

  console.log((new Date()).toLocaleString('ja-JP') + " " + "popupjs_document.addEventListener" + "[end]");
});

〇background.js


let intervalId;
let intervalSeconds;

let counter;

// インストール時かバージョンアップ時に実行されるリスナー
chrome.runtime.onInstalled.addListener(function () {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.runtime.onInstalled.addListener" + "[start]");
  SetContextMenus();
  onExtensionStart();
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.runtime.onInstalled.addListener" + "[end]");
});

// ブラウザ起動時に実行されるリスナー
chrome.runtime.onStartup.addListener(function () {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.runtime.onStartup.addListener" + "[start]");
  SetContextMenus();
  onExtensionStart();
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.runtime.onStartup.addListener" + "[end]");
});

// インストール時かバージョンアップ時、ブラウザ起動時に共通の処理をまとめる関数
function onExtensionStart() {
  chrome.storage.local.clear();

  // ボタンの状態を保存
  chrome.storage.local.set({ 'buttonStatus': "start" });
  chrome.storage.local.set({ 'intervalSeconds': 15 });
  startProgram();
};

// メッセージ受信時の処理を行う関数
chrome.runtime.onMessage.addListener(function (request) {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.runtime.onMessage.addListener" + "[start]");

  // メッセージから取得したリロード間隔を整数に変換して保存
  intervalSeconds = parseInt(request.reloadSeconds);
  chrome.storage.local.set({ 'intervalSeconds': intervalSeconds });

  // メッセージのアクションに応じてプログラムを開始または停止
  if (request.action === 'startRun') startProgram();
  if (request.action === 'stopRun') stopProgram();

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.runtime.onMessage.addListener" + "[end]");
});

// プログラムを開始する関数
function startProgram() {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_startProgram" + "[start]");

  // カウンターをリセットしてローカルストレージに保存
  chrome.storage.local.set({ 'counter': 0 });

  // 1秒ごとにupdateBadgeText関数を呼び出すタイマーを開始し、タイマーIDをローカルストレージに保存
  intervalId = setInterval(updateBadgeText, 1000);
  chrome.storage.local.set({ 'intervalId': intervalId });

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_startProgram" + "[end]");
};

// プログラムを停止する関数
function stopProgram() {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_stopProgram" + "[start]");

  // バッジのテキストを空にして表示を消す
  chrome.action.setBadgeText({ text: "" });

  // ローカルストレージからタイマーIDを取得して、タイマーをクリアしてプログラムの実行を停止
  chrome.storage.local.get(["intervalId"], function (result) { intervalId = result.intervalId; });
  clearInterval(intervalId);

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_stopProgram" + "[end]");
};

// バッジのテキストを更新する関数
async function updateBadgeText() {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_updateBadgeText" + "[start]");

  // ローカルストレージからリロード間隔を取得
  chrome.storage.local.get(["intervalSeconds"], function (result) { intervalSeconds = result.intervalSeconds; });

  // ローカルストレージからカウンターの値を取得し、カウンターの値を保存
  let result = await chrome.storage.local.get(["counter"]);
  counter = result.counter;
  await chrome.storage.local.set({ 'counter': counter });

  // カウンターの値から表示するバッジのテキストを計算し、バッジに反映
  let counterDisplay = parseInt(intervalSeconds) - parseInt(counter);
  chrome.action.setBadgeText({ text: String(counterDisplay) });

  // カウンターの値がリロード間隔に達した場合、タブをリロードしてカウンターをリセット
  if (intervalSeconds === counter) {
    refreshTabs();
    counter = 0;
  } else {
    counter++;
  }

  // カウンターの値を保存
  await chrome.storage.local.set({ 'counter': counter });

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_updateBadgeText" + "[end]");
}

function refreshTabs() {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_refreshTabs" + "[start]");

  // Chromeの全てのタブを取得し、各タブに対して処理を行う
  chrome.tabs.query({}, function (tabs) {
    tabs.forEach(function (tab) {

      // タブのURLが特定の条件を満たす場合のみリロードを行う(例です)
      // if (tab.url.startsWith("https://xxxxxxxxxxxxxxx")) {
      if (tab.url.indexOf("https://xxxxxxxxxxxxxxx") > -1) {

        // タブをリロードしてページを更新
        chrome.tabs.update(tab.id, { url: tab.url });

        /* ***** */
        // タブの情報をログ出力する(デバッグ用)
        const myArray = [
          "tab.active" + ":" + tab.active,
          "tab.audible" + ":" + tab.audible,
          "tab.autoDiscardable" + ":" + tab.autoDiscardable,
          "tab.discarded" + ":" + tab.discarded,
          "tab.favIconUrl" + ":" + tab.favIconUrl,
          "tab.groupId" + ":" + tab.groupId,
          "tab.height" + ":" + tab.height,
          "tab.highlighted" + ":" + tab.highlighted,
          "tab.id" + ":" + tab.id,
          "tab.incognito" + ":" + tab.incognito,
          "tab.index" + ":" + tab.index,
          "tab.mutedInfo" + ":" + tab.mutedInfo,
          "tab.mutedInfo.extensionId" + ":" + tab.mutedInfo.extensionId,
          "tab.mutedInfo.muted" + ":" + tab.mutedInfo.muted,
          "tab.mutedInfo.reason" + ":" + tab.mutedInfo.reason,
          "tab.openerTabId" + ":" + tab.openerTabId,
          "tab.pendingUrl" + ":" + tab.pendingUrl,
          "tab.pinned" + ":" + tab.pinned,
          "tab.selected" + ":" + tab.selected,
          "tab.sessionId" + ":" + tab.sessionId,
          "tab.status" + ":" + tab.status,
          "tab.title" + ":" + tab.title,
          "tab.url" + ":" + tab.url,
          "tab.width" + ":" + tab.width,
          "tab.windowId" + ":" + tab.windowId
        ];
        let myString = "\n" + myArray.join("\n");
        /* ***** */

        console.log((new Date()).toLocaleString('ja-JP') + " " + myString);
      }
    });
  });

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_refreshTabs" + "[end]");
}

// コンテキストメニューを設定する関数
function SetContextMenus() {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_SetcontextMenus" + "[start]");

  // コンテキストメニューを作成し、各項目に対してidとタイトルを設定
  chrome.contextMenus.create({
    id: "url01",
    title: "url01",
    contexts: ["all"]
  });

  chrome.contextMenus.create({
    id: "url02",
    title: "url02",
    contexts: ["all"]
  });

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_SetcontextMenus" + "[end]");
};

// コンテキストメニューのクリックイベントを処理するコールバック関数
chrome.contextMenus.onClicked.addListener(function (info, tab) {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.contextMenus.onClicked.addListener" + "[start]");

  // メニュー項目のIDに応じて、対応するURLを設定し、新しいタブで開く
  let u;
  switch (info.menuItemId) {
    case "url01":
      u = "https://xxxxxxxxxxxxxxx";
      chrome.tabs.create({ url: u });
      console.log((new Date()).toLocaleString('ja-JP') + " " + "info.selectionText:" + info.selectionText + " " + encodeURIComponent(info.selectionText));
      break;
    case "url02":
      u = "https://xxxxxxxxxxxxxxx";
      chrome.tabs.create({ url: u });
      console.log((new Date()).toLocaleString('ja-JP') + " " + "info.selectionText:" + info.selectionText + " " + encodeURIComponent(info.selectionText));
      break;
  }

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_chrome.contextMenus.onClicked.addListener" + "[end]");
});

〇icon.png:前項の試作1と同様の手順で用意する。

スポンサーリンク

コードの説明

〇popup.js

ポップアップスクリプトです。拡張機能のポップアップウィンドウ内に「start」と「stop」のボタンがあり、ユーザーがそれぞれのボタンをクリックすることで、バックグラウンドスクリプトに対してプログラムの開始または停止をリクエストする仕組みです。

以下に関数ごとの処理を説明します。

1.document.addEventListener('DOMContentLoaded', function ()の部分

この関数は、ポップアップが読み込まれた際のイベントリスナーです。ポップアップが読み込まれると、リスナー内の処理が実行されます。

2.chrome.storage.local.getの部分

ローカルストレージから buttonStatus の値を取得し、それに応じてボタンの状態(有効・無効)を設定します。startButtonstopButtondisabled プロパティを変更して、ボタンの有効・無効を切り替えます。

3.startButton.addEventListener('click', function ()の部分

「start」ボタンがクリックされた際のイベントリスナーです。ボタンがクリックされると、バックグラウンドスクリプトにメッセージを送信してプログラムを開始するようリクエストします。「start」ボタンを無効にし、「stop」ボタンを有効に設定します。buttonStatus"start" としてローカルストレージに保存します。

4.stopButton.addEventListener('click', function ()の部分

「stop」ボタンがクリックされた際のイベントリスナーです。ボタンがクリックされると、バックグラウンドスクリプトにメッセージを送信してプログラムを停止するようリクエストします。「stop」ボタンを無効にし、「start」ボタンを有効に設定します。buttonStatus"stop" としてローカルストレージに保存します。


〇background.js

バックグラウンドスクリプトです。タブのリロード間隔を設定し、特定のURLに対して自動的にリロードを行うGoogle Chrome拡張機能のバックグラウンド処理を行うものです。

以下に関数ごとの処理を説明します。

1.chrome.runtime.onInstalled.addListenerchrome.runtime.onStartup.addListenerの部分

これらは、拡張機能がインストール時またはブラウザ起動時に実行されるリスナーです。onInstalled リスナーと onStartup リスナーは同じ処理を行います。SetContextMenus() 関数と onExtensionStart() 関数が呼び出されます。

2.onExtensionStart()の部分

インストール時かバージョンアップ時、ブラウザ起動時に共通の処理をまとめる関数です。ローカルストレージをクリアして、初期状態にリセットします。ボタンの状態を "start" として保存し、リロード間隔を 15 として保存します。startProgram() 関数を呼び出して、プログラムを開始します。

3.chrome.runtime.onMessage.addListenerの部分

メッセージ受信時の処理を行う関数です。メッセージから取得したリロード間隔を整数に変換して保存し、受信したアクションに応じて startProgram() または stopProgram() 関数を呼び出します。

4.startProgram()の部分

プログラムを開始する関数です。カウンターをリセットしてローカルストレージに保存します。1秒ごとに updateBadgeText 関数を呼び出すタイマーを開始し、タイマーIDをローカルストレージに保存します。

5.stopProgram()の部分

プログラムを停止する関数です。バッジのテキストを空にして表示を消します。ローカルストレージからタイマーIDを取得して、タイマーをクリアしてプログラムの実行を停止します。

6.updateBadgeText()の部分

バッジのテキストを更新する非同期関数です。ローカルストレージからリロード間隔とカウンターの値を取得して処理を行います。カウンターの値から表示するバッジのテキストを計算し、バッジに反映します。カウンターの値がリロード間隔に達した場合、refreshTabs() 関数を呼び出してタブをリロードし、カウンターをリセットします。

7.refreshTabs()の部分

Chromeの全てのタブを取得し、特定の条件を満たすタブのみをリロードする関数です。この例では、URLが "https://xxxxxxxxxxxxxxx" を含むタブがリロードの対象となります。リロードしたタブの情報をログ出力します(デバッグ用)。

8.SetContextMenus()の部分

コンテキストメニューを設定する関数です。コンテキストメニューを作成し、各項目に対してIDとタイトルを設定します。

9.chrome.contextMenus.onClicked.addListenerの部分

コンテキストメニューのクリックイベントを処理するコールバック関数です。メニュー項目のIDに応じて対応するURLを設定し、新しいタブで開きます。この例では、"url01" がクリックされた場合には "https://xxxxxxxxxxxxxxx" が、"url02" がクリックされた場合には "https://xxxxxxxxxxxxxxx" が開かれます。

スポンサーリンク

完成品2(機能を追加)

内容

完成品1に以下の機能を追加したものです

  • 1秒毎にランダムな背景色を生成してバッジの背景色を設定
  • 1秒毎(毎秒)にランダムな絵文字をアイコンとして設定
  • 更新間隔でアクションセンターにベーシック通知を発信

ファイル構成

  • manifest.json・・・完成品1と同じ
  • popup.html・・・完成品1と同じ
  • popup.js・・・完成品1と同じ
  • background.js・・・完成品1の一部を変更する
  • icon.png・・・完成品1と同じ
  • imagesフォルダを用意。そのフォルダ内に("confetti.png", "suit.png", "bow.png", "dog.png", "skull.png", "yoyo.png", "cat.png")を用意

ソースコード

〇background.js の updateBadgeText関数 ・・・ 完成品1のbackground.jsのupdateBadgeText関数を上書きする。


// バッジのテキストを更新する関数
async function updateBadgeText() {
  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_updateBadgeText" + "[start]");

  // ローカルストレージからリロード間隔を取得
  chrome.storage.local.get(["intervalSeconds"], function (result) { intervalSeconds = result.intervalSeconds; });

  // ローカルストレージからカウンターの値を取得し、カウンターの値を保存
  let result = await chrome.storage.local.get(["counter"]);
  counter = result.counter;
  await chrome.storage.local.set({ 'counter': counter });

  /* ********************************************************************* */
  // ランダムな背景色を生成してバッジの背景色を設定
  // chrome.action.setBadgeBackgroundColor({color: [255, 0, 0, 255]});
  let colorRandom = [0, 0, 0].map(() => Math.floor(Math.random() * 255));
  if (Math.random() < 0.1) {
    colorRandom.push(0);
  } else {
    colorRandom.push(255);
  }
  chrome.action.setBadgeBackgroundColor({ color: colorRandom });
  /* ********************************************************************* */
  // ランダムな絵文字をアイコンとして設定
  const EMOJI = ["confetti", "suit", "bow", "dog", "skull", "yoyo", "cat"];

  let lastIconIndex = 0;

  let index = lastIconIndex;
  index = Math.floor(Math.random() * EMOJI.length);
  if (index === lastIconIndex) {
    index = (index + 1) % EMOJI.length;
  }
  const emojiFile = `images/emoji-${EMOJI[index]}.png`;
  lastIconIndex = index;

  const response = await fetch(chrome.runtime.getURL(emojiFile));
  const blob = await response.blob();
  const imageBitmap = await createImageBitmap(blob);
  const osc = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);
  let ctx = osc.getContext('2d');
  ctx.drawImage(imageBitmap, 0, 0);
  const imageData = ctx.getImageData(0, 0, osc.width, osc.height);

  chrome.action.setIcon({ imageData });
  /* ********************************************************************* */
  // カウンターの値から表示するバッジのテキストを計算し、バッジに反映
  let counterDisplay = parseInt(intervalSeconds) - parseInt(counter);
  chrome.action.setBadgeText({ text: String(counterDisplay) });
  /* ********************************************************************* */
  // カウンターの値がリロード間隔に達した場合、タブをリロードしてカウンターをリセット
  if (intervalSeconds === counter) {
    /* ********************************************************************* */
    // アクションセンターに通知を作成
    let opt = {
      type: "basic",
      title: "リロードのお知らせ",
      message: "リロード間隔に達しました。ページを更新します。",
      iconUrl: "icon.png"
    }
    chrome.notifications.create(opt);
    /* ********************************************************************* */
    refreshTabs();
    counter = 0;
  } else {
    counter++;
  }

  // カウンターの値を保存
  await chrome.storage.local.set({ 'counter': counter });

  console.log((new Date()).toLocaleString('ja-JP') + " " + "backgroundjs_updateBadgeText" + "[end]");
}

〇imagesフォルダを用意。そのフォルダ内に("confetti.png", "suit.png", "bow.png", "dog.png", "skull.png", "yoyo.png", "cat.png")を用意

API reference - Chrome Developers の左側にある「Sample」のリンク先 GitHub - GoogleChrome-chrome-extensions-samples- Chrome Extensions Samples のファイルを一括ダウンロードして、「api-samples」フォルダ内の「action」フォルダ内の「images」フォルダを用意する

スポンサーリンク

作成する上でのポイント

以下のように、ローカルストレージは、GetしたらすぐにSetしないと上手く行かない。


  await chrome.storage.local.get(['counter'], function (result) {
    counter = result.counter;
  });
  await chrome.storage.local.set({ 'counter': counter });

備考

一定間隔で更新処理を行うときは、リクエスト先に負担をかけることになるため、問題ないことをご自身で確認の上ご利用ください。

参考サイト/関連サイト

Extensions - Chrome Developers

chrome.runtime - Chrome Developers

chrome.browserAction - Chrome Developers

GitHub - GoogleChrome-chrome-extensions-samples- Chrome Extensions Samples

Chromeの拡張機能ってどうやって作るの?開発ガイドライン&作り方をまとめた - Workship MAGAZINE(ワークシップマガジン)

Chrome拡張機能を作ってみよう③ - Qiita

Chrome拡張機能を自作する方法 - Hakky Handbook

Chrome拡張機能開発の公式チュートリアルを解説+補足 前編 - Qiita

Chrome拡張機能開発の公式チュートリアルを解説+補足 後編 - Qiita

Chrome拡張開発入門(manifest.json, Content Scripts, Service Workers) - わくわくBank

Google Chrome拡張機能の作り方 - NullNull

【Chrome extension】拡張機能を開発してみよう vol.3 - ブラウザで開いているタブ情報を取得する(ChromeAPI) -|事務職たらこ

Chrome拡張機能でタブの情報を取得する方法 - Qiita

【Chrome拡張機能】現在表示しているタブIDを取得する方法 - NullNull

Chrome拡張でページ内のwindowオブジェクトを取得してpopupやbackgroundに送信する方法

Chrome拡張でとっても役立つAPIのまとめ - Qiita

Chrome の拡張機能を Manifest V3 に対応する - バグ取りの日々

拡張機能マニフェストで API アクセス許可を宣言する - Microsoft Edge Development - Microsoft Learn

【ブラウザ拡張機能開発】Chrome Storage APIの使い方(備忘録) - BlossomsArchive

chrome extension StorageArea の仕様

chrome.storage を利用したchromeへのデータ保存 - Qiita

Chrome 拡張機能を作って公開しよう② 〜ポップアップの作成〜 - Qiita

chrome.actionの使い方 - おてて動かそう

Google Chromeの起動で実行する拡張機能の作り方

コンテキストメニューに項目を追加する Chrome拡張機能を作る – ラボラジアン

これで解決!Chrome拡張機能 にコンテストメニューを表示する-スケ郎のお話

Chrome拡張のManifest v3でpage_actionのコンテキストメニューが出ないとき

Chrome拡張機能で右クリックメニューを作る方法 - Qiita

v3マニフェストではpage_actionは廃止されておりactionに統合された · mstssk-pixiv_bookmark_helper@aaa8e84 · GitHub

javascript - Have chrome.tabs.reload() reload a tab other than the active tab - Stack Overflow

javascript - javascript - 非同期待機によるchrome.runtime.onMessage応答

【JavaScript】 日時と時刻を取得して表示してみる

JavaScriptの.indexOf()でURLを部分一致で検索して一致する場合にモーダルを表示 - DUB DESiGN

カラーコードのランダム生成で注意すること - Qiita

最後までお付き合いいただきありがとうございます!

この情報が誰かの役にたてれば幸いです。

スポンサーリンク

-その他