Firefox userChrome.js用ユーザースクリプトを作成する

Firefox を使いやすくするため、タブのタイトルとURLをコピーしたかったけど、できなかったので自作してみた。

userChrome.js とは

userChrome.js は、 Firefox のブラウザに対するユーザースクリプトを実行する方法です。(ページのユーザースクリプトとは異なります)

Firefox の拡張機能(WebExtensions)と同様にブラウザを制御できます。ですが、 userChrome.js は拡張機能よりも制約がゆるく細やかな制御が可能です。逆に言えば、拡張機能よりも危険です。

userChrome.js の導入方法

次のスクリプト導入法を参照してください。

※userChrome.js は、正式な機能ではないため、バージョン毎に導入方法が変化しています。また、今後 userChrome.js が廃止される可能性があります。

Firefox92+の導入方法(最新版用)

  • alice0775/userChrome.js - GitHub
    • 下記の3ファイルを所定の場所に配置する。
    • /92/userChrome.js
    • /92/install_folder/config.js
    • /92/install_folder/defaults/pref/config-prefs.js
  1. 以前にuserChrome.jsを導入している場合、以前のファイルをすべて削除する
    • 以前のファイルが残っている場合、誤動作や動作しないことがあります
  2. プロファイルフォルダの「chrome」フォルダを作成して次のファイルを配置する
    • /92/userChrome.js
  3. アプリケーションのインストールフォルダに次のファイルを配置する
    • /92/install_folder/config.js
    • /92/install_folder/defaults/pref/config-prefs.js
      • config.jsは、インストールフォルダ直下に配置する
      • config-prefs.jsは、インストールフォルダの「/defaults/pref」に配置する

※プロファイルフォルダは、「about:support」のプロファイルフォルダーを確認する。
※アプリケーションのインストールフォルダは、「firefox.exe」の格納フォルダです。
 例:C:\Program Files\Mozilla Firefox
※上記のファイルは、 UTF-8 で保存してください。文字コードが異なると動作しません。

Firefox72-91の導入方法(ESR78用・ESR91用)

  • alice0775/userChrome.js - GitHub
    • 下記の3ファイルを所定の場所に配置する。
    • /72/userChrome.js
    • /72/install_folder/config.js
    • /72/install_folder/defaults/pref/config-prefs.js
  1. 以前にuserChrome.jsを導入している場合、以前のファイルをすべて削除する
    • 以前のファイルが残っている場合、誤動作や動作しないことがあります
  2. プロファイルフォルダの「chrome」フォルダを作成して次のファイルを配置する
    • /72/userChrome.js
  3. アプリケーションのインストールフォルダに次のファイルを配置する
    • /72/install_folder/config.js
    • /72/install_folder/defaults/pref/config-prefs.js
      • config.jsは、インストールフォルダ直下に配置する
      • config-prefs.jsは、インストールフォルダの「/defaults/pref」に配置する

※プロファイルフォルダは、「about:support」のプロファイルフォルダーを確認する。
※アプリケーションのインストールフォルダは、「firefox.exe」の格納フォルダです。
 例:C:\Program Files\Mozilla Firefox
※上記のファイルは、 UTF-8 で保存してください。文字コードが異なると動作しません。

alice0775版以外の userChrome.js

探せば、他にもあるかもしれません。それぞれでメタデータの扱いや BootstorapLoader の有無などが異なります。

※userChrome.js 毎でユーザースクリプトの変更が必要となる場合があります。

Windows 以外への導入(macOS / Linux)

install_folder とプロファイルディレクトリー(フォルダー)に上記と同様にファイルを配置してください。 install_folder とプロファイルディレクトリーは、次の場所にあります。

  • install_folder を探す
    1. about:support を開く
    2. [アプリケーションの実行ファイル] を開く
    3. 付近のディレクトリーを探す
      • 例:macOS12
        • アプリケーションの実行ファイル:/Applications/Firefox.app/Contents/MacOS/firefox
        • install_folder//Applications/Firefox.app/Contents/Resources/
        • install_folder/defaults/pref//Applications/Firefox.app/Contents/Resources/defaults/pref/
      • 例:Ubuntu20
        • アプリケーションの実行ファイル:/usr/lib/firefox/firefox
        • install_folder//usr/lib/firefox/
        • install_folder/defaults/pref//usr/lib/firefox/defaults/pref/
  • プロファイルディレクトリーを探す
    1. about:support を開く
    2. [プロファイルディレクトリー] を開く
      • 例:macOS12
        • /Users/(ユーザー名)/Library/Application Support/Firefox/Profiles/(プロファイル名)/
      • 例:Ubuntu20
        • /home/(ユーザー名)/.mozilla/firefox/(プロファイル名)/

コードを書くために

userChrome.js のドキュメントは、特に存在しません(筆者は見つけられませんでした)。そのため、ブラウザーツールボックスを確認したり、他人の書いた動作するソースコードを元に求める機能を実現する必要があります。

また、 Firefox のバージョンによって動作しないコードもあります。バージョンアップ後動作しなくなる場合、動作に必要な機能がなくなっていたり、名称が変更していたりします。また、 userChrome.js そのものが、動作していなくなっていることもあります。

覚書(全般)

userChrome.js を探す

ユーザースクリプトの設定

ファイルの先頭にユーザースクリプトの設定を記述する。

// ==UserScript==
// @name        スクリプト名.uc.js
// @description 機能の概要
// @version     1.0.0
// @include     main
// @charset     UTF-8
// ==/UserScript==

※日本語を使用する場合、 charset に UTF-8 を指定することで文字コード関連の大抵の問題を回避できます。

ログ出力する

// 「console.log() を表示する」参照
console.log('Hello World');

// アラーム
alert('Hello World');

console.log() を表示する

ブラウザーツールボックスで確認する

  1. ブラウザーツールボックスを有効化する
    • [F12] > […] > [設定] > [詳細設定]
      • 「ブラウザーとアドオンのデバッグを有効化」をチェックする
      • 「リモートデバッガーを有効化」をチェックする
  2. ブラウザーツールボックスを開く
    • Ctrl + Alt + Shift + i
    • 「接続しますか?」を「OK」する
  3. コンソールタブを表示する
    • console.log() 出力が表示されます
    • ただし、 Firefox の各種出力も表示されます

覚書(アクション系)

コンテキストメニューを追加する

var menuItem = document.createXULElement("menuitem");
menuItem.setAttribute("id", "context-menu-item-id");
menuItem.setAttribute("label", "追加したメニューラベル");
menuItem.addEventListener('command', function() {
  console.log('追加したメニューラベルを選択しました。');
  alert('追加したメニューラベルを選択しました');
});
var contextMenu = document.getElementById("contentAreaContextMenu");
contextMenu.appendChild(menuItem, contextMenu.firstChild);

タブコンテキストメニューの末尾追加する

// 「コンテキストメニューを追加する」参照
var tabContextMenu = document.getElementById("tabContextMenu");
//var contextMenu = document.getElementById("contentAreaContextMenu");
tabContextMenu.appendChild(menuItem, tabContextMenu.firstChild);

タブコンテキストメニューに先頭追加する

// 「コンテキストメニューを追加する」参照
var tabContextMenu = document.getElementById("tabContextMenu");
tabContextMenu.insertBefore(menuItem, tabContextMenu.firstChild);

タブコンテキストメニューにセパレータを追加する

var menusSparator = document.createXULElement("menuseparator");
var tabContextMenu = document.getElementById("tabContextMenu");
tabContextMenu.appendChild(menusSparator, tabContextMenu.firstChild);

アクセスキー(ショートカットキー)を変更する

// [閉じたタブを開きなおす(O)] のアクセスキーを Uキーに置き換える(Firefox87-の設定に戻す)
document.getElementById('tabContextMenu').addEventListener('popupshowing', function() {
  //document.getElementById('context_undoCloseTab').setAttribute('label', '閉じたタブを元に戻す');
  document.getElementById('context_undoCloseTab').setAttribute('accesskey', 'u');
});

※メニュー表示前に変更すると元の設定に戻される
 もっと良い方法を要検討

ツールチップを配置する

try {
  var id = 'id';
  // 複数ウィンドウで使用する場合、ウィンドウ毎に個別のIDを指定する必要がある
  //var id = 'id'+(''+Math.random()).substring(2);
  CustomizableUI.createWidget({
    id: id,
    type: 'custom',
    defaultArea: CustomizableUI.AREA_NAVBAR,  // ナビゲーション
//  defaultArea: CustomizableUI.AREA_TABSTRIP,// タブステップ
    onBuild: function(doc) {
      const toolbaritem = doc.createXULElement('toolbarbutton');
      const props = {
        id: id,
        class: 'toolbarbutton-1 chromeclass-toolbar-additional',
        removable: false,
        label: 'ラベル',
        tooltiptext: 'ツールチップテキスト',
        image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIj4KICAgIDxwYXRoIHN0cm9rZS13aWR0aD0iMjQiIGZpbGw9IiM1NTU1NTUiIHN0cm9rZT0iI2ZmZmZmZiIgZD0iTSA2IDQ4MCBsIDUwMCAwIGwgMCAtNjAgbCAtNTAgMCBsIDAgLTIyMCBsIC00MDAgMCBsIDAgMjIwIGwgLTUwIDAgeiIvPgogICAgPHBhdGggc3Ryb2tlLXdpZHRoPSIzMCIgZmlsbD0iIzQ0ODhmZiIgc3Ryb2tlPSIjZGRlZWZmIiBkPSJNIDI3MiAzMiBsIC0xNjAgMTMwIGwgMTYwIDEzMCBsIDAgLTc1IGwgNjAgMCBhIDYwIDYwIDAgMCAxIDAgMTIwIGwgLTIwIDAgbCAwIDExMCBsIDIwIDAgYSAxNzAgMTcwIDAgMCAwIDAgLTM0MCBsIC02MCAwIHoiLz4KPC9zdmc+',
        // BASE64で画像を埋め込み
        onclick: `
          if (event.button == 0) {
            console.log('ツールチップをクリックしました。');
            alert('ツールチップをクリックしました。');
          }
        `,
      };
      for (var key in props) {
        Object.keys(props).forEach(key => toolbaritem.setAttribute(key, props[key]));
      }
      return toolbaritem;
    }
  });
} catch (e) {}

覚書(イベント系)

簡易なイベント

マウスジェスチャ用ですが動作します。簡易の「戻る」「進む」などがあります。

次のコードも参考になります。

新しいタブを開く

var url = 'https://www.yahoo.co.jp/';
gBrowser.addTrustedTab(url);

新しいタブを選択する(アクティブにする)

var url = 'https://www.yahoo.co.jp/';
gBrowser.selectedTab = gBrowser.addTrustedTab(url);

クリップボードにコピー

var text = 'Hello World';
var clipboard = Cc['@mozilla.org/widget/clipboardhelper;1'].getService(Ci.nsIClipboardHelper);
clipboard.copyString(text);

サンプル

作るもの

下記の機能を実現します。

  • タブコンテキストメニューからタイトルのコピー
  • タブコンテキストメニューからURLのコピー
タブメニュー

ソースコード

CopyTabTitleUrl.uc.js// ==UserScript==
// @name          CopyTabTitleUrl.uc.js
// @description   タブコンテキストメニューから、タイトルとURLをコピーする。
// @include       main
// @charset       UTF-8
// @author        toshi (https://github.com/k08045kk)
// @license       MIT License | https://opensource.org/licenses/MIT
// @compatibility 69+
// @version       5
// @since         1 - 20180212 - 初版
// @since         2 - 20180212 - 「CopyTabTitleAndURL.uc.js」から名称変更
// @since         3 - 20190905 - Firefox69対応 createElement → createXULElement に置換
// @since         4 - 20201122 - 「タイトルとURLをコピー」を追加
// @since         4 - 20201122 - リファクタリング
// @since         5 - 20210910 - メタデータ修正
// @since         5 - 20211008 - メタデータ修正
// @see           https://github.com/k08045kk/userChrome.js
// @see           https://www.bugbugnow.net/2018/02/CopyTabTitleAndURL.uc.js.html
// ==/UserScript==

(function() {
  // クリップボードコピー
  const copyToClipboard = function(text) {
    Cc['@mozilla.org/widget/clipboardhelper;1'].getService(Ci.nsIClipboardHelper).copyString(text);
  };
  // copyToClipboard('['+title+']('+url+')');             // Markdown
  // copyToClipboard('<a href="'+url+'">'+title+'</a>');  // Hyperlink


  // タイトルとURLをコピー
  const m0 = document.createXULElement('menuitem');
  m0.setAttribute('id', 'context-copytab-titleurl');
  m0.setAttribute('label', 'タイトルとURLをコピー');
  m0.addEventListener('command', function() {
    const title = TabContextMenu.contextTab.linkedBrowser.contentTitle;
    const url = TabContextMenu.contextTab.linkedBrowser.currentURI.spec;
    copyToClipboard(title+'\n'+url);
  });

  // タイトルをコピー
  const m1 = document.createXULElement('menuitem');
  m1.setAttribute('id', 'context-copytab-title');
  m1.setAttribute('label', 'タイトルをコピー');
  m1.addEventListener('command', function() {
    const title = TabContextMenu.contextTab.linkedBrowser.contentTitle;
    copyToClipboard(title);
  });

  // URLをコピー
  const m2 = document.createXULElement('menuitem');
  m2.setAttribute('id', 'context-copytab-url');
  m2.setAttribute('label', 'URLをコピー');
  m2.addEventListener('command', function() {
    const url = TabContextMenu.contextTab.linkedBrowser.currentURI.spec;
    copyToClipboard(url);
  });

  // セパレータ
  const ms = document.createXULElement('menuseparator');
  ms.setAttribute('id', 'context-copytab-sep');

  // メニューバーの最上部に要素を追加
  // タイトルとURLをコピー
  // タイトルをコピー
  // URLをコピー
  // セパレータ
  const tabContextMenu = document.getElementById('tabContextMenu');
  tabContextMenu.insertBefore(ms, tabContextMenu.firstChild);
  tabContextMenu.insertBefore(m2, tabContextMenu.firstChild);
  tabContextMenu.insertBefore(m1, tabContextMenu.firstChild);
  tabContextMenu.insertBefore(m0, tabContextMenu.firstChild);
}());

※プロファイルフォルダ内のchromeフォルダにCopyTabTitleUrl.uc.jsの名称で保存
※本スクリプトは、「k08045kk/userChrome.js - GitHub」でも公開

関連記事

  1. Firefox userChrome.js用ユーザースクリプトを作成する
  2. Firefox用WebExtensions拡張機能を作成する
  3. Chromeの拡張機能を作成する
  4. Firefox用WebExtensions拡張機能を国際化する
  5. Chrome、Firefox、Edgeの拡張機能を判定する
  6. Chrome、Firefox拡張機能のブラウザアクションでポップアップあり・なしを共存する
  7. Chrome、Firefoxの拡張機能にショートカットを実装