Bug 1262594 - Rebuild screenshot capturing after iframe mozbrowser; r=jryans
MozReview-Commit-ID: ASQujGzJa6s
--- a/devtools/client/responsive.html/actions/screenshot.js
+++ b/devtools/client/responsive.html/actions/screenshot.js
@@ -1,54 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env browser */
"use strict";
-const HTML_NS = "http://www.w3.org/1999/xhtml";
-
const {
TAKE_SCREENSHOT_START,
TAKE_SCREENSHOT_END,
} = require("./index");
-const { getRect } = require("devtools/shared/layout/utils");
const { getFormatStr } = require("../utils/l10n");
const { getToplevelWindow } = require("sdk/window/utils");
-const { Task: { spawn, async } } = require("resource://gre/modules/Task.jsm");
+const { Task: { spawn } } = require("resource://gre/modules/Task.jsm");
+const e10s = require("../utils/e10s");
const BASE_URL = "resource://devtools/client/responsive.html";
const audioCamera = new window.Audio(`${BASE_URL}/audio/camera-click.mp3`);
+const animationFrame = () => new Promise(resolve => {
+ window.requestAnimationFrame(resolve);
+});
+
function getFileName() {
let date = new Date();
let month = ("0" + (date.getMonth() + 1)).substr(-2);
let day = ("0" + date.getDate()).substr(-2);
let dateString = [date.getFullYear(), month, day].join("-");
let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
return getFormatStr("responsive.screenshotGeneratedFilename", dateString,
timeString);
}
function createScreenshotFor(node) {
- let { top, left, width, height } = getRect(window, node, window);
+ let mm = node.frameLoader.messageManager;
- const canvas = document.createElementNS(HTML_NS, "canvas");
- const ctx = canvas.getContext("2d");
- const ratio = window.devicePixelRatio;
- canvas.width = width * ratio;
- canvas.height = height * ratio;
- ctx.scale(ratio, ratio);
- ctx.drawWindow(window, left, top, width, height, "#fff");
-
- return canvas.toDataURL("image/png", "");
+ return e10s.request(mm, "RequestScreenshot");
}
function saveToFile(data, filename) {
return spawn(function* () {
const chromeWindow = getToplevelWindow(window);
const chromeDocument = chromeWindow.document;
// append .png extension to filename if it doesn't exist
@@ -68,22 +62,21 @@ function simulateCameraEffects(node) {
module.exports = {
takeScreenshot() {
return function* (dispatch, getState) {
yield dispatch({ type: TAKE_SCREENSHOT_START });
// Waiting the next repaint, to ensure the react components
// can be properly render after the action dispatched above
- window.requestAnimationFrame(async(function* () {
- let iframe = document.querySelector("iframe");
- let data = createScreenshotFor(iframe);
+ yield animationFrame();
+
+ let iframe = document.querySelector("iframe");
+ let data = yield createScreenshotFor(iframe);
- simulateCameraEffects(iframe);
+ simulateCameraEffects(iframe);
- yield saveToFile(data, getFileName());
+ yield saveToFile(data, getFileName());
- dispatch({ type: TAKE_SCREENSHOT_END });
- }));
+ dispatch({ type: TAKE_SCREENSHOT_END });
};
}
-
};
--- a/devtools/client/responsive.html/components/browser.js
+++ b/devtools/client/responsive.html/components/browser.js
@@ -8,17 +8,17 @@
const { Task } = require("resource://gre/modules/Task.jsm");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { getToplevelWindow } = require("sdk/window/utils");
const { DOM: dom, createClass, addons, PropTypes } =
require("devtools/client/shared/vendor/react");
const Types = require("../types");
-const { waitForMessage } = require("../utils/e10s");
+const e10s = require("../utils/e10s");
module.exports = createClass({
/**
* This component is not allowed to depend directly on frequently changing
* data (width, height) due to the use of `dangerouslySetInnerHTML` below.
* Any changes in props will cause the <iframe> to be removed and added again,
* throwing away the current state of the page.
*/
@@ -40,44 +40,43 @@ module.exports = createClass({
let { onContentResize } = this;
let browser = this.refs.browserContainer.querySelector("iframe.browser");
let mm = browser.frameLoader.messageManager;
// Notify tests when the content has received a resize event. This is not
// quite the same timing as when we _set_ a new size around the browser,
// since it still needs to do async work before the content is actually
// resized to match.
- mm.addMessageListener("ResponsiveMode:OnContentResize", onContentResize);
+ e10s.on(mm, "OnContentResize", onContentResize);
- let ready = waitForMessage(mm, "ResponsiveMode:ChildScriptReady");
+ let ready = e10s.once(mm, "ChildScriptReady");
mm.loadFrameScript("resource://devtools/client/responsivedesign/" +
"responsivedesign-child.js", true);
yield ready;
let browserWindow = getToplevelWindow(window);
let requiresFloatingScrollbars =
!browserWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
- let started = waitForMessage(mm, "ResponsiveMode:Start:Done");
- mm.sendAsyncMessage("ResponsiveMode:Start", {
+
+ yield e10s.request(mm, "Start", {
requiresFloatingScrollbars,
// Tests expect events on resize to yield on various size changes
notifyOnResize: DevToolsUtils.testing,
});
- yield started;
// manager.js waits for this signal before allowing browser tests to start
this.props.onBrowserMounted();
}),
componentWillUnmount() {
let { onContentResize } = this;
let browser = this.refs.browserContainer.querySelector("iframe.browser");
let mm = browser.frameLoader.messageManager;
- mm.removeMessageListener("ResponsiveMode:OnContentResize", onContentResize);
- mm.sendAsyncMessage("ResponsiveMode:Stop");
+ e10s.off(mm, "OnContentResize", onContentResize);
+ e10s.emit(mm, "Stop");
},
onContentResize(msg) {
let { onContentResize } = this.props;
let { width, height } = msg.data;
onContentResize({
width,
height,
--- a/devtools/client/responsive.html/utils/e10s.js
+++ b/devtools/client/responsive.html/utils/e10s.js
@@ -1,23 +1,103 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
-const promise = require("promise");
+const { defer } = require("promise");
+
+// The prefix used for RDM messages in content.
+// see: devtools/client/responsivedesign/responsivedesign-child.js
+const MESSAGE_PREFIX = "ResponsiveMode:";
+const REQUEST_DONE_SUFFIX = ":Done";
-module.exports = {
+/**
+ * Registers a message `listener` that is called every time messages of
+ * specified `message` is emitted on the given message manager.
+ * @param {nsIMessageListenerManager} mm
+ * The Message Manager
+ * @param {String} message
+ * The message. It will be prefixed with the constant `MESSAGE_PREFIX`
+ * @param {Function} listener
+ * The listener function that processes the message.
+ */
+function on(mm, message, listener) {
+ mm.addMessageListener(MESSAGE_PREFIX + message, listener);
+}
+exports.on = on;
- waitForMessage(mm, message) {
- let deferred = promise.defer();
+/**
+ * Removes a message `listener` for the specified `message` on the given
+ * message manager.
+ * @param {nsIMessageListenerManager} mm
+ * The Message Manager
+ * @param {String} message
+ * The message. It will be prefixed with the constant `MESSAGE_PREFIX`
+ * @param {Function} listener
+ * The listener function that processes the message.
+ */
+function off(mm, message, listener) {
+ mm.removeMessageListener(MESSAGE_PREFIX + message, listener);
+}
+exports.off = off;
- let onMessage = event => {
- mm.removeMessageListener(message, onMessage);
- deferred.resolve();
- };
- mm.addMessageListener(message, onMessage);
+/**
+ * Resolves a promise the next time the specified `message` is sent over the
+ * given message manager.
+ * @param {nsIMessageListenerManager} mm
+ * The Message Manager
+ * @param {String} message
+ * The message. It will be prefixed with the constant `MESSAGE_PREFIX`
+ * @returns {Promise}
+ * A promise that is resolved when the given message is emitted.
+ */
+function once(mm, message) {
+ let { resolve, promise } = defer();
+
+ on(mm, message, function onMessage({data}) {
+ off(mm, message, onMessage);
+ resolve(data);
+ });
+
+ return promise;
+}
+exports.once = once;
- return deferred.promise;
- },
+/**
+ * Asynchronously emit a `message` to the listeners of the given message
+ * manager.
+ *
+ * @param {nsIMessageListenerManager} mm
+ * The Message Manager
+ * @param {String} message
+ * The message. It will be prefixed with the constant `MESSAGE_PREFIX`.
+ * @param {Object} data
+ * A JSON object containing data to be delivered to the listeners.
+ */
+function emit(mm, message, data) {
+ mm.sendAsyncMessage(MESSAGE_PREFIX + message, data);
+}
+exports.emit = emit;
-};
+/**
+ * Asynchronously send a "request" over the given message manager, and returns
+ * a promise that is resolved when the request is complete.
+ *
+ * @param {nsIMessageListenerManager} mm
+ * The Message Manager
+ * @param {String} message
+ * The message. It will be prefixed with the constant `MESSAGE_PREFIX`, and
+ * also suffixed with `REQUEST_DONE_SUFFIX` for the reply.
+ * @param {Object} data
+ * A JSON object containing data to be delivered to the listeners.
+ * @returns {Promise}
+ * A promise that is resolved when the request is done.
+ */
+function request(mm, message, data) {
+ let done = once(mm, message + REQUEST_DONE_SUFFIX);
+
+ emit(mm, message, data);
+
+ return done;
+}
+exports.request = request;
--- a/devtools/client/responsivedesign/responsivedesign-child.js
+++ b/devtools/client/responsivedesign/responsivedesign-child.js
@@ -145,22 +145,24 @@ var global = this;
docShell.contentViewer.sticky = false;
docShell.contentViewer.hide();
docShell.contentViewer.show();
docShell.contentViewer.sticky = isSticky;
}
function screenshot() {
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
- let width = content.innerWidth;
- let height = content.innerHeight;
+ let ratio = content.devicePixelRatio;
+ let width = content.innerWidth * ratio;
+ let height = content.innerHeight * ratio;
canvas.mozOpaque = true;
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
+ ctx.scale(ratio, ratio);
ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff");
sendAsyncMessage("ResponsiveMode:RequestScreenshot:Done", canvas.toDataURL());
}
var WebProgressListener = {
onLocationChange(webProgress, request, URI, flags) {
if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
return;