summaryrefslogtreecommitdiffstats
path: root/devtools/client/responsivedesign/test
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /devtools/client/responsivedesign/test
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/client/responsivedesign/test')
-rw-r--r--devtools/client/responsivedesign/test/.eslintrc.js10
-rw-r--r--devtools/client/responsivedesign/test/browser.ini21
-rw-r--r--devtools/client/responsivedesign/test/browser_responsive_cmd.js143
-rw-r--r--devtools/client/responsivedesign/test/browser_responsive_devicewidth.js68
-rw-r--r--devtools/client/responsivedesign/test/browser_responsivecomputedview.js67
-rw-r--r--devtools/client/responsivedesign/test/browser_responsiveruleview.js95
-rw-r--r--devtools/client/responsivedesign/test/browser_responsiveui.js250
-rw-r--r--devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js56
-rw-r--r--devtools/client/responsivedesign/test/browser_responsiveui_touch.js148
-rw-r--r--devtools/client/responsivedesign/test/browser_responsiveui_window_close.js25
-rw-r--r--devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js121
-rw-r--r--devtools/client/responsivedesign/test/head.js302
-rw-r--r--devtools/client/responsivedesign/test/touch.html85
13 files changed, 1391 insertions, 0 deletions
diff --git a/devtools/client/responsivedesign/test/.eslintrc.js b/devtools/client/responsivedesign/test/.eslintrc.js
new file mode 100644
index 000000000..ba1263286
--- /dev/null
+++ b/devtools/client/responsivedesign/test/.eslintrc.js
@@ -0,0 +1,10 @@
+"use strict";
+
+module.exports = {
+ // Extend from the shared list of defined globals for mochitests.
+ "extends": "../../../.eslintrc.mochitests.js",
+ "globals": {
+ "ResponsiveUI": true,
+ "helpers": true
+ }
+};
diff --git a/devtools/client/responsivedesign/test/browser.ini b/devtools/client/responsivedesign/test/browser.ini
new file mode 100644
index 000000000..6a8f5a8d9
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+ head.js
+ touch.html
+ !/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
+
+[browser_responsive_cmd.js]
+[browser_responsivecomputedview.js]
+skip-if = e10s && debug # Bug 1252201 - Docshell leak on debug e10s
+[browser_responsiveruleview.js]
+skip-if = e10s && debug # Bug 1252201 - Docshell leak on debug e10s
+[browser_responsiveui.js]
+[browser_responsiveui_touch.js]
+[browser_responsiveuiaddcustompreset.js]
+[browser_responsive_devicewidth.js]
+[browser_responsiveui_customuseragent.js]
+[browser_responsiveui_window_close.js]
+skip-if = (os == 'linux') && e10s && debug # Bug 1277274
diff --git a/devtools/client/responsivedesign/test/browser_responsive_cmd.js b/devtools/client/responsivedesign/test/browser_responsive_cmd.js
new file mode 100644
index 000000000..8c8e798d0
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsive_cmd.js
@@ -0,0 +1,143 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+
+function test() {
+ let manager = ResponsiveUIManager;
+ let done;
+
+ function isOpen() {
+ return gBrowser.getBrowserContainer(gBrowser.selectedBrowser)
+ .hasAttribute("responsivemode");
+ }
+
+ helpers.addTabWithToolbar("data:text/html;charset=utf-8,hi", (options) => {
+ return helpers.audit(options, [
+ {
+ setup() {
+ done = once(manager, "on");
+ return helpers.setInput(options, "resize toggle");
+ },
+ check: {
+ input: "resize toggle",
+ hints: "",
+ markup: "VVVVVVVVVVVVV",
+ status: "VALID"
+ },
+ exec: {
+ output: ""
+ },
+ post: Task.async(function* () {
+ yield done;
+ ok(isOpen(), "responsive mode is open");
+ }),
+ },
+ {
+ setup() {
+ done = once(manager, "off");
+ return helpers.setInput(options, "resize toggle");
+ },
+ check: {
+ input: "resize toggle",
+ hints: "",
+ markup: "VVVVVVVVVVVVV",
+ status: "VALID"
+ },
+ exec: {
+ output: ""
+ },
+ post: Task.async(function* () {
+ yield done;
+ ok(!isOpen(), "responsive mode is closed");
+ }),
+ },
+ {
+ setup() {
+ done = once(manager, "on");
+ return helpers.setInput(options, "resize on");
+ },
+ check: {
+ input: "resize on",
+ hints: "",
+ markup: "VVVVVVVVV",
+ status: "VALID"
+ },
+ exec: {
+ output: ""
+ },
+ post: Task.async(function* () {
+ yield done;
+ ok(isOpen(), "responsive mode is open");
+ }),
+ },
+ {
+ setup() {
+ done = once(manager, "off");
+ return helpers.setInput(options, "resize off");
+ },
+ check: {
+ input: "resize off",
+ hints: "",
+ markup: "VVVVVVVVVV",
+ status: "VALID"
+ },
+ exec: {
+ output: ""
+ },
+ post: Task.async(function* () {
+ yield done;
+ ok(!isOpen(), "responsive mode is closed");
+ }),
+ },
+ {
+ setup() {
+ done = once(manager, "on");
+ return helpers.setInput(options, "resize to 400 400");
+ },
+ check: {
+ input: "resize to 400 400",
+ hints: "",
+ markup: "VVVVVVVVVVVVVVVVV",
+ status: "VALID",
+ args: {
+ width: { value: 400 },
+ height: { value: 400 },
+ }
+ },
+ exec: {
+ output: ""
+ },
+ post: Task.async(function* () {
+ yield done;
+ ok(isOpen(), "responsive mode is open");
+ }),
+ },
+ {
+ setup() {
+ done = once(manager, "off");
+ return helpers.setInput(options, "resize off");
+ },
+ check: {
+ input: "resize off",
+ hints: "",
+ markup: "VVVVVVVVVV",
+ status: "VALID"
+ },
+ exec: {
+ output: ""
+ },
+ 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
new file mode 100644
index 000000000..604a20783
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsive_devicewidth.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function* () {
+ let tab = yield addTab("about:logo");
+ let { rdm, manager } = yield openRDM(tab);
+ ok(rdm, "An instance of the RDM should be attached to the tab.");
+ yield setSize(rdm, manager, 110, 500);
+
+ info("Checking initial width/height properties.");
+ yield doInitialChecks();
+
+ info("Changing the RDM size");
+ yield setSize(rdm, manager, 90, 500);
+
+ info("Checking for screen props");
+ yield checkScreenProps();
+
+ info("Setting docShell.deviceSizeIsPageSize to false");
+ yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ docShell.deviceSizeIsPageSize = false;
+ });
+
+ info("Checking for screen props once again.");
+ yield checkScreenProps2();
+
+ yield closeRDM(rdm);
+});
+
+function* doInitialChecks() {
+ let {innerWidth, matchesMedia} = yield grabContentInfo();
+ is(innerWidth, 110, "initial width should be 110px");
+ ok(!matchesMedia, "media query shouldn't match.");
+}
+
+function* checkScreenProps() {
+ let {matchesMedia, screen} = yield grabContentInfo();
+ ok(matchesMedia, "media query should match");
+ isnot(window.screen.width, screen.width,
+ "screen.width should not be the size of the screen.");
+ is(screen.width, 90, "screen.width should be the page width");
+ is(screen.height, 500, "screen.height should be the page height");
+}
+
+function* checkScreenProps2() {
+ let {matchesMedia, screen} = yield grabContentInfo();
+ ok(!matchesMedia, "media query should be re-evaluated.");
+ is(window.screen.width, screen.width,
+ "screen.width should be the size of the screen.");
+}
+
+function grabContentInfo() {
+ return ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ return {
+ screen: {
+ width: content.screen.width,
+ height: content.screen.height
+ },
+ innerWidth: content.innerWidth,
+ matchesMedia: content.matchMedia("(max-device-width:100px)").matches
+ };
+ });
+}
diff --git a/devtools/client/responsivedesign/test/browser_responsivecomputedview.js b/devtools/client/responsivedesign/test/browser_responsivecomputedview.js
new file mode 100644
index 000000000..eee2dbc03
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsivecomputedview.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the computed-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+ "div {" +
+ " width: 500px;" +
+ " height: 10px;" +
+ " background: purple;" +
+ "} " +
+ "@media screen and (max-width: 200px) {" +
+ " div { " +
+ " width: 100px;" +
+ " }" +
+ "};" +
+ "</style><div></div></html>";
+
+add_task(function* () {
+ yield addTab(TEST_URI);
+
+ info("Open the responsive design mode and set its size to 500x500 to start");
+ 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, manager);
+
+ info("Try growing the viewport and checking the applied styles");
+ yield testGrow(view, inspector, rdm, manager);
+
+ yield closeRDM(rdm);
+ yield closeToolbox();
+});
+
+function* testShrink(computedView, inspector, rdm, manager) {
+ is(computedWidth(computedView), "500px", "Should show 500px initially.");
+
+ let onRefresh = inspector.once("computed-view-refreshed");
+ yield setSize(rdm, manager, 100, 100);
+ yield onRefresh;
+
+ is(computedWidth(computedView), "100px", "Should be 100px after shrinking.");
+}
+
+function* testGrow(computedView, inspector, rdm, manager) {
+ let onRefresh = inspector.once("computed-view-refreshed");
+ yield setSize(rdm, manager, 500, 500);
+ yield onRefresh;
+
+ is(computedWidth(computedView), "500px", "Should be 500px after growing.");
+}
+
+function computedWidth(computedView) {
+ for (let prop of computedView.propertyViews) {
+ if (prop.name === "width") {
+ return prop.valueNode.textContent;
+ }
+ }
+ return null;
+}
diff --git a/devtools/client/responsivedesign/test/browser_responsiveruleview.js b/devtools/client/responsivedesign/test/browser_responsiveruleview.js
new file mode 100644
index 000000000..5c3698e78
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsiveruleview.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the rule-view refreshes.
+// Also test that ESC does open the split-console, and that the RDM menu item
+// gets updated correctly when needed.
+// TODO: split this test.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+ "div {" +
+ " width: 500px;" +
+ " height: 10px;" +
+ " background: purple;" +
+ "} " +
+ "@media screen and (max-width: 200px) {" +
+ " div { " +
+ " width: 100px;" +
+ " }" +
+ "};" +
+ "</style><div></div></html>";
+
+add_task(function* () {
+ yield addTab(TEST_URI);
+
+ info("Open the responsive design mode and set its size to 500x500 to start");
+ 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, manager);
+
+ info("Try growing the viewport and checking the applied styles");
+ yield testGrow(view, rdm, manager);
+
+ info("Check that ESC still opens the split console");
+ yield testEscapeOpensSplitConsole(inspector);
+
+ yield closeToolbox();
+
+ info("Test the state of the RDM menu item");
+ yield testMenuItem(rdm);
+
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+});
+
+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");
+ yield setSize(rdm, manager, 100, 100);
+ yield onRefresh;
+
+ is(numberOfRules(ruleView), 3, "Should have three rules after shrinking.");
+}
+
+function* testGrow(ruleView, rdm, manager) {
+ info("Resize to 500x500 and wait for the rule-view to update");
+ let onRefresh = ruleView.once("ruleview-refreshed");
+ yield setSize(rdm, manager, 500, 500);
+ yield onRefresh;
+
+ is(numberOfRules(ruleView), 2, "Should have two rules after growing.");
+}
+
+function* testEscapeOpensSplitConsole(inspector) {
+ ok(!inspector._toolbox._splitConsole, "Console is not split.");
+
+ info("Press escape");
+ let onSplit = inspector._toolbox.once("split-console");
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield onSplit;
+
+ ok(inspector._toolbox._splitConsole, "Console is split after pressing ESC.");
+}
+
+function* testMenuItem(rdm) {
+ is(document.getElementById("menu_responsiveUI").getAttribute("checked"),
+ "true", "The menu item is checked");
+
+ yield closeRDM(rdm);
+
+ is(document.getElementById("menu_responsiveUI").getAttribute("checked"),
+ "false", "The menu item is unchecked");
+}
+
+function numberOfRules(ruleView) {
+ return ruleView.element.querySelectorAll(".ruleview-code").length;
+}
diff --git a/devtools/client/responsivedesign/test/browser_responsiveui.js b/devtools/client/responsivedesign/test/browser_responsiveui.js
new file mode 100644
index 000000000..283974d0f
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsiveui.js
@@ -0,0 +1,250 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function* () {
+ let tab = yield addTab("data:text/html,mop");
+
+ let {rdm, manager} = yield openRDM(tab, "menu");
+ let container = gBrowser.getBrowserContainer();
+ is(container.getAttribute("responsivemode"), "true",
+ "Should be in responsive mode.");
+ is(document.getElementById("menu_responsiveUI").getAttribute("checked"),
+ "true", "Menu item should be checked");
+
+ ok(rdm, "An instance of the RDM should be attached to the tab.");
+
+ let originalWidth = (yield getSizing()).width;
+
+ let documentLoaded = waitForDocLoadComplete();
+ gBrowser.loadURI("data:text/html;charset=utf-8,mop" +
+ "<div style%3D'height%3A5000px'><%2Fdiv>");
+ yield documentLoaded;
+
+ let newWidth = (yield getSizing()).width;
+ is(originalWidth, newWidth, "Floating scrollbars shouldn't change the width");
+
+ yield testPresets(rdm, manager);
+
+ info("Testing mouse resizing");
+ yield testManualMouseResize(rdm, manager);
+
+ info("Testing mouse resizing with shift key");
+ yield testManualMouseResize(rdm, manager, "shift");
+
+ info("Testing mouse resizing with ctrl key");
+ yield testManualMouseResize(rdm, manager, "ctrl");
+
+ info("Testing resizing with user custom keyboard input");
+ yield testResizeUsingCustomInput(rdm, manager);
+
+ info("Testing invalid keyboard input");
+ yield testInvalidUserInput(rdm);
+
+ info("Testing rotation");
+ yield testRotate(rdm, manager);
+
+ let {width: widthBeforeClose, height: heightBeforeClose} = yield getSizing();
+
+ 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");
+ is(currentSize.height, heightBeforeClose, "height should be restored");
+
+ container = gBrowser.getBrowserContainer();
+ is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
+ is(document.getElementById("menu_responsiveUI").getAttribute("checked"),
+ "true", "menu item should be checked");
+
+ let isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
+ if (!isWinXP) {
+ yield testScreenshot(rdm);
+ }
+
+ yield closeRDM(rdm);
+ is(document.getElementById("menu_responsiveUI").getAttribute("checked"),
+ "false", "menu item should be unchecked");
+});
+
+function* testPresets(rdm, manager) {
+ // Starting from length - 4 because last 3 items are not presets :
+ // the separator, the add button and the remove button
+ 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"));
+ yield setPresetIndex(rdm, manager, c);
+
+ let {width: contentWidth, height: contentHeight} = yield getSizing();
+ is(contentWidth, width, "preset" + c + ": the width should be changed");
+ is(contentHeight, height, "preset" + c + ": the height should be changed");
+ }
+}
+
+function* testManualMouseResize(rdm, manager, pressedKey) {
+ yield setSize(rdm, manager, 100, 100);
+
+ let {width: initialWidth, height: initialHeight} = yield getSizing();
+ is(initialWidth, 100, "Width should be reset to 100");
+ is(initialHeight, 100, "Height should be reset to 100");
+
+ let x = 2, y = 2;
+ EventUtils.synthesizeMouse(rdm.resizer, x, y, {type: "mousedown"}, window);
+
+ let mouseMoveParams = {type: "mousemove"};
+ if (pressedKey == "shift") {
+ x += 23; y += 10;
+ mouseMoveParams.shiftKey = true;
+ } else if (pressedKey == "ctrl") {
+ x += 120; y += 60;
+ mouseMoveParams.ctrlKey = true;
+ } else {
+ x += 20; y += 10;
+ }
+
+ EventUtils.synthesizeMouse(rdm.resizer, x, y, mouseMoveParams, window);
+ EventUtils.synthesizeMouse(rdm.resizer, x, y, {type: "mouseup"}, window);
+
+ yield once(manager, "content-resize");
+
+ let expectedWidth = initialWidth + 20;
+ let expectedHeight = initialHeight + 10;
+ info("initial width: " + initialWidth);
+ info("initial height: " + initialHeight);
+
+ yield verifyResize(rdm, expectedWidth, expectedHeight);
+}
+
+function* testResizeUsingCustomInput(rdm, manager) {
+ let {width: initialWidth, height: initialHeight} = yield getSizing();
+ let expectedWidth = initialWidth - 20, expectedHeight = initialHeight - 10;
+
+ let userInput = expectedWidth + " x " + expectedHeight;
+ rdm.menulist.inputField.value = "";
+ rdm.menulist.focus();
+ processStringAsKey(userInput);
+
+ // While typing, the size should not change
+ let currentSize = yield getSizing();
+ is(currentSize.width, initialWidth, "Typing shouldn't change the width");
+ is(currentSize.height, initialHeight, "Typing shouldn't change the height");
+
+ // Only the `change` event must change the size
+ EventUtils.synthesizeKey("VK_RETURN", {});
+
+ yield once(manager, "content-resize");
+
+ yield verifyResize(rdm, expectedWidth, expectedHeight);
+}
+
+function* testInvalidUserInput(rdm) {
+ let {width: initialWidth, height: initialHeight} = yield getSizing();
+ let index = rdm.menulist.selectedIndex;
+ let expectedValue = initialWidth + "\u00D7" + initialHeight;
+ let expectedLabel = rdm.menulist.firstChild.firstChild.getAttribute("label");
+
+ let userInput = "I'm wrong";
+
+ rdm.menulist.inputField.value = "";
+ rdm.menulist.focus();
+ processStringAsKey(userInput);
+ EventUtils.synthesizeKey("VK_RETURN", {});
+
+ let currentSize = yield getSizing();
+ is(currentSize.width, initialWidth, "Width should not change");
+ is(currentSize.height, initialHeight, "Height should not change");
+ is(rdm.menulist.selectedIndex, index, "Selected item should not change.");
+ is(rdm.menulist.value, expectedValue, "Value should be reset");
+
+ let label = rdm.menulist.firstChild.firstChild.getAttribute("label");
+ is(label, expectedLabel, "Custom menuitem's label should not change");
+}
+
+function* testRotate(rdm, manager) {
+ yield setSize(rdm, manager, 100, 200);
+
+ let {width: initialWidth, height: initialHeight} = yield getSizing();
+ rdm.rotate();
+
+ yield once(manager, "content-resize");
+
+ let newSize = yield getSizing();
+ is(newSize.width, initialHeight, "The width should now be the height.");
+ is(newSize.height, initialWidth, "The height should now be the width.");
+
+ let label = rdm.menulist.firstChild.firstChild.getAttribute("label");
+ let [width, height] = extractSizeFromString(label);
+ is(width, initialHeight, "Width in label should be updated");
+ is(height, initialWidth, "Height in label should be updated");
+}
+
+function* verifyResize(rdm, expectedWidth, expectedHeight) {
+ let currentSize = yield getSizing();
+ is(currentSize.width, expectedWidth, "Width should now change");
+ is(currentSize.height, expectedHeight, "Height should now change");
+
+ is(rdm.menulist.selectedIndex, -1, "Custom menuitem cannot be selected");
+
+ let label = rdm.menulist.firstChild.firstChild.getAttribute("label");
+ let value = rdm.menulist.value;
+ isnot(label, value,
+ "The menulist item label should be different than the menulist value");
+
+ let [width, height] = extractSizeFromString(label);
+ is(width, expectedWidth, "Width in label should be updated");
+ is(height, expectedHeight, "Height in label should be updated");
+
+ [width, height] = extractSizeFromString(value);
+ is(width, expectedWidth, "Value should be updated with new width");
+ is(height, expectedHeight, "Value should be updated with new height");
+}
+
+function* testScreenshot(rdm) {
+ info("Testing screenshot");
+ rdm.screenshot("responsiveui");
+ let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
+
+ while (true) {
+ // while(true) until we find the file.
+ // no need for a timeout, the test will get killed anyway.
+ let file = FileUtils.getFile("DfltDwnld", [ "responsiveui.png" ]);
+ if (file.exists()) {
+ ok(true, "Screenshot file exists");
+ file.remove(false);
+ break;
+ }
+ info("checking if file exists in 200ms");
+ yield wait(200);
+ }
+}
+
+function* getSizing() {
+ let browser = gBrowser.selectedBrowser;
+ let sizing = yield ContentTask.spawn(browser, {}, function* () {
+ return {
+ width: content.innerWidth,
+ height: content.innerHeight
+ };
+ });
+ return sizing;
+}
+
+function extractSizeFromString(str) {
+ let numbers = str.match(/(\d+)[^\d]*(\d+)/);
+ if (numbers) {
+ return [numbers[1], numbers[2]];
+ }
+ return [null, null];
+}
+
+function processStringAsKey(str) {
+ for (let i = 0, l = str.length; i < l; i++) {
+ EventUtils.synthesizeKey(str.charAt(i), {});
+ }
+}
diff --git a/devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js b/devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js
new file mode 100644
index 000000000..35efc4c14
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html, Custom User Agent test";
+const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
+ .getService(Ci.nsIHttpProtocolHandler)
+ .userAgent;
+const CHROME_UA = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36" +
+ " (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";
+add_task(function* () {
+ yield addTab(TEST_URI);
+
+ let {rdm, manager} = yield openRDM();
+ yield testUserAgent(DEFAULT_UA);
+
+ info("Setting UA to " + CHROME_UA);
+ yield setUserAgent(CHROME_UA, rdm, manager);
+ yield testUserAgent(CHROME_UA);
+
+ info("Resetting UA");
+ yield setUserAgent("", rdm, manager);
+ yield testUserAgent(DEFAULT_UA);
+
+ info("Setting UA to " + CHROME_UA);
+ yield setUserAgent(CHROME_UA, rdm, manager);
+ yield testUserAgent(CHROME_UA);
+
+ info("Closing responsive mode");
+
+ yield closeRDM(rdm);
+ yield testUserAgent(DEFAULT_UA);
+});
+
+function* setUserAgent(ua, rdm, manager) {
+ let input = rdm.userAgentInput;
+ input.focus();
+ input.value = ua;
+ let onUAChanged = once(manager, "userAgentChanged");
+ input.blur();
+ yield onUAChanged;
+
+ if (ua !== "") {
+ ok(input.hasAttribute("attention"), "UA input should be highlighted");
+ } else {
+ ok(!input.hasAttribute("attention"), "UA input shouldn't be highlighted");
+ }
+}
+
+function* testUserAgent(value) {
+ let ua = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ return content.navigator.userAgent;
+ });
+ is(ua, value, `UA should be set to ${value}`);
+}
diff --git a/devtools/client/responsivedesign/test/browser_responsiveui_touch.js b/devtools/client/responsivedesign/test/browser_responsiveui_touch.js
new file mode 100644
index 000000000..c23d2dd12
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsiveui_touch.js
@@ -0,0 +1,148 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://mochi.test:8888/browser/devtools/client/" +
+ "responsivedesign/test/touch.html";
+const layoutReflowSynthMouseMove = "layout.reflow.synthMouseMove";
+const domViewportEnabled = "dom.meta-viewport.enabled";
+
+add_task(function* () {
+ let tab = yield addTab(TEST_URI);
+ let {rdm} = yield openRDM(tab);
+ yield pushPrefs([layoutReflowSynthMouseMove, false]);
+ yield testWithNoTouch();
+ yield rdm.enableTouch();
+ yield testWithTouch();
+ yield rdm.disableTouch();
+ yield testWithNoTouch();
+ yield closeRDM(rdm);
+});
+
+function* testWithNoTouch() {
+ let div = content.document.querySelector("div");
+ let x = 0, y = 0;
+
+ info("testWithNoTouch: Initial test parameter and mouse mouse outside div element");
+ x = -1, y = -1;
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+ div.style.transform = "none";
+ div.style.backgroundColor = "";
+
+ info("testWithNoTouch: Move mouse into the div element");
+ yield BrowserTestUtils.synthesizeMouseAtCenter("div", { type: "mousemove", isSynthesized: false },
+ gBrowser.selectedBrowser);
+ is(div.style.backgroundColor, "red", "mouseenter or mouseover should work");
+
+ info("testWithNoTouch: Drag the div element");
+ yield BrowserTestUtils.synthesizeMouseAtCenter("div", { type: "mousedown", isSynthesized: false },
+ gBrowser.selectedBrowser);
+ x = 100; y = 100;
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+ is(div.style.transform, "none", "touchmove shouldn't work");
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mouseup", isSynthesized: false }, gBrowser.selectedBrowser);
+
+ info("testWithNoTouch: Move mouse out of the div element");
+ x = -1; y = -1;
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+ is(div.style.backgroundColor, "blue", "mouseout or mouseleave should work");
+
+ info("testWithNoTouch: Click the div element");
+ yield synthesizeClick(div);
+ is(div.dataset.isDelay, "false", "300ms delay between touch events and mouse events should not work");
+}
+
+function* testWithTouch() {
+ let div = content.document.querySelector("div");
+ let x = 0, y = 0;
+
+ info("testWithTouch: Initial test parameter and mouse mouse outside div element");
+ x = -1, y = -1;
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+ div.style.transform = "none";
+ div.style.backgroundColor = "";
+
+ info("testWithTouch: Move mouse into the div element");
+ yield BrowserTestUtils.synthesizeMouseAtCenter("div", { type: "mousemove", isSynthesized: false },
+ gBrowser.selectedBrowser);
+ isnot(div.style.backgroundColor, "red", "mouseenter or mouseover should not work");
+
+ info("testWithTouch: Drag the div element");
+ yield BrowserTestUtils.synthesizeMouseAtCenter("div", { type: "mousedown", isSynthesized: false },
+ gBrowser.selectedBrowser);
+ x = 100; y = 100;
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+ isnot(div.style.transform, "none", "touchmove should work");
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mouseup", isSynthesized: false }, gBrowser.selectedBrowser);
+
+ info("testWithTouch: Move mouse out of the div element");
+ x = -1; y = -1;
+ yield BrowserTestUtils.synthesizeMouse("div", x, y,
+ { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+ isnot(div.style.backgroundColor, "blue", "mouseout or mouseleave should not work");
+
+ yield testWithMetaViewportEnabled();
+ yield testWithMetaViewportDisabled();
+}
+
+function* testWithMetaViewportEnabled() {
+ yield pushPrefs([domViewportEnabled, true]);
+ let meta = content.document.querySelector("meta[name=viewport]");
+ let div = content.document.querySelector("div");
+ div.dataset.isDelay = "false";
+
+ info("testWithMetaViewportEnabled: click the div element with <meta name='viewport'>");
+ meta.content = "";
+ yield synthesizeClick(div);
+ is(div.dataset.isDelay, "true", "300ms delay between touch events and mouse events should work");
+
+ info("testWithMetaViewportEnabled: click the div element with <meta name='viewport' content='user-scalable=no'>");
+ meta.content = "user-scalable=no";
+ yield synthesizeClick(div);
+ is(div.dataset.isDelay, "false", "300ms delay between touch events and mouse events should not work");
+
+ info("testWithMetaViewportEnabled: click the div element with <meta name='viewport' content='minimum-scale=maximum-scale'>");
+ meta.content = "minimum-scale=maximum-scale";
+ yield synthesizeClick(div);
+ is(div.dataset.isDelay, "false", "300ms delay between touch events and mouse events should not work");
+
+ info("testWithMetaViewportEnabled: click the div element with <meta name='viewport' content='width=device-width'>");
+ meta.content = "width=device-width";
+ yield synthesizeClick(div);
+ is(div.dataset.isDelay, "false", "300ms delay between touch events and mouse events should not work");
+}
+
+function* testWithMetaViewportDisabled() {
+ yield pushPrefs([domViewportEnabled, false]);
+ let meta = content.document.querySelector("meta[name=viewport]");
+ let div = content.document.querySelector("div");
+ div.dataset.isDelay = "false";
+
+ info("testWithMetaViewportDisabled: click the div element with <meta name='viewport'>");
+ meta.content = "";
+ yield synthesizeClick(div);
+ is(div.dataset.isDelay, "true", "300ms delay between touch events and mouse events should work");
+}
+
+function synthesizeClick(element) {
+ let waitForClickEvent = BrowserTestUtils.waitForEvent(element, "click");
+ BrowserTestUtils.synthesizeMouseAtCenter(element, { type: "mousedown", isSynthesized: false },
+ gBrowser.selectedBrowser);
+ BrowserTestUtils.synthesizeMouseAtCenter(element, { type: "mouseup", isSynthesized: false },
+ gBrowser.selectedBrowser);
+ return waitForClickEvent;
+}
+
+function pushPrefs(...aPrefs) {
+ let deferred = promise.defer();
+ SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
+ return deferred.promise;
+}
diff --git a/devtools/client/responsivedesign/test/browser_responsiveui_window_close.js b/devtools/client/responsivedesign/test/browser_responsiveui_window_close.js
new file mode 100644
index 000000000..a5f890a86
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsiveui_window_close.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function* () {
+ let newWindowPromise = BrowserTestUtils.waitForNewWindow();
+ window.open("about:blank", "_blank");
+ let newWindow = yield newWindowPromise;
+
+ newWindow.focus();
+ yield once(newWindow.gBrowser, "load", true);
+
+ let tab = newWindow.gBrowser.selectedTab;
+ yield ResponsiveUIManager.openIfNeeded(newWindow, tab);
+
+ // Close the window on a tab with an active responsive design UI and
+ // wait for the UI to gracefully shutdown. This has leaked the window
+ // in the past.
+ ok(ResponsiveUIManager.isActiveForTab(tab),
+ "ResponsiveUI should be active for tab when the window is closed");
+ let offPromise = once(ResponsiveUIManager, "off");
+ yield BrowserTestUtils.closeWindow(newWindow);
+ yield offPromise;
+});
diff --git a/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js b/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
new file mode 100644
index 000000000..3ab54b601
--- /dev/null
+++ b/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
@@ -0,0 +1,121 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function* () {
+ let tab = yield addTab("data:text/html;charset=utf8,Test RDM custom presets");
+
+ let { rdm, manager } = yield openRDM(tab);
+
+ let oldPrompt = Services.prompt;
+ Services.prompt = {
+ value: "",
+ returnBool: true,
+ prompt: function (parent, dialogTitle, text, value, checkMsg, checkState) {
+ value.value = this.value;
+ return this.returnBool;
+ }
+ };
+
+ registerCleanupFunction(() => {
+ Services.prompt = oldPrompt;
+ });
+
+ // Is it open?
+ let container = gBrowser.getBrowserContainer();
+ is(container.getAttribute("responsivemode"), "true",
+ "Should be in responsive mode.");
+
+ ok(rdm, "RDM instance should be attached to the tab.");
+
+ // Tries to add a custom preset and cancel the prompt
+ let idx = rdm.menulist.selectedIndex;
+ let presetCount = rdm.presets.length;
+
+ Services.prompt.value = "";
+ Services.prompt.returnBool = false;
+ rdm.addbutton.doCommand();
+
+ is(idx, rdm.menulist.selectedIndex,
+ "selected item shouldn't change after add preset and cancel");
+ is(presetCount, rdm.presets.length,
+ "number of presets shouldn't change after add preset and cancel");
+
+ // Adds the custom preset with "Testing preset"
+ Services.prompt.value = "Testing preset";
+ Services.prompt.returnBool = true;
+
+ let resized = once(manager, "content-resize");
+ let customHeight = 123, customWidth = 456;
+ rdm.startResizing({});
+ rdm.setViewportSize({
+ width: customWidth,
+ height: customHeight,
+ });
+ rdm.stopResizing({});
+
+ rdm.addbutton.doCommand();
+ yield resized;
+
+ yield closeRDM(rdm);
+
+ ({rdm} = yield openRDM(tab));
+ is(container.getAttribute("responsivemode"), "true",
+ "Should be in responsive mode.");
+
+ let presetLabel = "456" + "\u00D7" + "123 (Testing preset)";
+ let customPresetIndex = yield getPresetIndex(rdm, manager, presetLabel);
+ ok(customPresetIndex >= 0, "(idx = " + customPresetIndex + ") should be the" +
+ " previously added preset in the list of items");
+
+ yield setPresetIndex(rdm, manager, customPresetIndex);
+
+ let browser = gBrowser.selectedBrowser;
+ yield ContentTask.spawn(browser, null, function* () {
+ let {innerWidth, innerHeight} = content;
+ Assert.equal(innerWidth, 456, "Selecting preset should change the width");
+ Assert.equal(innerHeight, 123, "Selecting preset should change the height");
+ });
+
+ info(`menulist count: ${rdm.menulist.itemCount}`);
+
+ rdm.removebutton.doCommand();
+
+ yield setPresetIndex(rdm, manager, 2);
+ let deletedPresetA = rdm.menulist.selectedItem.getAttribute("label");
+ rdm.removebutton.doCommand();
+
+ yield setPresetIndex(rdm, manager, 2);
+ let deletedPresetB = rdm.menulist.selectedItem.getAttribute("label");
+ rdm.removebutton.doCommand();
+
+ yield closeRDM(rdm);
+ ({rdm} = yield openRDM(tab));
+
+ customPresetIndex = yield getPresetIndex(rdm, manager, deletedPresetA);
+ is(customPresetIndex, -1,
+ "Deleted preset " + deletedPresetA + " should not be in the list anymore");
+
+ customPresetIndex = yield getPresetIndex(rdm, manager, deletedPresetB);
+ is(customPresetIndex, -1,
+ "Deleted preset " + deletedPresetB + " should not be in the list anymore");
+
+ yield closeRDM(rdm);
+});
+
+var getPresetIndex = Task.async(function* (rdm, manager, presetLabel) {
+ var testOnePreset = Task.async(function* (c) {
+ if (c == 0) {
+ return -1;
+ }
+ 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
new file mode 100644
index 000000000..3228021f6
--- /dev/null
+++ b/devtools/client/responsivedesign/test/head.js
@@ -0,0 +1,302 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+// shared-head.js handles imports, constants, and utility functions
+let sharedHeadURI = testDir + "../../../framework/test/shared-head.js";
+Services.scriptloader.loadSubScript(sharedHeadURI, this);
+
+// Import the GCLI test helper
+let gcliHelpersURI = testDir + "../../../commandline/test/helpers.js";
+Services.scriptloader.loadSubScript(gcliHelpersURI, this);
+
+flags.testing = true;
+Services.prefs.setBoolPref("devtools.responsive.html.enabled", false);
+
+registerCleanupFunction(() => {
+ flags.testing = false;
+ Services.prefs.clearUserPref("devtools.responsive.html.enabled");
+ 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();
+
+const { ResponsiveUIManager } = Cu.import("resource://devtools/client/responsivedesign/responsivedesign.jsm", {});
+
+/**
+ * Open the Responsive Design Mode
+ * @param {Tab} The browser tab to open it into (defaults to the selected tab).
+ * @param {method} The method to use to open the RDM (values: menu, keyboard)
+ * @return {rdm, manager} Returns the RUI instance and the manager
+ */
+var openRDM = Task.async(function* (tab = gBrowser.selectedTab,
+ method = "menu") {
+ let manager = ResponsiveUIManager;
+
+ let opened = once(manager, "on");
+ let resized = once(manager, "content-resize");
+ if (method == "menu") {
+ document.getElementById("menu_responsiveUI").doCommand();
+ } else {
+ synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
+ }
+ 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};
+});
+
+/**
+ * Close a responsive mode instance
+ * @param {rdm} ResponsiveUI instance for the tab
+ */
+var closeRDM = Task.async(function* (rdm) {
+ let manager = ResponsiveUIManager;
+ if (!rdm) {
+ rdm = manager.getResponsiveUIForTab(gBrowser.selectedTab);
+ }
+ let closed = once(manager, "off");
+ let resized = once(manager, "content-resize");
+ rdm.close();
+ yield resized;
+ yield closed;
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible.
+ * @return a promise that resolves when the inspector is ready
+ */
+var openInspector = Task.async(function* () {
+ info("Opening the inspector");
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+
+ let inspector, toolbox;
+
+ // Checking if the toolbox and the inspector are already loaded
+ // The inspector-updated event should only be waited for if the inspector
+ // isn't loaded yet
+ toolbox = gDevTools.getToolbox(target);
+ if (toolbox) {
+ inspector = toolbox.getPanel("inspector");
+ if (inspector) {
+ info("Toolbox and inspector already open");
+ return {
+ toolbox: toolbox,
+ inspector: inspector
+ };
+ }
+ }
+
+ info("Opening the toolbox");
+ toolbox = yield gDevTools.showToolbox(target, "inspector");
+ yield waitForToolboxFrameFocus(toolbox);
+ inspector = toolbox.getPanel("inspector");
+
+ info("Waiting for the inspector to update");
+ if (inspector._updateProgress) {
+ yield inspector.once("inspector-updated");
+ }
+
+ return {
+ toolbox: toolbox,
+ inspector: inspector
+ };
+});
+
+var closeToolbox = Task.async(function* () {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ yield gDevTools.closeToolbox(target);
+});
+
+/**
+ * Wait for the toolbox frame to receive focus after it loads
+ * @param {Toolbox} toolbox
+ * @return a promise that resolves when focus has been received
+ */
+function waitForToolboxFrameFocus(toolbox) {
+ info("Making sure that the toolbox's frame is focused");
+ let def = promise.defer();
+ waitForFocus(def.resolve, toolbox.win);
+ return def.promise;
+}
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the sidebar that
+ * corresponds to the given id selected
+ * @return a promise that resolves when the inspector is ready and the sidebar
+ * view is visible and ready
+ */
+var openInspectorSideBar = Task.async(function* (id) {
+ let {toolbox, inspector} = yield openInspector();
+
+ info("Selecting the " + id + " sidebar");
+ inspector.sidebar.select(id);
+
+ return {
+ toolbox: toolbox,
+ inspector: inspector,
+ view: inspector[id].view || inspector[id].computedView
+ };
+});
+
+/**
+ * Checks whether the inspector's sidebar corresponding to the given id already
+ * exists
+ * @param {InspectorPanel}
+ * @param {String}
+ * @return {Boolean}
+ */
+function hasSideBarTab(inspector, id) {
+ return !!inspector.sidebar.getWindowForTab(id);
+}
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the computed-view
+ * sidebar tab selected.
+ * @return a promise that resolves when the inspector is ready and the computed
+ * view is visible and ready
+ */
+function openComputedView() {
+ return openInspectorSideBar("computedview");
+}
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the rule-view
+ * sidebar tab selected.
+ * @return a promise that resolves when the inspector is ready and the rule
+ * view is visible and ready
+ */
+function openRuleView() {
+ return openInspectorSideBar("ruleview");
+}
+
+/**
+ * Add a new test tab in the browser and load the given url.
+ * @param {String} url The url to be loaded in the new tab
+ * @return a promise that resolves to the tab object when the url is loaded
+ */
+var addTab = Task.async(function* (url) {
+ info("Adding a new tab with URL: '" + url + "'");
+
+ window.focus();
+
+ let tab = gBrowser.selectedTab = gBrowser.addTab(url);
+ let browser = tab.linkedBrowser;
+
+ yield BrowserTestUtils.browserLoaded(browser);
+ info("URL '" + url + "' loading complete");
+
+ return tab;
+});
+
+/**
+ * Waits for the next load to complete in the current browser.
+ *
+ * @return promise
+ */
+function waitForDocLoadComplete(aBrowser = gBrowser) {
+ let deferred = promise.defer();
+ let progressListener = {
+ onStateChange: function (webProgress, req, flags, status) {
+ let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
+ Ci.nsIWebProgressListener.STATE_STOP;
+ info(`Saw state ${flags.toString(16)} and status ${status.toString(16)}`);
+
+ // When a load needs to be retargetted to a new process it is cancelled
+ // with NS_BINDING_ABORTED so ignore that case
+ if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
+ aBrowser.removeProgressListener(progressListener);
+ info("Browser loaded");
+ deferred.resolve();
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference])
+ };
+ aBrowser.addProgressListener(progressListener);
+ info("Waiting for browser load");
+ return deferred.promise;
+}
+
+/**
+ * Get the NodeFront for a node that matches a given css selector, via the
+ * protocol.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves to the NodeFront instance
+ */
+function getNodeFront(selector, {walker}) {
+ if (selector._form) {
+ return selector;
+ }
+ return walker.querySelector(walker.rootNode, selector);
+}
+
+/**
+ * Set the inspector's current selection to the first match of the given css
+ * selector
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @param {String} reason Defaults to "test" which instructs the inspector not
+ * to highlight the node upon selection
+ * @return {Promise} Resolves when the inspector is updated with the new node
+ */
+var selectNode = Task.async(function* (selector, inspector, reason = "test") {
+ info("Selecting the node for '" + selector + "'");
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let updated = inspector.once("inspector-updated");
+ 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("content-resize", onResize);
+ info(`Got content-resize to ${width} x ${height}`);
+ resolve();
+ };
+ info(`Waiting for content-resize to ${width} x ${height}`);
+ manager.on("content-resize", 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, "content-resize");
+ 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.setViewportSize({ width, height });
+ yield resized;
+ }
+});
diff --git a/devtools/client/responsivedesign/test/touch.html b/devtools/client/responsivedesign/test/touch.html
new file mode 100644
index 000000000..f93d36f6c
--- /dev/null
+++ b/devtools/client/responsivedesign/test/touch.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+
+<meta charset="utf-8" />
+<meta name="viewport" />
+<title>test</title>
+
+
+<style>
+ div {
+ border:1px solid red;
+ width: 100px; height: 100px;
+ }
+</style>
+
+<div data-is-delay="false"></div>
+
+<script>
+ var div = document.querySelector("div");
+ var initX, initY;
+ var previousEvent = "", touchendTime = 0;
+ var updatePreviousEvent = function(e){
+ previousEvent = e.type;
+ };
+
+ div.style.transform = "none";
+ div.style.backgroundColor = "";
+
+ div.addEventListener("touchstart", function(evt) {
+ var touch = evt.changedTouches[0];
+ initX = touch.pageX;
+ initY = touch.pageY;
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("touchmove", function(evt) {
+ var touch = evt.changedTouches[0];
+ var deltaX = touch.pageX - initX;
+ var deltaY = touch.pageY - initY;
+ div.style.transform = "translate(" + deltaX + "px, " + deltaY + "px)";
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("touchend", function(evt) {
+ if (!evt.touches.length) {
+ div.style.transform = "none";
+ }
+ touchendTime = performance.now();
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("mouseenter", function(evt) {
+ div.style.backgroundColor = "red";
+ updatePreviousEvent(evt);
+ }, true);
+ div.addEventListener("mouseover", function(evt) {
+ div.style.backgroundColor = "red";
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("mouseout", function(evt) {
+ div.style.backgroundColor = "blue";
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("mouseleave", function(evt) {
+ div.style.backgroundColor = "blue";
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("mousedown", function(evt){
+ if (previousEvent === "touchend" && touchendTime !== 0) {
+ let now = performance.now();
+ div.dataset.isDelay = ((now - touchendTime) >= 300) ? true : false;
+ } else {
+ div.dataset.isDelay = false;
+ }
+ updatePreviousEvent(evt);
+ }, true);
+
+ div.addEventListener("mousemove", updatePreviousEvent, true);
+
+ div.addEventListener("mouseup", updatePreviousEvent, true);
+
+ div.addEventListener("click", updatePreviousEvent, true);
+</script>