Bug 883294 - Add ability to take full viewport screenshots. r=mdas
--- a/testing/marionette/client/marionette/runner/mixins/reporting.py
+++ b/testing/marionette/client/marionette/runner/mixins/reporting.py
@@ -241,19 +241,19 @@ class HTMLReportingTestResultMixin(objec
test.debug = None
if result_actual is not 'PASS':
test.debug = self.gather_debug()
return result_expected, result_actual, output, context
def gather_debug(self):
debug = {}
try:
- # TODO make screenshot consistant size by using full viewport
- # Bug 883294 - Add ability to take full viewport screenshots
+ self.marionette.switch_context(self.marionette.CONTEXT_CHROME)
debug['screenshot'] = self.marionette.screenshot()
+ self.marionette.switch_context(self.marionette.CONTEXT_CONTENT)
debug['source'] = self.marionette.page_source
self.marionette.switch_to_frame()
debug['settings'] = json.dumps(self.marionette.execute_async_script("""
SpecialPowers.addPermission('settings-read', true, document);
SpecialPowers.addPermission('settings-api-read', true, document);
var req = window.navigator.mozSettings.createLock().get('*');
req.onsuccess = function() {
marionetteScriptFinished(req.result);
--- a/testing/marionette/client/marionette/tests/unit/test_screenshot.py
+++ b/testing/marionette/client/marionette/tests/unit/test_screenshot.py
@@ -1,44 +1,59 @@
+import base64
+import imghdr
+
from marionette_test import MarionetteTestCase
-import base64
+
RED_ELEMENT_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVUlEQVRoge3PsQ0AIAzAsI78fzBwBhHykD2ePev80LweAAGJB1ILpBZILZBaILVAaoHUAqkFUgukFkgtkFogtUBqgdQCqQVSC6QWSC2QWiC1QGp9A7ma+7nyXgOpzQAAAABJRU5ErkJggg=='
GREEN_ELEMENT_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAV0lEQVRoge3PQRGAQAwAsWINvXgsNnI3+4iAzM7sDWZn9vneoxXRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNHcF7nBD/Ha5Ye4BbsYAAAAAElFTkSuQmCC'
class ScreenshotTests(MarionetteTestCase):
+ def testWeCanTakeAScreenShotOfEntireViewport(self):
+ test_url = self.marionette.absolute_url('html5Page.html')
+ self.marionette.navigate(test_url)
+ content = self.marionette.screenshot()
+ self.marionette.set_context(self.marionette.CONTEXT_CHROME)
+ chrome = self.marionette.screenshot()
+ # Check the base64 decoded string is a PNG file.
+ image = base64.decodestring(chrome)
+ self.assertEqual(imghdr.what('', image), 'png')
+ self.assertNotEqual(content, chrome)
+
def testWeCanTakeAScreenShotOfAnElement(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
el = self.marionette.find_element('id', 'red')
self.assertEqual(RED_ELEMENT_BASE64,
self.marionette.screenshot(element=el))
def testWeCanTakeAScreenShotWithHighlightOfAnElement(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
el = self.marionette.find_element('id', 'green')
self.assertEqual(GREEN_ELEMENT_BASE64,
self.marionette.screenshot(element=el, highlights=[el]))
- def testWeCanTakeAScreenShotEntireCanvas(self):
+ def testWeCanTakeAScreenShotOfEntireCanvas(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
- self.assertTrue('iVBORw0KGgo' in
- self.marionette.screenshot())
+ # Check the base64 decoded string is a PNG file.
+ image = base64.decodestring(self.marionette.screenshot())
+ self.assertEqual(imghdr.what('', image), 'png')
def testWeCanTakeABinaryScreenShotOfAnElement(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
el = self.marionette.find_element('id', 'red')
binary_data = self.marionette.screenshot(element=el, format="binary")
self.assertEqual(RED_ELEMENT_BASE64,
base64.b64encode(binary_data))
-
+
def testNotAllowedScreenshotFormatRaiseValueError(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
el = self.marionette.find_element('id', 'red')
self.assertRaises(ValueError,
self.marionette.screenshot,
element=el,
format="unknowformat")
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -2366,36 +2366,80 @@ MarionetteServerConnection.prototype = {
catch (e) {
this.sendError("Could not clear imported scripts", 500, e.name + ": " + e.message, command_id);
return;
}
this.sendOk(command_id);
},
/**
- * Takes a screenshot of a web element or the current frame.
+ * Takes a screenshot of a web element, current frame, or viewport.
*
* The screen capture is returned as a lossless PNG image encoded as
- * a base 64 string. If the <code>id</code> argument is not null
- * and refers to a present and visible web element's ID, the capture
- * area will be limited to the bounding box of that element.
- * Otherwise, the capture area will be the bounding box of the
- * current frame.
+ * a base 64 string.
+ *
+ * If called in the content context, the <code>id</code> argument is not null
+ * and refers to a present and visible web element's ID, the capture area
+ * will be limited to the bounding box of that element. Otherwise, the
+ * capture area will be the bounding box of the current frame.
*
- * @param id an optional reference to a web element
- * @param highlights an optional list of web elements to draw a red
- * box around in the returned capture
- * @return PNG image encoded as base 64 string
- */
+ * If called in the chrome context, the screenshot will always represent the
+ * entire viewport.
+ *
+ * @param {string} [id] Reference to a web element.
+ * @param {string} [highlights] List of web elements to highlight.
+ * @return {string} PNG image encoded as base 64 string.
+ */
takeScreenshot: function MDA_takeScreenshot(aRequest) {
this.command_id = this.getCommandId();
- this.sendAsync("takeScreenshot",
+ if (this.context == "chrome") {
+ var win = this.getCurrentWindow();
+ var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ var doc;
+ if (appName == "B2G") {
+ doc = win.document.body;
+ } else {
+ doc = win.document.getElementsByTagName('window')[0];
+ }
+ var docRect = doc.getBoundingClientRect();
+ var width = docRect.width;
+ var height = docRect.height;
+
+ // Convert width and height from CSS pixels (potentially fractional)
+ // to device pixels (integer).
+ var scale = win.devicePixelRatio;
+ canvas.setAttribute("width", Math.round(width * scale));
+ canvas.setAttribute("height", Math.round(height * scale));
+
+ var context = canvas.getContext("2d");
+ var flags;
+ if (appName == "B2G") {
+ flags =
+ context.DRAWWINDOW_DRAW_CARET |
+ context.DRAWWINDOW_DRAW_VIEW |
+ context.DRAWWINDOW_USE_WIDGET_LAYERS;
+ } else {
+ // Bug 1075168 - CanvasRenderingContext2D image is distorted
+ // when using certain flags in chrome context.
+ flags =
+ context.DRAWWINDOW_DRAW_VIEW |
+ context.DRAWWINDOW_USE_WIDGET_LAYERS;
+ }
+ context.scale(scale, scale);
+ context.drawWindow(win, 0, 0, width, height, "rgb(255,255,255)", flags);
+ var dataUrl = canvas.toDataURL("image/png", "");
+ var data = dataUrl.substring(dataUrl.indexOf(",") + 1);
+ this.sendResponse(data, this.command_id);
+ }
+ else {
+ this.sendAsync("takeScreenshot",
{id: aRequest.parameters.id,
highlights: aRequest.parameters.highlights},
this.command_id);
+ }
},
/**
* Get the current browser orientation.
*
* Will return one of the valid primary orientation values
* portrait-primary, landscape-primary, portrait-secondary, or
* landscape-secondary.