[拡張機能]サイトブロッカーの作り方 -webNavigationの使い方-
webNavigation
を使ったサンプルコードとして、サイトブロッカーを実装してみます。3つのファイルで動きます。次の内容のファイルを作成し、拡張機能を読み込み、Youtubeに訪れてみてください。blocked.html
にリダイレクトされます。拡張機能が作れます。
{
"manifest_version": 3,
"name": "Site Blocker",
"version": "1.0",
"permissions": ["webNavigation", "tabs"], // ①
"background": {
"service_worker": "background.js"
},
"host_permissions": ["*://*/*"]
}
chrome.webNavigation.onBeforeNavigate.addListener(details => { // ②
const blockedSites = ["youtube.com", "twitter.com"]; // ③
if (blockedSites.some(site => details.url.includes(site))) { //④
chrome.tabs.update(details.tabId, { url: "blocked.html" }); // ⑤
}
}, { url: [{ urlMatches: "http://*/*" }, { urlMatches: "https://*/*" }] }); // ⑥
<!DOCTYPE html>
<html>
<head>
<title>Blocked!</title>
</head>
<body>
<h1>This site is blocked.</h1>
</body>
</html>
webNavigation
① ナビゲーションとは、ユーザーが指定したURLにアクセスして、そのページを表示するまでの一連のプロセスのことをいいます。ページが画面に表示されるまでには様々な段階があり、それに対応したイベントが発生しています。これら発生する一連のイベントを追跡するための機能がwebNavigation
APIです。
例えばこのサイトブロッカーではonBeforeNavigate
イベントを検知したときに実行するイベントリスナーを定義しています。
webNavigation.onBeforeNavigate
② onBeforeNavigate
イベントはページの読み込みが発生する前に発生するイベントです。このサイトブロッカーではページ読み込みが発生する前に別ページにリダイレクトしているので違和感なく動作しています。しかし以下のようにonComplated
イベント発生時にイベントリスナーを追加するとサイトが読み込まれた後にリダイレクトされて少し不自然なサイトブロッカーになります。
chrome.webNavigation.onCompleted.addListener()
このように、webNavigation
APIで扱うイベントはブラウザーの視覚的な変化に対応したものに関連します。(webNavigation
APIに似たものにwebRequest
APIがありますが、こちらはHTTPレイヤーからの下位レベルのビュー寄りです。)
次の図はナビゲーションの発生するイベントのフロー図です。再掲しますがナビゲーションとは、ユーザーが指定したURLにアクセスして、そのページを表示するまでの一連のプロセスのことをいいます。
イベント | 説明 |
---|---|
onCreatedNavigationTarget |
新しいタブやウィンドウが作成されたタイミング |
onBeforeNavigate |
リンクをクリックした直後などナビゲーションを開始した直後に発生するイベント。ページ読み込みが行われる前のタイミング。 |
onCommitted |
ナビゲーションが確定し、URLに移動することが決定し、ドキュメントがロードされる直前に発生するイベント。 |
onDOMContentLoaded |
DOMツリーの構築が完了したが、画像やCSSなどのリソースは読み込まれていないタイミング |
onHistoryStateUpdated |
History API を使ってURLを更新するタイミング。SPAのサイトでページ移動するときなどがある。 |
onReferenceFragmentUpdated |
URIフラグメントが変更されたタイミング。URIフラグメントとはページ内リンクなどURLの後ろに# に続くもの。 |
onCompleted |
ページとリソースが完全に読み込まれたタイミング |
onErrorOcurred |
ナビゲーションの途中でエラーが発生したタイミング |
多くの場合、必要になるのはonBeforeNavigate
とonComplated
でしょう。
これらのイベントについて少し補足します。
history.pushstate() はReact RouterやVue Routerなどのルーティングライブラリで使われる。これらのライブラリでフロントエンドを作っているとき、ページ遷移時は再度読み込みではなく History APIを使って行われる。ReactやVueで作られるサイトのページ遷移の検出に役立つ。一方、ページの内容が実際にどう変わったのかはイベント自体からは直接わからないため、ページのDOMを解析したり、特定の条件に基づいて動作を変える追加のロジックが必要。onHistoryStateUpdated イベントはブラウザが
onReferenceFragmentUpdated
イベントとonHistoryStateUpdated
イベントはonComplete
イベント発生後でも発生します。
④ details
②のコールバック関数ではdetails
という引数にナビゲーションの情報が格納されます。このdetails
に格納されるプロパティは検知したイベントによって異なります。下の表はonBeforeNavigate
イベントので利用できるプロパティです。
プロパティ | 説明 |
---|---|
tabId |
ナビゲーションが行われるタブのID |
url |
移動さきのURL |
frameId |
ナビゲーションが行われようとするフレームを示す。1 はiframeであり、2,3,...はネストされたiframeを指す。0は最上位の閲覧コンテキストで、現在見ているページ全体で移動されていると考えるといい。 |
timeStamp |
ナビゲーションを開始している時刻 |
https://runebook.dev/ja/docs/web_extensions/api/webnavigation/onbeforenavigate
下の表はonComplete
イベントで利用できるプロパティです。
https://developer.chrome.com/docs/extensions/reference/webNavigation/#event-onCompleted
chrome.tabs.update
⑤ chrome.tabs.update(details.tabId, { url: "blocked.html" }); // ⑤
この箇所でページを更新しています。tab.update
を使うとタブの情報を編集できるのですがここではURLをパラメーターに渡してページ移動させています。また、webNavigation.onBeforeNavigate
イベントと組み合わせて、ページが読み込まれる前に用意したページに移動せさることでリダイレクトを実現しています。
⑥ {url: [{urlMatches: }]}
webNavigation.onBeforeNavigate
の第二引数であるフィルタリング条件を定義しています。こここでは
{ url: [{ urlMatches: "http://*/*" }, { urlMatches: "https://*/*" }] });
と定義しているので、コールバック関数で定義したイベントリスナーはonNavigate
イベントが発生してかつ、httpかhttpsで始まるURLに移動するときにのみ実行されます。このようなフィルタリング条件を設定すると、chrome://
やfile://
など他のプロトコルを使用するURLに対して反応しなくなり、コードを実行する範囲を最小限にします。
Discussion