先日、Greasemonkey 2.0がリリースされました。 このバージョンには、2つの後方互換性のない変更が存在します。
1つ目に、@grant none
モードがデフォルトになりました。以前のバージョンでは、GM_
プレフィックスのAPIは何もしなくても利用可能になっていましたが、2.0ではすべてのAPIがデフォルトでは使用できません。
使用したい場合には、Metadata Blockに@grant
を記述し、APIの使用許可を得る必要があります。
2つ目に、Webページ (Content Page) のJavaScriptとUserScriptの実行コンテキストが分離され、unsafeWindow
に対して直接値を設定できなくなりました。
書き込みたい場合には、cloneInto()
、exportFunction()
およびcreateObjectIn()
を利用する必要があります。
1つ目については、単純に@grant
を記述すればよいため、対応は簡単です。
2つ目は、単に対象のWebページを操作したりイベントハンドラを追加したりするだけであれば問題ないのですが、WebページがUserScript向けに提供するAPIを利用する場合など、WebページのJavaScriptとUserScriptで連携する場合には対応が必要です。
対策としては、すでにGoogle ChromeのContent Scriptでも広く利用されているContent Script Injectionを採用するのが手っ取り早いかと思います。 Content Script Injectionは、DOMツリーにscript要素を追加してJavaScriptコードを注入することで、Content Pageと同じコンテキストで任意のJavaScriptを実行する手法です。
function contentEval(source) {
if ('function' == typeof source) {
// 入力が関数だった場合は関数呼び出しの形にしておく
source = '(' + source + ')();'
}
// script要素を生成する
var script = document.createElement('script');
script.setAttribute("type", "application/javascript");
script.textContent = source;
// ページにscript要素を挿入し、スクリプトが実行されたらすぐに要素を取り除く
document.body.appendChild(script);
document.body.removeChild(script);
}
// 実行
contentEval("alert('running in the page')");
contentEval( function() { alert("This function is running in the page scope.") } );
ただ、Content PageのコンテキストにはGM_
プレフィックスの関数は存在しないため、そこをケアする必要があります。
これには、exportFunction()
を利用します。
exportFunction()
は、UserScriptの関数をContent Pageにエクスポートするための関数です。
これを利用してGM_
関数のラッパーをエクスポートしておくことで、Content Pageから間接的にGM_
関数呼び出せるようになります。
// UserScript
exportFunction(function(url) {
GM_openInTab(url); // Metadata Blockに @grant GM_openInTab が必要
}, unsafeWindow, {defineAs: 'openInTab'});
// Content Page
window.openInTab(url);
若干面倒になりましたが、それほど難しくはないと思います。 Greasemonkey向けのUserScriptをバージョンアップする際にはぜひ試してみてください。