summaryrefslogtreecommitdiffstats
path: root/devtools/client/responsive/actions
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/responsive/actions/devices.js254
-rw-r--r--devtools/client/responsive/actions/index.js109
-rw-r--r--devtools/client/responsive/actions/moz.build13
-rw-r--r--devtools/client/responsive/actions/screenshot.js36
-rw-r--r--devtools/client/responsive/actions/ui.js71
-rw-r--r--devtools/client/responsive/actions/viewports.js128
6 files changed, 611 insertions, 0 deletions
diff --git a/devtools/client/responsive/actions/devices.js b/devtools/client/responsive/actions/devices.js
new file mode 100644
index 0000000000..b3f2bab982
--- /dev/null
+++ b/devtools/client/responsive/actions/devices.js
@@ -0,0 +1,254 @@
+/* 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 asyncStorage = require("resource://devtools/shared/async-storage.js");
+
+const {
+ ADD_DEVICE,
+ ADD_DEVICE_TYPE,
+ EDIT_DEVICE,
+ LOAD_DEVICE_LIST_START,
+ LOAD_DEVICE_LIST_ERROR,
+ LOAD_DEVICE_LIST_END,
+ REMOVE_DEVICE,
+ UPDATE_DEVICE_DISPLAYED,
+ UPDATE_DEVICE_MODAL,
+} = require("resource://devtools/client/responsive/actions/index.js");
+const {
+ post,
+} = require("resource://devtools/client/responsive/utils/message.js");
+
+const {
+ addDevice,
+ editDevice,
+ getDevices,
+ removeDevice,
+} = require("resource://devtools/client/shared/devices.js");
+const {
+ changeUserAgent,
+ toggleTouchSimulation,
+} = require("resource://devtools/client/responsive/actions/ui.js");
+const {
+ changeDevice,
+ changePixelRatio,
+ changeViewportAngle,
+} = require("resource://devtools/client/responsive/actions/viewports.js");
+
+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
+ * - removed: Names of the devices that were explicitly removed by the user
+ */
+function loadPreferredDevices() {
+ const preferredDevices = {
+ added: new Set(),
+ removed: new Set(),
+ };
+
+ if (Services.prefs.prefHasUserValue(DISPLAYED_DEVICES_PREF)) {
+ try {
+ let savedData = Services.prefs.getStringPref(DISPLAYED_DEVICES_PREF);
+ savedData = JSON.parse(savedData);
+ if (savedData.added && savedData.removed) {
+ preferredDevices.added = new Set(savedData.added);
+ preferredDevices.removed = new Set(savedData.removed);
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ return preferredDevices;
+}
+
+/**
+ * Update the displayed device list preference with the given device list.
+ *
+ * @param {Object} containing two Sets:
+ * - added: Names of the devices that were explicitly enabled by the user
+ * - removed: Names of the devices that were explicitly removed by the user
+ */
+function updatePreferredDevices(devices) {
+ let devicesToSave = {
+ added: Array.from(devices.added),
+ removed: Array.from(devices.removed),
+ };
+ devicesToSave = JSON.stringify(devicesToSave);
+ Services.prefs.setStringPref(DISPLAYED_DEVICES_PREF, devicesToSave);
+}
+
+module.exports = {
+ // This function is only exported for testing purposes
+ _loadPreferredDevices: loadPreferredDevices,
+
+ updatePreferredDevices,
+
+ addCustomDevice(device) {
+ return async function ({ dispatch }) {
+ // Add custom device to device storage
+ await addDevice(device, "custom");
+ dispatch({
+ type: ADD_DEVICE,
+ device,
+ deviceType: "custom",
+ });
+ };
+ },
+
+ addDevice(device, deviceType) {
+ return {
+ type: ADD_DEVICE,
+ device,
+ deviceType,
+ };
+ },
+
+ addDeviceType(deviceType) {
+ return {
+ type: ADD_DEVICE_TYPE,
+ deviceType,
+ };
+ },
+
+ editCustomDevice(viewport, oldDevice, newDevice) {
+ return async function ({ dispatch }) {
+ // Edit custom device in storage
+ await editDevice(oldDevice, newDevice, "custom");
+ // Notify the window that the device should be updated in the device selector.
+ post(window, {
+ type: "change-device",
+ device: newDevice,
+ viewport,
+ });
+
+ // Update UI if the device is selected.
+ if (viewport) {
+ dispatch(changeUserAgent(newDevice.userAgent));
+ dispatch(toggleTouchSimulation(newDevice.touch));
+ }
+
+ dispatch({
+ type: EDIT_DEVICE,
+ deviceType: "custom",
+ viewport,
+ oldDevice,
+ newDevice,
+ });
+ };
+ },
+
+ removeCustomDevice(device) {
+ return async function ({ dispatch }) {
+ // Remove custom device from device storage
+ await removeDevice(device, "custom");
+ dispatch({
+ type: REMOVE_DEVICE,
+ device,
+ deviceType: "custom",
+ });
+ };
+ },
+
+ updateDeviceDisplayed(device, deviceType, displayed) {
+ return {
+ type: UPDATE_DEVICE_DISPLAYED,
+ device,
+ deviceType,
+ displayed,
+ };
+ },
+
+ loadDevices() {
+ return async function ({ dispatch }) {
+ dispatch({ type: LOAD_DEVICE_LIST_START });
+ const preferredDevices = loadPreferredDevices();
+ let deviceByTypes;
+
+ try {
+ deviceByTypes = await getDevices();
+ } catch (e) {
+ console.error("Could not load device list: " + e);
+ dispatch({ type: LOAD_DEVICE_LIST_ERROR });
+ return;
+ }
+
+ for (const [type, devices] of deviceByTypes.entries()) {
+ dispatch(module.exports.addDeviceType(type));
+ for (const device of devices) {
+ if (device.os == "fxos") {
+ continue;
+ }
+
+ const newDevice = Object.assign({}, device, {
+ displayed:
+ preferredDevices.added.has(device.name) ||
+ (device.featured && !preferredDevices.removed.has(device.name)),
+ });
+
+ dispatch(module.exports.addDevice(newDevice, type));
+ }
+ }
+
+ // Add an empty "custom" type if it doesn't exist in device storage
+ if (!deviceByTypes.has("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;
+ }
+
+ const viewport = getState().viewports[0];
+
+ post(window, {
+ type: "change-device",
+ device,
+ viewport,
+ });
+
+ dispatch(changeDevice(id, device.name, deviceType));
+ dispatch(changeViewportAngle(id, viewport.angle));
+ dispatch(changePixelRatio(id, device.pixelRatio));
+ dispatch(changeUserAgent(device.userAgent));
+ dispatch(toggleTouchSimulation(device.touch));
+ };
+ },
+
+ updateDeviceModal(isOpen, modalOpenedFromViewport = null) {
+ return {
+ type: UPDATE_DEVICE_MODAL,
+ isOpen,
+ modalOpenedFromViewport,
+ };
+ },
+};
diff --git a/devtools/client/responsive/actions/index.js b/devtools/client/responsive/actions/index.js
new file mode 100644
index 0000000000..7d42c06053
--- /dev/null
+++ b/devtools/client/responsive/actions/index.js
@@ -0,0 +1,109 @@
+/* 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";
+
+// This file lists all of the actions available in responsive design. This
+// central list of constants makes it easy to see all possible action names at
+// a glance. Please add a comment with each new action type.
+
+const { createEnum } = require("resource://devtools/client/shared/enum.js");
+
+const {
+ CHANGE_NETWORK_THROTTLING,
+} = require("resource://devtools/client/shared/components/throttling/actions.js");
+
+createEnum(
+ [
+ // Add a new device.
+ "ADD_DEVICE",
+
+ // Add a new device type.
+ "ADD_DEVICE_TYPE",
+
+ // Add an additional viewport to display the document.
+ "ADD_VIEWPORT",
+
+ // Change the device displayed in the viewport.
+ "CHANGE_DEVICE",
+
+ // Change the location of the page. This may be triggered by the user
+ // directly entering a new URL, navigating with links, etc.
+ "CHANGE_LOCATION",
+
+ // The pixel ratio of the display has changed. This may be triggered by the user
+ // when changing the monitor resolution, or when the window is dragged to a different
+ // display with a different pixel ratio.
+ "CHANGE_DISPLAY_PIXEL_RATIO",
+
+ // Change the network throttling profile.
+ CHANGE_NETWORK_THROTTLING,
+
+ // Change the user agent of the viewport.
+ "CHANGE_USER_AGENT",
+
+ // The pixel ratio of the viewport has changed. This may be triggered by the user
+ // when changing the device displayed in the viewport, or when a pixel ratio is
+ // selected from the device pixel ratio dropdown.
+ "CHANGE_PIXEL_RATIO",
+
+ // Change the viewport angle.
+ "CHANGE_VIEWPORT_ANGLE",
+
+ // Edit a device.
+ "EDIT_DEVICE",
+
+ // Indicates that the device list is being loaded.
+ "LOAD_DEVICE_LIST_START",
+
+ // Indicates that the device list loading action threw an error.
+ "LOAD_DEVICE_LIST_ERROR",
+
+ // Indicates that the device list has been loaded successfully.
+ "LOAD_DEVICE_LIST_END",
+
+ // Remove a device.
+ "REMOVE_DEVICE",
+
+ // Remove the viewport's device assocation.
+ "REMOVE_DEVICE_ASSOCIATION",
+
+ // Resize the viewport.
+ "RESIZE_VIEWPORT",
+
+ // Rotate the viewport.
+ "ROTATE_VIEWPORT",
+
+ // Take a screenshot of the viewport.
+ "TAKE_SCREENSHOT_START",
+
+ // Indicates when the screenshot action ends.
+ "TAKE_SCREENSHOT_END",
+
+ // Toggles the left alignment of the viewports.
+ "TOGGLE_LEFT_ALIGNMENT",
+
+ // Toggles the reload on touch simulation changes.
+ "TOGGLE_RELOAD_ON_TOUCH_SIMULATION",
+
+ // Toggles the reload on user agent changes.
+ "TOGGLE_RELOAD_ON_USER_AGENT",
+
+ // Toggles the touch simulation state of the viewports.
+ "TOGGLE_TOUCH_SIMULATION",
+
+ // Toggles the user agent input displayed in the toolbar.
+ "TOGGLE_USER_AGENT_INPUT",
+
+ // Update the device display state in the device selector.
+ "UPDATE_DEVICE_DISPLAYED",
+
+ // Update the device modal state.
+ "UPDATE_DEVICE_MODAL",
+
+ // Zoom the viewport.
+ "ZOOM_VIEWPORT",
+ ],
+ module.exports
+);
diff --git a/devtools/client/responsive/actions/moz.build b/devtools/client/responsive/actions/moz.build
new file mode 100644
index 0000000000..d4fa0d243f
--- /dev/null
+++ b/devtools/client/responsive/actions/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "devices.js",
+ "index.js",
+ "screenshot.js",
+ "ui.js",
+ "viewports.js",
+)
diff --git a/devtools/client/responsive/actions/screenshot.js b/devtools/client/responsive/actions/screenshot.js
new file mode 100644
index 0000000000..c429b41060
--- /dev/null
+++ b/devtools/client/responsive/actions/screenshot.js
@@ -0,0 +1,36 @@
+/* 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 {
+ TAKE_SCREENSHOT_START,
+ TAKE_SCREENSHOT_END,
+} = require("resource://devtools/client/responsive/actions/index.js");
+
+const message = require("resource://devtools/client/responsive/utils/message.js");
+
+const animationFrame = () =>
+ new Promise(resolve => {
+ window.requestAnimationFrame(resolve);
+ });
+
+module.exports = {
+ takeScreenshot() {
+ return async function ({ dispatch }) {
+ await dispatch({ type: TAKE_SCREENSHOT_START });
+
+ // Waiting the next repaint, to ensure the react components
+ // can be properly render after the action dispatched above
+ await animationFrame();
+
+ window.postMessage({ type: "screenshot" }, "*");
+ await message.wait(window, "screenshot-captured");
+
+ dispatch({ type: TAKE_SCREENSHOT_END });
+ };
+ },
+};
diff --git a/devtools/client/responsive/actions/ui.js b/devtools/client/responsive/actions/ui.js
new file mode 100644
index 0000000000..b2f05cb7c9
--- /dev/null
+++ b/devtools/client/responsive/actions/ui.js
@@ -0,0 +1,71 @@
+/* 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 {
+ CHANGE_DISPLAY_PIXEL_RATIO,
+ CHANGE_USER_AGENT,
+ TOGGLE_LEFT_ALIGNMENT,
+ TOGGLE_RELOAD_ON_TOUCH_SIMULATION,
+ TOGGLE_RELOAD_ON_USER_AGENT,
+ TOGGLE_TOUCH_SIMULATION,
+ TOGGLE_USER_AGENT_INPUT,
+} = require("resource://devtools/client/responsive/actions/index.js");
+
+module.exports = {
+ /**
+ * The pixel ratio of the display has changed. This may be triggered by the user
+ * when changing the monitor resolution, or when the window is dragged to a different
+ * display with a different pixel ratio.
+ */
+ changeDisplayPixelRatio(displayPixelRatio) {
+ return {
+ type: CHANGE_DISPLAY_PIXEL_RATIO,
+ displayPixelRatio,
+ };
+ },
+
+ changeUserAgent(userAgent) {
+ return {
+ type: CHANGE_USER_AGENT,
+ userAgent,
+ };
+ },
+
+ toggleLeftAlignment(enabled) {
+ return {
+ type: TOGGLE_LEFT_ALIGNMENT,
+ enabled,
+ };
+ },
+
+ toggleReloadOnTouchSimulation(enabled) {
+ return {
+ type: TOGGLE_RELOAD_ON_TOUCH_SIMULATION,
+ enabled,
+ };
+ },
+
+ toggleReloadOnUserAgent(enabled) {
+ return {
+ type: TOGGLE_RELOAD_ON_USER_AGENT,
+ enabled,
+ };
+ },
+
+ toggleTouchSimulation(enabled) {
+ return {
+ type: TOGGLE_TOUCH_SIMULATION,
+ enabled,
+ };
+ },
+
+ toggleUserAgentInput(enabled) {
+ return {
+ type: TOGGLE_USER_AGENT_INPUT,
+ enabled,
+ };
+ },
+};
diff --git a/devtools/client/responsive/actions/viewports.js b/devtools/client/responsive/actions/viewports.js
new file mode 100644
index 0000000000..f8fec1a8c5
--- /dev/null
+++ b/devtools/client/responsive/actions/viewports.js
@@ -0,0 +1,128 @@
+/* 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("resource://devtools/shared/async-storage.js");
+
+const {
+ ADD_VIEWPORT,
+ CHANGE_DEVICE,
+ CHANGE_PIXEL_RATIO,
+ CHANGE_VIEWPORT_ANGLE,
+ REMOVE_DEVICE_ASSOCIATION,
+ RESIZE_VIEWPORT,
+ ROTATE_VIEWPORT,
+ ZOOM_VIEWPORT,
+} = require("resource://devtools/client/responsive/actions/index.js");
+
+const {
+ post,
+} = require("resource://devtools/client/responsive/utils/message.js");
+
+module.exports = {
+ /**
+ * Add an additional viewport to display the document.
+ */
+ addViewport(userContextId = 0) {
+ return {
+ type: ADD_VIEWPORT,
+ userContextId,
+ };
+ },
+
+ /**
+ * Change the viewport device.
+ */
+ changeDevice(id, device, deviceType) {
+ return async function ({ dispatch }) {
+ dispatch({
+ type: CHANGE_DEVICE,
+ id,
+ device,
+ deviceType,
+ });
+
+ try {
+ await asyncStorage.setItem("devtools.responsive.deviceState", {
+ id,
+ device,
+ deviceType,
+ });
+ } catch (e) {
+ console.error(e);
+ }
+ };
+ },
+
+ /**
+ * Change the viewport pixel ratio.
+ */
+ changePixelRatio(id, pixelRatio = 0) {
+ return {
+ type: CHANGE_PIXEL_RATIO,
+ id,
+ pixelRatio,
+ };
+ },
+
+ changeViewportAngle(id, angle) {
+ return {
+ type: CHANGE_VIEWPORT_ANGLE,
+ id,
+ angle,
+ };
+ },
+
+ /**
+ * Remove the viewport's device assocation.
+ */
+ removeDeviceAssociation(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 {
+ type: RESIZE_VIEWPORT,
+ id,
+ width,
+ height,
+ };
+ },
+
+ /**
+ * Rotate the viewport.
+ */
+ rotateViewport(id) {
+ return {
+ type: ROTATE_VIEWPORT,
+ id,
+ };
+ },
+
+ /**
+ * Zoom the viewport.
+ */
+ zoomViewport(id, zoom) {
+ return {
+ type: ZOOM_VIEWPORT,
+ id,
+ zoom,
+ };
+ },
+};