Bug 800170 - Modify mozbrowser's getScreenshot() so it takes max-width, max-height parameters. r=smaug

This commit is contained in:
Justin Lebar 2012-10-17 00:23:08 -04:00
Родитель f8c980d8d8
Коммит fff4070d9d
11 изменённых файлов: 195 добавлений и 30 удалений

Просмотреть файл

@ -452,20 +452,56 @@ BrowserElementChild.prototype = {
_recvGetScreenshot: function(data) {
debug("Received getScreenshot message: (" + data.json.id + ")");
// You can think of the screenshotting algorithm as carrying out the
// following steps:
//
// - Let max-width be data.json.args.width, and let max-height be
// data.json.args.height.
//
// - Let scale-width be the factor by which we'd need to downscale the
// viewport so it would fit within max-width. (If the viewport's width
// is less than max-width, let scale-width be 1.) Compute scale-height
// the same way.
//
// - Scale the viewport by max(scale-width, scale-height). Now either the
// viewport's width is no larger than max-width, the viewport's height is
// no larger than max-height, or both.
//
// - Crop the viewport so its width is no larger than max-width and its
// height is no larger than max-height.
//
// - Return a screenshot of the page's viewport scaled and cropped per
// above.
let maxWidth = data.json.args.width;
let maxHeight = data.json.args.height;
let scaleWidth = Math.min(1, maxWidth / content.innerWidth);
let scaleHeight = Math.min(1, maxHeight / content.innerHeight);
let scale = Math.max(scaleWidth, scaleHeight);
let canvasWidth = Math.min(maxWidth, Math.round(content.innerWidth * scale));
let canvasHeight = Math.min(maxHeight, Math.round(content.innerHeight * scale));
var canvas = content.document
.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
var ctx = canvas.getContext("2d");
canvas.mozOpaque = true;
canvas.height = content.innerHeight;
canvas.width = content.innerWidth;
ctx.drawWindow(content, 0, 0, content.innerWidth,
content.innerHeight, "rgb(255,255,255)");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
var ctx = canvas.getContext("2d");
ctx.scale(scale, scale);
ctx.drawWindow(content, 0, 0, content.innerWidth, content.innerHeight,
"rgb(255,255,255)");
sendAsyncMsg('got-screenshot', {
id: data.json.id,
// Hack around the fact that we can't specify opaque PNG, this requires
// us to unpremultiply the alpha channel which is expensive on ARM
// processors because they lack a hardware integer division instruction.
rv: canvas.toDataURL("image/jpeg")
successRv: canvas.toDataURL("image/jpeg")
});
},
@ -550,7 +586,7 @@ BrowserElementChild.prototype = {
var webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
sendAsyncMsg('got-can-go-back', {
id: data.json.id,
rv: webNav.canGoBack
successRv: webNav.canGoBack
});
},
@ -558,7 +594,7 @@ BrowserElementChild.prototype = {
var webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
sendAsyncMsg('got-can-go-forward', {
id: data.json.id,
rv: webNav.canGoForward
successRv: webNav.canGoForward
});
},

Просмотреть файл

@ -243,7 +243,7 @@ function BrowserElementParent(frameLoader, hasRemoteFrame) {
defineMethod('goForward', this._goForward);
defineMethod('reload', this._reload);
defineMethod('stop', this._stop);
defineDOMRequestMethod('getScreenshot', 'get-screenshot');
defineMethod('getScreenshot', this._getScreenshot);
defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
@ -470,15 +470,18 @@ BrowserElementParent.prototype = {
* Kick off a DOMRequest in the child process.
*
* We'll fire an event called |msgName| on the child process, passing along
* an object with a single field, id, containing the ID of this request.
* an object with two fields:
*
* - id: the ID of this request.
* - arg: arguments to pass to the child along with this request.
*
* We expect the child to pass the ID back to us upon completion of the
* request; see _gotDOMRequestResult.
* request. See _gotDOMRequestResult.
*/
_sendDOMRequest: function(msgName) {
_sendDOMRequest: function(msgName, args) {
let id = 'req_' + this._domRequestCounter++;
let req = Services.DOMRequest.createRequest(this._window);
if (this._sendAsyncMsg(msgName, {id: id})) {
if (this._sendAsyncMsg(msgName, {id: id, args: args})) {
this._pendingDOMRequests[id] = req;
} else {
Services.DOMRequest.fireErrorAsync(req, "fail");
@ -487,17 +490,30 @@ BrowserElementParent.prototype = {
},
/**
* Called when the child process finishes handling a DOMRequest. We expect
* data.json to have two fields:
* Called when the child process finishes handling a DOMRequest. data.json
* must have the fields [id, successRv], if the DOMRequest was successful, or
* [id, errorMsg], if the request was not successful.
*
* - id: the ID of the DOM request (see _sendDOMRequest), and
* - rv: the request's return value.
* The fields have the following meanings:
*
* - id: the ID of the DOM request (see _sendDOMRequest)
* - successRv: the request's return value, if the request succeeded
* - errorMsg: the message to pass to DOMRequest.fireError(), if the request
* failed.
*
*/
_gotDOMRequestResult: function(data) {
let req = this._pendingDOMRequests[data.json.id];
delete this._pendingDOMRequests[data.json.id];
Services.DOMRequest.fireSuccess(req, data.json.rv);
if ('successRv' in data.json) {
debug("Successful gotDOMRequestResult.");
Services.DOMRequest.fireSuccess(req, data.json.successRv);
}
else {
debug("Got error in gotDOMRequestResult.");
Services.DOMRequest.fireErrorAsync(req, data.json.errorMsg);
}
},
_setVisible: function(visible) {
@ -548,6 +564,18 @@ BrowserElementParent.prototype = {
this._sendAsyncMsg('stop');
},
_getScreenshot: function(_width, _height) {
let width = parseInt(_width);
let height = parseInt(_height);
if (isNaN(width) || isNaN(height) || width < 0 || height < 0) {
throw Components.Exception("Invalid argument",
Cr.NS_ERROR_INVALID_ARG);
}
return this._sendDOMRequest('get-screenshot',
{width: width, height: height});
},
_fireKeyEvent: function(data) {
let evt = this._window.document.createEvent("KeyboardEvent");
evt.initKeyEvent(data.json.type, true, true, this._window,

Просмотреть файл

@ -43,6 +43,8 @@ MOCHITEST_FILES = \
test_browserElement_inproc_Iconchange.html \
browserElement_GetScreenshot.js \
test_browserElement_inproc_GetScreenshot.html \
browserElement_BadScreenshot.js \
test_browserElement_inproc_BadScreenshot.html \
browserElement_SetVisible.js \
test_browserElement_inproc_SetVisible.html \
browserElement_SetVisibleFrames.js \
@ -169,6 +171,7 @@ MOCHITEST_FILES += \
test_browserElement_oop_TopBarrier.html \
test_browserElement_oop_Iconchange.html \
test_browserElement_oop_GetScreenshot.html \
test_browserElement_oop_BadScreenshot.html \
test_browserElement_oop_SetVisible.html \
test_browserElement_oop_SetVisibleFrames.html \
test_browserElement_oop_SetVisibleFrames2.html \

Просмотреть файл

@ -0,0 +1,74 @@
/* Any copyright is dedicated to the public domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Bug 800170 - Test that we get errors when we pass bad arguments to
// mozbrowser's getScreenshot.
"use strict";
SimpleTest.waitForExplicitFinish();
var iframe;
var numPendingTests = 0;
// Call iframe.getScreenshot with the given args. If expectSuccess is true, we
// expect the screenshot's onsuccess handler to fire. Otherwise, we expect
// getScreenshot() to throw an exception.
function checkScreenshotResult(expectSuccess, args) {
var req;
try {
req = iframe.getScreenshot.apply(iframe, args);
}
catch(e) {
ok(!expectSuccess, "getScreenshot(" + JSON.stringify(args) + ") threw an exception.");
return;
}
numPendingTests++;
req.onsuccess = function() {
ok(expectSuccess, "getScreenshot(" + JSON.stringify(args) + ") succeeded.");
numPendingTests--;
if (numPendingTests == 0) {
SimpleTest.finish();
}
};
// We never expect to see onerror.
req.onerror = function() {
ok(false, "getScreenshot(" + JSON.stringify(args) + ") ran onerror.");
numPendingTests--;
if (numPendingTests == 0) {
SimpleTest.finish();
}
};
}
function runTest() {
dump("XXX runTest\n");
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
iframe = document.createElement('iframe');
iframe.mozbrowser = true;
document.body.appendChild(iframe);
iframe.src = 'data:text/html,<html>' +
'<body style="background:green">hello</body></html>';
iframe.addEventListener('mozbrowserfirstpaint', function() {
// This one should succeed.
checkScreenshotResult(true, [100, 100]);
// These should fail.
checkScreenshotResult(false, []);
checkScreenshotResult(false, [100]);
checkScreenshotResult(false, ['a', 100]);
checkScreenshotResult(false, [100, 'a']);
checkScreenshotResult(false, [-1, 100]);
checkScreenshotResult(false, [100, -1]);
if (numPendingTests == 0) {
SimpleTest.finish();
}
});
}
runTest();

Просмотреть файл

@ -27,7 +27,7 @@ function runTest() {
SimpleTest.executeSoon(nextTest);
}
var domRequest = iframe1.getScreenshot();
var domRequest = iframe1.getScreenshot(1000, 1000);
domRequest.onsuccess = function(e) {
testEnd();
}

Просмотреть файл

@ -37,7 +37,7 @@ function runTest() {
}
// We continually take screenshots until we get one that we are
// happy with
// happy with.
function waitForScreenshot(filter) {
function screenshotLoaded(e) {
@ -50,13 +50,13 @@ function runTest() {
SimpleTest.finish();
} else {
content.document.defaultView.setTimeout(function() {
iframe1.getScreenshot().onsuccess = screenshotLoaded;
iframe1.getScreenshot(1000, 1000).onsuccess = screenshotLoaded;
}, 200);
}
}
var attempts = 10;
iframe1.getScreenshot().onsuccess = screenshotLoaded;
iframe1.getScreenshot(1000, 1000).onsuccess = screenshotLoaded;
}
function iframeLoadedHandler() {
@ -72,5 +72,3 @@ function runTest() {
}
addEventListener('load', function() { SimpleTest.executeSoon(runTest); });

Просмотреть файл

@ -55,7 +55,7 @@ function runTest() {
else if (e.detail.message == 'finish') {
// We assume here that iframe is completely blank, and spin until popup's
// screenshot is not the same as iframe.
iframe.getScreenshot().onsuccess = function(e) {
iframe.getScreenshot(1000, 1000).onsuccess = function(e) {
test2(popup, e.target.result, popup);
};
}
@ -72,7 +72,7 @@ var prevScreenshot;
function test2(popup, blankScreenshot) {
// Take screenshots of popup until it doesn't equal blankScreenshot (or we
// time out).
popup.getScreenshot().onsuccess = function(e) {
popup.getScreenshot(1000, 1000).onsuccess = function(e) {
var screenshot = e.target.result;
if (screenshot != blankScreenshot) {
SimpleTest.finish();

Просмотреть файл

@ -25,7 +25,7 @@ function runTest() {
// taking the screenshot).
e.preventDefault();
iframe.getScreenshot().onsuccess = function(sshot) {
iframe.getScreenshot(1000, 1000).onsuccess = function(sshot) {
if (initialScreenshot == null)
initialScreenshot = sshot.target.result;
e.detail.unblock();
@ -37,7 +37,7 @@ function runTest() {
case 'finish':
// The page has now attempted to load the X-Frame-Options page; take
// another screenshot.
iframe.getScreenshot().onsuccess = function(sshot) {
iframe.getScreenshot(1000, 1000).onsuccess = function(sshot) {
is(sshot.target.result, initialScreenshot, "Screenshots should be identical");
SimpleTest.finish();
};

Просмотреть файл

@ -28,7 +28,7 @@ function runTest() {
// taking the screenshot).
e.preventDefault();
iframe.getScreenshot().onsuccess = function(sshot) {
iframe.getScreenshot(1000, 1000).onsuccess = function(sshot) {
initialScreenshot = sshot.target.result;
e.detail.unblock();
};
@ -36,7 +36,7 @@ function runTest() {
case 'step 2':
// The page has now attempted to load the X-Frame-Options page; take
// another screenshot.
iframe.getScreenshot().onsuccess = function(sshot) {
iframe.getScreenshot(1000, 1000).onsuccess = function(sshot) {
is(sshot.target.result, initialScreenshot, "Screenshots should be identical");
SimpleTest.finish();
};

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 800170</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_BadScreenshot.js">
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 800170</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_BadScreenshot.js">
</script>
</body>
</html>