ブラウザウィンドウのサムネイルを描画する(改訂版)

タブカタログ拡張機能のようにWebページのサムネイル画像を表示する拡張機能の多くは、 html:canvas 要素の二次元描画コンテクストの drawWindow メソッドへWebページの window オブジェクトなどを引数で渡してサムネイルの描画を行っています(canvas を使って図形を描く – MDC)。

この drawWindow メソッドの引数にWebページの window オブジェクトではなく、ブラウザウィンドウの ChromeWindow オブジェクトを渡すことで、ブラウザウィンドウのサムネイル画像を描画することも可能です。しかし、以下のようにブラウザタブ内に表示されたWebページまでは描画されず、背景色で塗りつぶされたようになってしまいます。

この問題を解決するには、ブラウザウィンドウのサムネイルの上に、Web ページのサムネイルを重ねて描画する必要があります。この記事では、その方式を紹介します。

XUL

ブラウザウィンドウのサムネイル描画用の html:canvas 要素を生成します。

<html:canvas id="testCanvas" />

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");

簡便のため、 html:canvas 要素への参照を取得しておきます。

var canvas = document.getElementById("testCanvas");

ブラウザウィンドウのサイズに合わせ、 html:canvas 要素のサイズを調整します。

var w = win.innerWidth;
var h = win.innerHeight;
canvas.width  = w * scale;
canvas.height = h * scale;

二次元描画コンテクストを取得し、ブラウザウィンドウのサムネイルを描画します。

var ctx = canvas.getContext("2d");
ctx.save();
ctx.scale(scale, scale);
ctx.drawWindow(win, 0, 0, w, h, "rgb(255,255,255)");

引き続き、Webページのサムネイルを描画しますが、その前に原点を左上(座標0, 0)から移動させる必要があります。原点を移動させるためには、二次元描画コンテクストの translate メソッドを使います。移動量は、ブラウザウィンドウに対する現在のブラウザ(xul:browser 要素)の位置から算出します。

var rect = win.gBrowser.mCurrentBrowser.getBoundingClientRect();
ctx.translate(rect.left, rect.top);

最後にWebページのサムネイルを重ねて描画します。なお、 win.contentwin に対応するブラウザウィンドウの現在のタブに表示されたWebページの window オブジェクトを表します。

w = win.content.innerWidth  || win.content.document.documentElement.clientWidth;
h = win.content.innerHeight || win.content.document.documentElement.clientHeight;
ctx.drawWindow(win.content, win.content.scrollX, win.content.scrollY, w, h, "rgb(255,255,255)");
ctx.restore();

以上のコードに適当に肉付けして実行してみると、以下のようにブラウザタブ内のWebページも含めてブラウザウィンドウのサムネイルが描画されます。

注意

今回はブラウザウィンドウ内の現在のブラウザタブのWebページだけが見えている前提となっていますが、分割ブラウザ (Split Browser)を使っている場合、この限りではありません。そのような場合にも対応させるには、さらなる工夫が必要となります。

補足

元々筆者はブラウザウィンドウのサムネイルを描画するために、ブラウザウィンドウ用とWebページ用の2つの html:canvas 要素を xul:stack 要素で重ねる方式を考え付きましたが、Piroさんのアドバイスにより、この記事で紹介したように原点を移動させつつ1個の html:canvas 要素に対して2回 drawWindow メソッドを使用する方式が可能なことがわかりました。