Bug 1578824 - Land initial boilerplate for embedding RDM UI into the browser. r=mtigl...
[gecko.git] / devtools / client / responsive / index.js
blob6d7b80aa572c4f7fe9477475735dcf86694cd5e0
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /* eslint-env browser */
7 "use strict";
9 const { BrowserLoader } = ChromeUtils.import(
10   "resource://devtools/client/shared/browser-loader.js"
12 const { require } = BrowserLoader({
13   baseURI: "resource://devtools/client/responsive/",
14   window,
15 });
16 const Services = require("Services");
17 const Telemetry = require("devtools/client/shared/telemetry");
19 const {
20   createFactory,
21   createElement,
22 } = require("devtools/client/shared/vendor/react");
23 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
24 const { Provider } = require("devtools/client/shared/vendor/react-redux");
26 const message = require("./utils/message");
27 const App = createFactory(require("./components/App"));
28 const Store = require("./store");
29 const { loadDevices, restoreDeviceState } = require("./actions/devices");
30 const {
31   addViewport,
32   resizeViewport,
33   zoomViewport,
34 } = require("./actions/viewports");
35 const { changeDisplayPixelRatio } = require("./actions/ui");
37 // Exposed for use by tests
38 window.require = require;
40 if (Services.prefs.getBoolPref("devtools.responsive.browserUI.enabled")) {
41   // Tell the ResponsiveUIManager that the frame script has begun initializing.
42   message.post(window, "script-init");
45 const bootstrap = {
46   telemetry: new Telemetry(),
48   store: null,
50   async init() {
51     // responsive is not connected with a toolbox so we pass -1 as the
52     // toolbox session id.
53     this.telemetry.toolOpened("responsive", -1, this);
55     const store = (this.store = Store());
56     const provider = createElement(Provider, { store }, App());
57     ReactDOM.render(provider, document.querySelector("#root"));
58     message.post(window, "init:done");
59   },
61   destroy() {
62     this.store = null;
64     // responsive is not connected with a toolbox so we pass -1 as the
65     // toolbox session id.
66     this.telemetry.toolClosed("responsive", -1, this);
67     this.telemetry = null;
68   },
70   /**
71    * While most actions will be dispatched by React components, some external
72    * APIs that coordinate with the larger browser UI may also have actions to
73    * to dispatch.  They can do so here.
74    */
75   dispatch(action) {
76     if (!this.store) {
77       // If actions are dispatched after store is destroyed, ignore them.  This
78       // can happen in tests that close the tool quickly while async tasks like
79       // initDevices() below are still pending.
80       return;
81     }
82     this.store.dispatch(action);
83   },
86 // manager.js sends a message to signal init
87 message.wait(window, "init").then(() => bootstrap.init());
89 // manager.js sends a message to signal init is done, which can be used for delayed
90 // startup work that shouldn't block initial load
91 message.wait(window, "post-init").then(() => {
92   bootstrap.store.dispatch(loadDevices()).then(() => {
93     bootstrap.dispatch(restoreDeviceState());
94   });
95 });
97 window.addEventListener(
98   "unload",
99   function() {
100     bootstrap.destroy();
101   },
102   { once: true }
105 // Allows quick testing of actions from the console
106 window.dispatch = action => bootstrap.dispatch(action);
108 // Expose the store on window for testing
109 Object.defineProperty(window, "store", {
110   get: () => bootstrap.store,
111   enumerable: true,
114 // Dispatch a `changeDisplayPixelRatio` action when the browser's pixel ratio is changing.
115 // This is usually triggered when the user changes the monitor resolution, or when the
116 // browser's window is dragged to a different display with a different pixel ratio.
117 // TODO: It would be better to move this watching into the actor, so that it can be
118 // better synchronized with any overrides that might be applied.  Also, reading a single
119 // value like this makes less sense with multiple viewports.
120 function onDevicePixelRatioChange() {
121   const dpr = window.devicePixelRatio;
122   const mql = window.matchMedia(`(resolution: ${dpr}dppx)`);
124   function listener() {
125     bootstrap.dispatch(changeDisplayPixelRatio(window.devicePixelRatio));
126     mql.removeListener(listener);
127     onDevicePixelRatioChange();
128   }
130   mql.addListener(listener);
134  * Called by manager.js to add the initial viewport based on the original page.
135  */
136 window.addInitialViewport = ({ userContextId }) => {
137   try {
138     onDevicePixelRatioChange();
139     bootstrap.dispatch(changeDisplayPixelRatio(window.devicePixelRatio));
140     bootstrap.dispatch(addViewport(userContextId));
141   } catch (e) {
142     console.error(e);
143   }
147  * Called by manager.js when tests want to check the viewport size.
148  */
149 window.getViewportSize = () => {
150   const { viewports } = bootstrap.store.getState();
151   if (!viewports.length) {
152     return null;
153   }
155   const { width, height } = viewports[0];
156   return { width, height };
160  * Called by manager.js to set viewport size from tests, etc.
161  */
162 window.setViewportSize = ({ width, height }) => {
163   try {
164     bootstrap.dispatch(resizeViewport(0, width, height));
165   } catch (e) {
166     console.error(e);
167   }
171  * Called by manager.js to access the viewport's browser, either for testing
172  * purposes or to reload it when touch simulation is enabled.
173  * A messageManager getter is added on the object to provide an easy access
174  * to the message manager without pulling the frame loader.
175  */
176 window.getViewportBrowser = () => {
177   const browser = document.querySelector("iframe.browser");
178   if (!browser.messageManager) {
179     Object.defineProperty(browser, "messageManager", {
180       get() {
181         return this.frameLoader.messageManager;
182       },
183       configurable: true,
184       enumerable: true,
185     });
186   }
187   return browser;
191  * Called by manager.js to zoom the viewport.
192  */
193 window.setViewportZoom = zoom => {
194   try {
195     bootstrap.dispatch(zoomViewport(0, zoom));
196   } catch (e) {
197     console.error(e);
198   }