import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,6 @@
"use strict";
module.exports = {
// Extend from the shared list of defined globals for mochitests.
"extends": "../../../../.eslintrc.mochitests.js"
};

View file

@ -0,0 +1,44 @@
[DEFAULT]
tags = devtools
subsuite = devtools
# !e10s: RDM only works for remote tabs
skip-if = !e10s
support-files =
devices.json
doc_page_state.html
geolocation.html
head.js
touch.html
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/framework/test/shared-redux-head.js
!/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_device_change.js]
[browser_device_modal_error.js]
[browser_device_modal_exit.js]
[browser_device_modal_submit.js]
[browser_device_width.js]
[browser_dpr_change.js]
[browser_exit_button.js]
[browser_frame_script_active.js]
[browser_menu_item_01.js]
[browser_menu_item_02.js]
[browser_mouse_resize.js]
[browser_navigation.js]
[browser_network_throttling.js]
[browser_page_state.js]
[browser_permission_doorhanger.js]
[browser_resize_cmd.js]
[browser_screenshot_button.js]
[browser_tab_close.js]
[browser_tab_remoteness_change.js]
[browser_toolbox_computed_view.js]
[browser_toolbox_rule_view.js]
[browser_toolbox_swap_browsers.js]
[browser_touch_device.js]
[browser_touch_simulation.js]
[browser_viewport_basics.js]
[browser_window_close.js]

View file

@ -0,0 +1,95 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests changing viewport device
const TEST_URL = "data:text/html;charset=utf-8,Device list test";
const DEFAULT_DPPX = window.devicePixelRatio;
const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
.getService(Ci.nsIHttpProtocolHandler)
.userAgent;
const Types = require("devtools/client/responsive.html/types");
const testDevice = {
"name": "Fake Phone RDM Test",
"width": 320,
"height": 570,
"pixelRatio": 5.5,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "custom",
"featured": true,
};
// Add the new device to the list
addDeviceForTest(testDevice);
addRDMTask(TEST_URL, function* ({ ui, manager }) {
let { store } = ui.toolWindow;
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
// Test defaults
testViewportDimensions(ui, 320, 480);
yield testUserAgent(ui, DEFAULT_UA);
yield testDevicePixelRatio(ui, DEFAULT_DPPX);
yield testTouchEventsOverride(ui, false);
testViewportDeviceSelectLabel(ui, "no device selected");
// Test device with custom properties
yield selectDevice(ui, "Fake Phone RDM Test");
yield waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
yield testUserAgent(ui, testDevice.userAgent);
yield testDevicePixelRatio(ui, testDevice.pixelRatio);
yield testTouchEventsOverride(ui, true);
// Test resetting device when resizing viewport
let deviceRemoved = once(ui, "device-removed");
yield testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
yield deviceRemoved;
yield testUserAgent(ui, DEFAULT_UA);
yield testDevicePixelRatio(ui, DEFAULT_DPPX);
yield testTouchEventsOverride(ui, false);
testViewportDeviceSelectLabel(ui, "no device selected");
// Test device with generic properties
yield selectDevice(ui, "Laptop (1366 x 768)");
yield waitForViewportResizeTo(ui, 1366, 768);
yield testUserAgent(ui, DEFAULT_UA);
yield testDevicePixelRatio(ui, 1);
yield testTouchEventsOverride(ui, false);
});
function testViewportDimensions(ui, w, h) {
let viewport = ui.toolWindow.document.querySelector(".viewport-content");
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
`${w}px`, `Viewport should have width of ${w}px`);
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
`${h}px`, `Viewport should have height of ${h}px`);
}
function* testUserAgent(ui, expected) {
let ua = yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
return content.navigator.userAgent;
});
is(ua, expected, `UA should be set to ${expected}`);
}
function* testDevicePixelRatio(ui, expected) {
let dppx = yield getViewportDevicePixelRatio(ui);
is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
}
function* getViewportDevicePixelRatio(ui) {
return yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
return content.devicePixelRatio;
});
}

View file

@ -0,0 +1,35 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test to check that RDM can handle properly an error in the device list
const TEST_URL = "data:text/html;charset=utf-8,";
const Types = require("devtools/client/responsive.html/types");
const { getStr } = require("devtools/client/responsive.html/utils/l10n");
// Set a wrong URL for the device list file
add_task(function* () {
yield SpecialPowers.pushPrefEnv({
set: [["devtools.devices.url", TEST_URI_ROOT + "wrong_devices_file.json"]],
});
});
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
let select = document.querySelector(".viewport-device-selector");
// Wait until the viewport has been added and the device list state indicates
// an error
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.ERROR);
// The device selector placeholder should be set accordingly
let placeholder = select.options[select.selectedIndex].innerHTML;
ok(placeholder == getStr("responsive.deviceListError"),
"Device selector indicates an error");
// The device selector should be disabled
ok(select.disabled, "Device selector is disabled");
});

View file

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test submitting display device changes on the device modal
const TEST_URL = "data:text/html;charset=utf-8,";
const Types = require("devtools/client/responsive.html/types");
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
let modal = document.querySelector("#device-modal-wrapper");
let closeButton = document.querySelector("#device-close-button");
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
openDeviceModal(ui);
let preferredDevicesBefore = _loadPreferredDevices();
info("Check the first unchecked device and exit the modal.");
let uncheckedCb = [...document.querySelectorAll(".device-input-checkbox")]
.filter(cb => !cb.checked)[0];
let value = uncheckedCb.value;
uncheckedCb.click();
closeButton.click();
ok(modal.classList.contains("closed") && !modal.classList.contains("opened"),
"The device modal is closed on exit.");
info("Check that the device list remains unchanged after exitting.");
let preferredDevicesAfter = _loadPreferredDevices();
is(preferredDevicesBefore.added.size, preferredDevicesAfter.added.size,
"Got expected number of added devices.");
is(preferredDevicesBefore.removed.size, preferredDevicesAfter.removed.size,
"Got expected number of removed devices.");
ok(!preferredDevicesAfter.removed.has(value),
value + " was not added to removed device list.");
});

View file

@ -0,0 +1,146 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test submitting display device changes on the device modal
const { getDevices } = require("devtools/client/shared/devices");
const addedDevice = {
"name": "Fake Phone RDM Test",
"width": 320,
"height": 570,
"pixelRatio": 1.5,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": false,
"os": "custom",
"featured": true,
};
const TEST_URL = "data:text/html;charset=utf-8,";
const Types = require("devtools/client/responsive.html/types");
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
let modal = document.querySelector("#device-modal-wrapper");
let select = document.querySelector(".viewport-device-selector");
let submitButton = document.querySelector("#device-submit-button");
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
openDeviceModal(ui);
info("Checking displayed device checkboxes are checked in the device modal.");
let checkedCbs = [...document.querySelectorAll(".device-input-checkbox")]
.filter(cb => cb.checked);
let remoteList = yield getDevices();
let featuredCount = remoteList.TYPES.reduce((total, type) => {
return total + remoteList[type].reduce((subtotal, device) => {
return subtotal + ((device.os != "fxos" && device.featured) ? 1 : 0);
}, 0);
}, 0);
is(featuredCount, checkedCbs.length,
"Got expected number of displayed devices.");
for (let cb of checkedCbs) {
ok(Object.keys(remoteList).filter(type => remoteList[type][cb.value]),
cb.value + " is correctly checked.");
}
// Tests where the user adds a non-featured device
info("Check the first unchecked device and submit new device list.");
let uncheckedCb = [...document.querySelectorAll(".device-input-checkbox")]
.filter(cb => !cb.checked)[0];
let value = uncheckedCb.value;
uncheckedCb.click();
submitButton.click();
ok(modal.classList.contains("closed") && !modal.classList.contains("opened"),
"The device modal is closed on submit.");
info("Checking that the new device is added to the user preference list.");
let preferredDevices = _loadPreferredDevices();
ok(preferredDevices.added.has(value), value + " in user added list.");
info("Checking new device is added to the device selector.");
let options = [...select.options];
is(options.length - 2, featuredCount + 1,
"Got expected number of devices in device selector.");
ok(options.filter(o => o.value === value)[0],
value + " added to the device selector.");
info("Reopen device modal and check new device is correctly checked");
openDeviceModal(ui);
ok([...document.querySelectorAll(".device-input-checkbox")]
.filter(cb => cb.checked && cb.value === value)[0],
value + " is checked in the device modal.");
// Tests where the user removes a featured device
info("Uncheck the first checked device different than the previous one");
let checkedCb = [...document.querySelectorAll(".device-input-checkbox")]
.filter(cb => cb.checked && cb.value != value)[0];
let checkedVal = checkedCb.value;
checkedCb.click();
submitButton.click();
info("Checking that the device is removed from the user preference list.");
preferredDevices = _loadPreferredDevices();
ok(preferredDevices.removed.has(checkedVal), checkedVal + " in removed list");
info("Checking that the device is not in the device selector.");
options = [...select.options];
is(options.length - 2, featuredCount,
"Got expected number of devices in device selector.");
ok(!options.filter(o => o.value === checkedVal)[0],
checkedVal + " removed from the device selector.");
info("Reopen device modal and check device is correctly unchecked");
openDeviceModal(ui);
ok([...document.querySelectorAll(".device-input-checkbox")]
.filter(cb => !cb.checked && cb.value === checkedVal)[0],
checkedVal + " is unchecked in the device modal.");
// Let's add a dummy device to simulate featured flag changes for next test
addDeviceForTest(addedDevice);
});
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
let select = document.querySelector(".viewport-device-selector");
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
openDeviceModal(ui);
let remoteList = yield getDevices();
let featuredCount = remoteList.TYPES.reduce((total, type) => {
return total + remoteList[type].reduce((subtotal, device) => {
return subtotal + ((device.os != "fxos" && device.featured) ? 1 : 0);
}, 0);
}, 0);
let preferredDevices = _loadPreferredDevices();
// Tests to prove that reloading the RDM didn't break our device list
info("Checking new featured device appears in the device selector.");
let options = [...select.options];
is(options.length - 2, featuredCount
- preferredDevices.removed.size + preferredDevices.added.size,
"Got expected number of devices in device selector.");
ok(options.filter(o => o.value === addedDevice.name)[0],
"dummy device added to the device selector.");
ok(options.filter(o => preferredDevices.added.has(o.value))[0],
"device added by user still in the device selector.");
ok(!options.filter(o => preferredDevices.removed.has(o.value))[0],
"device removed by user not in the device selector.");
});

View file

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URL = "data:text/html;charset=utf-8,";
addRDMTask(TEST_URL, function* ({ ui, manager }) {
ok(ui, "An instance of the RDM should be attached to the tab.");
yield setViewportSize(ui, manager, 110, 500);
info("Checking initial width/height properties.");
yield doInitialChecks(ui);
info("Changing the RDM size");
yield setViewportSize(ui, manager, 90, 500);
info("Checking for screen props");
yield checkScreenProps(ui);
info("Setting docShell.deviceSizeIsPageSize to false");
yield ContentTask.spawn(ui.getViewportBrowser(), {}, 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(ui);
});
function* doInitialChecks(ui) {
let { innerWidth, matchesMedia } = yield grabContentInfo(ui);
is(innerWidth, 110, "initial width should be 110px");
ok(!matchesMedia, "media query shouldn't match.");
}
function* checkScreenProps(ui) {
let { matchesMedia, screen } = yield grabContentInfo(ui);
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(ui) {
let { matchesMedia, screen } = yield grabContentInfo(ui);
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(ui) {
return ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
return {
screen: {
width: content.screen.width,
height: content.screen.height
},
innerWidth: content.innerWidth,
matchesMedia: content.matchMedia("(max-device-width:100px)").matches
};
});
}

View file

@ -0,0 +1,140 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests changing viewport DPR
const TEST_URL = "data:text/html;charset=utf-8,DPR list test";
const DEFAULT_DPPX = window.devicePixelRatio;
const VIEWPORT_DPPX = DEFAULT_DPPX + 2;
const Types = require("devtools/client/responsive.html/types");
const testDevice = {
"name": "Fake Phone RDM Test",
"width": 320,
"height": 470,
"pixelRatio": 5.5,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "custom",
"featured": true,
};
// Add the new device to the list
addDeviceForTest(testDevice);
addRDMTask(TEST_URL, function* ({ ui, manager }) {
yield waitStartup(ui);
yield testDefaults(ui);
yield testChangingDevice(ui);
yield testResetWhenResizingViewport(ui);
yield testChangingDPR(ui);
});
function* waitStartup(ui) {
let { store } = ui.toolWindow;
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
}
function* testDefaults(ui) {
info("Test Defaults");
yield testDevicePixelRatio(ui, window.devicePixelRatio);
testViewportDPRSelect(ui, {value: window.devicePixelRatio, disabled: false});
testViewportDeviceSelectLabel(ui, "no device selected");
}
function* testChangingDevice(ui) {
info("Test Changing Device");
let waitPixelRatioChange = onceDevicePixelRatioChange(ui);
yield selectDevice(ui, testDevice.name);
yield waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
yield waitPixelRatioChange;
yield testDevicePixelRatio(ui, testDevice.pixelRatio);
testViewportDPRSelect(ui, {value: testDevice.pixelRatio, disabled: true});
testViewportDeviceSelectLabel(ui, testDevice.name);
}
function* testResetWhenResizingViewport(ui) {
info("Test reset when resizing the viewport");
let waitPixelRatioChange = onceDevicePixelRatioChange(ui);
let deviceRemoved = once(ui, "device-removed");
yield testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
yield deviceRemoved;
yield waitPixelRatioChange;
yield testDevicePixelRatio(ui, window.devicePixelRatio);
testViewportDPRSelect(ui, {value: window.devicePixelRatio, disabled: false});
testViewportDeviceSelectLabel(ui, "no device selected");
}
function* testChangingDPR(ui) {
info("Test changing device pixel ratio");
let waitPixelRatioChange = onceDevicePixelRatioChange(ui);
yield selectDPR(ui, VIEWPORT_DPPX);
yield waitPixelRatioChange;
yield testDevicePixelRatio(ui, VIEWPORT_DPPX);
testViewportDPRSelect(ui, {value: VIEWPORT_DPPX, disabled: false});
testViewportDeviceSelectLabel(ui, "no device selected");
}
function testViewportDPRSelect(ui, expected) {
info("Test viewport's DPR Select");
let select = ui.toolWindow.document.querySelector("#global-dpr-selector > select");
is(select.value, expected.value,
`DPR Select value should be: ${expected.value}`);
is(select.disabled, expected.disabled,
`DPR Select should be ${expected.disabled ? "disabled" : "enabled"}.`);
}
function* testDevicePixelRatio(ui, expected) {
info("Test device pixel ratio");
let dppx = yield getViewportDevicePixelRatio(ui);
is(dppx, expected, `devicePixelRatio should be: ${expected}`);
}
function* getViewportDevicePixelRatio(ui) {
return yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
return content.devicePixelRatio;
});
}
function onceDevicePixelRatioChange(ui) {
return ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
info(`Listening for a pixel ratio change (current: ${content.devicePixelRatio}dppx)`);
let pixelRatio = content.devicePixelRatio;
let mql = content.matchMedia(`(resolution: ${pixelRatio}dppx)`);
return new Promise(resolve => {
const onWindowCreated = () => {
if (pixelRatio !== content.devicePixelRatio) {
resolve();
}
};
addEventListener("DOMWindowCreated", onWindowCreated, {once: true});
mql.addListener(function listener() {
mql.removeListener(listener);
removeEventListener("DOMWindowCreated", onWindowCreated, {once: true});
resolve();
});
});
});
}

View file

@ -0,0 +1,70 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URL = "data:text/html;charset=utf-8,";
// Test global exit button
addRDMTask(TEST_URL, function* (...args) {
yield testExitButton(...args);
});
// Test global exit button on detached tab.
// See Bug 1262806
add_task(function* () {
let tab = yield addTab(TEST_URL);
let { ui, manager } = yield openRDM(tab);
yield waitBootstrap(ui);
let waitTabIsDetached = Promise.all([
once(tab, "TabClose"),
once(tab.linkedBrowser, "SwapDocShells")
]);
// Detach the tab with RDM open.
let newWindow = gBrowser.replaceTabWithWindow(tab);
// Waiting the tab is detached.
yield waitTabIsDetached;
// Get the new tab instance.
tab = newWindow.gBrowser.tabs[0];
// Detaching a tab closes RDM.
ok(!manager.isActiveForTab(tab),
"Responsive Design Mode is not active for the tab");
// Reopen the RDM and test the exit button again.
yield testExitButton(yield openRDM(tab));
yield BrowserTestUtils.closeWindow(newWindow);
});
function* waitBootstrap(ui) {
let { toolWindow, tab } = ui;
let { store } = toolWindow;
let url = String(tab.linkedBrowser.currentURI.spec);
// Wait until the viewport has been added.
yield waitUntilState(store, state => state.viewports.length == 1);
// Wait until the document has been loaded.
yield waitForFrameLoad(ui, url);
}
function* testExitButton({ui, manager}) {
yield waitBootstrap(ui);
let exitButton = ui.toolWindow.document.getElementById("global-exit-button");
ok(manager.isActiveForTab(ui.tab),
"Responsive Design Mode active for the tab");
exitButton.click();
yield once(manager, "off");
ok(!manager.isActiveForTab(ui.tab),
"Responsive Design Mode is not active for the tab");
}

View file

@ -0,0 +1,46 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Verify frame script is active when expected.
const e10s = require("devtools/client/responsive.html/utils/e10s");
const TEST_URL = "http://example.com/";
add_task(function* () {
let tab = yield addTab(TEST_URL);
let { ui } = yield openRDM(tab);
let mm = ui.getViewportBrowser().messageManager;
let { active } = yield e10s.request(mm, "IsActive");
is(active, true, "Frame script is active");
yield closeRDM(tab);
// Must re-get the messageManager on each run since it changes when RDM opens
// or closes due to the design of swapFrameLoaders. Also, we only have access
// to a valid `ui` instance while RDM is open.
mm = tab.linkedBrowser.messageManager;
({ active } = yield e10s.request(mm, "IsActive"));
is(active, false, "Frame script is active");
// Try another round as well to be sure there is no state saved anywhere
({ ui } = yield openRDM(tab));
mm = ui.getViewportBrowser().messageManager;
({ active } = yield e10s.request(mm, "IsActive"));
is(active, true, "Frame script is active");
yield closeRDM(tab);
// Must re-get the messageManager on each run since it changes when RDM opens
// or closes due to the design of swapFrameLoaders. Also, we only have access
// to a valid `ui` instance while RDM is open.
mm = tab.linkedBrowser.messageManager;
({ active } = yield e10s.request(mm, "IsActive"));
is(active, false, "Frame script is active");
yield removeTab(tab);
});

View file

@ -0,0 +1,62 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test RDM menu item is checked when expected, on multiple tabs.
const TEST_URL = "data:text/html;charset=utf-8,";
const tabUtils = require("sdk/tabs/utils");
const { startup } = require("sdk/window/helpers");
const activateTab = (tab) => new Promise(resolve => {
let { tabContainer } = tabUtils.getOwnerWindow(tab).gBrowser;
tabContainer.addEventListener("TabSelect", function listener({type}) {
tabContainer.removeEventListener(type, listener);
resolve();
});
tabUtils.activateTab(tab);
});
const isMenuChecked = () => {
let menu = document.getElementById("menu_responsiveUI");
return menu.getAttribute("checked") === "true";
};
add_task(function* () {
yield startup(window);
ok(!isMenuChecked(),
"RDM menu item is unchecked by default");
const tab = yield addTab(TEST_URL);
ok(!isMenuChecked(),
"RDM menu item is unchecked for new tab");
yield openRDM(tab);
ok(isMenuChecked(),
"RDM menu item is checked with RDM open");
const tab2 = yield addTab(TEST_URL);
ok(!isMenuChecked(),
"RDM menu item is unchecked for new tab");
yield activateTab(tab);
ok(isMenuChecked(),
"RDM menu item is checked for the tab where RDM is open");
yield closeRDM(tab);
ok(!isMenuChecked(),
"RDM menu item is unchecked after RDM is closed");
yield removeTab(tab);
yield removeTab(tab2);
});

View file

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test RDM menu item is checked when expected, on multiple windows.
const TEST_URL = "data:text/html;charset=utf-8,";
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const isMenuCheckedFor = ({document}) => {
let menu = document.getElementById("menu_responsiveUI");
return menu.getAttribute("checked") === "true";
};
add_task(function* () {
const window1 = yield BrowserTestUtils.openNewBrowserWindow();
let { gBrowser } = window1;
yield BrowserTestUtils.withNewTab({ gBrowser, url: TEST_URL },
function* (browser) {
let tab = gBrowser.getTabForBrowser(browser);
is(window1, getMostRecentBrowserWindow(),
"The new window is the active one");
ok(!isMenuCheckedFor(window1),
"RDM menu item is unchecked by default");
yield openRDM(tab);
ok(isMenuCheckedFor(window1),
"RDM menu item is checked with RDM open");
yield closeRDM(tab);
ok(!isMenuCheckedFor(window1),
"RDM menu item is unchecked with RDM closed");
});
yield BrowserTestUtils.closeWindow(window1);
is(window, getMostRecentBrowserWindow(),
"The original window is the active one");
ok(!isMenuCheckedFor(window),
"RDM menu item is unchecked");
});

View file

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URL = "data:text/html;charset=utf-8,";
addRDMTask(TEST_URL, function* ({ ui, manager }) {
let store = ui.toolWindow.store;
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
yield setViewportSize(ui, manager, 300, 300);
// Do horizontal + vertical resize
yield testViewportResize(ui, ".viewport-resize-handle",
[10, 10], [320, 310], [10, 10]);
// Do horizontal resize
yield testViewportResize(ui, ".viewport-horizontal-resize-handle",
[-10, 10], [300, 310], [-10, 0]);
// Do vertical resize
yield testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [300, 300], [0, -10], ui);
});

View file

@ -0,0 +1,98 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the primary browser navigation UI to verify it's connected to the viewport.
const DUMMY_1_URL = "http://example.com/";
const TEST_URL = `${URL_ROOT}doc_page_state.html`;
const DUMMY_2_URL = "http://example.com/browser/";
const DUMMY_3_URL = "http://example.com/browser/devtools/";
add_task(function* () {
// Load up a sequence of pages:
// 0. DUMMY_1_URL
// 1. TEST_URL
// 2. DUMMY_2_URL
let tab = yield addTab(DUMMY_1_URL);
let browser = tab.linkedBrowser;
yield load(browser, TEST_URL);
yield load(browser, DUMMY_2_URL);
// Check session history state
let history = yield getSessionHistory(browser);
is(history.index, 2, "At page 2 in history");
is(history.entries.length, 3, "3 pages in history");
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
// Go back one so we're at the test page
yield back(browser);
// Check session history state
history = yield getSessionHistory(browser);
is(history.index, 1, "At page 1 in history");
is(history.entries.length, 3, "3 pages in history");
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
yield openRDM(tab);
ok(browser.webNavigation.canGoBack, "Going back is allowed");
ok(browser.webNavigation.canGoForward, "Going forward is allowed");
is(browser.documentURI.spec, TEST_URL, "documentURI matches page 1");
is(browser.contentTitle, "Page State Test", "contentTitle matches page 1");
yield forward(browser);
ok(browser.webNavigation.canGoBack, "Going back is allowed");
ok(!browser.webNavigation.canGoForward, "Going forward is not allowed");
is(browser.documentURI.spec, DUMMY_2_URL, "documentURI matches page 2");
is(browser.contentTitle, "mochitest index /browser/", "contentTitle matches page 2");
yield back(browser);
yield back(browser);
ok(!browser.webNavigation.canGoBack, "Going back is not allowed");
ok(browser.webNavigation.canGoForward, "Going forward is allowed");
is(browser.documentURI.spec, DUMMY_1_URL, "documentURI matches page 0");
is(browser.contentTitle, "mochitest index /", "contentTitle matches page 0");
let receivedStatusChanges = new Promise(resolve => {
let statusChangesSeen = 0;
let statusChangesExpected = 2;
let progressListener = {
onStatusChange(webProgress, request, status, message) {
info(message);
if (++statusChangesSeen == statusChangesExpected) {
gBrowser.removeProgressListener(progressListener);
ok(true, `${statusChangesExpected} status changes while loading`);
resolve();
}
}
};
gBrowser.addProgressListener(progressListener);
});
yield load(browser, DUMMY_3_URL);
yield receivedStatusChanges;
ok(browser.webNavigation.canGoBack, "Going back is allowed");
ok(!browser.webNavigation.canGoForward, "Going forward is not allowed");
is(browser.documentURI.spec, DUMMY_3_URL, "documentURI matches page 3");
is(browser.contentTitle, "mochitest index /browser/devtools/",
"contentTitle matches page 3");
yield closeRDM(tab);
// Check session history state
history = yield getSessionHistory(browser);
is(history.index, 1, "At page 1 in history");
is(history.entries.length, 2, "2 pages in history");
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
is(history.entries[1].uri, DUMMY_3_URL, "Page 1 URL matches");
yield removeTab(tab);
});

View file

@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");
// Tests changing network throttling
const TEST_URL = "data:text/html;charset=utf-8,Network throttling test";
addRDMTask(TEST_URL, function* ({ ui, manager }) {
let { store } = ui.toolWindow;
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
// Test defaults
testNetworkThrottlingSelectorLabel(ui, "No throttling");
yield testNetworkThrottlingState(ui, null);
// Test a fast profile
yield testThrottlingProfile(ui, "Wi-Fi");
// Test a slower profile
yield testThrottlingProfile(ui, "Regular 3G");
// Test switching back to no throttling
yield selectNetworkThrottling(ui, "No throttling");
testNetworkThrottlingSelectorLabel(ui, "No throttling");
yield testNetworkThrottlingState(ui, null);
});
function testNetworkThrottlingSelectorLabel(ui, expected) {
let selector = "#global-network-throttling-selector";
let select = ui.toolWindow.document.querySelector(selector);
is(select.selectedOptions[0].textContent, expected,
`Select label should be changed to ${expected}`);
}
var testNetworkThrottlingState = Task.async(function* (ui, expected) {
let state = yield ui.emulationFront.getNetworkThrottling();
Assert.deepEqual(state, expected, "Network throttling state should be " +
JSON.stringify(expected, null, 2));
});
var testThrottlingProfile = Task.async(function* (ui, profile) {
yield selectNetworkThrottling(ui, profile);
testNetworkThrottlingSelectorLabel(ui, profile);
let data = throttlingProfiles.find(({ id }) => id == profile);
let { download, upload, latency } = data;
yield testNetworkThrottlingState(ui, {
downloadThroughput: download,
uploadThroughput: upload,
latency,
});
});

View file

@ -0,0 +1,76 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test page state to ensure page is not reloaded and session history is not
// modified.
const DUMMY_1_URL = "http://example.com/";
const TEST_URL = `${URL_ROOT}doc_page_state.html`;
const DUMMY_2_URL = "http://example.com/browser/";
add_task(function* () {
// Load up a sequence of pages:
// 0. DUMMY_1_URL
// 1. TEST_URL
// 2. DUMMY_2_URL
let tab = yield addTab(DUMMY_1_URL);
let browser = tab.linkedBrowser;
yield load(browser, TEST_URL);
yield load(browser, DUMMY_2_URL);
// Check session history state
let history = yield getSessionHistory(browser);
is(history.index, 2, "At page 2 in history");
is(history.entries.length, 3, "3 pages in history");
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
// Go back one so we're at the test page
yield back(browser);
// Check session history state
history = yield getSessionHistory(browser);
is(history.index, 1, "At page 1 in history");
is(history.entries.length, 3, "3 pages in history");
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
// Click on content to set an altered state that would be lost on reload
yield BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser);
let { ui } = yield openRDM(tab);
// Check color inside the viewport
let color = yield spawnViewportTask(ui, {}, function* () {
// eslint-disable-next-line mozilla/no-cpows-in-tests
return content.getComputedStyle(content.document.body)
.getPropertyValue("background-color");
});
is(color, "rgb(0, 128, 0)",
"Content is still modified from click in viewport");
yield closeRDM(tab);
// Check color back in the browser tab
color = yield ContentTask.spawn(browser, {}, function* () {
// eslint-disable-next-line mozilla/no-cpows-in-tests
return content.getComputedStyle(content.document.body)
.getPropertyValue("background-color");
});
is(color, "rgb(0, 128, 0)",
"Content is still modified from click in browser tab");
// Check session history state
history = yield getSessionHistory(browser);
is(history.index, 1, "At page 1 in history");
is(history.entries.length, 3, "3 pages in history");
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
yield removeTab(tab);
});

View file

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that permission popups asking for user approval still appear in RDM
const DUMMY_URL = "http://example.com/";
const TEST_URL = `${URL_ROOT}geolocation.html`;
function waitForGeolocationPrompt(win, browser) {
return new Promise(resolve => {
win.PopupNotifications.panel.addEventListener("popupshown", function popupShown() {
let notification = win.PopupNotifications.getNotification("geolocation", browser);
if (notification) {
win.PopupNotifications.panel.removeEventListener("popupshown", popupShown);
resolve();
}
});
});
}
add_task(function* () {
let tab = yield addTab(DUMMY_URL);
let browser = tab.linkedBrowser;
let win = browser.ownerGlobal;
let waitPromptPromise = waitForGeolocationPrompt(win, browser);
// Checks if a geolocation permission doorhanger appears when openning a page
// requesting geolocation
yield load(browser, TEST_URL);
yield waitPromptPromise;
ok(true, "Permission doorhanger appeared without RDM enabled");
// Lets switch back to the dummy website and enable RDM
yield load(browser, DUMMY_URL);
let { ui } = yield openRDM(tab);
let newBrowser = ui.getViewportBrowser();
waitPromptPromise = waitForGeolocationPrompt(win, newBrowser);
// Checks if the doorhanger appeared again when reloading the geolocation
// page inside RDM
yield load(browser, TEST_URL);
yield waitPromptPromise;
ok(true, "Permission doorhanger appeared inside RDM");
yield closeRDM(tab);
yield removeTab(tab);
});

View file

@ -0,0 +1,148 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* global ResponsiveUIManager */
/* eslint key-spacing: 0 */
add_task(function* () {
let manager = ResponsiveUIManager;
let done;
function isOpen() {
return ResponsiveUIManager.isActiveForTab(gBrowser.selectedTab);
}
const TEST_URL = "data:text/html;charset=utf-8,hi";
yield helpers.addTabWithToolbar(TEST_URL, (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");
}),
},
]);
});
yield helpers.addTabWithToolbar(TEST_URL, (options) => {
return helpers.audit(options, [
{
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");
}),
},
]);
});
yield helpers.addTabWithToolbar(TEST_URL, (options) => {
return helpers.audit(options, [
{
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");
}),
},
]);
});
});

View file

@ -0,0 +1,59 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test global exit button
const TEST_URL = "data:text/html;charset=utf-8,";
const { OS } = require("resource://gre/modules/osfile.jsm");
function* waitUntilScreenshot() {
return new Promise(Task.async(function* (resolve) {
let { Downloads } = require("resource://gre/modules/Downloads.jsm");
let list = yield Downloads.getList(Downloads.ALL);
let view = {
onDownloadAdded: download => {
download.whenSucceeded().then(() => {
resolve(download.target.path);
list.removeView(view);
});
}
};
yield list.addView(view);
}));
}
addRDMTask(TEST_URL, function* ({ ui: {toolWindow} }) {
let { store, document } = toolWindow;
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
info("Click the screenshot button");
let screenshotButton = document.getElementById("global-screenshot-button");
screenshotButton.click();
let whenScreenshotSucceeded = waitUntilScreenshot();
let filePath = yield whenScreenshotSucceeded;
let image = new Image();
image.src = OS.Path.toFileURI(filePath);
yield once(image, "load");
// We have only one viewport at the moment
let viewport = store.getState().viewports[0];
let ratio = window.devicePixelRatio;
is(image.width, viewport.width * ratio,
"screenshot width has the expected width");
is(image.height, viewport.height * ratio,
"screenshot width has the expected height");
yield OS.File.remove(filePath);
});

View file

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Verify RDM closes synchronously when tabs are closed.
const TEST_URL = "http://example.com/";
add_task(function* () {
let tab = yield addTab(TEST_URL);
let { ui } = yield openRDM(tab);
let clientClosed = waitForClientClose(ui);
closeRDM(tab, {
reason: "TabClose",
});
// This flag is set at the end of `ResponsiveUI.destroy`. If it is true
// without yielding on `closeRDM` above, then we must have closed
// synchronously.
is(ui.destroyed, true, "RDM closed synchronously");
yield clientClosed;
yield removeTab(tab);
});
add_task(function* () {
let tab = yield addTab(TEST_URL);
let { ui } = yield openRDM(tab);
let clientClosed = waitForClientClose(ui);
yield removeTab(tab);
// This flag is set at the end of `ResponsiveUI.destroy`. If it is true without
// yielding on `closeRDM` itself and only removing the tab, then we must have closed
// synchronously in response to tab closing.
is(ui.destroyed, true, "RDM closed synchronously");
yield clientClosed;
});

View file

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Verify RDM closes synchronously when tabs change remoteness.
const TEST_URL = "http://example.com/";
add_task(function* () {
let tab = yield addTab(TEST_URL);
let { ui } = yield openRDM(tab);
let clientClosed = waitForClientClose(ui);
closeRDM(tab, {
reason: "BeforeTabRemotenessChange",
});
// This flag is set at the end of `ResponsiveUI.destroy`. If it is true
// without yielding on `closeRDM` above, then we must have closed
// synchronously.
is(ui.destroyed, true, "RDM closed synchronously");
yield clientClosed;
yield removeTab(tab);
});
add_task(function* () {
let tab = yield addTab(TEST_URL);
let { ui } = yield openRDM(tab);
let clientClosed = waitForClientClose(ui);
// Load URL that requires the main process, forcing a remoteness flip
yield load(tab.linkedBrowser, "about:robots");
// This flag is set at the end of `ResponsiveUI.destroy`. If it is true without
// yielding on `closeRDM` itself and only removing the tab, then we must have closed
// synchronously in response to tab closing.
is(ui.destroyed, true, "RDM closed synchronously");
yield clientClosed;
yield removeTab(tab);
});

View file

@ -0,0 +1,63 @@
/* 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>";
addRDMTask(TEST_URI, function* ({ ui, manager }) {
info("Open the responsive design mode and set its size to 500x500 to start");
yield setViewportSize(ui, 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, ui, manager);
info("Try growing the viewport and checking the applied styles");
yield testGrow(view, inspector, ui, manager);
yield closeToolbox();
});
function* testShrink(computedView, inspector, ui, manager) {
is(computedWidth(computedView), "500px", "Should show 500px initially.");
let onRefresh = inspector.once("computed-view-refreshed");
yield setViewportSize(ui, manager, 100, 100);
yield onRefresh;
is(computedWidth(computedView), "100px", "Should be 100px after shrinking.");
}
function* testGrow(computedView, inspector, ui, manager) {
let onRefresh = inspector.once("computed-view-refreshed");
yield setViewportSize(ui, 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;
}

View file

@ -0,0 +1,78 @@
/* 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.
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>";
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
});
addRDMTask(TEST_URI, function* ({ ui, manager }) {
info("Open the responsive design mode and set its size to 500x500 to start");
yield setViewportSize(ui, 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, ui, manager);
info("Try growing the viewport and checking the applied styles");
yield testGrow(view, ui, manager);
info("Check that ESC still opens the split console");
yield testEscapeOpensSplitConsole(inspector);
yield closeToolbox();
});
function* testShrink(ruleView, ui, 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 setViewportSize(ui, manager, 100, 100);
yield onRefresh;
is(numberOfRules(ruleView), 3, "Should have three rules after shrinking.");
}
function* testGrow(ruleView, ui, manager) {
info("Resize to 500x500 and wait for the rule-view to update");
let onRefresh = ruleView.once("ruleview-refreshed");
yield setViewportSize(ui, 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 numberOfRules(ruleView) {
return ruleView.element.querySelectorAll(".ruleview-code").length;
}

View file

@ -0,0 +1,128 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Verify that toolbox remains open when opening and closing RDM.
const TEST_URL = "http://example.com/";
function getServerConnections(browser) {
ok(browser.isRemoteBrowser, "Content browser is remote");
return ContentTask.spawn(browser, {}, function* () {
const Cu = Components.utils;
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { DebuggerServer } = require("devtools/server/main");
if (!DebuggerServer._connections) {
return 0;
}
return Object.getOwnPropertyNames(DebuggerServer._connections);
});
}
let checkServerConnectionCount = Task.async(function* (browser, expected, msg) {
let conns = yield getServerConnections(browser);
is(conns.length || 0, expected, "Server connection count: " + msg);
});
let checkToolbox = Task.async(function* (tab, location) {
let target = TargetFactory.forTab(tab);
ok(!!gDevTools.getToolbox(target), `Toolbox exists ${location}`);
});
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
set: [["dom.ipc.processCount", 1]]
});
});
add_task(function* () {
let tab = yield addTab(TEST_URL);
let tabsInDifferentProcesses = E10S_MULTI_ENABLED &&
(gBrowser.tabs[0].linkedBrowser.frameLoader.childID !=
gBrowser.tabs[1].linkedBrowser.frameLoader.childID);
info("Open toolbox outside RDM");
{
// 0: No DevTools connections yet
yield checkServerConnectionCount(tab.linkedBrowser, 0,
"0: No DevTools connections yet");
let { toolbox } = yield openInspector();
if (tabsInDifferentProcesses) {
// 1: Two tabs open, but only one per content process
yield checkServerConnectionCount(tab.linkedBrowser, 1,
"1: Two tabs open, but only one per content process");
} else {
// 2: One for each tab (starting tab plus the one we opened)
yield checkServerConnectionCount(tab.linkedBrowser, 2,
"2: One for each tab (starting tab plus the one we opened)");
}
yield checkToolbox(tab, "outside RDM");
let { ui } = yield openRDM(tab);
if (tabsInDifferentProcesses) {
// 2: RDM UI adds an extra connection, 1 + 1 = 2
yield checkServerConnectionCount(ui.getViewportBrowser(), 2,
"2: RDM UI uses an extra connection");
} else {
// 3: RDM UI adds an extra connection, 2 + 1 = 3
yield checkServerConnectionCount(ui.getViewportBrowser(), 3,
"3: RDM UI uses an extra connection");
}
yield checkToolbox(tab, "after opening RDM");
yield closeRDM(tab);
if (tabsInDifferentProcesses) {
// 1: RDM UI closed, return to previous connection count
yield checkServerConnectionCount(tab.linkedBrowser, 1,
"1: RDM UI closed, return to previous connection count");
} else {
// 2: RDM UI closed, return to previous connection count
yield checkServerConnectionCount(tab.linkedBrowser, 2,
"2: RDM UI closed, return to previous connection count");
}
yield checkToolbox(tab, tab.linkedBrowser, "after closing RDM");
yield toolbox.destroy();
// 0: All DevTools usage closed
yield checkServerConnectionCount(tab.linkedBrowser, 0,
"0: All DevTools usage closed");
}
info("Open toolbox inside RDM");
{
// 0: No DevTools connections yet
yield checkServerConnectionCount(tab.linkedBrowser, 0,
"0: No DevTools connections yet");
let { ui } = yield openRDM(tab);
// 1: RDM UI uses an extra connection
yield checkServerConnectionCount(ui.getViewportBrowser(), 1,
"1: RDM UI uses an extra connection");
let { toolbox } = yield openInspector();
if (tabsInDifferentProcesses) {
// 2: Two tabs open, but only one per content process
yield checkServerConnectionCount(ui.getViewportBrowser(), 2,
"2: Two tabs open, but only one per content process");
} else {
// 3: One for each tab (starting tab plus the one we opened)
yield checkServerConnectionCount(ui.getViewportBrowser(), 3,
"3: One for each tab (starting tab plus the one we opened)");
}
yield checkToolbox(tab, ui.getViewportBrowser(), "inside RDM");
yield closeRDM(tab);
if (tabsInDifferentProcesses) {
// 1: RDM UI closed, one less connection
yield checkServerConnectionCount(tab.linkedBrowser, 1,
"1: RDM UI closed, one less connection");
} else {
// 2: RDM UI closed, one less connection
yield checkServerConnectionCount(tab.linkedBrowser, 2,
"2: RDM UI closed, one less connection");
}
yield checkToolbox(tab, tab.linkedBrowser, "after closing RDM");
yield toolbox.destroy();
// 0: All DevTools usage closed
yield checkServerConnectionCount(tab.linkedBrowser, 0,
"0: All DevTools usage closed");
}
yield removeTab(tab);
});

View file

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests changing viewport touch simulation
const TEST_URL = "data:text/html;charset=utf-8,touch simulation test";
const Types = require("devtools/client/responsive.html/types");
const testDevice = {
"name": "Fake Phone RDM Test",
"width": 320,
"height": 470,
"pixelRatio": 5.5,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "custom",
"featured": true,
};
// Add the new device to the list
addDeviceForTest(testDevice);
addRDMTask(TEST_URL, function* ({ ui, manager }) {
yield waitStartup(ui);
yield testDefaults(ui);
yield testChangingDevice(ui);
yield testResizingViewport(ui, true, false);
yield testEnableTouchSimulation(ui);
yield testResizingViewport(ui, false, true);
});
function* waitStartup(ui) {
let { store } = ui.toolWindow;
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
}
function* testDefaults(ui) {
info("Test Defaults");
yield testTouchEventsOverride(ui, false);
testViewportDeviceSelectLabel(ui, "no device selected");
}
function* testChangingDevice(ui) {
info("Test Changing Device");
yield selectDevice(ui, testDevice.name);
yield waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
yield testTouchEventsOverride(ui, true);
testViewportDeviceSelectLabel(ui, testDevice.name);
}
function* testResizingViewport(ui, device, expected) {
info(`Test resizing the viewport, device ${device}, expected ${expected}`);
let deviceRemoved = once(ui, "device-removed");
yield testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
if (device) {
yield deviceRemoved;
}
yield testTouchEventsOverride(ui, expected);
testViewportDeviceSelectLabel(ui, "no device selected");
}
function* testEnableTouchSimulation(ui) {
info("Test enabling touch simulation via button");
yield enableTouchSimulation(ui);
yield testTouchEventsOverride(ui, true);
}

View file

@ -0,0 +1,228 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test global touch simulation button
const TEST_URL = `${URL_ROOT}touch.html`;
const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled";
addRDMTask(TEST_URL, function* ({ ui }) {
yield waitBootstrap(ui);
yield testWithNoTouch(ui);
yield enableTouchSimulation(ui);
yield testWithTouch(ui);
yield testWithMetaViewportEnabled(ui);
yield testWithMetaViewportDisabled(ui);
testTouchButton(ui);
});
function* testWithNoTouch(ui) {
yield injectEventUtils(ui);
yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
let { EventUtils } = content;
let div = content.document.querySelector("div");
let x = 0, y = 0;
info("testWithNoTouch: Initial test parameter and mouse mouse outside div");
x = -1; y = -1;
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mousemove", isSynthesized: false }, content);
div.style.transform = "none";
div.style.backgroundColor = "";
info("testWithNoTouch: Move mouse into the div element");
yield EventUtils.synthesizeMouseAtCenter(div,
{ type: "mousemove", isSynthesized: false }, content);
is(div.style.backgroundColor, "red", "mouseenter or mouseover should work");
info("testWithNoTouch: Drag the div element");
yield EventUtils.synthesizeMouseAtCenter(div,
{ type: "mousedown", isSynthesized: false }, content);
x = 100; y = 100;
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mousemove", isSynthesized: false }, content);
is(div.style.transform, "none", "touchmove shouldn't work");
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mouseup", isSynthesized: false }, content);
info("testWithNoTouch: Move mouse out of the div element");
x = -1; y = -1;
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mousemove", isSynthesized: false }, content);
is(div.style.backgroundColor, "blue", "mouseout or mouseleave should work");
info("testWithNoTouch: Click the div element");
yield EventUtils.synthesizeClick(div);
is(div.dataset.isDelay, "false",
"300ms delay between touch events and mouse events should not work");
});
}
function* testWithTouch(ui) {
yield injectEventUtils(ui);
yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
let { EventUtils } = content;
let div = content.document.querySelector("div");
let x = 0, y = 0;
info("testWithTouch: Initial test parameter and mouse mouse outside div");
x = -1; y = -1;
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mousemove", isSynthesized: false }, content);
div.style.transform = "none";
div.style.backgroundColor = "";
info("testWithTouch: Move mouse into the div element");
yield EventUtils.synthesizeMouseAtCenter(div,
{ type: "mousemove", isSynthesized: false }, content);
isnot(div.style.backgroundColor, "red",
"mouseenter or mouseover should not work");
info("testWithTouch: Drag the div element");
yield EventUtils.synthesizeMouseAtCenter(div,
{ type: "mousedown", isSynthesized: false }, content);
x = 100; y = 100;
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mousemove", isSynthesized: false }, content);
isnot(div.style.transform, "none", "touchmove should work");
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mouseup", isSynthesized: false }, content);
info("testWithTouch: Move mouse out of the div element");
x = -1; y = -1;
yield EventUtils.synthesizeMouse(div, x, y,
{ type: "mousemove", isSynthesized: false }, content);
isnot(div.style.backgroundColor, "blue",
"mouseout or mouseleave should not work");
});
}
function* testWithMetaViewportEnabled(ui) {
yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, true]]});
yield injectEventUtils(ui);
yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
let { synthesizeClick } = content.EventUtils;
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(ui) {
yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, false]]});
yield injectEventUtils(ui);
yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
let { synthesizeClick } = content.EventUtils;
let meta = content.document.querySelector("meta[name=viewport]");
let div = content.document.querySelector("div");
div.dataset.isDelay = "false";
info("testWithMetaViewportDisabled: click the div 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 testTouchButton(ui) {
let { document } = ui.toolWindow;
let touchButton = document.querySelector("#global-touch-simulation-button");
ok(touchButton.classList.contains("active"),
"Touch simulation is active at end of test.");
touchButton.click();
ok(!touchButton.classList.contains("active"),
"Touch simulation is stopped on click.");
touchButton.click();
ok(touchButton.classList.contains("active"),
"Touch simulation is started on click.");
}
function* waitBootstrap(ui) {
let { store } = ui.toolWindow;
yield waitUntilState(store, state => state.viewports.length == 1);
yield waitForFrameLoad(ui, TEST_URL);
}
function* injectEventUtils(ui) {
yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
if ("EventUtils" in content) {
return;
}
let EventUtils = content.EventUtils = {};
EventUtils.window = {};
EventUtils.parent = EventUtils.window;
/* eslint-disable camelcase */
EventUtils._EU_Ci = Components.interfaces;
EventUtils._EU_Cc = Components.classes;
/* eslint-enable camelcase */
// EventUtils' `sendChar` function relies on the navigator to synthetize events.
EventUtils.navigator = content.navigator;
EventUtils.KeyboardEvent = content.KeyboardEvent;
EventUtils.synthesizeClick = element => new Promise(resolve => {
element.addEventListener("click", function onClick() {
element.removeEventListener("click", onClick);
resolve();
});
EventUtils.synthesizeMouseAtCenter(element,
{ type: "mousedown", isSynthesized: false }, content);
EventUtils.synthesizeMouseAtCenter(element,
{ type: "mouseup", isSynthesized: false }, content);
});
Services.scriptloader.loadSubScript(
"chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
});
}

View file

@ -0,0 +1,30 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test viewports basics after opening, like size and location
const TEST_URL = "http://example.org/";
addRDMTask(TEST_URL, function* ({ ui }) {
let store = ui.toolWindow.store;
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
// A single viewport of default size appeared
let viewport = ui.toolWindow.document.querySelector(".viewport-content");
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
"320px", "Viewport has default width");
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
"480px", "Viewport has default height");
// Browser's location should match original tab
yield waitForFrameLoad(ui, TEST_URL);
let location = yield spawnViewportTask(ui, {}, function* () {
return content.location.href; // eslint-disable-line
});
is(location, TEST_URL, "Viewport location matches");
});

View file

@ -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("data:text/html;charset=utf-8,", "_blank");
let newWindow = yield newWindowPromise;
newWindow.focus();
yield once(newWindow.gBrowser, "load", true);
let tab = newWindow.gBrowser.selectedTab;
yield openRDM(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;
});

View file

@ -0,0 +1,651 @@
{
"TYPES": [ "phones", "tablets", "laptops", "televisions", "consoles", "watches" ],
"phones": [
{
"name": "Firefox OS Flame",
"width": 320,
"height": 570,
"pixelRatio": 1.5,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "Alcatel One Touch Fire",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; ALCATELOneTouch4012X; rv:28.0) Gecko/28.0 Firefox/28.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "Alcatel One Touch Fire C",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; ALCATELOneTouch4019X; rv:28.0) Gecko/28.0 Firefox/28.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "Alcatel One Touch Fire E",
"width": 320,
"height": 480,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Mobile; ALCATELOneTouch6015X; rv:32.0) Gecko/32.0 Firefox/32.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "Apple iPhone 4",
"width": 320,
"height": 480,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
"touch": true,
"firefoxOS": false,
"os": "ios"
},
{
"name": "Apple iPhone 5",
"width": 320,
"height": 568,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
"touch": true,
"firefoxOS": false,
"os": "ios"
},
{
"name": "Apple iPhone 5s",
"width": 320,
"height": 568,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1",
"touch": true,
"firefoxOS": false,
"os": "ios",
"featured": true
},
{
"name": "Apple iPhone 6",
"width": 375,
"height": 667,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
"touch": true,
"firefoxOS": false,
"os": "ios"
},
{
"name": "Apple iPhone 6 Plus",
"width": 414,
"height": 736,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
"touch": true,
"firefoxOS": false,
"os": "ios",
"featured": true
},
{
"name": "Apple iPhone 6s",
"width": 375,
"height": 667,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
"touch": true,
"firefoxOS": false,
"os": "ios",
"featured": true
},
{
"name": "Apple iPhone 6s Plus",
"width": 414,
"height": 736,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
"touch": true,
"firefoxOS": false,
"os": "ios"
},
{
"name": "BlackBerry Z30",
"width": 360,
"height": 640,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
"touch": true,
"firefoxOS": false,
"os": "blackberryos"
},
{
"name": "Geeksphone Keon",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Geeksphone Peak, Revolution",
"width": 360,
"height": 640,
"pixelRatio": 1.5,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Google Nexus S",
"width": 320,
"height": 533,
"pixelRatio": 1.5,
"userAgent": "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Nexus S Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Google Nexus 4",
"width": 384,
"height": 640,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 4 Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 Mobile Safari/537.36",
"touch": true,
"firefoxOS": true,
"os": "android",
"featured": true
},
{
"name": "Google Nexus 5",
"width": 360,
"height": 640,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 5 Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 Mobile Safari/537.36",
"touch": true,
"firefoxOS": true,
"os": "android",
"featured": true
},
{
"name": "Google Nexus 6",
"width": 412,
"height": 732,
"pixelRatio": 3.5,
"userAgent": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36",
"touch": true,
"firefoxOS": true,
"os": "android",
"featured": true
},
{
"name": "Intex Cloud Fx",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; rv:32.0) Gecko/32.0 Firefox/32.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "KDDI Fx0",
"width": 360,
"height": 640,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Mobile; LGL25; rv:32.0) Gecko/32.0 Firefox/32.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "LG Fireweb",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; LG-D300; rv:18.1) Gecko/18.1 Firefox/18.1",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "LG Optimus L70",
"width": 384,
"height": 640,
"pixelRatio": 1.25,
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.1599.103 Mobile Safari/537.36",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "Nokia Lumia 520",
"width": 320,
"height": 533,
"pixelRatio": 1.4,
"userAgent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)",
"touch": true,
"firefoxOS": false,
"os": "android",
"featured": true
},
{
"name": "Nokia N9",
"width": 360,
"height": 640,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "OnePlus One",
"width": 360,
"height": 640,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (Android 5.1.1; Mobile; rv:43.0) Gecko/43.0 Firefox/43.0",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "Samsung Galaxy S3",
"width": 360,
"height": 640,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "Samsung Galaxy S4",
"width": 360,
"height": 640,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "Samsung Galaxy S5",
"width": 360,
"height": 640,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36",
"touch": true,
"firefoxOS": false,
"os": "android",
"featured": true
},
{
"name": "Samsung Galaxy S6",
"width": 360,
"height": 640,
"pixelRatio": 4,
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36",
"touch": true,
"firefoxOS": false,
"os": "android",
"featured": true
},
{
"name": "Sony Xperia Z3",
"width": 360,
"height": 640,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Spice Fire One Mi-FX1",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "Symphony GoFox F15",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; rv:30.0) Gecko/30.0 Firefox/30.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "ZTE Open",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; ZTEOPEN; rv:18.1) Gecko/18.0 Firefox/18.1",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "ZTE Open II",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; OPEN2; rv:28.0) Gecko/28.0 Firefox/28.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "ZTE Open C",
"width": 320,
"height": 450,
"pixelRatio": 1.5,
"userAgent": "Mozilla/5.0 (Mobile; OPENC; rv:32.0) Gecko/32.0 Firefox/32.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
},
{
"name": "Zen Fire 105",
"width": 320,
"height": 480,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
}
],
"tablets": [
{
"name": "Amazon Kindle Fire HDX 8.9",
"width": 1280,
"height": 800,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
"touch": true,
"firefoxOS": false,
"os": "fireos",
"featured": true
},
{
"name": "Apple iPad",
"width": 1024,
"height": 768,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
"touch": true,
"firefoxOS": false,
"os": "ios"
},
{
"name": "Apple iPad Air 2",
"width": 1024,
"height": 768,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPad; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8L1 Safari/6533.18.5",
"touch": true,
"firefoxOS": false,
"os": "ios",
"featured": true
},
{
"name": "Apple iPad Mini",
"width": 1024,
"height": 768,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (iPad; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8L1 Safari/6533.18.5",
"touch": true,
"firefoxOS": false,
"os": "ios"
},
{
"name": "Apple iPad Mini 2",
"width": 1024,
"height": 768,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (iPad; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8L1 Safari/6533.18.5",
"touch": true,
"firefoxOS": false,
"os": "ios",
"featured": true
},
{
"name": "BlackBerry PlayBook",
"width": 1024,
"height": 600,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+",
"touch": true,
"firefoxOS": false,
"os": "blackberryos"
},
{
"name": "Foxconn InFocus",
"width": 1280,
"height": 800,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Tablet; rv:32.0) Gecko/32.0 Firefox/32.0",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Google Nexus 7",
"width": 960,
"height": 600,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 Mobile Safari/537.36",
"touch": true,
"firefoxOS": false,
"os": "android",
"featured": true
},
{
"name": "Google Nexus 10",
"width": 1280,
"height": 800,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Linux; Android 4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 Mobile Safari/537.36",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "Samsung Galaxy Note 2",
"width": 360,
"height": 640,
"pixelRatio": 2,
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"touch": true,
"firefoxOS": false,
"os": "android"
},
{
"name": "Samsung Galaxy Note 3",
"width": 360,
"height": 640,
"pixelRatio": 3,
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"touch": true,
"firefoxOS": false,
"os": "android",
"featured": true
},
{
"name": "Tesla Model S",
"width": 1200,
"height": 1920,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (X11; Linux) AppleWebKit/534.34 (KHTML, like Gecko) QtCarBrowser Safari/534.34",
"touch": true,
"firefoxOS": false,
"os": "linux"
},
{
"name": "VIA Vixen",
"width": 1024,
"height": 600,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Tablet; rv:32.0) Gecko/32.0 Firefox/32.0",
"touch": true,
"firefoxOS": true,
"os": "fxos"
}
],
"laptops": [
{
"name": "Laptop (1366 x 768)",
"width": 1366,
"height": 768,
"pixelRatio": 1,
"userAgent": "",
"touch": false,
"firefoxOS": false,
"os": "windows",
"featured": true
},
{
"name": "Laptop (1920 x 1080)",
"width": 1280,
"height": 720,
"pixelRatio": 1.5,
"userAgent": "",
"touch": false,
"firefoxOS": false,
"os": "windows",
"featured": true
},
{
"name": "Laptop (1920 x 1080) with touch",
"width": 1280,
"height": 720,
"pixelRatio": 1.5,
"userAgent": "",
"touch": true,
"firefoxOS": false,
"os": "windows"
}
],
"televisions": [
{
"name": "720p HD Television",
"width": 1280,
"height": 720,
"pixelRatio": 1,
"userAgent": "",
"touch": false,
"firefoxOS": true,
"os": "custom"
},
{
"name": "1080p Full HD Television",
"width": 1920,
"height": 1080,
"pixelRatio": 1,
"userAgent": "",
"touch": false,
"firefoxOS": true,
"os": "custom"
},
{
"name": "4K Ultra HD Television",
"width": 3840,
"height": 2160,
"pixelRatio": 1,
"userAgent": "",
"touch": false,
"firefoxOS": true,
"os": "custom"
}
],
"consoles": [
{
"name": "Nintendo 3DS",
"width": 320,
"height": 240,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7585.EU",
"touch": true,
"firefoxOS": false,
"os": "nintendo"
},
{
"name": "Nintendo Wii U Gamepad",
"width": 854,
"height": 480,
"pixelRatio": 0.87,
"userAgent": "Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.28 (KHTML, like Gecko) NX/3.0.3.12.15 NintendoBrowser/4.1.1.9601.EU",
"touch": true,
"firefoxOS": false,
"os": "nintendo"
},
{
"name": "Sony PlayStation Vita",
"width": 960,
"height": 544,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Playstation Vita 1.61) AppleWebKit/531.22.8 (KHTML, like Gecko) Silk/3.2",
"touch": true,
"firefoxOS": false,
"os": "playstation"
}
],
"watches": [
{
"name": "LG G Watch",
"width": 280,
"height": 280,
"pixelRatio": 1,
"userAgent": "",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "LG G Watch R",
"width": 320,
"height": 320,
"pixelRatio": 1,
"userAgent": "",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Motorola Moto 360",
"width": 320,
"height": 290,
"pixelRatio": 1,
"userAgent": "Mozilla/5.0 (Linux; Android 5.0.1; Moto 360 Build/LWX48T) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36",
"touch": true,
"firefoxOS": true,
"os": "android"
},
{
"name": "Samsung Gear Live",
"width": 320,
"height": 320,
"pixelRatio": 1,
"userAgent": "",
"touch": true,
"firefoxOS": true,
"os": "android"
}
]
}

View file

@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<title>Page State Test</title>
<style>
body {
height: 100vh;
background: red;
}
body.modified {
background: green;
}
</style>
</head>
<body onclick="this.classList.add('modified')"/>
</html>

View file

@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Geolocation permission test</title>
</head>
<body>
<script type="text/javascript">
"use strict";
navigator.geolocation.getCurrentPosition(function (pos) {});
</script>
</body>
</html>

View file

@ -0,0 +1,401 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../../framework/test/shared-head.js */
/* import-globals-from ../../../framework/test/shared-redux-head.js */
/* import-globals-from ../../../commandline/test/helpers.js */
/* import-globals-from ../../../inspector/test/shared-head.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
this);
// Import the GCLI test helper
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
this);
// Import helpers registering the test-actor in remote targets
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
this);
// Import helpers for the inspector that are also shared with others
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
this);
const E10S_MULTI_ENABLED = Services.prefs.getIntPref("dom.ipc.processCount") > 1;
const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.html/test/browser/";
const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
const { getOwnerWindow } = require("sdk/tabs/utils");
const asyncStorage = require("devtools/shared/async-storage");
const { addDevice, removeDevice } = require("devtools/client/shared/devices");
SimpleTest.requestCompleteLog();
SimpleTest.waitForExplicitFinish();
// Toggling the RDM UI involves several docShell swap operations, which are somewhat slow
// on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
// should be enough.
requestLongerTimeout(2);
flags.testing = true;
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
Services.prefs.setCharPref("devtools.devices.url",
TEST_URI_ROOT + "devices.json");
Services.prefs.setBoolPref("devtools.responsive.html.enabled", true);
registerCleanupFunction(() => {
flags.testing = false;
Services.prefs.clearUserPref("devtools.devices.url");
Services.prefs.clearUserPref("devtools.responsive.html.enabled");
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
asyncStorage.removeItem("devtools.devices.url_cache");
});
// This depends on the "devtools.responsive.html.enabled" pref
const { ResponsiveUIManager } = require("resource://devtools/client/responsivedesign/responsivedesign.jsm");
/**
* Open responsive design mode for the given tab.
*/
var openRDM = Task.async(function* (tab) {
info("Opening responsive design mode");
let manager = ResponsiveUIManager;
let ui = yield manager.openIfNeeded(getOwnerWindow(tab), tab);
info("Responsive design mode opened");
return { ui, manager };
});
/**
* Close responsive design mode for the given tab.
*/
var closeRDM = Task.async(function* (tab, options) {
info("Closing responsive design mode");
let manager = ResponsiveUIManager;
yield manager.closeIfNeeded(getOwnerWindow(tab), tab, options);
info("Responsive design mode closed");
});
/**
* Adds a new test task that adds a tab with the given URL, opens responsive
* design mode, runs the given generator, closes responsive design mode, and
* removes the tab.
*
* Example usage:
*
* addRDMTask(TEST_URL, function*({ ui, manager }) {
* // Your tests go here...
* });
*/
function addRDMTask(url, generator) {
add_task(function* () {
const tab = yield addTab(url);
const results = yield openRDM(tab);
try {
yield* generator(results);
} catch (err) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err));
}
yield closeRDM(tab);
yield removeTab(tab);
});
}
function spawnViewportTask(ui, args, task) {
return ContentTask.spawn(ui.getViewportBrowser(), args, task);
}
function waitForFrameLoad(ui, targetURL) {
return spawnViewportTask(ui, { targetURL }, function* (args) {
if ((content.document.readyState == "complete" ||
content.document.readyState == "interactive") &&
content.location.href == args.targetURL) {
return;
}
yield ContentTaskUtils.waitForEvent(this, "DOMContentLoaded");
});
}
function waitForViewportResizeTo(ui, width, height) {
return new Promise(Task.async(function* (resolve) {
let isSizeMatching = (data) => data.width == width && data.height == height;
// If the viewport has already the expected size, we resolve the promise immediately.
let size = yield getContentSize(ui);
if (isSizeMatching(size)) {
resolve();
return;
}
// Otherwise, we'll listen to both content's resize event and browser's load end;
// since a racing condition can happen, where the content's listener is added after
// the resize, because the content's document was reloaded; therefore the test would
// hang forever. See bug 1302879.
let browser = ui.getViewportBrowser();
let onResize = (_, data) => {
if (!isSizeMatching(data)) {
return;
}
ui.off("content-resize", onResize);
browser.removeEventListener("mozbrowserloadend", onBrowserLoadEnd);
info(`Got content-resize to ${width} x ${height}`);
resolve();
};
let onBrowserLoadEnd = Task.async(function* () {
let data = yield getContentSize(ui);
onResize(undefined, data);
});
info(`Waiting for content-resize to ${width} x ${height}`);
ui.on("content-resize", onResize);
browser.addEventListener("mozbrowserloadend",
onBrowserLoadEnd, { once: true });
}));
}
var setViewportSize = Task.async(function* (ui, manager, width, height) {
let size = ui.getViewportSize();
info(`Current size: ${size.width} x ${size.height}, ` +
`set to: ${width} x ${height}`);
if (size.width != width || size.height != height) {
let resized = waitForViewportResizeTo(ui, width, height);
ui.setViewportSize({ width, height });
yield resized;
}
});
function getElRect(selector, win) {
let el = win.document.querySelector(selector);
return el.getBoundingClientRect();
}
/**
* Drag an element identified by 'selector' by [x,y] amount. Returns
* the rect of the dragged element as it was before drag.
*/
function dragElementBy(selector, x, y, win) {
let React = win.require("devtools/client/shared/vendor/react");
let { Simulate } = React.addons.TestUtils;
let rect = getElRect(selector, win);
let startPoint = {
clientX: rect.left + Math.floor(rect.width / 2),
clientY: rect.top + Math.floor(rect.height / 2),
};
let endPoint = [ startPoint.clientX + x, startPoint.clientY + y ];
let elem = win.document.querySelector(selector);
// mousedown is a React listener, need to use its testing tools to avoid races
Simulate.mouseDown(elem, startPoint);
// mousemove and mouseup are regular DOM listeners
EventUtils.synthesizeMouseAtPoint(...endPoint, { type: "mousemove" }, win);
EventUtils.synthesizeMouseAtPoint(...endPoint, { type: "mouseup" }, win);
return rect;
}
function* testViewportResize(ui, selector, moveBy,
expectedViewportSize, expectedHandleMove) {
let win = ui.toolWindow;
let resized = waitForViewportResizeTo(ui, ...expectedViewportSize);
let startRect = dragElementBy(selector, ...moveBy, win);
yield resized;
let endRect = getElRect(selector, win);
is(endRect.left - startRect.left, expectedHandleMove[0],
`The x move of ${selector} is as expected`);
is(endRect.top - startRect.top, expectedHandleMove[1],
`The y move of ${selector} is as expected`);
}
function openDeviceModal({ toolWindow }) {
let { document } = toolWindow;
let React = toolWindow.require("devtools/client/shared/vendor/react");
let { Simulate } = React.addons.TestUtils;
let select = document.querySelector(".viewport-device-selector");
let modal = document.querySelector("#device-modal-wrapper");
info("Checking initial device modal state");
ok(modal.classList.contains("closed") && !modal.classList.contains("opened"),
"The device modal is closed by default.");
info("Opening device modal through device selector.");
select.value = OPEN_DEVICE_MODAL_VALUE;
Simulate.change(select);
ok(modal.classList.contains("opened") && !modal.classList.contains("closed"),
"The device modal is displayed.");
}
function changeSelectValue({ toolWindow }, selector, value) {
info(`Selecting ${value} in ${selector}.`);
return new Promise(resolve => {
let select = toolWindow.document.querySelector(selector);
isnot(select, null, `selector "${selector}" should match an existing element.`);
let option = [...select.options].find(o => o.value === String(value));
isnot(option, undefined, `value "${value}" should match an existing option.`);
let event = new toolWindow.UIEvent("change", {
view: toolWindow,
bubbles: true,
cancelable: true
});
select.addEventListener("change", () => {
is(select.value, value,
`Select's option with value "${value}" should be selected.`);
resolve();
}, { once: true });
select.value = value;
select.dispatchEvent(event);
});
}
const selectDevice = (ui, value) => Promise.all([
once(ui, "device-changed"),
changeSelectValue(ui, ".viewport-device-selector", value)
]);
const selectDPR = (ui, value) =>
changeSelectValue(ui, "#global-dpr-selector > select", value);
const selectNetworkThrottling = (ui, value) => Promise.all([
once(ui, "network-throttling-changed"),
changeSelectValue(ui, "#global-network-throttling-selector", value)
]);
function getSessionHistory(browser) {
return ContentTask.spawn(browser, {}, function* () {
/* eslint-disable no-undef */
let { interfaces: Ci } = Components;
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
let sessionHistory = webNav.sessionHistory;
let result = {
index: sessionHistory.index,
entries: []
};
for (let i = 0; i < sessionHistory.count; i++) {
let entry = sessionHistory.getEntryAtIndex(i, false);
result.entries.push({
uri: entry.URI.spec,
title: entry.title
});
}
return result;
/* eslint-enable no-undef */
});
}
function getContentSize(ui) {
return spawnViewportTask(ui, {}, () => ({
width: content.screen.width,
height: content.screen.height
}));
}
function waitForPageShow(browser) {
let mm = browser.messageManager;
return new Promise(resolve => {
let onShow = message => {
if (message.target != browser) {
return;
}
mm.removeMessageListener("PageVisibility:Show", onShow);
resolve();
};
mm.addMessageListener("PageVisibility:Show", onShow);
});
}
function waitForViewportLoad(ui) {
return new Promise(resolve => {
let browser = ui.getViewportBrowser();
browser.addEventListener("mozbrowserloadend", () => {
resolve();
}, { once: true });
});
}
function load(browser, url) {
let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
browser.loadURI(url, null, null);
return loaded;
}
function back(browser) {
let shown = waitForPageShow(browser);
browser.goBack();
return shown;
}
function forward(browser) {
let shown = waitForPageShow(browser);
browser.goForward();
return shown;
}
function addDeviceForTest(device) {
info(`Adding Test Device "${device.name}" to the list.`);
addDevice(device);
registerCleanupFunction(() => {
// Note that assertions in cleanup functions are not displayed unless they failed.
ok(removeDevice(device), `Removed Test Device "${device.name}" from the list.`);
});
}
function waitForClientClose(ui) {
return new Promise(resolve => {
info("Waiting for RDM debugger client to close");
ui.client.addOneTimeListener("closed", () => {
info("RDM's debugger client is now closed");
resolve();
});
});
}
function* testTouchEventsOverride(ui, expected) {
let { document } = ui.toolWindow;
let touchButton = document.querySelector("#global-touch-simulation-button");
let flag = yield ui.emulationFront.getTouchEventsOverride();
is(flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED, expected,
`Touch events override should be ${expected ? "enabled" : "disabled"}`);
is(touchButton.classList.contains("active"), expected,
`Touch simulation button should be ${expected ? "" : "in"}active.`);
}
function testViewportDeviceSelectLabel(ui, expected) {
info("Test viewport's device select label");
let select = ui.toolWindow.document.querySelector(".viewport-device-selector");
is(select.selectedOptions[0].textContent, expected,
`Device Select value should be: ${expected}`);
}
function* enableTouchSimulation(ui) {
let { document } = ui.toolWindow;
let touchButton = document.querySelector("#global-touch-simulation-button");
let loaded = waitForViewportLoad(ui);
touchButton.click();
yield loaded;
}

View file

@ -0,0 +1,86 @@
<!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 type="text/javascript;version=1.8">
"use strict";
let div = document.querySelector("div");
let initX, initY;
let previousEvent = "", touchendTime = 0;
let updatePreviousEvent = function (e) {
previousEvent = e.type;
};
div.style.transform = "none";
div.style.backgroundColor = "";
div.addEventListener("touchstart", function (evt) {
let touch = evt.changedTouches[0];
initX = touch.pageX;
initY = touch.pageY;
updatePreviousEvent(evt);
}, true);
div.addEventListener("touchmove", function (evt) {
let touch = evt.changedTouches[0];
let deltaX = touch.pageX - initX;
let 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);
} else {
div.dataset.isDelay = false;
}
updatePreviousEvent(evt);
}, true);
div.addEventListener("mousemove", updatePreviousEvent, true);
div.addEventListener("mouseup", updatePreviousEvent, true);
div.addEventListener("click", updatePreviousEvent, true);
</script>