画像を置換
画像を置換するやつを作りたいけど、そんなすぐ書ける人でもないので、そういう拡張を作った記事があったのでそちらを読む。
画像をピカチュウに置き換えるやつ。
manifest.json
{
"name": "Pikachu Everywhere",
"version": "0.1",
"description": "Replace every image with Pikachu images.",
"manifest_version": 2,
"icons": {
"16": "assets/images/icon16.png",
"32": "assets/images/icon32.png",
"48": "assets/images/icon48.png",
"128": "assets/images/icon128.png"
}
}
ここは特に変わらず。こちらの拡張は、コンテンツスクリプトを使うみたい。なので
,
"content_scripts": [{
"matches": [""],
"all_frames": true,
"js": ["assets/js/contentScript.js"]
}]
と追記。matchesが適用サイト。all_framesが指定されたURL要件に一致するすべてのフレームに挿入するか、タブの一番上のフレームにのみ挿入するかを指定、フレームってなんですかね。jsがcontentScript.jsを置いてる場所で相対パスでフォルダ作ってそん中に入れた。
contentScript.js
確認
console.log("Pikachu Everywhere - Content Script is Running");
取りあえず動くか確認用。
動いとりますね。
ピカチュウAPI
https://some-random-api.ml/pikachuimg
こちらを使う。これ面白いね。アクセスすると
{"link":"https://i.imgur.com/oH5Vi6I.gif"}
みたいなJSONを返してくれるサイトみたい。
流れ
- Webページ上のすべての画像要素を取得します。
- APIにGETリクエストを実行して、ランダムなピカチュウ画像リンクを取得します。
- 画像要素の
src
属性を、取得したリンクに置き換えます。
ふむふむ。この2のところがコンテンツスクリプトだけじゃできないから、その部分はバックグラウンドを使うみたい。コンテンツスクリプトはWebページ依存だけど、バックグラウンドはそうじゃないから、クロスオリジンリクエストを処理できる、のか。クロスオリジンリクエストっつーのはAのサイトに書かれたjsからBのサイトにリクエストを投げるみたいなことらし。これはさせないようにしてるってことなんかな。
コンテンツスクリプトから送信されたバックグラウンドスクリプトでメッセージをリッスンします。Webページが開くたびに、コンテンツスクリプトが実行され、画像のURLを要求するメッセージをバックグラウンドスクリプトに送信します。バックグラウンドスクリプトは、その後、ピカチュウAPIへの非同期呼び出しを実行し、リンクを取得してコンテンツスクリプトに送り返します。
っていう流れ。難しそ。
sendMessege
コンテンツスクリプトとバックグラウンドスクリプトのやり取りっていうのがメッセージでやるらしい。とりあえずコンテンツスクリプトからメッセージを送信する。
let images = document.getElementsByTagName('img');
for(let i = 0; i < images.length; i++){
chrome.runtime.sendMessage({msg: 'image', index: i}, function({data, index}){
images[index].src = data.link;
});
}
画像要素を取得してから、その全てをなめてく。chrome.runtime.sendMessageでメッセージ送る。メッセージはJSON形式のオブジェクトで、ここでは形式と、画像のインデックスを送ってるみたい。メッセージが返ってきたらコールバックが呼ばれる。dataが多分APIの取得結果が入って、indexは送ったデータがそのまま返ってくるような感じかなぁ。ほんでimgタグのsrcに返答のデータのリンクを設定するみたいな感じかね。
background.js
background.jsでメッセージを受け取って、APIでJSONを受け取って、それをコンテンツスクリプトに返す。
chrome.runtime.onMessage.addListener(function (message, sender, senderResponse) {
if (message.msg === "image") {
fetch('https://some-random-api.ml/pikachuimg')
.then(response => response.text())
.then(data => {
let dataObj = JSON.parse(data);
senderResponse({ data: dataObj, index: message.index });
})
.catch(error => console.log("error", error))
return true; // Will respond asynchronously.
}
});
chrome.runtime.onMessage.addListenerは設定するとメッセージが送られてくるのを待つみたいなやつ。で、送られてきたらコールバック関数を実行する感じ。messageに送られてきたデータ、senderが送ってきた対象、senderResponseが送り返すデータ(JSON)。message.msg === “image”でコンテンツスクリプトから送られてきたデータかを判断してから、fetchする、fetchってなんですか。
https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch
説明があった。HTTPでのやり取りを分かりやすくやってくれるやつみたい。ネットワークでのやり取りは応答待ちがあって、待ってる間に処理が次に進んだりしちゃうと困るから、応答が来てから次の処理に進むみたいな時に使うやつみたい。非同期処理ってやつか。ちゅーことで、
fetch(‘https://some-random-api.ml/pikachuimg’)でAPIにアクセス。この=>矢印みたいなのはアロー関数とかアロー式っていうらしい。thenっていうのが、アクセスした結果が返ってきたら、って感じで、これだけで非同期な処理ができるようでっす。初めてなので、アロー関数を使わない書き方にすると
.then(function (response) {
return response.text()
})
こんな書き方になるはず。だからAPIにリクエスト送った返事をテキストに変換してるんだと思う。そんでdata、このdataはどっから出てきたんだと思ったけど多分textに変換した結果が入ってんだろうな、を、JSON形式にパースして、senderResponseにdataとindexをつけて入れて返すみたい。この時senderResponseはコンテンツスクリプト内のプログラムで使えるような形式じゃないとだめだし、この場合dataObjの中にはAPIから取得した
{"link":"https://i.imgur.com/oH5Vi6I.gif"}
が入ってるはず。catchはfetch中にエラーが起きたらそこに飛んでエラーですよってコンソールに表示するやつ。return trueが、このリスナ内の処理は非同期処理がありますよーってやるために書くみたい。これが無いと、非同期処理であることを無視して処理が次に進むらしい。そんで、APIを利用してる時は、manifestに許可が必要みたい。
,
"permissions": [
"https://some-random-api.ml/*"
]
うむ。
結果
試しにブログトップに行ってみたら
あれ?広告とアイコンぐらいしか変わってない。なんでだろーと思ってソース見てみたらエントリーのとこに使われてる画像はsrcじゃなくて、data-srcとかsrcsetとかがあった。
data-srcはよく分かんなかったので、srcsetの方に変えてみたら
変わってないのもあるけどほぼなった。webむつかしーね。