diff options
Diffstat (limited to 'devtools/client/application/initializer.js')
-rw-r--r-- | devtools/client/application/initializer.js | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/devtools/client/application/initializer.js b/devtools/client/application/initializer.js new file mode 100644 index 0000000000..9fad2cb924 --- /dev/null +++ b/devtools/client/application/initializer.js @@ -0,0 +1,158 @@ +/* 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 { BrowserLoader } = ChromeUtils.import( + "resource://devtools/client/shared/browser-loader.js" +); +const require = BrowserLoader({ + baseURI: "resource://devtools/client/application/", + window, +}).require; + +const { createFactory } = require("devtools/client/shared/vendor/react"); +const { + render, + unmountComponentAtNode, +} = require("devtools/client/shared/vendor/react-dom"); +const Provider = createFactory( + require("devtools/client/shared/vendor/react-redux").Provider +); +const { bindActionCreators } = require("devtools/client/shared/vendor/redux"); +const { l10n } = require("devtools/client/application/src/modules/l10n"); + +const { + configureStore, +} = require("devtools/client/application/src/create-store"); +const actions = require("devtools/client/application/src/actions/index"); + +const { WorkersListener } = require("devtools/client/shared/workers-listener"); +const Telemetry = require("devtools/client/shared/telemetry"); + +const { + services, +} = require("devtools/client/application/src/modules/application-services"); + +const App = createFactory( + require("devtools/client/application/src/components/App") +); + +const { safeAsyncMethod } = require("devtools/shared/async-utils"); + +/** + * Global Application object in this panel. This object is expected by panel.js and is + * called to start the UI for the panel. + */ +window.Application = { + async bootstrap({ toolbox, panel }) { + // bind event handlers to `this` + this.handleOnNavigate = this.handleOnNavigate.bind(this); + this.updateDomain = this.updateDomain.bind(this); + this.onTargetAvailable = this.onTargetAvailable.bind(this); + this.onTargetDestroyed = this.onTargetDestroyed.bind(this); + + // wrap updateWorkers to swallow rejections occurring after destroy + this.safeUpdateWorkers = safeAsyncMethod( + () => this.updateWorkers(), + () => this._destroyed + ); + + this.toolbox = toolbox; + // NOTE: the client is the same through the lifecycle of the toolbox, even + // though we get it from toolbox.target + this.client = toolbox.target.client; + + this.telemetry = new Telemetry(); + this.store = configureStore(this.telemetry, toolbox.sessionId); + this.actions = bindActionCreators(actions, this.store.dispatch); + + services.init(this.toolbox); + await l10n.init(["devtools/client/application.ftl"]); + + await this.updateWorkers(); + this.workersListener = new WorkersListener(this.client.mainRoot); + this.workersListener.addListener(this.safeUpdateWorkers); + + const deviceFront = await this.client.mainRoot.getFront("device"); + const { canDebugServiceWorkers } = await deviceFront.getDescription(); + this.actions.updateCanDebugWorkers( + canDebugServiceWorkers && services.features.doesDebuggerSupportWorkers + ); + + // awaiting for watchTargets will return the targets that are currently + // available, so we can have our first render with all the data ready + await this.toolbox.targetList.watchTargets( + [this.toolbox.targetList.TYPES.FRAME], + this.onTargetAvailable, + this.onTargetDestroyed + ); + + // Render the root Application component. + this.mount = document.querySelector("#mount"); + const app = App({ + client: this.client, + fluentBundles: l10n.getBundles(), + }); + render(Provider({ store: this.store }, app), this.mount); + }, + + handleOnNavigate() { + this.updateDomain(); + this.actions.resetManifest(); + }, + + async updateWorkers() { + const registrationsWithWorkers = await this.client.mainRoot.listAllServiceWorkers(); + this.actions.updateWorkers(registrationsWithWorkers); + }, + + updateDomain() { + this.actions.updateDomain(this.toolbox.target.url); + }, + + setupTarget(targetFront) { + this.handleOnNavigate(); // update domain and manifest for the new target + targetFront.on("navigate", this.handleOnNavigate); + }, + + cleanUpTarget(targetFront) { + targetFront.off("navigate", this.handleOnNavigate); + }, + + onTargetAvailable({ targetFront }) { + if (!targetFront.isTopLevel) { + return; // ignore target frames that are not top level for now + } + + this.setupTarget(targetFront); + }, + + onTargetDestroyed({ targetFront }) { + if (!targetFront.isTopLevel) { + return; // ignore target frames that are not top level for now + } + + this.cleanUpTarget(targetFront); + }, + + destroy() { + this.workersListener.removeListener(); + + this.toolbox.targetList.unwatchTargets( + [this.toolbox.targetList.TYPES.FRAME], + this.onTargetAvailable, + this.onTargetDestroyed + ); + + this.cleanUpTarget(this.toolbox.target); + + unmountComponentAtNode(this.mount); + this.mount = null; + this.toolbox = null; + this.client = null; + this.workersListener = null; + this._destroyed = true; + }, +}; |