ブラウザウィンドウのサムネイルを描画する
タブカタログ拡張機能のようにWebページのサムネイル画像を表示する拡張機能の多くは、 html:canvas 要素の二次元描画コンテクストの drawWindow メソッドへWebページの window オブジェクトなどを引数で渡してサムネイルの描画を行っています(canvas を使って図形を描く – MDC)。
この drawWindow メソッドの引数にWebページの window オブジェクトではなく、ブラウザウィンドウの ChromeWindow オブジェクトを渡すことで、ブラウザウィンドウのサムネイル画像を描画することも可能です。しかし、以下のようにブラウザタブ内に表示されたWebページまでは描画されず、背景色で塗りつぶされたようになってしまいます。
この問題を解決するには、ブラウザウィンドウのサムネイルの上に、Web ページのサムネイルを重ねて描画する必要がありそうです。ここでは、 Tab Flick 拡張機能で実際に使っている、 html:canvas 要素を2個重ねる方式を紹介します。
XUL
ブラウザウィンドウのサムネイル描画用とWebページのサムネイル描画用の、2つの html:canvas 要素を xul:stack 要素で重ねます。
<stack>
<html:canvas id="outerCanvas" /><!-- ブラウザウィンドウ用 -->
<html:canvas id="innerCanvas" /><!-- Webページ用 -->
</stack>
JavaScript
最初にサムネイルの拡大/縮小率を定めておきます。ここでは50%に縮小することとします。
const scale = 0.5;
サムネイル描画の対象とするブラウザウィンドウとして、 nsIWindowMediator を使って直近のブラウザウィンドウの ChromeWindow オブジェクトを取得します。
var win = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
簡便のため、2つの html:canvas 要素への参照を取得しておきます。
var oCanvas = document.getElementById("outerCanvas");
var iCanvas = document.getElementById("innerCanvas");
ブラウザウィンドウに対する現在のブラウザ(xul:browser 要素)の位置の相対関係から、Webページ用の html:canvas 要素の位置を調整します。
var rect = win.gBrowser.mCurrentBrowser.getBoundingClientRect();
iCanvas.style.left = Math.round(rect.left * scale) + "px";
iCanvas.style.top = Math.round(rect.top * scale) + "px";
この時点で2つの html:canvas 要素に枠線を付加して表示してみると、以下のようになっています。
なお、この時点では html:canvas 要素のサイズはまだ調整されていません。
次の処理へ移る前に、あらかじめ関数を作っておきます。引数で指定したウィンドウのサムネイルを、指定した html:canvas 要素に描画する関数です。
// @param aWindow ウィンドウのオブジェクト
// @param aCavas html:canvas要素
// @param aScale サムネイルの拡大/縮小率
function drawPreviewForWindow(aWindow, aCanvas, aScale) {
var w = aWindow.innerWidth || aWindow.document.documentElement.clientWidth;
var h = aWindow.innerHeight || aWindow.document.documentElement.clientHeight;
aCanvas.width = Math.round(w * aScale);
aCanvas.height = Math.round(h * aScale);
var ctx = aCanvas.getContext("2d");
ctx.clearRect(0, 0, aCanvas.width, aCanvas.height);
ctx.save();
ctx.scale(aScale, aScale);
ctx.drawWindow(aWindow, aWindow.scrollX, aWindow.scrollY, w, h, "rgb(255,255,255)");
ctx.restore();
}
先ほど作った関数を使い、ブラウザウィンドウと、そのブラウザウィンドウの現在のタブに表示されたWebページの両方のサムネイルを描画します。なお、 win.content
は win
に対応するブラウザウィンドウの現在のタブに表示されたWebページの window オブジェクトを表します。
drawPreviewForWindow(win, oCanvas, scale);
drawPreviewForWindow(win.content, iCanvas, scale);
以上のコードに適当に肉付けして実行してみると、以下のようにブラウザタブ内のWebページも含めてブラウザウィンドウのサムネイルが描画されます。
なお、最終的に2つの html:canvas 要素は以下のような位置関係・サイズになっています。
別の方式…1個の html:canvas 要素で実現可能か?
今回は2つの html:canvas 要素を重ねる方式を紹介しましたが、別の方式として、1個の html:canvas 要素に対して2回 drawWindow メソッドを使用してブラウザウィンドウとWebページの2つのサムネイルを重ねて描画する方式も思いつきました。しかし、筆者が調べた限りでは、 drawWindow メソッドは必ず html:canvas 要素の左上位置(座標 0, 0)から描画を開始するため、Webページのサムネイルを正しい位置に重ねることができないようでした。したがって、別の方式は断念しました。
hatena.ne.jp/piro_or/ :
Gomita :