summaryrefslogtreecommitdiffstats
path: root/devtools/client/responsive/components/App.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/responsive/components/App.js')
-rw-r--r--devtools/client/responsive/components/App.js447
1 files changed, 447 insertions, 0 deletions
diff --git a/devtools/client/responsive/components/App.js b/devtools/client/responsive/components/App.js
new file mode 100644
index 0000000000..42b8f82e26
--- /dev/null
+++ b/devtools/client/responsive/components/App.js
@@ -0,0 +1,447 @@
+/* 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 {
+ createFactory,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const {
+ connect,
+} = require("resource://devtools/client/shared/vendor/react-redux.js");
+
+const Toolbar = createFactory(
+ require("resource://devtools/client/responsive/components/Toolbar.js")
+);
+
+loader.lazyGetter(this, "DeviceModal", () =>
+ createFactory(
+ require("resource://devtools/client/responsive/components/DeviceModal.js")
+ )
+);
+
+const {
+ changeNetworkThrottling,
+} = require("resource://devtools/client/shared/components/throttling/actions.js");
+const {
+ addCustomDevice,
+ editCustomDevice,
+ removeCustomDevice,
+ updateDeviceDisplayed,
+ updateDeviceModal,
+ updatePreferredDevices,
+} = require("resource://devtools/client/responsive/actions/devices.js");
+const {
+ takeScreenshot,
+} = require("resource://devtools/client/responsive/actions/screenshot.js");
+const {
+ changeUserAgent,
+ toggleLeftAlignment,
+ toggleReloadOnTouchSimulation,
+ toggleReloadOnUserAgent,
+ toggleTouchSimulation,
+ toggleUserAgentInput,
+} = require("resource://devtools/client/responsive/actions/ui.js");
+const {
+ changeDevice,
+ changePixelRatio,
+ changeViewportAngle,
+ removeDeviceAssociation,
+ resizeViewport,
+ rotateViewport,
+} = require("resource://devtools/client/responsive/actions/viewports.js");
+const {
+ getOrientation,
+} = require("resource://devtools/client/responsive/utils/orientation.js");
+
+const Types = require("resource://devtools/client/responsive/types.js");
+
+class App extends PureComponent {
+ static get propTypes() {
+ return {
+ devices: PropTypes.shape(Types.devices).isRequired,
+ dispatch: PropTypes.func.isRequired,
+ leftAlignmentEnabled: PropTypes.bool.isRequired,
+ networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
+ screenshot: PropTypes.shape(Types.screenshot).isRequired,
+ viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
+ this.onBrowserContextMenu = this.onBrowserContextMenu.bind(this);
+ this.onChangeDevice = this.onChangeDevice.bind(this);
+ this.onChangeNetworkThrottling = this.onChangeNetworkThrottling.bind(this);
+ this.onChangePixelRatio = this.onChangePixelRatio.bind(this);
+ this.onChangeTouchSimulation = this.onChangeTouchSimulation.bind(this);
+ this.onChangeUserAgent = this.onChangeUserAgent.bind(this);
+ this.onChangeViewportOrientation =
+ this.onChangeViewportOrientation.bind(this);
+ this.onDeviceListUpdate = this.onDeviceListUpdate.bind(this);
+ this.onEditCustomDevice = this.onEditCustomDevice.bind(this);
+ this.onExit = this.onExit.bind(this);
+ this.onRemoveCustomDevice = this.onRemoveCustomDevice.bind(this);
+ this.onRemoveDeviceAssociation = this.onRemoveDeviceAssociation.bind(this);
+ this.doResizeViewport = this.doResizeViewport.bind(this);
+ this.onRotateViewport = this.onRotateViewport.bind(this);
+ this.onScreenshot = this.onScreenshot.bind(this);
+ this.onToggleLeftAlignment = this.onToggleLeftAlignment.bind(this);
+ this.onToggleReloadOnTouchSimulation =
+ this.onToggleReloadOnTouchSimulation.bind(this);
+ this.onToggleReloadOnUserAgent = this.onToggleReloadOnUserAgent.bind(this);
+ this.onToggleUserAgentInput = this.onToggleUserAgentInput.bind(this);
+ this.onUpdateDeviceDisplayed = this.onUpdateDeviceDisplayed.bind(this);
+ this.onUpdateDeviceModal = this.onUpdateDeviceModal.bind(this);
+ }
+
+ componentWillUnmount() {
+ this.browser.removeEventListener("contextmenu", this.onContextMenu);
+ this.browser = null;
+ }
+
+ onAddCustomDevice(device) {
+ this.props.dispatch(addCustomDevice(device));
+ }
+
+ onBrowserContextMenu() {
+ // Update the position of remote browser so that makes the context menu to show at
+ // proper position before showing.
+ this.browser.frameLoader.requestUpdatePosition();
+ }
+
+ onChangeDevice(id, device, deviceType) {
+ // Resize the viewport first.
+ this.doResizeViewport(id, device.width, device.height);
+
+ // TODO: Bug 1332754: Move messaging and logic into the action creator so that the
+ // message is sent from the action creator and device property changes are sent from
+ // there instead of this function.
+ window.postMessage(
+ {
+ type: "change-device",
+ device,
+ viewport: device,
+ },
+ "*"
+ );
+
+ const orientation = getOrientation(device, device);
+
+ this.props.dispatch(changeViewportAngle(0, orientation.angle));
+ this.props.dispatch(changeDevice(id, device.name, deviceType));
+ this.props.dispatch(changePixelRatio(id, device.pixelRatio));
+ this.props.dispatch(changeUserAgent(device.userAgent));
+ this.props.dispatch(toggleTouchSimulation(device.touch));
+ }
+
+ onChangeNetworkThrottling(enabled, profile) {
+ window.postMessage(
+ {
+ type: "change-network-throttling",
+ enabled,
+ profile,
+ },
+ "*"
+ );
+ this.props.dispatch(changeNetworkThrottling(enabled, profile));
+ }
+
+ onChangePixelRatio(pixelRatio) {
+ window.postMessage(
+ {
+ type: "change-pixel-ratio",
+ pixelRatio,
+ },
+ "*"
+ );
+ this.props.dispatch(changePixelRatio(0, pixelRatio));
+ }
+
+ onChangeTouchSimulation(enabled) {
+ window.postMessage(
+ {
+ type: "change-touch-simulation",
+ enabled,
+ },
+ "*"
+ );
+ this.props.dispatch(toggleTouchSimulation(enabled));
+ }
+
+ onChangeUserAgent(userAgent) {
+ window.postMessage(
+ {
+ type: "change-user-agent",
+ userAgent,
+ },
+ "*"
+ );
+ this.props.dispatch(changeUserAgent(userAgent));
+ }
+
+ onChangeViewportOrientation(id, type, angle, isViewportRotated = false) {
+ window.postMessage(
+ {
+ type: "viewport-orientation-change",
+ orientationType: type,
+ angle,
+ isViewportRotated,
+ },
+ "*"
+ );
+
+ if (isViewportRotated) {
+ this.props.dispatch(changeViewportAngle(id, angle));
+ }
+ }
+
+ onDeviceListUpdate(devices) {
+ updatePreferredDevices(devices);
+ }
+
+ onEditCustomDevice(oldDevice, newDevice) {
+ // If the edited device is currently selected, then update its original association
+ // and reset UI state.
+ let viewport = this.props.viewports.find(
+ ({ device }) => device === oldDevice.name
+ );
+
+ if (viewport) {
+ viewport = {
+ ...viewport,
+ device: newDevice.name,
+ deviceType: "custom",
+ height: newDevice.height,
+ width: newDevice.width,
+ pixelRatio: newDevice.pixelRatio,
+ touch: newDevice.touch,
+ userAgent: newDevice.userAgent,
+ };
+ }
+
+ this.props.dispatch(editCustomDevice(viewport, oldDevice, newDevice));
+ }
+
+ onExit() {
+ window.postMessage({ type: "exit" }, "*");
+ }
+
+ onRemoveCustomDevice(device) {
+ // If the custom device is currently selected on any of the viewports,
+ // remove the device association and reset all the ui state.
+ for (const viewport of this.props.viewports) {
+ if (viewport.device === device.name) {
+ this.onRemoveDeviceAssociation(viewport.id);
+ }
+ }
+
+ this.props.dispatch(removeCustomDevice(device));
+ }
+
+ onRemoveDeviceAssociation(id) {
+ // TODO: Bug 1332754: Move messaging and logic into the action creator so that device
+ // property changes are sent from there instead of this function.
+ this.props.dispatch(removeDeviceAssociation(id));
+ this.props.dispatch(toggleTouchSimulation(false));
+ this.props.dispatch(changePixelRatio(id, 0));
+ this.props.dispatch(changeUserAgent(""));
+ }
+
+ doResizeViewport(id, width, height) {
+ // This is the setter function that we pass to Toolbar and Viewports
+ // so they can modify the viewport.
+ window.postMessage(
+ {
+ type: "viewport-resize",
+ width,
+ height,
+ },
+ "*"
+ );
+ this.props.dispatch(resizeViewport(id, width, height));
+ }
+
+ /**
+ * Dispatches the rotateViewport action creator. This utilized by the RDM toolbar as
+ * a prop.
+ *
+ * @param {Number} id
+ * The viewport ID.
+ */
+ onRotateViewport(id) {
+ let currentDevice;
+ const viewport = this.props.viewports[id];
+
+ for (const type of this.props.devices.types) {
+ for (const device of this.props.devices[type]) {
+ if (viewport.device === device.name) {
+ currentDevice = device;
+ }
+ }
+ }
+
+ // If no device is selected, then assume the selected device's primary orientation is
+ // opposite of the viewport orientation.
+ if (!currentDevice) {
+ currentDevice = {
+ height: viewport.width,
+ width: viewport.height,
+ };
+ }
+
+ const currentAngle = Services.prefs.getIntPref(
+ "devtools.responsive.viewport.angle"
+ );
+ const angleToRotateTo = currentAngle === 90 ? 0 : 90;
+ const { type, angle } = getOrientation(
+ currentDevice,
+ viewport,
+ angleToRotateTo
+ );
+
+ this.onChangeViewportOrientation(id, type, angle, true);
+ this.props.dispatch(rotateViewport(id));
+
+ window.postMessage(
+ {
+ type: "viewport-resize",
+ height: viewport.width,
+ width: viewport.height,
+ },
+ "*"
+ );
+ }
+
+ onScreenshot() {
+ this.props.dispatch(takeScreenshot());
+ }
+
+ onToggleLeftAlignment() {
+ this.props.dispatch(toggleLeftAlignment());
+
+ window.postMessage(
+ {
+ type: "toggle-left-alignment",
+ leftAlignmentEnabled: this.props.leftAlignmentEnabled,
+ },
+ "*"
+ );
+ }
+
+ onToggleReloadOnTouchSimulation() {
+ this.props.dispatch(toggleReloadOnTouchSimulation());
+ }
+
+ onToggleReloadOnUserAgent() {
+ this.props.dispatch(toggleReloadOnUserAgent());
+ }
+
+ onToggleUserAgentInput() {
+ this.props.dispatch(toggleUserAgentInput());
+ }
+
+ onUpdateDeviceDisplayed(device, deviceType, displayed) {
+ this.props.dispatch(updateDeviceDisplayed(device, deviceType, displayed));
+ }
+
+ onUpdateDeviceModal(isOpen, modalOpenedFromViewport) {
+ this.props.dispatch(updateDeviceModal(isOpen, modalOpenedFromViewport));
+ window.postMessage({ type: "update-device-modal", isOpen }, "*");
+ }
+
+ render() {
+ const { devices, networkThrottling, screenshot, viewports } = this.props;
+
+ const {
+ onAddCustomDevice,
+ onChangeDevice,
+ onChangeNetworkThrottling,
+ onChangePixelRatio,
+ onChangeTouchSimulation,
+ onChangeUserAgent,
+ onDeviceListUpdate,
+ onEditCustomDevice,
+ onExit,
+ onRemoveCustomDevice,
+ onRemoveDeviceAssociation,
+ doResizeViewport,
+ onRotateViewport,
+ onScreenshot,
+ onToggleLeftAlignment,
+ onToggleReloadOnTouchSimulation,
+ onToggleReloadOnUserAgent,
+ onToggleUserAgentInput,
+ onUpdateDeviceDisplayed,
+ onUpdateDeviceModal,
+ } = this;
+
+ if (!viewports.length) {
+ return null;
+ }
+
+ const selectedDevice = viewports[0].device;
+ const selectedPixelRatio = viewports[0].pixelRatio;
+
+ let deviceAdderViewportTemplate = {};
+ if (devices.modalOpenedFromViewport !== null) {
+ deviceAdderViewportTemplate = viewports[devices.modalOpenedFromViewport];
+ }
+
+ return dom.div(
+ { id: "app" },
+ Toolbar({
+ devices,
+ networkThrottling,
+ screenshot,
+ selectedDevice,
+ selectedPixelRatio,
+ viewport: viewports[0],
+ onChangeDevice,
+ onChangeNetworkThrottling,
+ onChangePixelRatio,
+ onChangeTouchSimulation,
+ onChangeUserAgent,
+ onExit,
+ onRemoveDeviceAssociation,
+ doResizeViewport,
+ onRotateViewport,
+ onScreenshot,
+ onToggleLeftAlignment,
+ onToggleReloadOnTouchSimulation,
+ onToggleReloadOnUserAgent,
+ onToggleUserAgentInput,
+ onUpdateDeviceModal,
+ }),
+ devices.isModalOpen
+ ? DeviceModal({
+ deviceAdderViewportTemplate,
+ devices,
+ onAddCustomDevice,
+ onDeviceListUpdate,
+ onEditCustomDevice,
+ onRemoveCustomDevice,
+ onUpdateDeviceDisplayed,
+ onUpdateDeviceModal,
+ })
+ : null
+ );
+ }
+}
+
+const mapStateToProps = state => {
+ return {
+ ...state,
+ leftAlignmentEnabled: state.ui.leftAlignmentEnabled,
+ };
+};
+
+module.exports = connect(mapStateToProps)(App);