Bug 1248619 - Part 2: Restore the previous device state in RDM. r=rcaliman
☠☠ backed out by fb0c2bcc9bc6 ☠ ☠
authorGabriel Luong <gabriel.luong@gmail.com>
Tue, 09 Oct 2018 14:53:29 -0400
changeset 496045 ae15dbcedd8a73fc2a53b231354c49321da74ee1
parent 496044 a7a2ce19cf57ed93bcf26d6fce128d6a2497ecc8
child 496046 fb2a9e0b7538881fbe1c526d961338108ca4c967
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcaliman
bugs1248619
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1248619 - Part 2: Restore the previous device state in RDM. r=rcaliman
devtools/client/responsive.html/actions/devices.js
devtools/client/responsive.html/actions/viewports.js
devtools/client/responsive.html/index.js
devtools/client/responsive.html/test/browser/browser.ini
devtools/client/responsive.html/test/browser/browser_device_change.js
devtools/client/responsive.html/test/browser/browser_device_state_restore.js
devtools/client/responsive.html/test/browser/head.js
--- a/devtools/client/responsive.html/actions/devices.js
+++ b/devtools/client/responsive.html/actions/devices.js
@@ -1,28 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
+const asyncStorage = require("devtools/shared/async-storage");
 
 const {
   ADD_DEVICE,
   ADD_DEVICE_TYPE,
   LOAD_DEVICE_LIST_START,
   LOAD_DEVICE_LIST_ERROR,
   LOAD_DEVICE_LIST_END,
   REMOVE_DEVICE,
   UPDATE_DEVICE_DISPLAYED,
   UPDATE_DEVICE_MODAL,
 } = require("./index");
+const { post } = require("../utils/message");
 
 const { addDevice, getDevices, removeDevice } = require("devtools/client/shared/devices");
+const { changeUserAgent, toggleTouchSimulation } = require("./ui");
+const { changeDevice, changePixelRatio, resizeViewport } = require("./viewports");
 
 const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";
 
 /**
  * Returns an object containing the user preference of displayed devices.
  *
  * @return {Object} containing two Sets:
  * - added: Names of the devices that were explicitly enabled by the user
@@ -155,16 +159,55 @@ module.exports = {
       if (!devices.TYPES.find(type => type == "custom")) {
         dispatch(module.exports.addDeviceType("custom"));
       }
 
       dispatch({ type: LOAD_DEVICE_LIST_END });
     };
   },
 
+  restoreDeviceState() {
+    return async function(dispatch, getState) {
+      const deviceState = await asyncStorage.getItem("devtools.responsive.deviceState");
+      if (!deviceState) {
+        return;
+      }
+
+      const { id, device: deviceName, deviceType } = deviceState;
+      const devices = getState().devices;
+
+      if (!devices.types.includes(deviceType)) {
+        // Can't find matching device type.
+        return;
+      }
+
+      const device = devices[deviceType].find(d => d.name === deviceName);
+      if (!device) {
+        // Can't find device with the same device name.
+        return;
+      }
+
+      post(window, {
+        type: "viewport-resize",
+        width: device.width,
+        height: device.height,
+      });
+      post(window, {
+        type: "change-device",
+        device,
+      });
+
+      dispatch(resizeViewport(id, device.width, device.height));
+      dispatch(changeDevice(id, device.name, deviceType));
+      dispatch(changePixelRatio(id, device.pixelRatio));
+      dispatch(changeUserAgent(device.userAgent));
+      dispatch(toggleTouchSimulation(device.touch));
+    };
+  },
+
   updateDeviceModal(isOpen, modalOpenedFromViewport = null) {
     return {
       type: UPDATE_DEVICE_MODAL,
       isOpen,
       modalOpenedFromViewport,
     };
   },
 
--- a/devtools/client/responsive.html/actions/viewports.js
+++ b/devtools/client/responsive.html/actions/viewports.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
 
 "use strict";
 
+const asyncStorage = require("devtools/shared/async-storage");
+
 const {
   ADD_VIEWPORT,
   CHANGE_DEVICE,
   CHANGE_PIXEL_RATIO,
   REMOVE_DEVICE_ASSOCIATION,
   RESIZE_VIEWPORT,
   ROTATE_VIEWPORT
 } = require("./index");
@@ -28,21 +30,30 @@ module.exports = {
       userContextId,
     };
   },
 
   /**
    * Change the viewport device.
    */
   changeDevice(id, device, deviceType) {
-    return {
-      type: CHANGE_DEVICE,
-      id,
-      device,
-      deviceType,
+    return async function(dispatch) {
+      try {
+        await asyncStorage.setItem("devtools.responsive.deviceState",
+          { id, device, deviceType });
+      } catch (e) {
+        console.error(e);
+      }
+
+      dispatch({
+        type: CHANGE_DEVICE,
+        id,
+        device,
+        deviceType,
+      });
     };
   },
 
   /**
    * Change the viewport pixel ratio.
    */
   changePixelRatio(id, pixelRatio = 0) {
     return {
@@ -51,20 +62,25 @@ module.exports = {
       pixelRatio,
     };
   },
 
   /**
    * Remove the viewport's device assocation.
    */
   removeDeviceAssociation(id) {
-    post(window, "remove-device-association");
-    return {
-      type: REMOVE_DEVICE_ASSOCIATION,
-      id,
+    return async function(dispatch) {
+      post(window, "remove-device-association");
+
+      dispatch({
+        type: REMOVE_DEVICE_ASSOCIATION,
+        id,
+      });
+
+      await asyncStorage.removeItem("devtools.responsive.deviceState");
     };
   },
 
   /**
    * Resize the viewport.
    */
   resizeViewport(id, width, height) {
     return {
--- a/devtools/client/responsive.html/index.js
+++ b/devtools/client/responsive.html/index.js
@@ -17,17 +17,17 @@ const Telemetry = require("devtools/clie
 const { createFactory, createElement } =
   require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const message = require("./utils/message");
 const App = createFactory(require("./components/App"));
 const Store = require("./store");
-const { loadDevices } = require("./actions/devices");
+const { loadDevices, restoreDeviceState } = require("./actions/devices");
 const { addViewport, resizeViewport } = require("./actions/viewports");
 const { changeDisplayPixelRatio } = require("./actions/ui");
 
 // Exposed for use by tests
 window.require = require;
 
 const bootstrap = {
 
@@ -73,17 +73,19 @@ const bootstrap = {
 };
 
 // manager.js sends a message to signal init
 message.wait(window, "init").then(() => bootstrap.init());
 
 // manager.js sends a message to signal init is done, which can be used for delayed
 // startup work that shouldn't block initial load
 message.wait(window, "post-init").then(() => {
-  bootstrap.dispatch(loadDevices());
+  bootstrap.store.dispatch(loadDevices()).then(() => {
+    bootstrap.dispatch(restoreDeviceState());
+  });
 });
 
 window.addEventListener("unload", function() {
   bootstrap.destroy();
 }, {once: true});
 
 // Allows quick testing of actions from the console
 window.dispatch = action => bootstrap.dispatch(action);
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -27,16 +27,17 @@ support-files =
 [browser_contextual_identity.js]
 [browser_device_change.js]
 [browser_device_custom_remove.js]
 [browser_device_custom.js]
 [browser_device_modal_error.js]
 [browser_device_modal_exit.js]
 [browser_device_modal_submit.js]
 [browser_device_pixel_ratio_change.js]
+[browser_device_state_restore.js]
 [browser_device_width.js]
 [browser_exit_button.js]
 [browser_ext_messaging.js]
 tags = devtools webextensions
 [browser_favicon.js]
 [browser_frame_script_active.js]
 [browser_hide_container.js]
 [browser_menu_item_01.js]
--- a/devtools/client/responsive.html/test/browser/browser_device_change.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_change.js
@@ -79,18 +79,20 @@ add_task(async function() {
   const tab = await addTab(TEST_URL);
   const { ui } = await openRDM(tab);
 
   const { store } = ui.toolWindow;
 
   reloadOnUAChange(true);
 
   // Wait until the viewport has been added and the device list has been loaded
-  await waitUntilState(store, state => state.viewports.length == 1
-    && state.devices.listState == Types.loadableState.LOADED);
+  await waitUntilState(store, state =>
+    state.viewports.length == 1 &&
+    state.viewports[0].device === "Laptop (1366 x 768)" &&
+    state.devices.listState == Types.loadableState.LOADED);
 
   // Select device with custom UA
   let reloaded = waitForViewportLoad(ui);
   await selectDevice(ui, "Fake Phone RDM Test");
   await reloaded;
   await waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
   info("Should have device UA now that device is applied");
   await testUserAgent(ui, testDevice.userAgent);
@@ -103,22 +105,8 @@ add_task(async function() {
   // Ensure UA is reset to default after closing RDM
   info("Should have default UA after closing RDM");
   await testUserAgentFromBrowser(tab.linkedBrowser, DEFAULT_UA);
 
   await removeTab(tab);
 
   reloadOnUAChange(false);
 });
-
-function testViewportDimensions(ui, w, h) {
-  const 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`);
-}
-
-async function testDevicePixelRatio(ui, expected) {
-  const dppx = await getViewportDevicePixelRatio(ui);
-  is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_device_state_restore.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the previous selected device is restored when reopening RDM.
+
+const TEST_URL = "data:text/html;charset=utf-8,";
+const DEFAULT_DPPX = window.devicePixelRatio;
+
+/* eslint-disable max-len */
+const TEST_DEVICE = {
+  "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
+};
+/* eslint-enable max-len */
+
+const Types = require("devtools/client/responsive.html/types");
+
+addRDMTask(TEST_URL, async function({ ui }) {
+  const { store } = ui.toolWindow;
+
+  reloadOnUAChange(true);
+
+  // Wait until the viewport has been added and the device list has been loaded
+  await waitUntilState(store, state => state.viewports.length == 1
+    && state.devices.listState == Types.loadableState.LOADED);
+
+  info("Checking the default RDM state.");
+  testViewportDeviceMenuLabel(ui, "Responsive");
+  testViewportDimensions(ui, 320, 480);
+  await testUserAgent(ui, DEFAULT_UA);
+  await testDevicePixelRatio(ui, DEFAULT_DPPX);
+  await testTouchEventsOverride(ui, false);
+
+  info("Select a device");
+  const reloaded = waitForViewportLoad(ui);
+  await selectDevice(ui, TEST_DEVICE.name);
+  await reloaded;
+  await waitForViewportResizeTo(ui, TEST_DEVICE.width, TEST_DEVICE.height);
+
+  info("Checking the RDM device state.");
+  testViewportDeviceMenuLabel(ui, TEST_DEVICE.name);
+  await testUserAgent(ui, TEST_DEVICE.userAgent);
+  await testDevicePixelRatio(ui, TEST_DEVICE.pixelRatio);
+  await testTouchEventsOverride(ui, TEST_DEVICE.touch);
+
+  reloadOnUAChange(false);
+});
+
+addRDMTask(TEST_URL, async function({ ui }) {
+  const { store } = ui.toolWindow;
+
+  reloadOnUAChange(true);
+
+  info("Reopening RDM and checking that the previous device state is restored.");
+
+  // Wait until the viewport has been added and the device list has been loaded
+  await waitUntilState(store, state =>
+    state.viewports.length == 1 &&
+    state.viewports[0].device === TEST_DEVICE.name &&
+    state.devices.listState == Types.loadableState.LOADED);
+  await waitForViewportResizeTo(ui, TEST_DEVICE.width, TEST_DEVICE.height);
+  await waitForViewportLoad(ui);
+
+  info("Checking the restored RDM state.");
+  testViewportDeviceMenuLabel(ui, TEST_DEVICE.name);
+  testViewportDimensions(ui, TEST_DEVICE.width, TEST_DEVICE.height);
+  await testUserAgent(ui, TEST_DEVICE.userAgent);
+  await testDevicePixelRatio(ui, TEST_DEVICE.pixelRatio);
+  await testTouchEventsOverride(ui, TEST_DEVICE.touch);
+
+  reloadOnUAChange(false);
+});
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -61,16 +61,17 @@ registerCleanupFunction(async () => {
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.reloadNotification.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent");
   Services.prefs.clearUserPref("devtools.responsive.show-setting-tooltip");
   Services.prefs.clearUserPref("devtools.responsive.showUserAgentInput");
   await asyncStorage.removeItem("devtools.devices.url_cache");
+  await asyncStorage.removeItem("devtools.responsive.deviceState");
   await removeLocalDevices();
 });
 
 /**
  * Open responsive design mode for the given tab.
  */
 var openRDM = async function(tab) {
   info("Opening responsive design mode");
@@ -372,16 +373,21 @@ function addDeviceForTest(device) {
 }
 
 async function waitForClientClose(ui) {
   info("Waiting for RDM debugger client to close");
   await ui.client.addOneTimeListener("closed");
   info("RDM's debugger client is now closed");
 }
 
+async function testDevicePixelRatio(ui, expected) {
+  const dppx = await getViewportDevicePixelRatio(ui);
+  is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
+}
+
 async function testTouchEventsOverride(ui, expected) {
   const { document } = ui.toolWindow;
   const touchButton = document.getElementById("touch-simulation-button");
 
   const flag = await ui.emulationFront.getTouchEventsOverride();
   is(flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED, expected,
     `Touch events override should be ${expected ? "enabled" : "disabled"}`);
   is(touchButton.classList.contains("checked"), expected,
@@ -419,16 +425,25 @@ async function testUserAgent(ui, expecte
 
 async function testUserAgentFromBrowser(browser, expected) {
   const ua = await ContentTask.spawn(browser, {}, async function() {
     return content.navigator.userAgent;
   });
   is(ua, expected, `UA should be set to ${expected}`);
 }
 
+function testViewportDimensions(ui, w, h) {
+  const 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`);
+}
+
 async function changeUserAgentInput(ui, value) {
   const { Simulate } =
     ui.toolWindow.require("devtools/client/shared/vendor/react-dom-test-utils");
   const { document, store } = ui.toolWindow;
 
   const userAgentInput = document.getElementById("user-agent-input");
   userAgentInput.value = value;
   Simulate.change(userAgentInput);