• 作成:

Firefoxからなんとか追い出されずにQuantumに移行して旧アドオンと似た環境を作れました

最新版に移行したメモ.

だいぶ前の話ですがFirefoxの最新版ではXPCOMやXULを使う拡張が動かなくなりました

Firefox QuantumではXPCOMやXULを使うアドオンは動かなくなりました.

リリース直後にはそうなると私は死しか無かったのでその場しのぎの回避策を使っていました.

Firefox ESRの使用です. ESR版は長くサポートされるので, 最新版ではなくても深刻なセキュリティリスクは小さいです. Gentooにもちゃんとセキュリティアップデートが降りてきます.

というかstableに対しては保守的なGentooではunstableを有効にしない限り, デフォルトではESR版のFirefoxがインストールされます.

やめてweb開発者さん古いブラウザを使うなと叩かないで私もweb開発者なんです.

しかしFirefoxのESRバージョンであるFirefox 52もサポートは9月に終了します.

なので頑張ってまともに使える環境を整備しました.

作業メモを公開します.

Firefox自体の移行はサポート終了ギリギリに行っていて, 少しずつ環境整備を行いました.

React Developer Tools

新バージョンがあります.

React Developer Tools – Firefox 向けアドオン

ツリー型タブ

同作者さんのWebExtensionsバージョンがリリースされています.

「変わりのアドオンを探す」をクリックすると何故か別のアドオンが出てくるので注意.

ツリー型タブ (Tree Style Tab) – Firefox 向けアドオン

サイドバーで実装されているため元のタブバーが消えないとか, サイドバーのヘッダが常に表示されて邪魔という問題は, 作者さんの指示通りchrome/userChrome.cssを編集すれば解決できます.

userChrome.css

私はKeySnailからツリー型タブのツリーを閉じる機能を呼び出すために以下のようなコードを書いていました.

key.setGlobalKey(
  "M-q",
  function(ev) {
    TreeStyleTabService.removeTabSubtree(gBrowser.selectedTab, true);
  },
  "ツリーを閉じる"
);

key.setGlobalKey(
  "C-M-q",
  function(ev) {
    MultipleTabService.closeSimilarTabsOf(gBrowser.selectedTab, null, true);
  },
  "似たタブを全て閉じる"
);

key.setViewKey(
  "d",
  function(ev) {
    gBrowser.treeStyleTab.detachTab(gBrowser.mCurrentTab);
  },
  "選択中のタブを親タブのサブツリーから切り離す"
);

key.setViewKey(
  "b",
  function(ev) {
    gBrowser.selectedTab = TreeStyleTabService.getParentTab(
      gBrowser.selectedTab
    );
  },
  "一つ上のツリーに移動"
);

これらを再現するのは手間がかかるなあと思っていました.

しかしツリーを閉じる機能は標準の機能に「このタブの配下のタブを全て閉じる」として実装されていて, キーバインド設定できました.

「似たタブを全て閉じる」はほとんど使っていなかったし削除で良いでしょう.

しかし「選択中のタブを親タブのサブツリーから切り離す」と「一つ上のツリーに移動」は, 標準に機能としては存在するのですが, Firefoxのキーバインドの制約の問題で, 修飾キーなしのキーに割り当てられませんでした.

これは他のアドオンと組み合わせて解決するしかなさそうですね.

プライベートタブ

これ最近使ってないので代わりは存在するようですがインストールしなくても良いでしょう.

KeySnail

KeySnailの存在はFirefox Quantumへの移行をもっとも躊躇わせていました.

KeySnailは単なるキーマッパではなく独自のエコシステムを持つプラットフォームです.

私の.keysnail.jsは1253行でした.

しかしサポートが終了する以上移行しないわけにもいかないので頑張って代替を探します.

markdown形式でページリンクとタイトルをコピーする

key.setGlobalKey(
  "C-M-l",
  function(ev, arg) {
    command.setClipboardText(
      `[${content.document.title}](${content.location.href})`
    );
  },
  "copy_document_url_markdown"
);

Copy as Markdown – Firefox 向けアドオン

があります. これインストールして直後にアドオンページをコピーしようとして動かなくてバグっているのか? と思いましたが最近のFirefoxはデフォルトではaddons.mozilla.orgではアドオンを動作させないんでした.

Firefox 60以降でもMozilla Add-ons(AMO)上で拡張機能を動作させる裏技 - Mozilla Flux

これで簡単にコピーすることが可能になりましたが, 出来ればキーバインドでコピーしたいですね. 頻繁に行う動作なので一々右クリックするのは面倒です.

Chromeだと拡張画面でホットキーを設定できるのですが, Firefoxには用意されていないようですね.

Google Chromeでは拡張機能が定義したキーボードショートカットを横断して制御できる設定画面をChrome自体が提供しているのに対し、Firefox 60にはまだその機能がありません。

WebExtensionsによるFirefox用の拡張機能で、キーボードショートカットの変更用UIを提供するライブラリ:ShortcutCustomizeUIMenuUI.js - ククログ(2018-05-14)

そのうち実装されるらしいのでその間は我慢しますか. 下記のSurfingkeysを使えばこれぐらいの機能は実装できそうに思えますが, そのうち実装されることが確定されているような機能をわざわざ書く気にもなれない.

ですが一々右クリックするの激面倒だったのでやっぱり以下のSurfingkeys向けに書きました.

// copy

mapkey("m", "Copy title and link to markdown without query", () => {
  Clipboard.write(
    `[${document.title}](${window.location.origin}${window.location.pathname})`
  );
});

mapkey("l", "Copy title and link to human readable without query", () => {
  Clipboard.write(
    `[${document.title}]: ${window.location.origin}${window.location.pathname}`
  );
});

Surfingkeys

Surfingkeys – Firefox 向けアドオン

KeySnailの作者も使っていたキーマッパです. とりあえずxkeysnailなどを検討する前にこれでいけるところまで設定を再現してみましょう.

いきなりVim風のエディタが出てきてEmacs派の私としては大丈夫かこれという気分になりますが, まあどっちだろうと再現出来れば問題ありません.

keysnailなき後の世界を生きる - Qiita を参考にさせて頂きました.

設定は以下に置いています.

ncaq/surfingkeys-config: My surfingkeys config

Surfingkeysで(たぶん)出来ないこと

C-oのような元々Firefoxにキーバインドが設定されているキーシーケンスには, Firefoxの設定が優先されて設定できません. というかツリー型タブのショートカットに設定しても出来ません.

Firefoxの組み込みのキーボードショートカットと衝突するショートカットを設定した場合、Firefoxの機能の方が優先的に動作します。アドオン側のショートカットを優先することは、現時点ではできません(1320332 - Support overriding existing keybinding through WebExtensionsも参照して下さい)。

WebExtensionsによるFirefox用の拡張機能で、キーボードショートカットの変更用UIを提供するライブラリ:ShortcutCustomizeUIMenuUI.js - ククログ(2018-05-14)

出来ないことを知るまでずいぶん試行錯誤して時間を無駄にしました.

C-gEscに割り当てると言った, 関数を割り当ててない低レイヤーなキー割り当てが出来ない.

インクリメンタル検索でEmacsのようにC-fで検索開始かつ次の検索結果に移動することが出来ない.

Firefoxのロケーションバーなどでのテキスト移動のキーバインド編集ができない. webサイトのテキストエリアでは出来るんですが… webサイトにスクリプト注入する関係上これは仕方がないですね.

M-sなどSurfingkeys標準に元々割り当てられているキーシーケンスに, 新しい関数を割り当てることが出来ない? そんな設計になっているはずが無いと思ったのですが, 実際上書き出来ないですね… 多分トグルは有効に出来なくなると困るので編集できなくしているのでしょう.

M-wをあらゆる場所でコピーに割り当てることができない.

about:系などFirefoxのUI上で使うことができない.

ローディングが終わる前の画面でタブ移動などを使うことができない.

GTK+のテーマ切り換えでは恐らく対応できない

エスケープやロケーションバーのスクロールなどの設定はテキストエリアの外に出るため, テキストエリアの編集を凌駕するレベルの操作は出来ないと思います.

例えばロケーションバーでテキストを入力して下に選択を移すなどの動作ですね.

またSurfingkeysの制限であるFirefox標準のキーシーケンスを編集出来ない制限を突破することが出来ません.

WildBindを使ってみます(失敗)

debug-ito/wild-bind: Dynamic key binding framework を使ってみようと思います.

同様のツールにはxkeysnailがありますが, 私はPythonで長い設定ファイルを書ける気がしないのでHaskellで書けるこちらを使ってみます.

xkeysnailはrootでしか使えないAPIを使うので高速らしいのですが, 私はそんなに高速な動作を必要としないのでtype safeなこちらを選択しました.

wild-bind-ncaqとしてパッケージを作って書いていきます.

公式READMEにはxdotool keyとspawnCommandを使ってキーシーケンスを弄る方法が書いてあったのですが, サンプルコードを見るとWildBind.X11.Emulate.sendKeyを使ってX11のキーシーケンスをいじっていて, こちらの方が型安全かつ高速っぽいのでこちらを使うことにしました.

しかし謎が起きています. whenFrontwinClassFirefoxのみを対象としてキーバインドを動かしてみたのですが, 全く動きませんし, traceShowIdを使っても Window {winInstance = "", winClass = "", winName = "", winID = 0} しか出てこないので完全に意味不明.

ターミナルで起動すると子プロセスしか見れないのか? と思ったのでstack installしてxmonadで起動してみます.

  • xmonadで起動
  • lilytermで起動
  • WildBind内部のキーシーケンスでFirefoxを起動してFirefoxをWildBindの子プロセスにする

を試してみましたが, いずれもキーシーケンスは再配置されませんしwinClass""のままですね.

全く何もわからなくなったのでとりあえずwhenFrontの条件が常にTrueになるようにしてみたら, 意図したキーシーケンスになっていませんね. C-aHomeに割り当ててみたのですが, キーを押すとそのまま反応はしませんがHomeのキーは入力されません.

キーバインドが全く効いていないかと言うとそうでもなくて,

on (ctrl xK_0) `run` liftIO (spawnCommand "firefox")

は有効に認識されてFirefoxが起動します.

しかし

on (ctrl xK_a) `run` push x11 xK_Home

は認識されません.

何の違いがあるのか考えると, やはりx11のコンテキストが異なるからなのでしょう.

printも動きますし, 関係ないxdotool keyを動かしてみたらどうかと思って切り替えてみましたが, うまくいきません.

LilyTermで

sleep 5 && xdotool key Home

でを動かしてみて, xdotool自体は動くことがわかりました. なぜWildBindから呼び出すと動かないのでしょう?

spawnCommand "ls" は動くのでspawnCommand自体が動かないということはなさそうですね.

xdotool key Homeじゃなくてxdotool key homeを投げてみたら

(symbol) No such key name 'home'. Ignoring it.

と帰ってきたので起動自体に失敗しているということは無さそうです.

何が起きているのか全くわからない, とりあえずstackの最新版じゃなくてGitHubの最新版を参照してみます.

変わらず動きません.

xkeysnailを使ってみます

全くわからないのでとりあえずxkeysnailを使ってみることにします. これですんなり設定できたらもうそれで良いでしょう.

設定は以下に置いています.

ncaq/.xkeysnail: My xkeysnail config

まずrootにパスが通った箇所にインストールするのが難しかった, というかrootにこれ以上PATHを追加したくなかった. のでebuildを書きました.

sudo xkeysnail config.py

で起動すると以下のエラーが出ました.

Failed to open `uinput` in write mode.
Make sure that you have executed xkeysnail with root privilege such as

    $ sudo xkeysnail config.py

なんでや, ちゃんとsudoで起動しとるやろ.

どうやら先にインストールしていたxkeysnailを見ていたようですね. ちゃんと消してportageのものだけを残したのですがやはり動きません.

薄々感づいていましたけどuinputモジュールが存在しないらしいですね.

Failed to open the uinput device: No such device - Stack Overflow

LinuxカーネルのINPUT_UINPUTオプションを有効にします. Gentooデフォルトだと無効のようです.

再起動して大概はうまく動きました.

Metaとの相性

Altキーを組み合わせたキーを入力する時, Firefoxのデフォルトだとメニューがポップアップしてうまく動きません. なのでabout:configui.key.menuAccessKeyFocusesfalseに変更する必要があります.

Dvorakとの相性が悪い

ログと挙動から考えて, xkeysnailはSystemdのキーボード設定をDvorakモードにしていてもQwertyでキーを読み込むみたいですね. キーの表示文字じゃなくてコードを読み取っているのでしょうか.

まあ低レイヤーで動いている以上仕方がないですね…

Qwertyでの表示に合わせて書き換えました.

読み取りがうまくいくようになったのは良いのですが, 書き込みもうまく動きません.

C-o(QwertyではC-s)にC-tを割り当てて, 新しいタブを開くようにしていたのですが, このキーバインドが読み取られません. FirefoxだけではなくChromiumでも無理だったので, Firefoxの問題では無さそうです.

同じようにC-y(QwertyではC-t)にpaste動作を割り当てようとしてもうまく行きません. t要素が入るとダメ?

いやM-w(QwertyではC-,)にコピーを割り当てようとしてもダメだったので, t要素に限りませんね.

configを眺めていて, 正常に動くのはUpなどのキーが入れ替わってないものだけだと気が付きました. 入力だけではなく出力もQwertyに合わせないとダメなようですね.

つまりDvorak環境でC-tを出力させたい時は K("C-k")と書かないとダメなのです.

よってDvorak環境でC-oで新しいタブを開く時は以下のように書きます.

K("C-s"): K("C-k"),

おそらく入力はQwertyそのままで扱われていて, 出力する時は仮想キーボードの入力として扱われるため, 仮想キーボードでkが入力されて, Dvorak変換でそれがtになっています.

変換が多重にかかっていて, 少し混乱しましたが無事設定を完了させることが出来ました.

変換プログラムを書くことも出来たでしょうが, 文字列処理が面倒でこれぐらいなら変換しなくても書けるのでそのままやってしまいました.

CapsLockをCtrlにする設定を感知しない

ラップトップでは多分xkbあたりでCapsLockをCtrlにしていたのですが(詳細は忘却), xkeysnailではそれは感知しないようですね.

デスクトップのHHKBでは標準でAの左がCtrlなので気が付きませんでした.

サンプルの通り冒頭に

define_modmap({
    Key.CAPSLOCK: Key.LEFT_CTRL
})

と書いてやれば解決です.

ibus/mozcのキーマップにも変更が及んでしまう

Firefoxのキーマップを変更するとFirefoxだけではなく, その上で動くmozc/Google日本語入力のキーも変更されてしまいます.

解決策は思いつきませんでした…

最新版を使いたいがリリースされていない

最新版ではquietwatchと言ったオプションがあるようですが, Pythonパッケージをしてリリースされてないので, 野良をインストールして管理するので面倒なので, エラーメッセージが溢れるのは諦めることにしました.

非常に不快ですが慣れたら快適であることを祈るしか無い

手足がもがれる感覚がします.

私は高校2年の頃, つまり2012の頃からFirefoxを使っていて, 早期にKeySnailを使っていました. 理由はGNU/Linux環境でOperaが不安定だったためです. 2018-2012=6年ものこのインターフェイスにどっぷり浸かっていたので, 再現しようと試みてもどうしても不満に思えてしまいます.

しかしFirefoxの開発チームだって私だけを対象にしてソフトウェアを開発しているわけではないので, いつか切り捨てられるのは仕方ないのでしょう.

しかし6年ものの月日を積み重ねて作られた設定群はもはや1つのソフトウェアとなっています. これを奪われるというのは仕方がないと言えど悲しいですね.

Threadripperの上で16スレッドで動くFirefoxの速度がこの不快感を取り去るほど良いものであると信じたいです.

既にいくつかのバグが修正されてて恩恵は受けているのですが.

特にFirefoxのUI部分にhookがかけられなくなったのはきつい. xkeysnailで対抗するにもJavaScript実行したい時は多い.

例えば私はsでタブを下に切り替えているのですが, ページが読み込まれるまで切り換えが出来無くなってしまいました. 外部から入力するxkeysnailではテキストエディットしているかわからないから, xkeysnailでタブ切り換えにすることは出来ないので, 非常に困りました.

FirefoxのWebExtensionはキーバインド設定APIが無いとか, 拡張性でもChromiumに負けてますね… サイドバー無かったら絶対乗り換えてました.

Vivaldiはソース公開してないので乗り換えるつもりはありません.

Android版のFirefoxと同期もしたいですからね.

OperaがLinuxでうまく動かなくなってChromiumベースになってFirefoxに移行して, Firefoxからも追い出されそうになりましたが, なんとか留まることが出来ました.

Windows環境のFirefoxは未整備

xkeysnailに頼ってしまったのでWindows環境に全く対応できていないのですが, 果たしてどうしましょうか…

Keyhac というxkeysnailに似ているツールで対応出来るようですが, ゲームするためだけの環境にこれを持ち出すのは割と面倒ですね. ゲーム中にググるのはラップトップでやると割り切ることにしましょうか?