mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 800170 - Modify mozbrowser's getScreenshot() so it takes max-width, max-height parameters. r=smaug
This commit is contained in:
parent
f8c980d8d8
commit
fff4070d9d
@ -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>
|
Loading…
Reference in New Issue
Block a user