Greasemonkey2.0対応 - LDRFullFeed、LDR NG、ldr_keyhack_jkc+n
以下のGreasemonkeyスクリプトを修正した件についての話。
Greasemonkey2.0
Greasemonkey2.0ではFirefoxの変更に合わせて、セキュリティ周りの変更がありました。 それにより、色々なGreasemonkeyがそのままだと動かなくなっています。
- Changes to unsafeWindow for the Add-on SDK | Mozilla Add-ons Blog
- Greasespot: Greasemonkey 2.0 Release
- UserScriptのGreasemonkey 2.0対応 | monoの開発ブログ
動かない原因は大きく分けて2つあります。
@grant none
がデフォルトになった- unsafeWindowの挙動が変わった(Firefox側の変更)
@grant none
に詳しい解説が書いてあります。
簡単にいうと、今までそのまま使えていたGM_*関数は特権関数なので、Greasemonkeyで使う場合は、 事前にスクリプトのメタ情報で
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_getResourceText
// @grant GM_getResourceURL
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
のように列挙する必要があります。
そのスクリプトで使っているAPIを列挙するのが面倒なので、 既存のスクリプトから、メタ情報のコメントを取得するコマンドラインツールを作りました。
npm install -g greasemonkey_grant_cli
greasemonkey_grant file.user.js
という感じでファイルを指定して実行すると
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
のようなコメント形式で使ってるAPI一覧を得られます。
@grant none
の挙動
@grant none
はpage contextで実行されるためGM_*関数
は使えません。
ブックマークレットと同じようなものなので、GM_関数
が必要ない場合はこれをつかったほうが楽です。
データの読み書き(localstorage)、GM_xmlhttpRequest(クロスドメインはそのままXHRと同じ)、GM_addStyleについてのshimライブラリが Greasemonkey "@grant none" compatibility shim. に用意されいます。
そのためサイト間をまたいだりしないGreasemonkeyは、これをつかって @grant none
で動かせるケースが多いでしょう。
(APIを叩く場合は大抵クロスドメイン跨ぐので無理かも)
実例
livedoor Reader で NG word フィルター を実現する Greasemonkey - zaknakの日記 で公開されていた
LDRのNGフィルタをするGreasemonkeyはGM_*関数
が必要なかったので、 @grant none
で動くように修正しました
unsafeWindowの挙動
もう一つの大きな変更が、unsafeWindow
周りの挙動です
unsafeWindow(特権context) -> window(page context) という一方通行なら、今までと同じ書き方で問題ありません。
(Greasemonkeyからpage contextの関数を呼び出すとかは unsafeWindow.hoge()
みたいにできる)
unsafeWindow(特権context) -> window(page context)にイベントハンドラを登録して、 window(page context)からそのイベントが発火してメソッドを呼ぶみたいなケースだと問題がおきます。
例えば、LDRをj、kで前後の記事、nで新しいタブで開くGreasemonkeyでは、 以下のようににunsafeWindow経由で、イベントをpage contextに設定していました。
つまり、Greasemonkey内で定義した関数をpage contextから呼ぶようになっていました。
var openAndGoNext = function () {
var item = w.get_active_item(true);
if (!item) {
return;
}
// background open
openNewBackgroundTab(htmlEntityDecode(item.link));
w.Control.go_next();
};
unsafeWindow.Keybind.add("k", openAndGoNext);
これだと、page contextからopenAndGoNext
を呼ぼうとすると
Error: Permission denied to access property 'call'
のようにパーミッションエラーという感じになって失敗します。
そのため、openAndGoNext
をpage contextから呼べるように定義する必要があります。
このケースだとexportFunction
というGreasemonkeyの関数をwindow(page context)に登録するものがあるのでこれを利用すれば、
以下のように修正できます。
function exportGMFunc(fn, name) {
var fnName = name || fn.name;
// page contextからfnNameを呼べるようにする特権作成関数
exportFunction(fn, unsafeWindow, {defineAs: fnName });
return unsafeWindow[fnName];
}
unsafeWindow.Keybind.add("k", exportGMFunc(openAndGoNext));
他にも変数やオブジェクトを登録する関数などがあり、以下に書いてあります。
この辺はProxy APIでラップしたwindowとかを作れば自動的にいけるんじゃないかと思って、azu/Greasemonkey-unsafeWindow-Proxy というのを書いてたんですが、何かよくわからなくなって放置してます。
実例
ldr_keyhack_jkc+nとLDRFullFeedはこの exportFunction
を使って修正してあります。
最近のGreasemonkey
箇条書きで最近のGreasemonkey
- scriptish/scriptish まだFirefox30対応してない
- 作者のErik VoldさんはJPM Betaに忙しい様子
- userscripts.org はほぼ完全に死んでる気がします
- 代わりに Greasy Fork が動いています。
- Greasy Forkは活発なのでこちらに移行するといいのでは
- GitHubにスクリプトを置いて自動的に同期する仕組みなどもある。
- Greasy Forkは外部スクリプトの読み込みに一部制限がある
最後のやつは外部スクリプトを置ける場所が制限されています(条件から外れた場合はインストールボタンが押せなくなる)
Greasy Fork policy on external scriptsに書いてある
CDNやGreasy Forkにおいてあるスクリプトは@require
で読み込んで使うことが出来ます。
簡単にいえば、動的にGreasemonkeyスクリプトの中身が変えられたりしないようにそういう制限を設けているという感じです。
Greasemonkeyは下火ですが、まあまだ簡単に書く場合はAddon SDKより楽なので使う機会は多いです。
JPM Beta が熟してきて、
Addon SDKでもnpmのエコシステムが回ったAddon作成ができるようになるといいですね。
(その場合でもGreasemonkeyは @grant none
- 常時実行するブックマークレットみたいなプラットフォームとして残る気はする)
今のJPM Betaはcfx
コマンドの代替的な感じです。(PythonベースじゃなくNode.jsベースになった)
Browserify
Greasemonkeyの@require
でモジュール管理は正直現代的じゃないし難しい気がしてるので、
最近はBrowserifyでビルドしてGreasemonkeyスクリプトを書いています。
- azu/check_changelog_from_release https://github.com/azu/check_changelog_from_release
- azu/github-releases-to-feedly https://github.com/azu/github-releases-to-feedly
- azu/show-diff-from-release https://github.com/azu/show-diff-from-release
ただし、この方法を使った場合はGreasy Forkのポリシーと反してる気がするので、 Greasy Forkでは公開できないかもしれません。
以下で議論してたけど、どうすればいいのかよくわからないのでGitHubに直接置いてます。
Browserifyでビルドする場合は、 git configでローカルのファイルパスを保存してビルドスクリプトを まわせば結構いい感じで開発出来ます。
git config greasemonkey.file /path/to/自分の.user.js
npm run watch # 監視 + ビルド
ファイルサイズが大きくなりやすいですが、管理しやすいし各スクリプトで共通モジュールが簡単にできたりするので、 自分用に書く場合は圧倒的に楽になると思います。
まとめ
- Greasemonkey 2.0(Firefox30)で色々破壊的な変更が起きた
- userscripts.org は死んでいる
- Greasy Forkがかわりに機能してる
- safeを意識してるのでポリシーは若干厳し目
- どちらにしてもGitHubにもコードを置いたほうがいいですね
- Browserifyを使ってGreasemonkeyスクリプトを書く方法もある
- Addon SDKのエコシステムが強化のためにJPM Betaがでてきた
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。