7

I am looking for firefox addon api to take screenshot of visible area of document.

Chrome and Safari have api's to achieve this. And they are pretty fast. I could not find anything specific for firefox.

I found a workaround at How do I use the canvas drawWindow function in an addon created using the addon sdk? but this solution takes full page screenshot with scrolls including (hidden parts of document). There are 2 issues for this solution;

1- if page has long scroll, it takes long time to complete screenshot process. Because it is using canvas based drawing.

2- I would like to get screenshot of visible area of document, not whole document.

Is there any workaround for this?

Thanks.

Community
  • 1
  • 1
Teoman shipahi
  • 47,454
  • 15
  • 134
  • 158
  • There are a lot of Firefox add-ons that do screenshots. Download the XPI archive and take a look at the source code, that will get you started. – Julien Aug 15 '14 at 22:01
  • [`drawWindow`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#drawWindow%28%29) is the only way I know how. You can supply 2nd and 3rd argument which is x and y to start the drawing and 4th and 5th args tell how much width and height to draw. If you only supply first arg of window than it will draw the whole thing. So supply 2nd, 3rd, 4th, and 5th, to equal that of window.scrollTop + window.screenHeight and window.scrollLeft + window.screenWidth. (note: i guessed at these window scroll properties so you may have to look up the proper ones) – Noitidart Aug 15 '14 at 22:25
  • "Abduction" works like a charm. https://addons.mozilla.org/en-US/firefox/addon/abduction/?src=search – Lori Aug 17 '14 at 21:00
  • @Teoman shipahi How did you did it?. I don't understand the code – abhilash Oct 23 '14 at 10:43

2 Answers2

9

Using the SDK you can do something like this:

const { window: { document } } = require('sdk/addon/window');
const { getTabContentWindow, getActiveTab } = require('sdk/tabs/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');

const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
document.documentElement.appendChild(canvas);

function captureTab(tab=getActiveTab(getMostRecentBrowserWindow())) {
  let contentWindow = getTabContentWindow(tab);

  let w = contentWindow.innerWidth;
  let h = contentWindow.innerHeight;
  let x = contentWindow.scrollX;
  let y = contentWindow.scrollY;

  canvas.width = w;
  canvas.height = h;

  let ctx = canvas.getContext('2d');

  ctx.drawWindow(contentWindow, x, y, w, h, '#000');
  return canvas.toDataURL();
}

That should takes only the visible area. By default, it grabs the active tab, but you can pass any other tab – because is designed as low level API it takes a native tab, however, not a SDK tab. You can put in a module and exports just the captureTab function.

Edit: e10s version

The code above is not currently compatible with Firefox with e10s available, as Ian Bicking noted in the comment. An easy way to workaround this issue, is create a temporary canvas in the same document and content process we want to capture the screenshot:

const { getTabContentWindow, getActiveTab } = require('sdk/tabs/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');

function captureTab(tab=getActiveTab(getMostRecentBrowserWindow())) {
  let contentWindow = getTabContentWindow(tab);
  let { document } = contentWindow;

  let w = contentWindow.innerWidth;
  let h = contentWindow.innerHeight;
  let x = contentWindow.scrollX;
  let y = contentWindow.scrollY;

  let canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');

  canvas.width = w;
  canvas.height = h;

  let ctx = canvas.getContext('2d');

  ctx.drawWindow(contentWindow, x, y, w, h, '#000');

  let dataURL = canvas.toDataURL();

  canvas = null;

  return dataURL;
}

That works in both e10s and no-e10s FF version; the downside comparing to the previous one is creating a canvas every time we want to take a screenshot, but I think is acceptable.

ZER0
  • 24,846
  • 5
  • 51
  • 54
  • Very nice solution easy to copy paste I like those. – Noitidart Aug 18 '14 at 15:01
  • Could you please tell me this in sdk 1.17 view? – abhilash Oct 23 '14 at 10:44
  • @abhilash Not sure what you mean with 'view'. Could you please elaborate? That should works in 1.17 too. – ZER0 Oct 30 '14 at 08:56
  • Note this does not work with e10s (multiprocess): you'll get a confusing error: `TypeError: Argument 1 of CanvasRenderingContext2D.drawWindow does not implement interface Window` I think that's because the window object is just a proxy, and this all has to be run in some kind of frame script. Unfortunately I haven't figured out how to make a frame script from Jetpack/Addon-SDK. ctx.drawWindow isn't available in contentScripts. – Ian Bicking Dec 09 '14 at 16:52
  • It's absolutely true; this code was made for FF prior e10s. I updated the answer with a workaround for e10s too. Thanks. – ZER0 Dec 11 '14 at 17:17
  • Does `getTabContentWindow` still work for you? I just tried this in the latest nightly in my add-on with multiprocess enabled and I get `JPM [error] Message: TypeError: getBrowserForTab(...) is undefined Stack: getTabContentWindow@resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/tabs/utils.js:247:10` – Sebastian Blask Dec 01 '15 at 16:13
  • @ZER0 Does the above code make any difference on working in cfx and jpm tool of firefox addon development? – Muthu Mar 04 '16 at 07:38
  • Is there a way to compress the resulting dataUrl with reduced picture quality? – Muthu Mar 11 '16 at 10:06
  • There is a similar solution to do this in chrome and safari? – Ivan Carosati Nov 02 '16 at 18:29
3

Your assumption that taking a screenshot on Firefox with canvas is somehow slow is wrong.

I did a couple of screenshots and Firefox/canvas was faster than Chrome/captureVisibleTab.

Actually Firefox is better suited for as-fast-as-possible screenshots, since its canvas expose to privileged code the mozFetchAsStream method, allowing to bypass the actual bottleneck which is the base64 encoding of the image data.

Some numbers

  • Chrome: captureVisibleTab 200-205ms
  • Firefox: drawImage 20-25ms + toDataURL 125-130ms

The devtools screenshot command is a good example of how to capture just the visible part

In all fairness, to make a meaningful comparison one has to take into account whether Chrome's PNG encoder favors compression over speed. Still, this doesn't change the fact that Firefox's canvas is fine.

edit: OK, that base64 encoding remark is dumb, I don't know what I was thinking. Perhaps what I should write instead is that Firefox's canvas is not only fast but also versatile.

paa
  • 5,048
  • 1
  • 18
  • 22
  • Superb testing man@ I like tests like these! Also learned a bit about `mozFetchAsStream` benefit. Didnt know base64 was bottleneck. – Noitidart Aug 17 '14 at 18:09
  • @Noitidart it's not, what I meant to say was that on Firefox you can avoid converting the b64 encoded data back to binary, if that's necessary. – paa Aug 18 '14 at 09:43
  • Example link is broken – EFernandes Aug 23 '19 at 11:12