前置き: vi では、削除やヤンク(コピー)した内容はレジスタと呼ばれる領域に格納される。これは vi 内で完結しているので、他のアプリケーションで参照することはできない。だが vim の場合はレジスタ名に「*」を用いるとクリップボードに対する読み書きになり、他のアプリケーションとの連携が可能になる。
で、ブラウザのエクステンションでクリップボードへのアクセスが可能かどうか。そういう API が提供されているかどうか。
- Opera
Opera では、クリップボードへのアクセスはできない。まあ Opera だからね! - Firefox
clipboard モジュールが提供されている。読み書きともにできる。ただし後述するような Chrome と同じ課題はある。 - Chrome
いつも 3 つのブラウザの機能を比較するときは Chrome を最初に持ってくるのを、今回は一番最後なのは訳がある。Chrome でもクリップボードへのアクセスは可能だ。まず manifest.json の permission に “clipboardRead” や “clipboardWrite” を追加する。
クリップボードとの実際のやり取りは、専用の API は提供されておらず、IE 起源のあやふやなメソッド execCommand() を用いる。ドキュメントに適当な textarea 要素を生成し、その内容をバッファにする。
読み込むときは
var ta = document.getElementById('clipboard-buffer');
ta.value = '';
document.execCommand('paste');
// ta.value はクリップボードの内容になっている
書き込むときは
var ta = document.getElementById('clipboard-buffer');
ta.value = 'コピーしたい内容';
ta.select();
document.execCommand('copy');
ただ、いくつか制限がある。クリップボードとのアクセスはバックグラウンドスクリプトでしか許されない。コンテントスクリプトではできない。したがって読み書き共にバックグランドへの通信と組み合わせる必要があるのだが、組み合わせること自体は大した問題ではなく……問題は、Chrome の場合、異なるドキュメント同士の通信は必ず非同期で行われることだ。これはクリップボードへの書き込みはともかく、読み込みの際に面倒なことになる。コンテントスクリプト側で、こんな感じでバックグラウンドに対してクリップボードからの読み込みを要求する:
getClipboard: function () {
// 1
var result = '';
this.postMessage({type:'get-clipboard'}, function (req) {
// 2
result = req.data || '';
});
// 3
return result;
}
これは 1 -> 3 -> 2 の順で実行されるので、単にこのメソッドを呼び出すだけではクリップボードの内容を得られない。wasavi 側のコマンド実行処理自体を非同期な仕組みにしないといけない。うーん。非同期ベースに書き換えるのはやぶさかではないけれど(大量の行に対して :s や :g を実行することを考えると、コマンドの実行を細切れにする、究極的には Web Worker に担当させるのはもっともな話だ)、そうすると将来 wasavi をプラグインによってスクリプタブルにした場合、プラグイン側も非同期を意識して書かないといけなくなる。これはちょっとお手軽感台なしだなーという気が。
いやそれ以前に自動テストのためのコードも全て非同期式に書き換えないといけないのだけど。自動テストでは
Wasavi.send('dd');
assertEquals('1\n2\n3', Wasavi.value);
みたいな感じなのだ。非同期にすると Wasavi.send() で送ったコマンドの実行が完了する前に assertEquals() に達してしまってテストが失敗する。
うーん! 困ったわぁ。
なんとか同期的な通信ができるか、コンテントスクリプト側でクリップボードの読み書きが行えればとりあえず解決するんだけどなー。