Continue slowly working on unified event bus
This commit is contained in:
parent
46cb5e1078
commit
a0388689cf
@ -2,16 +2,16 @@
|
||||
* For some reason, Chrome really doesn't like when chrome.runtime
|
||||
* methods are wrapped inside a ES6 proxy object. If 'port' is a
|
||||
* ES6 Proxy of a Port object that `chrome.runtime.connect()` creates,
|
||||
* then Chrome will do bullshits like `port.sendMessage` and
|
||||
* then Chrome will do bullshits like `port.sendMessage` and
|
||||
* `port.onMessage.addListener` crashing your Vue3 UI with bullshits
|
||||
* excuses, e.g.
|
||||
*
|
||||
*
|
||||
* | TypeError: Illegal invocation. Function must be called on
|
||||
* | an object of type Port
|
||||
*
|
||||
*
|
||||
* which is some grade A bullshit because Firefox can handle that just
|
||||
* fine.
|
||||
*
|
||||
*
|
||||
* There's two ways how I could handle this:
|
||||
* * Find out how to get the original object from the proxy Vue3
|
||||
* creates, which would take time and ruin my xmass holiday, or
|
||||
@ -19,8 +19,8 @@
|
||||
* the very real possibility that there's prolly a reason Chrome
|
||||
* does things in its own very special(tm) way, as if it had one
|
||||
* extra chromosome over Firefox.
|
||||
*
|
||||
* Easy chhoice, really.
|
||||
*
|
||||
* Easy choice, really.
|
||||
*/
|
||||
export class ChromeShittinessMitigations {
|
||||
static port = null;
|
||||
@ -30,4 +30,4 @@ export class ChromeShittinessMitigations {
|
||||
}
|
||||
}
|
||||
|
||||
export default ChromeShittinessMitigations;
|
||||
export default ChromeShittinessMitigations;
|
||||
|
@ -44,12 +44,22 @@ export default class UWServer {
|
||||
}],
|
||||
'replace-css': [{
|
||||
function: (message, context) => this.replaceCss(message.oldCssString, message.newCssString, context.comms.sender)
|
||||
}],
|
||||
'get-current-site': [{
|
||||
function: (message, context) => this.getCurrentSite()
|
||||
}]
|
||||
};
|
||||
|
||||
private gcTimeout: any;
|
||||
uiLoggerInitialized: boolean = false;
|
||||
|
||||
|
||||
//#region getters
|
||||
get activeTab() {
|
||||
return browser.tabs.query({currentWindow: true, active: true});
|
||||
}
|
||||
//#endregion
|
||||
|
||||
constructor() {
|
||||
this.setup();
|
||||
}
|
||||
@ -227,10 +237,26 @@ export default class UWServer {
|
||||
this.selectedSubitem[menu] = subitem;
|
||||
}
|
||||
|
||||
async getCurrentSite() {
|
||||
this.eventBus.send(
|
||||
'set-current-site',
|
||||
{
|
||||
site: await this.getVideoTab(),
|
||||
tabHostname: await this.getCurrentTabHostname(),
|
||||
},
|
||||
{
|
||||
comms: {
|
||||
forwardTo: 'popup'
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async getCurrentTab() {
|
||||
return (await browser.tabs.query({active: true, currentWindow: true}))[0];
|
||||
}
|
||||
|
||||
|
||||
async getVideoTab() {
|
||||
// friendly reminder: if current tab doesn't have a video,
|
||||
// there won't be anything in this.videoTabs[this.currentTabId]
|
||||
@ -279,8 +305,27 @@ export default class UWServer {
|
||||
}
|
||||
}
|
||||
|
||||
// chrome shitiness mitigation
|
||||
sendUnmarkPlayer(message) {
|
||||
this.comms.sendUnmarkPlayer(message);
|
||||
async getCurrentTabHostname() {
|
||||
const activeTab = await this.activeTab;
|
||||
|
||||
if (!activeTab || activeTab.length < 1) {
|
||||
this.logger.log('warn', 'comms', 'There is no active tab for some reason. activeTab:', activeTab);
|
||||
}
|
||||
|
||||
const url = activeTab[0].url;
|
||||
|
||||
var hostname;
|
||||
|
||||
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
|
||||
hostname = url.split('/')[2];
|
||||
}
|
||||
else {
|
||||
hostname = url.split('/')[0];
|
||||
}
|
||||
|
||||
hostname = hostname.split(':')[0]; //find & remove port number
|
||||
hostname = hostname.split('?')[0]; //find & remove "?"
|
||||
|
||||
return hostname;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ export interface EventBusContext {
|
||||
comms?: {
|
||||
sender?: any,
|
||||
port?: any,
|
||||
forwardTo?: 'all' | 'active' | 'contentScript' | 'sameOrigin',
|
||||
frame?: any,
|
||||
forwardTo?: 'all' | 'active' | 'contentScript' | 'server' | 'sameOrigin' | 'popup',
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +24,7 @@ export default class EventBus {
|
||||
private commands: { [x: string]: EventBusCommand[]} = {};
|
||||
private downstreamBuses: EventBus[] = [];
|
||||
private upstreamBus?: EventBus;
|
||||
private comms?: CommsClient;
|
||||
private comms?: CommsClient | CommsServer;
|
||||
|
||||
//#region lifecycle
|
||||
destroy() {
|
||||
@ -34,7 +35,7 @@ export default class EventBus {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
setComms(comms: CommsClient) {
|
||||
setComms(comms: CommsClient | CommsServer) {
|
||||
this.comms = comms;
|
||||
}
|
||||
|
||||
@ -86,7 +87,7 @@ export default class EventBus {
|
||||
}
|
||||
|
||||
if (this.comms && !context?.fromComms) {
|
||||
this.comms.sendMessage({command, config});
|
||||
this.comms.sendMessage({command, config}, context);
|
||||
}
|
||||
|
||||
if (context?.stopPropagation) {
|
||||
@ -114,7 +115,7 @@ export default class EventBus {
|
||||
}
|
||||
|
||||
|
||||
sendDownstream(command: string, config: any, context?: EventBusContext, sourceEventBus?: EventBus) {
|
||||
private sendDownstream(command: string, config: any, context?: EventBusContext, sourceEventBus?: EventBus) {
|
||||
for (const eventBus of this.downstreamBuses) {
|
||||
if (eventBus !== sourceEventBus) {
|
||||
// prevent eventBus.send from auto-propagating the command
|
||||
@ -124,7 +125,7 @@ export default class EventBus {
|
||||
}
|
||||
}
|
||||
|
||||
sendUpstream(command: string, config: any, context?: EventBusContext) {
|
||||
private sendUpstream(command: string, config: any, context?: EventBusContext) {
|
||||
if (this.upstreamBus) {
|
||||
// prevent eventBus.send from auto-propagating the command
|
||||
this.upstreamBus.send(command, config, {...context, stopPropagation: true});
|
||||
|
@ -25,22 +25,34 @@ if (process.env.CHANNEL !== 'stable'){
|
||||
*
|
||||
* EventBus is started first. Other components (including commsClient) follow later.
|
||||
*
|
||||
* Messages that pass through CommsServer need to define context object with
|
||||
* context.comms.forwardTo field defined, with one of the following values:
|
||||
*
|
||||
* - all : all content scripts of ALL TABS
|
||||
* - active : all content scripts in CURRENT TAB
|
||||
* - contentScript : specific content script (requires other EventBusContext fields!)
|
||||
* - backgroundScript : background script (considered default behaviour)
|
||||
* - sameOrigin : ???
|
||||
* - popup : extension popup
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* fig 0. ULTRAWIDIFY COMMUNICATION MAP
|
||||
*
|
||||
* CS EVENT BUS
|
||||
* (accessible within tab scripts)
|
||||
* | NOT EVENT BUS
|
||||
* PageInfo x (accessible within popup)
|
||||
* | BG EVENT BUS
|
||||
* PageInfo x (accessible within background page)
|
||||
* x |
|
||||
* : : x UWServer
|
||||
* x CommsClient <---------------x CommsServer x
|
||||
* | (Connect to popup)
|
||||
* |
|
||||
* x eventBus.sendToTunnel()
|
||||
* <iframe tunnel>
|
||||
* A
|
||||
* |
|
||||
* | (Connect to popup) X POPUP EVENT BUS
|
||||
* | A (accessible within popup) /todo
|
||||
* x eventBus.sendToTunnel() | |
|
||||
* <iframe tunnel> \----------------> ??? X
|
||||
* A |
|
||||
* | X App.vue
|
||||
* V
|
||||
* x <iframe tunnel>
|
||||
* |
|
||||
@ -99,7 +111,7 @@ class CommsClient {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
async sendMessage(message){
|
||||
async sendMessage(message, context?){
|
||||
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
|
||||
return browser.runtime.sendMessage(null, message, null);
|
||||
}
|
||||
|
0
src/ext/lib/comms/CommsPopupClient.ts
Normal file
0
src/ext/lib/comms/CommsPopupClient.ts
Normal file
@ -13,7 +13,6 @@ class CommsServer {
|
||||
settings: Settings;
|
||||
eventBus: EventBus;
|
||||
|
||||
|
||||
ports: {
|
||||
[tab: string] : {
|
||||
[frame: string] : {
|
||||
@ -23,49 +22,13 @@ class CommsServer {
|
||||
} = {};
|
||||
popupPort: any;
|
||||
|
||||
/**
|
||||
* commands — functions that handle incoming messages
|
||||
* functions can have the following arguments, which are,
|
||||
* in this order:
|
||||
* message — the message we received
|
||||
* port|sender — on persistent channels, second argument is port on which the server
|
||||
* listens. If the message was sent in non-persistent way, this is the
|
||||
* sender script/frame/whatever of the message
|
||||
* sendResponse — callback function on messages received via non-persistent channel
|
||||
*/
|
||||
commands: {[x: string]: ((a: any, b: any) => void | Promise<void>)[]} = {
|
||||
'get-current-site': [
|
||||
async (message, port) => {
|
||||
port.postMessage({
|
||||
cmd: 'set-current-site',
|
||||
site: await this.server.getVideoTab(),
|
||||
tabHostname: await this.getCurrentTabHostname()
|
||||
});
|
||||
},
|
||||
],
|
||||
|
||||
'logging-stop-and-save': [ // TODO: possibly never used/superseded — check
|
||||
(message, sender) => {
|
||||
this.logger.log('info', 'comms', "Received command to stop logging and export the received input");
|
||||
this.logger.addToGlobalHistory(`${message.host}::${sender?.tab?.id ?? '×'}-${sender.frameId ?? '×'}`, JSON.parse(message.history));
|
||||
this.logger.finish();
|
||||
}
|
||||
],
|
||||
'logging-save': [
|
||||
(message, sender) => {
|
||||
this.logger.log('info', 'comms', `Received command to save log for site ${message.host} (tabId ${sender.tab.id}, frameId ${sender.frameId}`);
|
||||
this.logger.addToGlobalHistory(`${message?.host}::${sender?.tab?.id ?? '×'}-${sender?.frameId ?? '×'}`, JSON.parse(message.history));
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
//#region getters
|
||||
get activeTab() {
|
||||
return browser.tabs.query({currentWindow: true, active: true});
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region lifecycle
|
||||
constructor(server) {
|
||||
this.server = server;
|
||||
this.logger = server.logger;
|
||||
@ -76,51 +39,69 @@ class CommsServer {
|
||||
browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender));
|
||||
}
|
||||
|
||||
subscribe(command, callback) {
|
||||
if (!this.commands[command]) {
|
||||
this.commands[command] = [callback];
|
||||
} else {
|
||||
this.commands[command].push(callback);
|
||||
private onConnect(port){
|
||||
// special case
|
||||
if (port.name === 'popup-port') {
|
||||
this.popupPort = port;
|
||||
this.popupPort.onMessage.addListener( (m,p) => this.processReceivedMessage(m,p));
|
||||
return;
|
||||
}
|
||||
|
||||
var tabId = port.sender.tab.id;
|
||||
var frameId = port.sender.frameId;
|
||||
if (! this.ports[tabId]){
|
||||
this.ports[tabId] = {};
|
||||
}
|
||||
if (! this.ports[tabId][frameId]) {
|
||||
this.ports[tabId][frameId] = {};
|
||||
}
|
||||
this.ports[tabId][frameId][port.name] = port;
|
||||
this.ports[tabId][frameId][port.name].onMessage.addListener( (m,p) => this.processReceivedMessage(m, p));
|
||||
|
||||
this.ports[tabId][frameId][port.name].onDisconnect.addListener( (p) => {
|
||||
try {
|
||||
delete this.ports[p.sender.tab.id][p.sender.frameId][port.name];
|
||||
} catch (e) {
|
||||
// no biggie if the thing above doesn't exist.
|
||||
}
|
||||
if (Object.keys(this.ports[tabId][frameId].length === 0)) {
|
||||
delete this.ports[tabId][frameId];
|
||||
if(Object.keys(this.ports[p.sender.tab.id]).length === 0) {
|
||||
delete this.ports[tabId];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getCurrentTabHostname() {
|
||||
const activeTab = await this.activeTab;
|
||||
//#endregion
|
||||
|
||||
if (!activeTab || activeTab.length < 1) {
|
||||
this.logger.log('warn', 'comms', 'There is no active tab for some reason. activeTab:', activeTab);
|
||||
}
|
||||
|
||||
const url = activeTab[0].url;
|
||||
|
||||
var hostname;
|
||||
|
||||
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
|
||||
hostname = url.split('/')[2];
|
||||
}
|
||||
else {
|
||||
hostname = url.split('/')[0];
|
||||
}
|
||||
|
||||
hostname = hostname.split(':')[0]; //find & remove port number
|
||||
hostname = hostname.split('?')[0]; //find & remove "?"
|
||||
|
||||
return hostname;
|
||||
}
|
||||
|
||||
sendMessage(message, context) {
|
||||
if (context?.forwardTo === 'all') {
|
||||
sendMessage(message, context?) {
|
||||
if (context?.comms.forwardTo === 'all') {
|
||||
return this.sendToAll(message);
|
||||
}
|
||||
if (context?.forwardTo === 'active') {
|
||||
if (context?.comms.forwardTo === 'active') {
|
||||
return this.sendToActive(message);
|
||||
}
|
||||
if (context?.forwardTo === 'contentScript') {
|
||||
if (context?.comms.forwardTo === 'contentScript') {
|
||||
return this.sendToFrame(message, context.tab, context.frame, context.port);
|
||||
}
|
||||
if (context?.comms.forwardTo === 'popup') {
|
||||
return this.sendToPopup(message);
|
||||
}
|
||||
}
|
||||
|
||||
sendToAll(message){
|
||||
/**
|
||||
* Sends a message to popup script
|
||||
*/
|
||||
sendToPopup(message) {
|
||||
this.popupPort.postMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a message to ALL **CONTENT SCRIPTS**
|
||||
* Does NOT send a message to popup.
|
||||
**/
|
||||
private sendToAll(message){
|
||||
for(const tid in this.ports){
|
||||
const tab = this.ports[tid];
|
||||
for(const frame in tab){
|
||||
@ -132,7 +113,7 @@ class CommsServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to addon content scripts.
|
||||
* Sends a message to addon content scripts in a single browser tab.
|
||||
* @param message message
|
||||
* @param tab the tab we want to send the message to
|
||||
* @param frame the frame within that tab that we want to send the message to
|
||||
@ -172,7 +153,6 @@ class CommsServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async sendToActive(message) {
|
||||
this.logger.log('info', 'comms', "%c[CommsServer::sendToActive] trying to send a message to active tab. Message:", "background: #dda; color: #11D", message);
|
||||
|
||||
@ -188,57 +168,26 @@ class CommsServer {
|
||||
}
|
||||
}
|
||||
|
||||
onConnect(port){
|
||||
// special case
|
||||
if (port.name === 'popup-port') {
|
||||
this.popupPort = port;
|
||||
this.popupPort.onMessage.addListener( (m,p) => this.processReceivedMessage(m,p));
|
||||
return;
|
||||
}
|
||||
|
||||
var tabId = port.sender.tab.id;
|
||||
var frameId = port.sender.frameId;
|
||||
if (! this.ports[tabId]){
|
||||
this.ports[tabId] = {};
|
||||
}
|
||||
if (! this.ports[tabId][frameId]) {
|
||||
this.ports[tabId][frameId] = {};
|
||||
}
|
||||
this.ports[tabId][frameId][port.name] = port;
|
||||
this.ports[tabId][frameId][port.name].onMessage.addListener( (m,p) => this.processReceivedMessage(m, p));
|
||||
|
||||
this.ports[tabId][frameId][port.name].onDisconnect.addListener( (p) => {
|
||||
try {
|
||||
delete this.ports[p.sender.tab.id][p.sender.frameId][port.name];
|
||||
} catch (e) {
|
||||
// no biggie if the thing above doesn't exist.
|
||||
}
|
||||
if (Object.keys(this.ports[tabId][frameId].length === 0)) {
|
||||
delete this.ports[tabId][frameId];
|
||||
if(Object.keys(this.ports[p.sender.tab.id]).length === 0) {
|
||||
delete this.ports[tabId];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async processReceivedMessage(message, port){
|
||||
private async processReceivedMessage(message, port){
|
||||
this.logger.log('info', 'comms', "[CommsServer.js::processReceivedMessage] Received message from popup/content script!", message, "port", port);
|
||||
|
||||
this.eventBus.send(message.command, message.config, {comms: {port}, fromComms: true});
|
||||
// this triggers events
|
||||
this.eventBus.send(
|
||||
message.command,
|
||||
message.config,
|
||||
{
|
||||
...message.context,
|
||||
comms: {port},
|
||||
fromComms: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
processReceivedMessage_nonpersistent(message, sender){
|
||||
private processReceivedMessage_nonpersistent(message, sender){
|
||||
this.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Received message from background script!", "background-color: #11D; color: #aad", message, sender);
|
||||
|
||||
this.eventBus.send(message.command, message.config, {comms: {sender}, fromComms: true});
|
||||
}
|
||||
|
||||
// chrome shitiness mitigation
|
||||
sendUnmarkPlayer(message) {
|
||||
this.logger.log('info', 'comms', '[CommsServer.js::sendUnmarkPlayer] Chrome is a shit browser that doesn\'t do port.postMessage() in unload events, so we have to resort to inelegant hacks. If you see this, then the workaround method works.');
|
||||
this.processReceivedMessage(message, this.popupPort);
|
||||
}
|
||||
}
|
||||
|
||||
export default CommsServer;
|
||||
|
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<pre>
|
||||
---- site:
|
||||
{{site}}
|
||||
{{site}}
|
||||
|
||||
----
|
||||
</pre>
|
||||
@ -93,9 +93,11 @@ export default {
|
||||
// get info about current site from background script
|
||||
while (true) {
|
||||
try {
|
||||
console.log('trying to get site ...');
|
||||
this.getSite();
|
||||
console.log('site gottne');
|
||||
} catch (e) {
|
||||
|
||||
console.warn('failed to load site:', e);
|
||||
}
|
||||
await this.sleep(5000);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user