【Chrome拡張】SPAやSSRのWebページでページ遷移を検知する

GitHub上で動作するChrome拡張を作成したかったのですが、目的のページへ遷移しても動作しなくてはまったので対応メモです。

やりたいこと

GitHub上の特定のページへ遷移すると発火するChrome拡張を作りたいです。

現象

manifest.jsonのcontent_scriptsのmatchesに当てはまるURLにアクセスしても、content_scriptsに書いたJavaScriptが動作しませんでした。

原因

SPAなのかSSRなのかはわかりませんが、GitHubはページ遷移時、ページ全体を読み込むのではなく差分のみを読み込んでレンダリングしているみたいです。

content scriptだとこの差分更新を上手く検知できないようです。ページの読み込みが発生しない&非同期で差分更新をしてるので当然っちゃ当然なんですが。

対策

以下の手順を踏むと、SPAとかでもページの遷移を検知できます。

イベントページを使う

content_scriptsではなく、イベントページ/バックグラウンドページを使います。

イベントページ/バックグラウンドページを使う場合、manifest.jsonに以下のように記述すると、イベントページ/バックグラウンドページとしてbackground.jsが起動します。

  "background": {
    "scripts": [
      "background.js"
    ],
    "persistent": false
  },

persistentはtrueに設定するとバックグラウンドページとして、falseに設定するとイベントページとして動作します。

バックグラウンドページは常にバックグラウンドで常駐するため、メモリを常に占有してしまいます。イベントページは必要な時のみ立ち上がって、処理が完了したら閉じます。

現在はイベントページが推奨されているようなので、特別理由がない限りはpersistentはfalseで良さそうです。

参考:

Chrome拡張の開発方法まとめ その1:概念編 - Qiita
Chrome拡張の開発に必要な知識とかの覚書です。 この記事では開発の前に知っておくべきChrome拡張の全容について解説していきます。 「実際に開発しながら学ぶ」形式の解説記事は多く見られるのですが、概念についてちゃんとまとめてある...

イベントページでのページ遷移の検知

chrome.tabs.onUpdated.addListenerを使うと、うまいことタブの更新を検知できます。

具体的には以下のような感じで使います。

chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
  if (info.status === 'complete' && tab.url.indexOf('https://github.com/') !== -1) {
    // いい感じの処理
  }
});

chrome.tabsを使うときはmanifest.jsonのpermissionsにtabsを追加する必要があります。

  "permissions": [
    "tabs",
  ],

このchrome.tabs.onUpdated.addListenerですが、読み込み時と読み込み完了時の2回走ります。

読み込み時はinfo.statusloadingになり、読み込み完了時はinfo.statuscompleteとなります。

今回は読み込み完了時だけ処理が走ればよく、またGitHub上でのみ動けばいい拡張機能を作りたかったので、上記のようにif分で条件を絞っています。

参考:

Chrome 拡張機能メモ : (*x).b=z->a+y/c
◆ localStorage の監視で sendMessage を減らせるかも◆ タブ更新したときの URL は info じゃなくて tab の方を使う

イベントページでDOMにアクセスする

Chrome拡張ではcontent_scriptsだとDOMに普通にアクセスできるのですが、イベントページやポップアップからDOMへのアクセスしようとすると一工夫必要です。

具体的には以下のような感じで、chrome.tabs.executeScriptを使用します。executeScriptを使えば、content_scriptsをイベントページに挿入できます。

chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
  if (info.status === 'complete' && tab.url.indexOf('https://github.com/') !== -1) {
    chrome.tabs.executeScript(null, { file: './contentScript.js' }, () => {});
  }
});

この例では./contentScript.jsが実行されます。この./contentScripts.jsはcontent_scriptsとして扱われるので普通にDOMにアクセスすることが可能です。

なお、content_scriptsとして扱われるためmanifest.jsonにURLを登録する必要があります。以下のようにpermissionsに./contentScripts.jsが走る可能性のあるURLを追加しましょう。

  "permissions": [
    "tabs",
    "https://github.com/*"
  ],

ここに追加されていないURL上で./contentScripts.jsが発火しようとすると、イベントページのデベロッパーツール上でエラーが発生します。

これでSPAとかのWebページでも意図したタイミングで発火するChrome拡張を作れそうです。

参考:

Chrome Extension - Get DOM content
I'm trying to access the activeTab DOM content from my popup. Here is my manifest: { "manifest_version": 2, "name": "Test", "description": "Test script"...
Content Scripts - Google Chrome

コメント