/* 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, }; }, };