diff --git a/devtools/client/responsivedesign/resize-commands.js b/devtools/client/responsivedesign/resize-commands.js
index 28b38a18e9af..4d7f68c13a7a 100644
--- a/devtools/client/responsivedesign/resize-commands.js
+++ b/devtools/client/responsivedesign/resize-commands.js
@@ -92,11 +92,11 @@ exports.items = [
}
];
-function gcli_cmd_resize(args, context) {
+function* gcli_cmd_resize(args, context) {
let browserWindow = context.environment.chromeWindow;
let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
- mgr.handleGcliCommand(browserWindow,
- browserWindow.gBrowser.selectedTab,
- this.name,
- args);
+ yield mgr.handleGcliCommand(browserWindow,
+ browserWindow.gBrowser.selectedTab,
+ this.name,
+ args);
}
diff --git a/devtools/client/responsivedesign/responsivedesign-child.js b/devtools/client/responsivedesign/responsivedesign-child.js
index 410696d957e3..de070c6e48fd 100644
--- a/devtools/client/responsivedesign/responsivedesign-child.js
+++ b/devtools/client/responsivedesign/responsivedesign-child.js
@@ -2,131 +2,181 @@
* 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/. */
-var Ci = Components.interfaces;
-const gDeviceSizeWasPageSize = docShell.deviceSizeIsPageSize;
-const gFloatingScrollbarsStylesheet = Services.io.newURI("chrome://devtools/skin/floating-scrollbars-responsive-design.css", null, null);
-var gRequiresFloatingScrollbars;
+"use strict";
-var active = false;
+/* global content, docShell, addEventListener, addMessageListener,
+ removeEventListener, removeMessageListener, sendAsyncMessage */
-addMessageListener("ResponsiveMode:Start", startResponsiveMode);
-addMessageListener("ResponsiveMode:Stop", stopResponsiveMode);
+var global = this;
-function startResponsiveMode({data:data}) {
- if (active) {
- return;
- }
- addMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
- addMessageListener("ResponsiveMode:NotifyOnResize", notifiyOnResize);
- let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
- webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_ALL);
- docShell.deviceSizeIsPageSize = true;
- gRequiresFloatingScrollbars = data.requiresFloatingScrollbars;
-
- // At this point, a content viewer might not be loaded for this
- // docshell. makeScrollbarsFloating will be triggered by onLocationChange.
- if (docShell.contentViewer) {
- makeScrollbarsFloating();
- }
- active = true;
- sendAsyncMessage("ResponsiveMode:Start:Done");
-}
-
-function notifiyOnResize() {
- content.addEventListener("resize", () => {
- sendAsyncMessage("ResponsiveMode:OnContentResize");
- }, false);
- sendAsyncMessage("ResponsiveMode:NotifyOnResize:Done");
-}
-
-function stopResponsiveMode() {
- if (!active) {
- return;
- }
- active = false;
- removeMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
- removeMessageListener("ResponsiveMode:NotifyOnResize", notifiyOnResize);
- let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
- webProgress.removeProgressListener(WebProgressListener);
- docShell.deviceSizeIsPageSize = gDeviceSizeWasPageSize;
- restoreScrollbars();
- sendAsyncMessage("ResponsiveMode:Stop:Done");
-}
-
-function makeScrollbarsFloating() {
- if (!gRequiresFloatingScrollbars) {
+// Guard against loading this frame script mutiple times
+(function() {
+ if (global.responsiveFrameScriptLoaded) {
return;
}
- let allDocShells = [docShell];
+ var Ci = Components.interfaces;
+ const gDeviceSizeWasPageSize = docShell.deviceSizeIsPageSize;
+ const gFloatingScrollbarsStylesheet = Services.io.newURI("chrome://devtools/skin/floating-scrollbars-responsive-design.css", null, null);
+ var gRequiresFloatingScrollbars;
- for (let i = 0; i < docShell.childCount; i++) {
- let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell);
- allDocShells.push(child);
+ var active = false;
+ var resizeNotifications = false;
+
+ addMessageListener("ResponsiveMode:Start", startResponsiveMode);
+ addMessageListener("ResponsiveMode:Stop", stopResponsiveMode);
+
+ function debug(msg) {
+ // dump(`RDM CHILD: ${msg}\n`);
}
- for (let d of allDocShells) {
- let win = d.contentViewer.DOMDocument.defaultView;
- let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- try {
- winUtils.loadSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET);
- } catch(e) { }
- }
-
- flushStyle();
-}
-
-function restoreScrollbars() {
- let allDocShells = [docShell];
- for (let i = 0; i < docShell.childCount; i++) {
- allDocShells.push(docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell));
- }
- for (let d of allDocShells) {
- let win = d.contentViewer.DOMDocument.defaultView;
- let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- try {
- winUtils.removeSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET);
- } catch(e) { }
- }
- flushStyle();
-}
-
-function flushStyle() {
- // Force presContext destruction
- let isSticky = docShell.contentViewer.sticky;
- 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;
- canvas.mozOpaque = true;
- canvas.width = width;
- canvas.height = height;
- let ctx = canvas.getContext("2d");
- 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) {
+ function startResponsiveMode({data:data}) {
+ debug("START");
+ if (active) {
return;
}
- makeScrollbarsFloating();
- },
- QueryInterface: function QueryInterface(aIID) {
- if (aIID.equals(Ci.nsIWebProgressListener) ||
- aIID.equals(Ci.nsISupportsWeakReference) ||
- aIID.equals(Ci.nsISupports)) {
- return this;
+ addMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
+ let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
+ webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_ALL);
+ docShell.deviceSizeIsPageSize = true;
+ gRequiresFloatingScrollbars = data.requiresFloatingScrollbars;
+ if (data.notifyOnResize) {
+ startOnResize();
}
- throw Components.results.NS_ERROR_NO_INTERFACE;
- }
-};
+ // At this point, a content viewer might not be loaded for this
+ // docshell. makeScrollbarsFloating will be triggered by onLocationChange.
+ if (docShell.contentViewer) {
+ makeScrollbarsFloating();
+ }
+ active = true;
+ sendAsyncMessage("ResponsiveMode:Start:Done");
+ }
+
+ function onResize() {
+ let { width, height } = content.screen;
+ debug(`EMIT RESIZE: ${width} x ${height}`);
+ sendAsyncMessage("ResponsiveMode:OnContentResize", {
+ width,
+ height,
+ });
+ }
+
+ function bindOnResize() {
+ content.addEventListener("resize", onResize, false);
+ }
+
+ function startOnResize() {
+ debug("START ON RESIZE");
+ if (resizeNotifications) {
+ return;
+ }
+ resizeNotifications = true;
+ bindOnResize();
+ addEventListener("DOMWindowCreated", bindOnResize, false);
+ }
+
+ function stopOnResize() {
+ debug("STOP ON RESIZE");
+ if (!resizeNotifications) {
+ return;
+ }
+ resizeNotifications = false;
+ content.removeEventListener("resize", onResize, false);
+ removeEventListener("DOMWindowCreated", bindOnResize, false);
+ }
+
+ function stopResponsiveMode() {
+ debug("STOP");
+ if (!active) {
+ return;
+ }
+ active = false;
+ removeMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
+ let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
+ webProgress.removeProgressListener(WebProgressListener);
+ docShell.deviceSizeIsPageSize = gDeviceSizeWasPageSize;
+ restoreScrollbars();
+ stopOnResize();
+ sendAsyncMessage("ResponsiveMode:Stop:Done");
+ }
+
+ function makeScrollbarsFloating() {
+ if (!gRequiresFloatingScrollbars) {
+ return;
+ }
+
+ let allDocShells = [docShell];
+
+ for (let i = 0; i < docShell.childCount; i++) {
+ let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell);
+ allDocShells.push(child);
+ }
+
+ for (let d of allDocShells) {
+ let win = d.contentViewer.DOMDocument.defaultView;
+ let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+ try {
+ winUtils.loadSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET);
+ } catch(e) { }
+ }
+
+ flushStyle();
+ }
+
+ function restoreScrollbars() {
+ let allDocShells = [docShell];
+ for (let i = 0; i < docShell.childCount; i++) {
+ allDocShells.push(docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell));
+ }
+ for (let d of allDocShells) {
+ let win = d.contentViewer.DOMDocument.defaultView;
+ let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+ try {
+ winUtils.removeSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET);
+ } catch(e) { }
+ }
+ flushStyle();
+ }
+
+ function flushStyle() {
+ // Force presContext destruction
+ let isSticky = docShell.contentViewer.sticky;
+ 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;
+ canvas.mozOpaque = true;
+ canvas.width = width;
+ canvas.height = height;
+ let ctx = canvas.getContext("2d");
+ 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;
+ }
+ makeScrollbarsFloating();
+ },
+ QueryInterface: function QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ };
+})();
+
+global.responsiveFrameScriptLoaded = true;
sendAsyncMessage("ResponsiveMode:ChildScriptReady");
diff --git a/devtools/client/responsivedesign/responsivedesign.jsm b/devtools/client/responsivedesign/responsivedesign.jsm
index c46765caa54c..367f3b7e139d 100644
--- a/devtools/client/responsivedesign/responsivedesign.jsm
+++ b/devtools/client/responsivedesign/responsivedesign.jsm
@@ -21,6 +21,7 @@ var { showDoorhanger } = require("devtools/client/shared/doorhanger");
var { TouchEventSimulator } = require("devtools/shared/touch/simulator");
var { Task } = require("resource://gre/modules/Task.jsm");
var promise = require("promise");
+var DevToolsUtils = require("devtools/shared/DevToolsUtils");
this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
@@ -37,6 +38,10 @@ const INPUT_PARSER = /(\d+)[^\d]+(\d+)/;
const SHARED_L10N = new ViewHelpers.L10N("chrome://devtools/locale/shared.properties");
+function debug(msg) {
+ // dump(`RDM UI: ${msg}\n`);
+}
+
var ActiveTabs = new Map();
var Manager = {
@@ -92,25 +97,27 @@ var Manager = {
* @param aCommand the command name.
* @param aArgs command arguments.
*/
- handleGcliCommand: function(aWindow, aTab, aCommand, aArgs) {
+ handleGcliCommand: Task.async(function*(aWindow, aTab, aCommand, aArgs) {
switch (aCommand) {
case "resize to":
this.runIfNeeded(aWindow, aTab);
- ActiveTabs.get(aTab).setSize(aArgs.width, aArgs.height);
+ let ui = ActiveTabs.get(aTab);
+ yield ui.inited;
+ ui.setSize(aArgs.width, aArgs.height);
break;
case "resize on":
this.runIfNeeded(aWindow, aTab);
break;
case "resize off":
if (this.isActiveForTab(aTab)) {
- ActiveTabs.get(aTab).close();
+ yield ActiveTabs.get(aTab).close();
}
break;
case "resize toggle":
- this.toggle(aWindow, aTab);
+ this.toggle(aWindow, aTab);
default:
}
- }
+ })
}
EventEmitter.decorate(Manager);
@@ -126,7 +133,7 @@ if (Services.prefs.getBoolPref("devtools.responsive.html.enabled")) {
this.ResponsiveUIManager = Manager;
}
-var presets = [
+var defaultPresets = [
// Phones
{key: "320x480", width: 320, height: 480}, // iPhone, B2G, with
{key: "360x640", width: 360, height: 640}, // Android 4, phones, with
@@ -155,59 +162,6 @@ function ResponsiveUI(aWindow, aTab)
this.stack = this.container.querySelector(".browserStack");
this._telemetry = new Telemetry();
- let childOn = () => {
- this.mm.removeMessageListener("ResponsiveMode:Start:Done", childOn);
- ResponsiveUIManager.emit("on", { tab: this.tab });
- }
- this.mm.addMessageListener("ResponsiveMode:Start:Done", childOn);
-
- let requiresFloatingScrollbars = !this.mainWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
- this.mm.loadFrameScript("resource://devtools/client/responsivedesign/responsivedesign-child.js", true);
- this.mm.addMessageListener("ResponsiveMode:ChildScriptReady", () => {
- this.mm.sendAsyncMessage("ResponsiveMode:Start", {
- requiresFloatingScrollbars: requiresFloatingScrollbars
- });
- });
-
- // Try to load presets from prefs
- if (Services.prefs.prefHasUserValue("devtools.responsiveUI.presets")) {
- try {
- presets = JSON.parse(Services.prefs.getCharPref("devtools.responsiveUI.presets"));
- } catch(e) {
- // User pref is malformated.
- Cu.reportError("Could not parse pref `devtools.responsiveUI.presets`: " + e);
- }
- }
-
- this.customPreset = {key: "custom", custom: true};
-
- if (Array.isArray(presets)) {
- this.presets = [this.customPreset].concat(presets);
- } else {
- Cu.reportError("Presets value (devtools.responsiveUI.presets) is malformated.");
- this.presets = [this.customPreset];
- }
-
- try {
- let width = Services.prefs.getIntPref("devtools.responsiveUI.customWidth");
- let height = Services.prefs.getIntPref("devtools.responsiveUI.customHeight");
- this.customPreset.width = Math.min(MAX_WIDTH, width);
- this.customPreset.height = Math.min(MAX_HEIGHT, height);
-
- this.currentPresetKey = Services.prefs.getCharPref("devtools.responsiveUI.currentPreset");
- } catch(e) {
- // Default size. The first preset (custom) is the one that will be used.
- let bbox = this.stack.getBoundingClientRect();
-
- this.customPreset.width = bbox.width - 40; // horizontal padding of the container
- this.customPreset.height = bbox.height - 80; // vertical padding + toolbar height
-
- this.currentPresetKey = this.presets[1].key; // most common preset
- }
-
- this.container.setAttribute("responsivemode", "true");
- this.stack.setAttribute("responsivemode", "true");
-
// Let's bind some callbacks.
this.bound_presetSelected = this.presetSelected.bind(this);
this.bound_handleManualInput = this.handleManualInput.bind(this);
@@ -220,34 +174,14 @@ function ResponsiveUI(aWindow, aTab)
this.bound_startResizing = this.startResizing.bind(this);
this.bound_stopResizing = this.stopResizing.bind(this);
this.bound_onDrag = this.onDrag.bind(this);
+ this.bound_onContentResize = this.onContentResize.bind(this);
- // Events
- this.tab.addEventListener("TabClose", this);
- this.tabContainer.addEventListener("TabSelect", this);
+ this.mm.addMessageListener("ResponsiveMode:OnContentResize",
+ this.bound_onContentResize);
- this.buildUI();
- this.checkMenus();
+ ActiveTabs.set(this.tab, this);
- try {
- if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
- this.rotate();
- }
- } catch(e) {}
-
- ActiveTabs.set(aTab, this);
-
- this._telemetry.toolOpened("responsive");
-
- // Touch events support
- this.touchEnableBefore = false;
- this.touchEventSimulator = new TouchEventSimulator(this.browser);
-
- // Hook to display promotional Developer Edition doorhanger. Only displayed once.
- showDoorhanger({
- window: this.mainWindow,
- type: "deveditionpromo",
- anchor: this.chromeDoc.querySelector("#content")
- });
+ this.inited = this.init();
}
ResponsiveUI.prototype = {
@@ -264,22 +198,131 @@ ResponsiveUI.prototype = {
}
},
+ init: Task.async(function*() {
+ debug("INIT BEGINS");
+
+ let ready = this.waitForMessage("ResponsiveMode:ChildScriptReady");
+ this.mm.loadFrameScript("resource://devtools/client/responsivedesign/responsivedesign-child.js", true);
+ yield ready;
+
+ let requiresFloatingScrollbars =
+ !this.mainWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
+ let started = this.waitForMessage("ResponsiveMode:Start:Done");
+ debug("SEND START");
+ this.mm.sendAsyncMessage("ResponsiveMode:Start", {
+ requiresFloatingScrollbars,
+ // Tests expect events on resize to yield on various size changes
+ notifyOnResize: DevToolsUtils.testing,
+ });
+ yield started;
+
+ // Load Presets
+ this.loadPresets();
+
+ // Events
+ this.tab.addEventListener("TabClose", this);
+ this.tabContainer.addEventListener("TabSelect", this);
+
+ // Setup the UI
+ this.container.setAttribute("responsivemode", "true");
+ this.stack.setAttribute("responsivemode", "true");
+ this.buildUI();
+ this.checkMenus();
+
+ // Rotate the responsive mode if needed
+ try {
+ if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
+ this.rotate();
+ }
+ } catch(e) {}
+
+ // Touch events support
+ this.touchEnableBefore = false;
+ this.touchEventSimulator = new TouchEventSimulator(this.browser);
+
+ // Hook to display promotional Developer Edition doorhanger.
+ // Only displayed once.
+ showDoorhanger({
+ window: this.mainWindow,
+ type: "deveditionpromo",
+ anchor: this.chromeDoc.querySelector("#content")
+ });
+
+ // Notify that responsive mode is on.
+ this._telemetry.toolOpened("responsive");
+ ResponsiveUIManager.emit("on", { tab: this.tab });
+ }),
+
+ loadPresets: function() {
+ // Try to load presets from prefs
+ let presets = defaultPresets;
+ if (Services.prefs.prefHasUserValue("devtools.responsiveUI.presets")) {
+ try {
+ presets = JSON.parse(Services.prefs.getCharPref("devtools.responsiveUI.presets"));
+ } catch(e) {
+ // User pref is malformated.
+ Cu.reportError("Could not parse pref `devtools.responsiveUI.presets`: " + e);
+ }
+ }
+
+ this.customPreset = {key: "custom", custom: true};
+
+ if (Array.isArray(presets)) {
+ this.presets = [this.customPreset].concat(presets);
+ } else {
+ Cu.reportError("Presets value (devtools.responsiveUI.presets) is malformated.");
+ this.presets = [this.customPreset];
+ }
+
+ try {
+ let width = Services.prefs.getIntPref("devtools.responsiveUI.customWidth");
+ let height = Services.prefs.getIntPref("devtools.responsiveUI.customHeight");
+ this.customPreset.width = Math.min(MAX_WIDTH, width);
+ this.customPreset.height = Math.min(MAX_HEIGHT, height);
+
+ this.currentPresetKey = Services.prefs.getCharPref("devtools.responsiveUI.currentPreset");
+ } catch(e) {
+ // Default size. The first preset (custom) is the one that will be used.
+ let bbox = this.stack.getBoundingClientRect();
+
+ this.customPreset.width = bbox.width - 40; // horizontal padding of the container
+ this.customPreset.height = bbox.height - 80; // vertical padding + toolbar height
+
+ this.currentPresetKey = this.presets[1].key; // most common preset
+ }
+ },
+
/**
* Destroy the nodes. Remove listeners. Reset the style.
*/
- close: function RUI_close() {
- if (this.closing)
+ close: Task.async(function*() {
+ debug("CLOSE BEGINS");
+ if (this.closing) {
+ debug("ALREADY CLOSING, ABORT");
return;
+ }
this.closing = true;
+ // If we're closing very fast (in tests), ensure init has finished.
+ debug("CLOSE: WAIT ON INITED");
+ yield this.inited;
+ debug("CLOSE: INITED DONE");
+
this.unCheckMenus();
// Reset style of the stack.
+ debug(`CURRENT SIZE: ${this.stack.getAttribute("style")}`);
let style = "max-width: none;" +
"min-width: 0;" +
"max-height: none;" +
"min-height: 0;";
+ debug("RESET STACK SIZE");
this.stack.setAttribute("style", style);
+ // Wait for resize message before stopping in the child when testing
+ if (DevToolsUtils.testing) {
+ yield this.waitForMessage("ResponsiveMode:OnContentResize");
+ }
+
if (this.isResizing)
this.stopResizing();
@@ -316,35 +359,33 @@ ResponsiveUI.prototype = {
this.touchEventSimulator.stop();
}
this._telemetry.toolClosed("responsive");
- let childOff = () => {
- this.mm.removeMessageListener("ResponsiveMode:Stop:Done", childOff);
- ResponsiveUIManager.emit("off", { tab: this.tab });
- }
- this.mm.addMessageListener("ResponsiveMode:Stop:Done", childOff);
+ let stopped = this.waitForMessage("ResponsiveMode:Stop:Done");
this.tab.linkedBrowser.messageManager.sendAsyncMessage("ResponsiveMode:Stop");
+ yield stopped;
+
+ this.inited = null;
+ ResponsiveUIManager.emit("off", { tab: this.tab });
+ }),
+
+ waitForMessage(message) {
+ return new Promise(resolve => {
+ let listener = () => {
+ this.mm.removeMessageListener(message, listener);
+ resolve();
+ };
+ this.mm.addMessageListener(message, listener);
+ });
},
/**
- * Notify when the content has been resized. Only used in tests.
+ * Emit an event when the content has been resized. Only used in tests.
*/
- _test_notifyOnResize: function() {
- let deferred = promise.defer();
- let mm = this.mm;
-
- this.bound_onContentResize = this.onContentResize.bind(this);
-
- mm.addMessageListener("ResponsiveMode:OnContentResize", this.bound_onContentResize);
-
- mm.sendAsyncMessage("ResponsiveMode:NotifyOnResize");
- mm.addMessageListener("ResponsiveMode:NotifyOnResize:Done", function onListeningResize() {
- mm.removeMessageListener("ResponsiveMode:NotifyOnResize:Done", onListeningResize);
- deferred.resolve();
+ onContentResize: function(msg) {
+ ResponsiveUIManager.emit("contentResize", {
+ tab: this.tab,
+ width: msg.data.width,
+ height: msg.data.height,
});
- return deferred.promise;
- },
-
- onContentResize: function() {
- ResponsiveUIManager.emit("contentResize", { tab: this.tab });
},
/**
@@ -863,6 +904,18 @@ ResponsiveUI.prototype = {
}
}),
+ /**
+ * Get the current width and height.
+ */
+ getSize() {
+ let width = Number(this.stack.style.minWidth.replace("px", ""));
+ let height = Number(this.stack.style.minHeight.replace("px", ""));
+ return {
+ width,
+ height,
+ };
+ },
+
/**
* Change the size of the browser.
*
@@ -870,6 +923,7 @@ ResponsiveUI.prototype = {
* @param aHeight height of the browser.
*/
setSize: function RUI_setSize(aWidth, aHeight) {
+ debug(`SET SIZE TO ${aWidth} x ${aHeight}`);
this.setWidth(aWidth);
this.setHeight(aHeight);
},
diff --git a/devtools/client/responsivedesign/test/browser_responsive_cmd.js b/devtools/client/responsivedesign/test/browser_responsive_cmd.js
index 0a5c4ba4b09f..2345a754cea7 100644
--- a/devtools/client/responsivedesign/test/browser_responsive_cmd.js
+++ b/devtools/client/responsivedesign/test/browser_responsive_cmd.js
@@ -11,6 +11,9 @@
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
function test() {
+ let manager = ResponsiveUI.ResponsiveUIManager;
+ let done;
+
function isOpen() {
return gBrowser.getBrowserContainer(gBrowser.selectedBrowser)
.hasAttribute("responsivemode");
@@ -19,7 +22,10 @@ function test() {
helpers.addTabWithToolbar("data:text/html;charset=utf-8,hi", (options) => {
return helpers.audit(options, [
{
- setup: "resize toggle",
+ setup() {
+ done = once(manager, "on");
+ return helpers.setInput(options, "resize toggle");
+ },
check: {
input: "resize toggle",
hints: "",
@@ -29,12 +35,16 @@ function test() {
exec: {
output: ""
},
- post: function() {
+ post: Task.async(function*() {
+ yield done;
ok(isOpen(), "responsive mode is open");
- },
+ }),
},
{
- setup: "resize toggle",
+ setup() {
+ done = once(manager, "off");
+ return helpers.setInput(options, "resize toggle");
+ },
check: {
input: "resize toggle",
hints: "",
@@ -44,12 +54,16 @@ function test() {
exec: {
output: ""
},
- post: function() {
+ post: Task.async(function*() {
+ yield done;
ok(!isOpen(), "responsive mode is closed");
- },
+ }),
},
{
- setup: "resize on",
+ setup() {
+ done = once(manager, "on");
+ return helpers.setInput(options, "resize on");
+ },
check: {
input: "resize on",
hints: "",
@@ -59,12 +73,16 @@ function test() {
exec: {
output: ""
},
- post: function() {
+ post: Task.async(function*() {
+ yield done;
ok(isOpen(), "responsive mode is open");
- },
+ }),
},
{
- setup: "resize off",
+ setup() {
+ done = once(manager, "off");
+ return helpers.setInput(options, "resize off");
+ },
check: {
input: "resize off",
hints: "",
@@ -74,12 +92,16 @@ function test() {
exec: {
output: ""
},
- post: function() {
+ post: Task.async(function*() {
+ yield done;
ok(!isOpen(), "responsive mode is closed");
- },
+ }),
},
{
- setup: "resize to 400 400",
+ setup() {
+ done = once(manager, "on");
+ return helpers.setInput(options, "resize to 400 400");
+ },
check: {
input: "resize to 400 400",
hints: "",
@@ -93,12 +115,16 @@ function test() {
exec: {
output: ""
},
- post: function() {
+ post: Task.async(function*() {
+ yield done;
ok(isOpen(), "responsive mode is open");
- },
+ }),
},
{
- setup: "resize off",
+ setup() {
+ done = once(manager, "off");
+ return helpers.setInput(options, "resize off");
+ },
check: {
input: "resize off",
hints: "",
@@ -108,9 +134,10 @@ function test() {
exec: {
output: ""
},
- post: function() {
+ post: Task.async(function*() {
+ yield done;
ok(!isOpen(), "responsive mode is closed");
- },
+ }),
},
]);
}).then(finish);
diff --git a/devtools/client/responsivedesign/test/browser_responsive_devicewidth.js b/devtools/client/responsivedesign/test/browser_responsive_devicewidth.js
index 67b3e3bc815b..5795275164e7 100644
--- a/devtools/client/responsivedesign/test/browser_responsive_devicewidth.js
+++ b/devtools/client/responsivedesign/test/browser_responsive_devicewidth.js
@@ -5,15 +5,15 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(function*() {
let tab = yield addTab("about:logo");
- let {rdm} = yield openRDM(tab);
+ let { rdm, manager } = yield openRDM(tab);
ok(rdm, "An instance of the RDM should be attached to the tab.");
- rdm.setSize(110, 500);
+ yield setSize(rdm, manager, 110, 500);
info("Checking initial width/height properties.");
yield doInitialChecks();
info("Changing the RDM size");
- rdm.setSize(90, 500);
+ yield setSize(rdm, manager, 90, 500);
info("Checking for screen props");
yield checkScreenProps();
diff --git a/devtools/client/responsivedesign/test/browser_responsivecomputedview.js b/devtools/client/responsivedesign/test/browser_responsivecomputedview.js
index 70fb04821498..67ac831a0eda 100644
--- a/devtools/client/responsivedesign/test/browser_responsivecomputedview.js
+++ b/devtools/client/responsivedesign/test/browser_responsivecomputedview.js
@@ -22,36 +22,36 @@ add_task(function*() {
yield addTab(TEST_URI);
info("Open the responsive design mode and set its size to 500x500 to start");
- let {rdm} = yield openRDM();
- rdm.setSize(500, 500);
+ let { rdm, manager } = yield openRDM();
+ yield setSize(rdm, manager, 500, 500);
info("Open the inspector, computed-view and select the test node");
let {inspector, view} = yield openComputedView();
yield selectNode("div", inspector);
info("Try shrinking the viewport and checking the applied styles");
- yield testShrink(view, inspector, rdm);
+ yield testShrink(view, inspector, rdm, manager);
info("Try growing the viewport and checking the applied styles");
- yield testGrow(view, inspector, rdm);
+ yield testGrow(view, inspector, rdm, manager);
yield closeRDM(rdm);
yield closeToolbox();
});
-function* testShrink(computedView, inspector, rdm) {
+function* testShrink(computedView, inspector, rdm, manager) {
is(computedWidth(computedView), "500px", "Should show 500px initially.");
let onRefresh = inspector.once("computed-view-refreshed");
- rdm.setSize(100, 100);
+ yield setSize(rdm, manager, 100, 100);
yield onRefresh;
is(computedWidth(computedView), "100px", "Should be 100px after shrinking.");
}
-function* testGrow(computedView, inspector, rdm) {
+function* testGrow(computedView, inspector, rdm, manager) {
let onRefresh = inspector.once("computed-view-refreshed");
- rdm.setSize(500, 500);
+ yield setSize(rdm, manager, 500, 500);
yield onRefresh;
is(computedWidth(computedView), "500px", "Should be 500px after growing.");
diff --git a/devtools/client/responsivedesign/test/browser_responsiveruleview.js b/devtools/client/responsivedesign/test/browser_responsiveruleview.js
index d6640710ee8f..2de0bbb44ef9 100644
--- a/devtools/client/responsivedesign/test/browser_responsiveruleview.js
+++ b/devtools/client/responsivedesign/test/browser_responsiveruleview.js
@@ -25,18 +25,18 @@ add_task(function*() {
yield addTab(TEST_URI);
info("Open the responsive design mode and set its size to 500x500 to start");
- let {rdm} = yield openRDM();
- rdm.setSize(500, 500);
+ let { rdm, manager } = yield openRDM();
+ yield setSize(rdm, manager, 500, 500);
info("Open the inspector, rule-view and select the test node");
let {inspector, view} = yield openRuleView();
yield selectNode("div", inspector);
info("Try shrinking the viewport and checking the applied styles");
- yield testShrink(view, rdm);
+ yield testShrink(view, rdm, manager);
info("Try growing the viewport and checking the applied styles");
- yield testGrow(view, rdm);
+ yield testGrow(view, rdm, manager);
info("Check that ESC still opens the split console");
yield testEscapeOpensSplitConsole(inspector);
@@ -49,21 +49,21 @@ add_task(function*() {
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
});
-function* testShrink(ruleView, rdm) {
+function* testShrink(ruleView, rdm, manager) {
is(numberOfRules(ruleView), 2, "Should have two rules initially.");
info("Resize to 100x100 and wait for the rule-view to update");
let onRefresh = ruleView.once("ruleview-refreshed");
- rdm.setSize(100, 100);
+ yield setSize(rdm, manager, 100, 100);
yield onRefresh;
is(numberOfRules(ruleView), 3, "Should have three rules after shrinking.");
}
-function* testGrow(ruleView, rdm) {
+function* testGrow(ruleView, rdm, manager) {
info("Resize to 500x500 and wait for the rule-view to update");
let onRefresh = ruleView.once("ruleview-refreshed");
- rdm.setSize(500, 500);
+ yield setSize(rdm, manager, 500, 500);
yield onRefresh;
is(numberOfRules(ruleView), 2, "Should have two rules after growing.");
diff --git a/devtools/client/responsivedesign/test/browser_responsiveui.js b/devtools/client/responsivedesign/test/browser_responsiveui.js
index 2394df39dd70..3cd4a77112ba 100644
--- a/devtools/client/responsivedesign/test/browser_responsiveui.js
+++ b/devtools/client/responsivedesign/test/browser_responsiveui.js
@@ -4,7 +4,6 @@
"use strict";
add_task(function*() {
- SimpleTest.requestCompleteLog();
let tab = yield addTab("data:text/html,mop");
let {rdm, manager} = yield openRDM(tab, "menu");
@@ -26,9 +25,6 @@ add_task(function*() {
let newWidth = (yield getSizing()).width;
is(originalWidth, newWidth, "Floating scrollbars shouldn't change the width");
- yield rdm._test_notifyOnResize();
- yield waitForTick();
-
yield testPresets(rdm, manager);
info("Testing mouse resizing");
@@ -53,7 +49,10 @@ add_task(function*() {
info("Restarting responsive mode");
yield closeRDM(rdm);
+
+ let resized = waitForResizeTo(manager, widthBeforeClose, heightBeforeClose);
({rdm} = yield openRDM(tab, "keyboard"));
+ yield resized;
let currentSize = yield getSizing();
is(currentSize.width, widthBeforeClose, "width should be restored");
@@ -80,9 +79,7 @@ function* testPresets(rdm, manager) {
for (let c = rdm.menulist.firstChild.childNodes.length - 4; c >= 0; c--) {
let item = rdm.menulist.firstChild.childNodes[c];
let [width, height] = extractSizeFromString(item.getAttribute("label"));
- let onContentResize = once(manager, "contentResize");
- rdm.menulist.selectedIndex = c;
- yield onContentResize;
+ yield setPresetIndex(rdm, manager, c);
let {width: contentWidth, height: contentHeight} = yield getSizing();
is(contentWidth, width, "preset" + c + ": the width should be changed");
@@ -91,8 +88,7 @@ function* testPresets(rdm, manager) {
}
function* testManualMouseResize(rdm, manager, pressedKey) {
- rdm.setSize(100, 100);
- yield once(manager, "contentResize");
+ yield setSize(rdm, manager, 100, 100);
let {width: initialWidth, height: initialHeight} = yield getSizing();
is(initialWidth, 100, "Width should be reset to 100");
@@ -171,8 +167,7 @@ function* testInvalidUserInput(rdm) {
}
function* testRotate(rdm, manager) {
- rdm.setSize(100, 200);
- yield once(manager, "contentResize");
+ yield setSize(rdm, manager, 100, 200);
let {width: initialWidth, height: initialHeight} = yield getSizing();
rdm.rotate();
diff --git a/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js b/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
index 01d402cc6eba..21f205e1f788 100644
--- a/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
+++ b/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
@@ -6,7 +6,7 @@
add_task(function*() {
let tab = yield addTab("data:text/html;charset=utf8,Test RDM custom presets");
- let {rdm} = yield openRDM(tab);
+ let { rdm, manager } = yield openRDM(tab);
let oldPrompt = Services.prompt;
Services.prompt = {
@@ -29,8 +29,6 @@ add_task(function*() {
ok(rdm, "RDM instance should be attached to the tab.");
- yield rdm._test_notifyOnResize();
-
// Tries to add a custom preset and cancel the prompt
let idx = rdm.menulist.selectedIndex;
let presetCount = rdm.presets.length;
@@ -48,35 +46,27 @@ add_task(function*() {
Services.prompt.value = "Testing preset";
Services.prompt.returnBool = true;
+ let resized = once(manager, "contentResize");
let customHeight = 123, customWidth = 456;
rdm.startResizing({});
rdm.setSize(customWidth, customHeight);
rdm.stopResizing({});
rdm.addbutton.doCommand();
-
- // Force document reflow to avoid intermittent failures.
- info("document height " + document.height);
+ yield resized;
yield closeRDM(rdm);
- // We're still in the loop of initializing the responsive mode.
- // Let's wait next loop to stop it.
- yield waitForTick();
-
({rdm} = yield openRDM(tab));
is(container.getAttribute("responsivemode"), "true",
"Should be in responsive mode.");
let presetLabel = "456" + "\u00D7" + "123 (Testing preset)";
- let customPresetIndex = getPresetIndex(rdm, presetLabel);
- info(customPresetIndex);
+ let customPresetIndex = yield getPresetIndex(rdm, manager, presetLabel);
ok(customPresetIndex >= 0, "(idx = " + customPresetIndex + ") should be the" +
" previously added preset in the list of items");
- let resizePromise = rdm._test_notifyOnResize();
- rdm.menulist.selectedIndex = customPresetIndex;
- yield resizePromise;
+ yield setPresetIndex(rdm, manager, customPresetIndex);
let browser = gBrowser.selectedBrowser;
let props = yield ContentTask.spawn(browser, {}, function*() {
@@ -87,43 +77,44 @@ add_task(function*() {
is(props.innerWidth, 456, "Selecting preset should change the width");
is(props.innerHeight, 123, "Selecting preset should change the height");
+ info(`menulist count: ${rdm.menulist.itemCount}`)
+
rdm.removebutton.doCommand();
- rdm.menulist.selectedIndex = 2;
+ yield setPresetIndex(rdm, manager, 2);
let deletedPresetA = rdm.menulist.selectedItem.getAttribute("label");
rdm.removebutton.doCommand();
- rdm.menulist.selectedIndex = 2;
+ yield setPresetIndex(rdm, manager, 2);
let deletedPresetB = rdm.menulist.selectedItem.getAttribute("label");
rdm.removebutton.doCommand();
yield closeRDM(rdm);
- yield waitForTick();
({rdm} = yield openRDM(tab));
- customPresetIndex = getPresetIndex(rdm, deletedPresetA);
+ customPresetIndex = yield getPresetIndex(rdm, manager, deletedPresetA);
is(customPresetIndex, -1,
"Deleted preset " + deletedPresetA + " should not be in the list anymore");
- customPresetIndex = getPresetIndex(rdm, deletedPresetB);
+ customPresetIndex = yield getPresetIndex(rdm, manager, deletedPresetB);
is(customPresetIndex, -1,
"Deleted preset " + deletedPresetB + " should not be in the list anymore");
yield closeRDM(rdm);
});
-function getPresetIndex(rdm, presetLabel) {
- function testOnePreset(c) {
+var getPresetIndex = Task.async(function*(rdm, manager, presetLabel) {
+ var testOnePreset = Task.async(function*(c) {
if (c == 0) {
return -1;
}
- rdm.menulist.selectedIndex = c;
+ yield setPresetIndex(rdm, manager, c);
let item = rdm.menulist.firstChild.childNodes[c];
if (item.getAttribute("label") === presetLabel) {
return c;
}
return testOnePreset(c - 1);
- }
+ });
return testOnePreset(rdm.menulist.firstChild.childNodes.length - 4);
-}
+});
diff --git a/devtools/client/responsivedesign/test/head.js b/devtools/client/responsivedesign/test/head.js
index 6481a4713552..d22f5c9c7364 100644
--- a/devtools/client/responsivedesign/test/head.js
+++ b/devtools/client/responsivedesign/test/head.js
@@ -15,11 +15,15 @@ Services.scriptloader.loadSubScript(gcliHelpersURI, this);
DevToolsUtils.testing = true;
registerCleanupFunction(() => {
DevToolsUtils.testing = false;
- while (gBrowser.tabs.length > 1) {
- gBrowser.removeCurrentTab();
- }
+ Services.prefs.clearUserPref("devtools.responsiveUI.currentPreset");
+ Services.prefs.clearUserPref("devtools.responsiveUI.customHeight");
+ Services.prefs.clearUserPref("devtools.responsiveUI.customWidth");
+ Services.prefs.clearUserPref("devtools.responsiveUI.presets");
+ Services.prefs.clearUserPref("devtools.responsiveUI.rotate");
});
+SimpleTest.requestCompleteLog();
+
/**
* Open the Responsive Design Mode
* @param {Tab} The browser tab to open it into (defaults to the selected tab).
@@ -29,19 +33,26 @@ registerCleanupFunction(() => {
var openRDM = Task.async(function*(tab = gBrowser.selectedTab,
method = "menu") {
let manager = ResponsiveUI.ResponsiveUIManager;
- let mgrOn = once(manager, "on");
+
+ let opened = once(manager, "on");
+ let resized = once(manager, "contentResize");
if (method == "menu") {
document.getElementById("Tools:ResponsiveUI").doCommand();
} else {
synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
}
- yield mgrOn;
+ yield opened;
let rdm = manager.getResponsiveUIForTab(tab);
rdm.transitionsEnabled = false;
registerCleanupFunction(() => {
rdm.transitionsEnabled = true;
});
+
+ // Wait for content to resize. This is triggered async by the preset menu
+ // auto-selecting its default entry once it's in the document.
+ yield resized;
+
return {rdm, manager};
});
@@ -50,13 +61,15 @@ var openRDM = Task.async(function*(tab = gBrowser.selectedTab,
* @param {rdm} ResponsiveUI instance for the tab
*/
var closeRDM = Task.async(function*(rdm) {
- let mgr = ResponsiveUI.ResponsiveUIManager;
+ let manager = ResponsiveUI.ResponsiveUIManager;
if (!rdm) {
- rdm = mgr.getResponsiveUIForTab(gBrowser.selectedTab);
+ rdm = manager.getResponsiveUIForTab(gBrowser.selectedTab);
}
- let mgrOff = mgr.once("off");
+ let closed = once(manager, "off");
+ let resized = once(manager, "contentResize");
rdm.close();
- yield mgrOff;
+ yield resized;
+ yield closed;
});
/**
@@ -252,3 +265,38 @@ var selectNode = Task.async(function*(selector, inspector, reason = "test") {
inspector.selection.setNodeFront(nodeFront, reason);
yield updated;
});
+
+function waitForResizeTo(manager, width, height) {
+ return new Promise(resolve => {
+ let onResize = (_, data) => {
+ if (data.width != width || data.height != height) {
+ return;
+ }
+ manager.off("contentResize", onResize);
+ info(`Got contentResize to ${width} x ${height}`);
+ resolve();
+ };
+ info(`Waiting for contentResize to ${width} x ${height}`);
+ manager.on("contentResize", onResize);
+ });
+}
+
+var setPresetIndex = Task.async(function*(rdm, manager, index) {
+ info(`Current preset: ${rdm.menulist.selectedIndex}, change to: ${index}`);
+ if (rdm.menulist.selectedIndex != index) {
+ let resized = once(manager, "contentResize");
+ rdm.menulist.selectedIndex = index;
+ yield resized;
+ }
+});
+
+var setSize = Task.async(function*(rdm, manager, width, height) {
+ let size = rdm.getSize();
+ info(`Current size: ${size.width} x ${size.height}, ` +
+ `set to: ${width} x ${height}`);
+ if (size.width != width || size.height != height) {
+ let resized = waitForResizeTo(manager, width, height);
+ rdm.setSize(width, height);
+ yield resized;
+ }
+});
diff --git a/devtools/client/shared/test/browser_telemetry_button_responsive.js b/devtools/client/shared/test/browser_telemetry_button_responsive.js
index e749cf09e087..f88eb47f2ba7 100644
--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js
+++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
@@ -35,28 +35,30 @@ function* testButton(toolbox, Telemetry) {
checkResults("_RESPONSIVE_", Telemetry);
}
-function delayedClicks(node, clicks) {
+function waitForToggle() {
return new Promise(resolve => {
- let clicked = 0;
-
- // See TOOL_DELAY for why we need setTimeout here
- setTimeout(function delayedClick() {
- info("Clicking button " + node.id);
- if (clicked >= clicks) {
- node.addEventListener("click", function listener() {
- node.removeEventListener("click", listener);
- resolve();
- });
- } else {
- setTimeout(delayedClick, TOOL_DELAY);
- }
-
- node.click();
- clicked++;
- }, TOOL_DELAY);
+ let handler = () => {
+ manager.off("on", handler);
+ manager.off("off", handler);
+ resolve();
+ };
+ let manager = ResponsiveUI.ResponsiveUIManager;
+ manager.on("on", handler);
+ manager.on("off", handler);
});
}
+var delayedClicks = Task.async(function*(node, clicks) {
+ for (let i = 0; i < clicks; i++) {
+ info("Clicking button " + node.id);
+ let toggled = waitForToggle();
+ node.click();
+ yield toggled;
+ // See TOOL_DELAY for why we need setTimeout here
+ yield DevToolsUtils.waitForTime(TOOL_DELAY);
+ }
+});
+
function checkResults(histIdFocus, Telemetry) {
let result = Telemetry.prototype.telemetryInfo;