ユーザースクリプト(UserScript)作成時の覚書

投稿日 2021/02/24 更新日 2021/09/13

ユーザースクリプトとは

ユーザースクリプトとは、ブラウザの対象ウェブページでユーザ作成のスクリプトを実行する為の仕組みです。元々は、Firefoxのアドオンとして作成されたGreasemonkey上で使用できるスクリプトでした。その後、後続の拡張機能も同様の仕様で実行可能なユーザースクリプトとして一般化しました。

拡張機能の種類

ユーザースクリプト用の拡張機能として、主に次の3種類が開発されています。

※AdGuardなどの例外も存在します。

ドキュメント

インストール

ユーザースクリプトの公開と検索

※ブログ等で公開されていることもあります。

デバッグ

ログ出力する

  • console.log()関数を使用する
    • Webコンソール上に出力する
  • alert()関数を使用する
    • ページ上に表示する
  • GM_log()関数を使用する
    • Webコンソール上に出力する
    • 使用には、明示的な@grant指定が必要です「// @grant GM_log
    • 補足:最新のGreasemonkeyでは既に廃止済みの機能です

エラー出力を確認する

ユーザースクリプトがエラーした場合、Webコンソール上に出力されます。ただし、確実にエラー出力されるとは限りません。

※無言死事例:GreasemonkeyのGM.xmlHttpRequest関数内のエラー

デバッガーを使用する

  • Greasemonkey
    • Firefox標準のデバッガーを使用する
      • [開発ツール] > [デバッガー] > [ソースファイル] > [user-script://] > [対象ユーザースクリプト]
  • Tampermonkey
    • ブラウザ標準のデバッガーを使用する
      • [Firefox] > [開発ツール] > [デバッガー] > [Tampermonkey] > [userscripts] > [対象ユーザースクリプト]
      • [Chrome] > [開発ツール] > [Sources] > [Tampermonkey] > [userscript.html?name=対象ユーザースクリプト]
    • スクリプト開始直後にデバッガーを起動する
      • [オプション] > [設定] > [全般]
        • [設定のモード] を [上級者] に設定する
        • [スクリプトをデバッグする] を有効にする
  • Violentmonkey
    • ブラウザ標準のデバッガーを使用する
      • [Firefox] > [開発ツール] > [デバッガー] > [Violentmonkey] > [対象ユーザースクリプト]
      • [Chrome] > [開発ツール] > [Sources] > [Violentmonkey] > [対象ユーザースクリプト]

ブラウザ標準デバッガーの使用方法

  • ブレークポイント: 行番号をクリック(選択)
  • ステップ実行(Play/pause): F8
    • 次のブレークポイントまで実行する
  • ステップオーバー(Step over): F10
    • 次の行まで実行する
  • ステップイン{Step in}: F11
    • 関数呼び出し以外、次の行まで実行する。関数呼び出し、呼び出した関数へ入る
  • ステップアウト(Step out): Shift+F11
    • 現在の関数の終端まで実行する

デバッガの使い方 - 開発ツール | MDN
 コードをステップ実行する - 開発ツール | MDN

備考

個人的な提案として初期開発は、Tampermonkey / Violentmonkeyを使用して。最終的にGreasemonkeyに対応させる開発の流れを考えます。

エラー出力の安定性、後方互換性、個別の独自機能など開発面では、Tampermonkey / Violentmonkeyに一日の長があると言えます。ですが、Greasemonkeyは、他と比べて高速であり。また、ユーザ数などでGreasemonkey非対応ともしづらいため、このような流れを考えます。

実行タイミング

ユーザースクリプトの実行タイミングは、@run-atで指定できます。

@run-at概要
document-startスクリプトをできるだけ早く実行する(※)
document-endDOMContentLoaded時に実行する(既定)
document-idleDOMContentLoaded起動後に実行する

※拡張機能のコンテンツスクリプトは、ウェブページの読み込みより先に実行できます。<head>を読み込むより早く実行することができます。ただし、WebExtensionAPIにアクセスする場合、非同期処理の影響で最終的に<head>読み込み中や<head>より後に実行することになります。そのため、「できるだけ早く実行する」と言う曖昧な表現になります。

別のタイミングで実行する

ページ読み込み時以外でユーザースクリプトを実行したいことがあります。拡張機能のユーザースクリプト実行タイミミングをこれ以上変更することはできませんが、JavaScriptの各種イベントのタイミングで処理を実行することはできます。

ユーザースクリプトで使えそうな主なイベント種類を次に示します。

  • クリックしたタイミング
    • clickイベント
    • ページ上の既存の要素にイベントを設定することもできます
      • イベント設定のない画像やアイコンに新たなイベントを追加する
    • 要素そのものをユーザースクリプトで挿入することもできます
      • 既存の要素間に新たな要素(ボタンなど)を配置する
      • z-index等で浮遊した要素(ボタンなど)を配置する
    • ページ全体のクリックイベントを監視することもできます
      • window.addEventListener('click', func);
  • 要素の状態が変化したタイミング
    • input/select/changeイベント
  • キーボード入力したタイミング
    • keydownイベント
    • ページ独自のショートカットキーを設定することができます
  • ジェスチャー操作したタイミング
    • mousedown/mousemove/mouseup/draggestureイベント
    • ページ独自のジェスチャーを設定することができます
  • 文字列選択したタイミング
    • mouseupイベント
    • window.getSelection();
  • スクロールしたタイミング
    • scrollイベント
    • IntersectionObserver
    • 無限スクロールを設定することができます
  • DOM要素の変更したタイミング
    • MutationObserver
    • ページ読み込み完了後に動的追加される要素を処理することができます
  • ページを閉じるタイミング
    • pagehideイベント
    • データを保存して、次の訪問時に使用することができます
  • コンテキストメニューを選択したタイミング
    • GM.registerMenuCommand()

よくある失敗事例

@run-at document-startでドキュメント要素へアクセスする

document-startのタイミングは、DOMContentLoadedより前のタイミングです。ドキュメントの解析が完了していません。そのため、ドキュメント要素を取得できず、エラー終了や意図しない動作をすることがあります。document-endまたは、document-idleの指定、もしくは@run-atを未指定とすることで問題を解決できます。

※対象要素をドキュメントの解析完了後に動的追加している場合、MutationObserverでドキュメント変更を監視して、対象要素が追加されたタイミングで処理を実行する必要があります。

@include/@matchの指定ミス

@include/@matchの指定ミスにより、ユーザースクリプトが実行されない。または、意図しないページで実行される問題が発生します。

@include/@matchが未指定の場合、「すべてのサイトにユーザースクリプトを適用する」設定になります。

簡単なバグフィックス・仕様の揺らぎ

@grant

  • @grant未指定
    • @grant noneの動作となる
  • @grant none@grant GMの重複指定
    • @grant noneの動作となる
    • GM関数は使用できない

ページのスクリプト無効時

NoScriptなどを使用してページのスクリプトを無効とした場合、WebWorkerが動作しません。また、FirefoxのTampermonkeyが動作しません。

Greasemonkey(独自)

  • Webコンソールにエラー出力しないことがある
    • Tampermonkey / Violentmonkeyでも発生することはある
    • ただし、Greasemonkeyは高頻度で発生する

Tampermonkey(独自)

  • 他と比べて低速

Violentmonkey(独自)

  • '@match'で省略されていないポート番号の指定が必要
    • 明示的なポート番号の指定が必要
    • 逆に明示的にポート番号を指定するとGreasemonkey / Tampermonkeyで動作しない
      • ポート番号のありなしを併記することで回避できる

後方互換性の問題

2014年6月~:Greasemonkey2.0

  • @grant noneがデフォルト設定になる
    • GM_の関数が@grant指定なしで使用不可になる
  • ユーザースクリプトの実行がウェブページのJavaScriptから分離される
    • ユーザースクリプト→ウェブページは、unsafeWindowでアクセスが可能
    • ウェブページ→ユーザースクリプトは、アクセス不可
      • ただし、cloneInto(), exportFunction(), createObjectIn()を使用すれば可能

※詳細:Greasespot: Greasemonkey 2.0 Release

2017年9月~:Greasemonkey4.0 WebExtension対応

  • GM_関数の廃止
    • GM.関数への移行
    • GM_GM.では関数の仕様が一部異なります
      • 同期関数だったものが、非同期関数へ移行しています
  • GM_log()関数の廃止
    • 代替:console.log()
  • GM_addStyle()関数の完全な廃止
  • GM_registerMenuCommand()関数の廃止
    • GM.registerMenuCommand()へ移行
      • v4.11(2021年1月)で復活
  • GM_getResourceText()関数の完全な廃止

※詳細:Greasespot: Greasemonkey 4 For Script Authors

ブックマークレット

ユーザースクリプトと類似するユーザ作成のスクリプトを実行する為の仕組みとして、ブックマークレットがあります。ユーザースクリプトと比べた場合のブックマークレトの利点と欠点を次に示します。

ブックマークレットの利点

ブックマークレットは、拡張機能なしで実行できます。拡張機能を利用できないIEなどの古いブラウザ環境で動作します。また、拡張機能が提供されていないモバイル環境でも動作します。

ブックマークレットは、ブックマークの選択時に実行できます。これは、GUIとしてとてもわかり易い実行タイミングです。ユーザースクリプトでこれと同様の動作を実現することはできません。

ブックマークレットの欠点

ブックマークレットは、CSP (Content-Security-Policy) 問題に対して無力です。CSPが設定されたページでは、ブックマークレットを起動できなくなります。

ブックマークレットは、CROS (Cross-Origin Resource Sharing) 問題に対して無力です。ブックマークレットでは、外部リソースの Access-Control-Allow-Origin しだいでダウンロードができません。ユーザースクリプトであれば、GM.xmlHttpRequest関数を使用することでこの問題を回避できます。

ブックマークレットは、ページのスクリプト無効環境に対して無力です。NoScriptなどを利用してスクリプトを無効化した環境でブックマークレットは、動作しません。

備考

ユーザースクリプトの名称

ユーザースクリプトの名称は、「スクリプト名.user.js」です。

@nameの名称に「.user.js」は付けない。
userChrome.jsの「スクリプト名.uc.js」と混同してはいけない。
@nameは、@name:ja/@name:enと言語毎に個別で指定できる。

無名関数で囲む

無名関数で囲むの例(function() {
  // コード
})();

旧バージョンのGreasemonkeyは、ユーザースクリプトを対象ページに直接書き込むことで、ユーザースクリプトの機能を実現していました。そのため、対象ページ本来のスクリプトと不用意に干渉しないよう無名関数で囲むことが一般的です。

ですが、現在のGreasemonkeyは、ユーザースクリプトを対象ページ本来のスクリプトとは隔離された環境で実行しています。そのため、無名関数で囲まなかったとしても特段問題が発生することはありません。

UserCSS

ユーザースクリプトと同様にユーザースタイルも存在します。スクリプトによる動的処理が不要で、CSS的な静的処理だけで問題を解決できる場合、特に有用です。

使用例として、次のようなものが考えられます。

  • 特定要素を目立たせる
  • 特定要素を非表示にする
  • ダークモード対応する
  • etc

コメント