diff options
Diffstat (limited to 'browser/components/newtab/NewTabWebChannel.jsm')
-rw-r--r-- | browser/components/newtab/NewTabWebChannel.jsm | 299 |
1 files changed, 0 insertions, 299 deletions
diff --git a/browser/components/newtab/NewTabWebChannel.jsm b/browser/components/newtab/NewTabWebChannel.jsm deleted file mode 100644 index 40ee73684..000000000 --- a/browser/components/newtab/NewTabWebChannel.jsm +++ /dev/null @@ -1,299 +0,0 @@ -/* global - NewTabPrefsProvider, - Services, - EventEmitter, - Preferences, - XPCOMUtils, - WebChannel, - NewTabRemoteResources -*/ -/* exported NewTabWebChannel */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["NewTabWebChannel"]; - -const {utils: Cu} = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", - "resource:///modules/NewTabPrefsProvider.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NewTabRemoteResources", - "resource:///modules/NewTabRemoteResources.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "WebChannel", - "resource://gre/modules/WebChannel.jsm"); -XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() { - const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {}); - return EventEmitter; -}); - -const CHAN_ID = "newtab"; -const PREF_ENABLED = "browser.newtabpage.remote"; -const PREF_MODE = "browser.newtabpage.remote.mode"; - -/** - * NewTabWebChannel is the conduit for all communication with unprivileged newtab instances. - * - * It allows for the ability to broadcast to all newtab browsers. - * If the browser.newtab.remote pref is false, the object will be in an uninitialized state. - * - * Mode choices: - * 'production': pages from our production CDN - * 'staging': pages from our staging CDN - * 'test': intended for tests - * 'test2': intended for tests - * 'dev': intended for development - * - * An unknown mode will result in 'production' mode, which is the default - * - * Incoming messages are expected to be JSON-serialized and in the format: - * - * { - * type: "REQUEST_SCREENSHOT", - * data: { - * url: "https://example.com" - * } - * } - * - * Or: - * - * { - * type: "REQUEST_SCREENSHOT", - * } - * - * Outgoing messages are expected to be objects serializable by structured cloning, in a similar format: - * { - * type: "RECEIVE_SCREENSHOT", - * data: { - * "url": "https://example.com", - * "image": "dataURi:....." - * } - * } - */ -let NewTabWebChannelImpl = function NewTabWebChannelImpl() { - EventEmitter.decorate(this); - this._handlePrefChange = this._handlePrefChange.bind(this); - this._incomingMessage = this._incomingMessage.bind(this); -}; - -NewTabWebChannelImpl.prototype = { - _prefs: {}, - _channel: null, - - // a WeakMap containing browsers as keys and a weak ref to their principal - // as value - _principals: null, - - // a Set containing weak refs to browsers - _browsers: null, - - /* - * Returns current channel's ID - */ - get chanId() { - return CHAN_ID; - }, - - /* - * Returns the number of browsers currently tracking - */ - get numBrowsers() { - return this._getBrowserRefs().length; - }, - - /* - * Returns current channel's origin - */ - get origin() { - if (!(this._prefs.mode in NewTabRemoteResources.MODE_CHANNEL_MAP)) { - this._prefs.mode = "production"; - } - return NewTabRemoteResources.MODE_CHANNEL_MAP[this._prefs.mode].origin; - }, - - /* - * Unloads all browsers and principals - */ - _unloadAll() { - if (this._principals != null) { - this._principals = new WeakMap(); - } - this._browsers = new Set(); - this.emit("targetUnloadAll"); - }, - - /* - * Checks if a browser is known - * - * This will cause an iteration through all known browsers. - * That's ok, we don't expect a lot of browsers - */ - _isBrowserKnown(browser) { - for (let bRef of this._getBrowserRefs()) { - let b = bRef.get(); - if (b && b.permanentKey === browser.permanentKey) { - return true; - } - } - - return false; - }, - - /* - * Obtains all known browser refs - */ - _getBrowserRefs() { - // Some code may try to emit messages after teardown. - if (!this._browsers) { - return []; - } - let refs = []; - for (let bRef of this._browsers) { - /* - * even though we hold a weak ref to browser, it seems that browser - * objects aren't gc'd immediately after a tab closes. They stick around - * in memory, but thankfully they don't have a documentURI in that case - */ - let browser = bRef.get(); - if (browser && browser.documentURI) { - refs.push(bRef); - } else { - // need to clean up principals because the browser object is not gc'ed - // immediately - this._principals.delete(browser); - this._browsers.delete(bRef); - this.emit("targetUnload"); - } - } - return refs; - }, - - /* - * Receives a message from content. - * - * Keeps track of browsers for broadcast, relays messages to listeners. - */ - _incomingMessage(id, message, target) { - if (this.chanId !== id) { - Cu.reportError(new Error("NewTabWebChannel unexpected message destination")); - } - - /* - * need to differentiate by browser, because event targets are created each - * time a message is sent. - */ - if (!this._isBrowserKnown(target.browser)) { - this._browsers.add(Cu.getWeakReference(target.browser)); - this._principals.set(target.browser, Cu.getWeakReference(target.principal)); - this.emit("targetAdd"); - } - - try { - let msg = JSON.parse(message); - this.emit(msg.type, {data: msg.data, target: target}); - } catch (err) { - Cu.reportError(err); - } - }, - - /* - * Sends a message to all known browsers - */ - broadcast(actionType, message) { - for (let bRef of this._getBrowserRefs()) { - let browser = bRef.get(); - try { - let principal = this._principals.get(browser).get(); - if (principal && browser && browser.documentURI) { - this._channel.send({type: actionType, data: message}, {browser, principal}); - } - } catch (e) { - Cu.reportError(new Error("NewTabWebChannel WeakRef is dead")); - this._principals.delete(browser); - } - } - }, - - /* - * Sends a message to a specific target - */ - send(actionType, message, target) { - try { - this._channel.send({type: actionType, data: message}, target); - } catch (e) { - // Web Channel might be dead - Cu.reportError(e); - } - }, - - /* - * Pref change observer callback - */ - _handlePrefChange(prefName, newState, forceState) { // eslint-disable-line no-unused-vars - switch (prefName) { - case PREF_ENABLED: - if (!this._prefs.enabled && newState) { - // changing state from disabled to enabled - this.setupState(); - } else if (this._prefs.enabled && !newState) { - // changing state from enabled to disabled - this.tearDownState(); - } - break; - case PREF_MODE: - if (this._prefs.mode !== newState) { - // changing modes - this.tearDownState(); - this.setupState(); - } - break; - } - }, - - /* - * Sets up the internal state - */ - setupState() { - this._prefs.enabled = Preferences.get(PREF_ENABLED, false); - - let mode = Preferences.get(PREF_MODE, "production"); - if (!(mode in NewTabRemoteResources.MODE_CHANNEL_MAP)) { - mode = "production"; - } - this._prefs.mode = mode; - this._principals = new WeakMap(); - this._browsers = new Set(); - - if (this._prefs.enabled) { - this._channel = new WebChannel(this.chanId, Services.io.newURI(this.origin, null, null)); - this._channel.listen(this._incomingMessage); - } - }, - - tearDownState() { - if (this._channel) { - this._channel.stopListening(); - } - this._prefs = {}; - this._unloadAll(); - this._channel = null; - this._principals = null; - this._browsers = null; - }, - - init() { - this.setupState(); - NewTabPrefsProvider.prefs.on(PREF_ENABLED, this._handlePrefChange); - NewTabPrefsProvider.prefs.on(PREF_MODE, this._handlePrefChange); - }, - - uninit() { - this.tearDownState(); - NewTabPrefsProvider.prefs.off(PREF_ENABLED, this._handlePrefChange); - NewTabPrefsProvider.prefs.off(PREF_MODE, this._handlePrefChange); - } -}; - -let NewTabWebChannel = new NewTabWebChannelImpl(); |