Personal Blocklist の代替になりそうな Chrome 拡張機能を作ってみた

(2022/08/11 追記)
uBlacklist の使い方に関する情報はやや古くなっています。最新の使い方を知るには、ウェブサイトを参照してください。

[基本的な使い方 | uBlacklist](https://iorate.github.io/ublacklist/ja/docs/getting-started)

# uBlacklist

Google の検索結果から指定したサイトをブロックする拡張機能を作ってみました。Personal Blocklist (by Google) の代替になるかもしれません。

![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/7920221f-7d85-1f97-c81f-47a31762d3cc.gif)

**Chrome 版**
https://chrome.google.com/webstore/detail/ublacklist/pncfbmialoiaghdehhbnbhkkgmjanfhe?hl=ja

**Firefox 版** (Android でも一部制限はありますが動作します)
https://addons.mozilla.org/ja/firefox/addon/ublacklist/

**GitHub**
https://github.com/iorate/uBlacklist

## 動機

Personal Blocklist は Google の検索結果から指定したドメインのページを除外する拡張機能です。以前試したことはありましたが、除外したページが最初に一瞬表示されるのが気になってしまい、しばらく使っていませんでした。
最近、ある種のワードで Google 検索したときに現れる大量の低品質なサイトに耐えられなくなってきたので、勉強を兼ねて Personal Blocklist の代替となる拡張機能を作ることにしました。
なおオリジナルの Personal Blocklist は Chrome ウェブストアでの配布が終了しています。

## 特徴

1. ブロックしたサイトが一瞬表示されるのを防ぎます。
2. ドメイン名でなくマッチパターンや正規表現を使うことで、柔軟にブロックするサイトを指定できます。
3. Google ドライブを使って、ブロックするサイトのリストをデバイス間で同期できます。
4. 指定した URL からリストを定期的にダウンロード (購読) することもできます。

## 使い方

### 検索結果からブロック

インストールすると、Google の検索結果に「このサイトをブロックする」リンクが表示されるので、ブロックしたいサイトのものを押します。

![block1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/de45d17b-1f72-902b-c9a9-f32a9744c9f0.png)

ダイアログが出てくるので、「ブロック」をクリックします。

![block2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/12d5a251-c2b4-6fa2-a2ed-b10caac32e04.png)

以後、ブロックしたサイトは検索結果に表示されなくなります。

![block3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/91391bf0-39c6-13bb-e4c3-c88de179cec4.png)

出てきたダイアログで「詳細」を押すと、ページのURL、追加されるルール、削除されるルールが確認できます。
追加されるルールはその場で編集できます。[マッチパターン](#マッチパターン)、[正規表現](#正規表現)の項を参考にして下さい。サブドメインごとブロックすることもできます。

![block4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/ac878dc4-5e49-ca68-ee95-6a5daae1bd36.png)

### ブロックを解除

ブロックを解除する場合は、検索結果上部の「表示する」リンクを押すと、

![unblock1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/d65d77d5-5613-0ec6-d1f7-56f1c3ef0a6b.png)

ブロックしたサイトが赤背景で表示されるので、「このサイトのブロックを解除する」リンクを押して、

![unblock2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/07fc1f43-a0c9-4208-c812-7e8353ccb394.png)

出てきたダイアログで「ブロックを解除」をクリックします。

![unblock3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/75610527-7c41-ae09-dc36-ef3ddf02622a.png)

### 開いているサイトをブロック

開いているサイトを Google の検索結果からブロックしたくなった場合は、ツールバーの uBlacklist のアイコンをクリックすると、その場でブロックすることができます。

![popup2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/b353c744-1f0e-7e82-6345-8702024e0e47.png)

既にブロックされている場合は、ブロックを解除できます。

## オプション

ツールバーの uBlacklist のアイコンを右クリックして「オプション」を選ぶと、オプションページが開きます (Chrome の場合)。
オプションページではブロックするサイトのリストを直接編集できます。Google ドライブを使ってリストを同期したり、指定した URL からリストを購読することもできます。

### リストの編集

![options1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/39e68c19-6d0e-96d1-cfac-ff10c9939b99.png)

テキストエリアの1行が1ルールになります。[マッチパターン](#マッチパターン)あるいは[正規表現](#正規表現)を使ってルールを書くことができます。無効な行は単に無視されるので、空行やコメント行を挿入できます。
編集したら「保存」ボタンを押して保存して下さい。

#### マッチパターン

マッチパターンはワイルドカードを含んだ URL です。詳細は [MDN web docs](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Match_patterns) などにありますが、ここでは例をいくつか示します。

有効なマッチパターンの例

|マッチパターン|ブロックされる URL の例|
|---|---|
|\*://example.com/\*<br><br>("example.com" 上の URL)|http\://example.com/<br><br>https\://example.com/hoge|
|\*://\*.example.net/\*<br><br>("example.net" かそのサブドメイン上の URL)|http\://example.net/<br><br>https\://foo.example.net/hoge|
|\*://example.org/hoge/\*<br><br>("example.org" 上の URL で、パスが "/hoge/" から始まるもの)|http\://example.org/hoge/fuga.html|

無効なマッチパターンの例

|無効なマッチパターン|理由|
|---|---|
|\*://www\.qinterest.\*/\*|"\*" がホストの先頭以外に置かれています。代わりに正規表現を使って下さい。|
|\<all_urls\>|サポートされていません。|

#### 正規表現

JavaScript の正規表現リテラルを使って、より自由度の高い記述ができます。

有効な正規表現の例

|正規表現|ブロックされる URL の例|
|---|---|
|/^https:\/\/www\\.qinterest\\./<br><br>("https\://www\.qinterest." から始まる URL)|https\://www\.qinterest.com/<br><br>https\://www\.qinterest.jp/hoge/|
|/^https?:\/\/([^/.]+\\.)\*?xn--/<br><br>(日本語など国際化ドメイン名を含む URL)|http\://例.テスト/|

無効な正規表現の例

|無効な正規表現|理由|
|---|---|
|^https?:\/\/example\\.com\/|正規表現が "/" で囲まれていません。|
|/^https?://example\\.com//|正規表現内の "/" がエスケープされていません。|

#### ブロック解除ルール

マッチパターンまたは正規表現の頭に `@` をつけると、そのサイトをブロックしないという意味になります。ブロック解除ルールは通常のルールより優先されます。
購読しているリストによるブロックを解除する場合に使用することを想定しています。

### Google ドライブでの同期

Google ドライブを経由して、複数のデバイス間でリストを同期することができます。
オプションページの「同期」セクションにある「同期を有効にする」をクリックします。

![options2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/6c1359d2-61b3-ef3d-4acf-35c4f1556cf7.png)

Firefox 版ではここで `www.googleapis.com` へのアクセス許可が求められますので、許可します。
表示される画面に従って、Google アカウントを選択し、Google ドライブへのアクセスを許可します。

![options3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/1fd9e82f-7e64-3679-28d1-b346c2787d28.png)

同期が有効になると、Google ドライブのルートフォルダにファイル `uBlacklist.txt` が作成されます。このファイルを介して、同期を有効にした全てのデバイスに、最新のリストが反映されます。

### 購読

指定した URL から定期的にリストをダウンロードして、検索結果に反映させることができます。
オプションページの「購読」セクションにある「購読を追加する」をクリックすると、ダイアログが開きます。
「名前」には任意の名前を、「URL」にはリストが置かれている URL を入力して、「追加」を押して下さい。

![options4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/0670062d-a6d7-8ec2-7609-8563ce23ce96.png)

URL のドメインへのアクセス許可が求められますので、「許可」をクリックします。[^1]

[^1]: インストール時には Google へのアクセス許可しか求めていませんので、追加のアクセス許可が必要になります。インストール時に全てのサイトへのアクセス許可を求めれば不要になりますが、それではセキュリティ的にあんまりでしょう。

![options5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/e7801f97-b07e-f894-9f2e-35f602c03902.png)

追加した購読については、右のボタンから表示、今すぐ更新、削除を行うことができます。

![options6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155657/24b6d53e-c237-ff31-8bd5-4b34f4311227.png)

#### 購読用のリストを公開する

購読用のリストを公開する場合は、リストを UTF-8 でエンコードしたテキストファイルにして、適当な HTTP/HTTPS サーバー上に置き、その URL を公開して下さい。
GitHub 上でファイルをホストしてその raw URL を公開するようにすれば、更新履歴も見やすくてよいと思います。

## Tips

### 表示件数を増やす

多数のサイトをブロックしていると、検索結果の表示件数が極端に少なくなってしまう場合があります。
Google 検索の設定で表示件数を増やすことができます。また AutoPagerize 系の拡張 (Chrome なら [uAutoPagerize](https://chrome.google.com/webstore/detail/uautopagerize/kdplapeciagkkjoignnkfpbfkebcfbpb?hl=ja)、Firefox なら [weAutoPagerize](https://addons.mozilla.org/ja/firefox/addon/weautopagerize/) など) と併用すれば、検索結果を自動的に継ぎ足すこともできます。

### 「このサイトをブロックする」リンクを表示しない

最初にいくつかのサイトをブロックし、それ以上追加しない場合など、「このサイトをブロックする」リンクが邪魔になることがあるかもしれません。
その場合は、オプションページに「「このサイトをブロックする」リンクを表示しない」という設定がありますので、有効にして下さい。

## 技術的な話

ブロックするサイトが一瞬表示されるのを防ぐためには、対象となるノードをできるだけ早く非表示にする必要があります。
コンテンツスクリプトの注入タイミング `"run_at"` は3通り指定できますが、この目的に間に合うのは `"document_start"` だけで、これは DOM の読み込み開始前にコンテンツスクリプトを注入することを意味します。このタイミングでもルートノードだけは触ることができるので、[MutationObserver](https://developer.mozilla.org/ja/docs/Web/API/MutationObserver) でルートノードの監視を開始し、その後追加されてくるノードをコールバックで処理するようにしました。これで一応うまくいっているようです。