mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-10 01:07:31 +09:00
Issue #303 Part 1: Move basilisk files from /browser to /application/basilisk
This commit is contained in:
parent
a951f19dfd
commit
177b28a898
1938 changed files with 0 additions and 0 deletions
299
application/basilisk/components/newtab/NewTabWebChannel.jsm
Normal file
299
application/basilisk/components/newtab/NewTabWebChannel.jsm
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
/* 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();
|
||||
Loading…
Add table
Add a link
Reference in a new issue