見出し画像

人生初のChrome拡張作成


Chrome拡張はJavascriptで書くそうです。

必要なファイルは次の通りです。

やりたいことで変わるので、この辺りは生成AIと相談しながらやりました。manifest.json(ファイル名は固定)
popup.html(ファイル名は何でも可)
popup.js(ファイル名は何でも可)
background.js(ファイル名は何でも可)
contents.js(ファイル名は何でも可)
iconファイル(ファイル名は何でも可)

概要図

概要図

今回は拡張の中のselectboxを選ぶと、選んだ内容に応じて、<p>タグにテキストを入れていくという単純なものを想定します。

manifest.json

{
    "manifest_version": 3,
    "name": "拡張機能",
    "version": "1.0.0",
    "description": "拡張機能の説明",
    
    "permissions": ["activeTab"],
    
    "action": {
        "default_popup": "popup.html"
    },

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

    "icons": {
        "16": "images/icon16.png",
        "48": "images/icon48.png",
        "128": "images/icon128.png"
    },

    "content_scripts": [
        {
            "matches": ["https://*/*", "http://*/*"],
            "js": ["contents.js"]
        }
    ]
}

manifestのバージョンが3のサンプルです。要はこの拡張機能で使うファイルが列挙されています。
ポイントはpermissions、default_popup、service_worker、jsといったところのようです。
permissionsは今回はactiveTabにしましたが、scriptingという権限もあり、backgrondからcontentなどに埋まっているスクリプトを実行することができるようですが、今回は非同期で動かれると困るので、止めました。
ファイル名が何でもいいというのはこのポイントで上げたところでファイル名を指定するのでファイル名は何でもいいという話なだけです。
これらのファイルは同じフォルダに保存してあります。imagesなどのフォルダを相対パスで指定してもいいそうです。

popup.htmlは省略します。

selectboxにlistenerを埋め込むため、<script src="popup.js"></script>と書いてあるだけなので。

popup.jsの概略

document.getElementById('selectBoxId').addEventListener('change', function() {
    const selectedValue = this.value;
    chrome.runtime.sendMessage({value: selectedValue}, function(response) {
        if(response.message){
            alert(response.message)
            console.log(response);
        }
    });
});

こんな感じでchrome.runtime.sendMessageでbackground.jsにメッセージを送ります。この場合はidが「selectBoxId」というselectboxが変更になったら、そのselectboxの値を送っているというものです。

background.jsの概略

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
        if(tabs.length === 0) {
            sendResponse({message: "activeTabがありません。"});
            return true;
        }

		//valueがからなら何もしないで返す
        const activeTab = tabs[0];
        if(request.value === ""){
            return true;
        }   

		//valueの値をontents.jsにsendMassageを送る
        chrome.tabs.sendMessage(activeTab.id, {value: request.value}, (response) => {
            if (chrome.runtime.lastError) {
                console.log("failed");
                console.log(chrome.runtime.lastError.message);
                sendResponse({message: "failed"});
                return true;
            } else {
                console.log("success");
                return true;
            }
        });

    return true;
});


activeTabがない場合はsendResponseでその旨をpopup.jsに渡して、受け取ったらalertでユーザに知らせています。また、valueがない場合はなにもせずに関数を終了し、ユーザーには知らせません。また、contents.jsにsendMessageした後にエラーが起きた場合も失敗したことsendResponseで返します。なお、なにをやってもreturn trueにしていますが、以下Chat-GPTの解説を引用します。

非同期処理を含むchrome.runtime.onMessageリスナーでsendResponseを正しく扱うためには、return true;をリスナーの最後に置くことで、メッセージの応答が非同期になる可能性があることを明示する必要があります。このコードはその要件を満たしています。

上記ソースをChat-GPTにチェックしてもらいました。

とのことです。確かfalseを返したら怒られたので、聞いてみたら、そういうことだったという仕様だったと思います。

Contents.jsの概略

chrome.runtime.onMessage.addListener(function(request) {
    console.log("case1:" + request.value);
    element = document.getElementById('selectcase1') //<p>タグにrequest.valueを入れている。
    if(element){
		if(element.innerHTML){
			element.innerHTML = ""
		} else {
	        element.innerHTML = loadContent(request.value);
    	}
    }
    return true;
});


function loadContent(value){
    switch(value){
        case case1value:
			return(case1html) //case1htmlは静的コンテンツです。
        //この後延々とvalueに合わせた処理を入れています。
    }
}


これも単純にしていますが、現在のtabのidがselectcase1という<p>タグに対して、loadContentという関数から返ってくる静的なhtmlを入れてあげているだけです。

以上、人生で初めてChrome拡張機能を作成した際のメモでした。

この記事が気に入ったらサポートをしてみませんか?