monoの開発ブログ

UserScriptのGreasemonkey 2.0対応

先日、Greasemonkey 2.0がリリースされました。 このバージョンには、2つの後方互換性のない変更が存在します。

1つ目に、@grant noneモードがデフォルトになりました。以前のバージョンでは、GM_プレフィックスのAPIは何もしなくても利用可能になっていましたが、2.0ではすべてのAPIがデフォルトでは使用できません。 使用したい場合には、Metadata Blockに@grantを記述し、APIの使用許可を得る必要があります。

2つ目に、Webページ (Content Page) のJavaScriptとUserScriptの実行コンテキストが分離されました。UserScriptからunsafeWindowを利用してContent Pageにアクセスすることは従来通り可能ですが、Content PageからUserScriptにはアクセスできません (2014-07-24 修正)。 アクセスできるようにしたい場合には、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をバージョンアップする際にはぜひ試してみてください。

2014-07-15 追記

os0xさんから許可をいただき、LDR open in bacground tab for GreasemonkeyのGreasemonkey 2.0対応バージョンを公開しました。Content Script InjectionとexportFunction()を両方とも利用していますので、参考にしてみてください。

2014-07-24 追記

unsafeWindowの挙動について修正しました。以下のページの情報が参考になると思います。