initializer.js (4795B)
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/. */ 4 5 "use strict"; 6 7 const { BrowserLoader } = ChromeUtils.importESModule( 8 "resource://devtools/shared/loader/browser-loader.sys.mjs" 9 ); 10 const require = BrowserLoader({ 11 baseURI: "resource://devtools/client/application/", 12 window, 13 }).require; 14 15 const { 16 createFactory, 17 } = require("resource://devtools/client/shared/vendor/react.mjs"); 18 const { 19 render, 20 unmountComponentAtNode, 21 } = require("resource://devtools/client/shared/vendor/react-dom.mjs"); 22 const Provider = createFactory( 23 require("resource://devtools/client/shared/vendor/react-redux.js").Provider 24 ); 25 const { 26 bindActionCreators, 27 } = require("resource://devtools/client/shared/vendor/redux.js"); 28 const { 29 START_IGNORE_ACTION, 30 } = require("resource://devtools/client/shared/redux/middleware/ignore.js"); 31 const { 32 l10n, 33 } = require("resource://devtools/client/application/src/modules/l10n.js"); 34 35 const { 36 configureStore, 37 } = require("resource://devtools/client/application/src/create-store.js"); 38 const actions = require("resource://devtools/client/application/src/actions/index.js"); 39 40 const { 41 WorkersListener, 42 } = require("resource://devtools/client/shared/workers-listener.js"); 43 44 const { 45 services, 46 } = require("resource://devtools/client/application/src/modules/application-services.js"); 47 48 const App = createFactory( 49 require("resource://devtools/client/application/src/components/App.js") 50 ); 51 52 const { 53 safeAsyncMethod, 54 } = require("resource://devtools/shared/async-utils.js"); 55 56 /** 57 * Global Application object in this panel. This object is expected by panel.js and is 58 * called to start the UI for the panel. 59 */ 60 window.Application = { 61 async bootstrap({ toolbox, commands }) { 62 // bind event handlers to `this` 63 this.updateDomain = this.updateDomain.bind(this); 64 65 // wrap updateWorkers to swallow rejections occurring after destroy 66 this.safeUpdateWorkers = safeAsyncMethod( 67 () => this.updateWorkers(), 68 () => this._destroyed 69 ); 70 71 this.toolbox = toolbox; 72 this._commands = commands; 73 this.client = commands.client; 74 75 this.store = configureStore(toolbox.telemetry); 76 this.actions = bindActionCreators(actions, this.store.dispatch); 77 78 services.init(this.toolbox); 79 await l10n.init(["devtools/client/application.ftl"]); 80 81 await this.updateWorkers(); 82 this.workersListener = new WorkersListener(this.client.mainRoot); 83 this.workersListener.addListener(this.safeUpdateWorkers); 84 85 const deviceFront = await this.client.mainRoot.getFront("device"); 86 const { canDebugServiceWorkers } = await deviceFront.getDescription(); 87 this.actions.updateCanDebugWorkers( 88 canDebugServiceWorkers && services.features.doesDebuggerSupportWorkers 89 ); 90 91 this.onResourceAvailable = this.onResourceAvailable.bind(this); 92 await this._commands.resourceCommand.watchResources( 93 [this._commands.resourceCommand.TYPES.DOCUMENT_EVENT], 94 { 95 onAvailable: this.onResourceAvailable, 96 } 97 ); 98 99 // Render the root Application component. 100 this.mount = document.querySelector("#mount"); 101 const app = App({ 102 client: this.client, 103 fluentBundles: l10n.getBundles(), 104 }); 105 render(Provider({ store: this.store }, app), this.mount); 106 }, 107 108 async updateWorkers() { 109 const registrationsWithWorkers = 110 await this.client.mainRoot.listAllServiceWorkers(); 111 this.actions.updateWorkers(registrationsWithWorkers); 112 }, 113 114 updateDomain() { 115 this.actions.updateDomain(this.toolbox.target.url); 116 }, 117 118 handleOnNavigate() { 119 this.updateDomain(); 120 this.actions.resetManifest(); 121 }, 122 123 onResourceAvailable(resources) { 124 // Only consider top level document, and ignore remote iframes top document 125 const hasDocumentDomComplete = resources.some( 126 resource => 127 resource.resourceType === 128 this._commands.resourceCommand.TYPES.DOCUMENT_EVENT && 129 resource.name === "dom-complete" && 130 resource.targetFront.isTopLevel 131 ); 132 if (hasDocumentDomComplete) { 133 this.handleOnNavigate(); // update domain and manifest for the new target 134 } 135 }, 136 137 destroy() { 138 // Prevents any further action from being dispatched 139 this.store.dispatch(START_IGNORE_ACTION); 140 141 this.workersListener.removeListener(); 142 143 this._commands.resourceCommand.unwatchResources( 144 [this._commands.resourceCommand.TYPES.DOCUMENT_EVENT], 145 { onAvailable: this.onResourceAvailable } 146 ); 147 148 unmountComponentAtNode(this.mount); 149 this.mount = null; 150 this.toolbox = null; 151 this.client = null; 152 this._commands = null; 153 this.workersListener = null; 154 this._destroyed = true; 155 }, 156 };