Source code
Revision control
Copy as Markdown
Other Tools
/* 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
"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,
};
},
};