Integrate comms client/server into eventBus

This commit is contained in:
Tamius Han 2022-07-31 00:15:28 +02:00
parent f0120010fe
commit 8806c8ea0c
8 changed files with 269 additions and 354 deletions

View File

@ -7,6 +7,7 @@ import CommsClient from './lib/comms/CommsClient';
import PageInfo from './lib/video-data/PageInfo'; import PageInfo from './lib/video-data/PageInfo';
import Logger, { baseLoggingOptions } from './lib/Logger'; import Logger, { baseLoggingOptions } from './lib/Logger';
import UWGlobals from './lib/UWGlobals'; import UWGlobals from './lib/UWGlobals';
import EventBus from './lib/EventBus';
export default class UWContent { export default class UWContent {
pageInfo: PageInfo; pageInfo: PageInfo;
@ -14,6 +15,7 @@ export default class UWContent {
settings: Settings; settings: Settings;
actionHandler: ActionHandler; actionHandler: ActionHandler;
logger: Logger; logger: Logger;
eventBus: EventBus;
commsHandlers: { commsHandlers: {
[x: string]: ((a: any, b?: any) => void | Promise<void>)[] [x: string]: ((a: any, b?: any) => void | Promise<void>)[]
@ -84,16 +86,13 @@ export default class UWContent {
console.error("logger init failed!", e) console.error("logger init failed!", e)
} }
// second — let's add some globals
if (! (window as any).ultrawidify) {
(window as any).ultrawidify = new UWGlobals();
((window as any).ultrawidify as UWGlobals).importSubscriptionsFromCommsHandlers(this.commsHandlers);
}
// init() is re-run any time settings change // init() is re-run any time settings change
if (this.comms) { if (this.comms) {
this.comms.destroy(); this.comms.destroy();
} }
if (this.eventBus) {
this.eventBus.destroy();
}
if (!this.settings) { if (!this.settings) {
this.settings = new Settings({ this.settings = new Settings({
onSettingsChanged: () => this.reloadSettings(), onSettingsChanged: () => this.reloadSettings(),
@ -102,51 +101,70 @@ export default class UWContent {
await this.settings.init(); await this.settings.init();
} }
this.comms = new CommsClient('content-main-port', this.logger, this.commsHandlers); this.eventBus = new EventBus();
this.eventBus.subscribe(
// če smo razširitev onemogočili v nastavitvah, ne naredimo ničesar 'uw-restart',
// If extension is soft-disabled, don't do shit {
function: () => this.initPhase2()
var extensionMode = this.settings.getExtensionMode();
this.logger.log('info', 'debug', "[uw::init] Extension mode:" + (extensionMode < 0 ? "disabled" : extensionMode == '1' ? 'basic' : 'full'));
const isSiteDisabled = extensionMode === ExtensionMode.Disabled
if (isSiteDisabled) {
if (this.settings.getExtensionMode('@global') === ExtensionMode.Disabled) {
this.logger.log('info', 'debug', "[uw::init] EXTENSION DISABLED, THEREFORE WONT BE STARTED")
return;
} }
} );
this.comms = new CommsClient('content-main-port', this.logger, this.eventBus);
try { this.initPhase2();
if (this.pageInfo) {
this.logger.log('info', 'debug', '[uw.js::setup] An instance of pageInfo already exists and will be destroyed.');
this.pageInfo.destroy();
}
this.pageInfo = new PageInfo(this.comms, this.settings, this.logger, extensionMode, isSiteDisabled);
this.logger.log('info', 'debug', "[uw.js::setup] pageInfo initialized.");
this.logger.log('info', 'debug', "[uw.js::setup] will try to initate ActionHandler.");
// start action handler only if extension is enabled for this site
if (!isSiteDisabled) {
if (this.actionHandler) {
this.actionHandler.destroy();
}
this.actionHandler = new ActionHandler(this.pageInfo.eventBus, this.settings, this.logger);
this.actionHandler.init();
this.logger.log('info', 'debug', "[uw.js::setup] ActionHandler initiated.");
}
} catch (e) {
console.error('Ultrawidify: failed to start extension. Error:', e)
this.logger.log('error', 'debug', "[uw::init] FAILED TO START EXTENSION. Error:", e);
}
} catch (e) { } catch (e) {
console.error('Ultrawidify initalization failed for some reason:', e); console.error('Ultrawidify initalization failed for some reason:', e);
} }
} }
initPhase2() {
// If extension is soft-disabled, don't do shit
var extensionMode = this.settings.getExtensionMode();
this.logger.log('info', 'debug', "[uw::init] Extension mode:" + (extensionMode < 0 ? "disabled" : extensionMode == '1' ? 'basic' : 'full'));
const isSiteDisabled = extensionMode === ExtensionMode.Disabled
if (isSiteDisabled) {
this.destroy();
if (this.settings.getExtensionMode('@global') === ExtensionMode.Disabled) {
this.logger.log('info', 'debug', "[uw::init] EXTENSION DISABLED, THEREFORE WONT BE STARTED")
return;
}
}
try {
if (this.pageInfo) {
this.logger.log('info', 'debug', '[uw.js::setup] An instance of pageInfo already exists and will be destroyed.');
this.pageInfo.destroy();
}
this.pageInfo = new PageInfo(this.eventBus, this.settings, this.logger, extensionMode, isSiteDisabled);
this.logger.log('info', 'debug', "[uw.js::setup] pageInfo initialized.");
this.logger.log('info', 'debug', "[uw.js::setup] will try to initate ActionHandler.");
// start action handler only if extension is enabled for this site
if (!isSiteDisabled) {
if (this.actionHandler) {
this.actionHandler.destroy();
}
this.actionHandler = new ActionHandler(this.eventBus, this.settings, this.logger);
this.actionHandler.init();
this.logger.log('info', 'debug', "[uw.js::setup] ActionHandler initiated.");
}
} catch (e) {
console.error('Ultrawidify: failed to start extension. Error:', e)
this.logger.log('error', 'debug', "[uw::init] FAILED TO START EXTENSION. Error:", e);
}
}
destroy() {
if (this.pageInfo) {
this.pageInfo.destroy();
}
if (this.actionHandler) {
this.actionHandler.destroy();
}
}
} }

View File

@ -7,11 +7,13 @@ import Logger, { baseLoggingOptions } from './lib/Logger';
import { sleep } from '../common/js/utils'; import { sleep } from '../common/js/utils';
import { browser } from 'webextension-polyfill-ts'; import { browser } from 'webextension-polyfill-ts';
import EventBus, { EventBusCommand } from './lib/EventBus';
export default class UWServer { export default class UWServer {
settings: Settings; settings: Settings;
logger: Logger; logger: Logger;
comms: CommsServer; comms: CommsServer;
eventBus: EventBus;
ports: any[] = []; ports: any[] = [];
hasVideos: boolean; hasVideos: boolean;
@ -24,6 +26,26 @@ export default class UWServer {
'videoSettings': undefined, 'videoSettings': undefined,
} }
eventBusCommands = {
'popup-set-selected-tab': [{
function: (message) => this.setSelectedTab(message.selectedMenu, message.selectedSubitem)
}],
'has-video': [{
function: (message, context) => this.registerVideo(context.comms.sender)
}],
'noVideo' : [{
function: (message, context) => this.unregisterVideo(context.comms.sender)
}],
'inject-css': [{
function: (message, context) => this.injectCss(message.cssString, context.comms.sender)
}],
'eject-css': [{
function: (message, context) => this.removeCss(message.cssString, context.comms.sender)
}],
'replace-css': [{
function: (message, context) => this.replaceCss(message.oldCssString, message.newCssString, context.comms.sender)
}]
};
private gcTimeout: any; private gcTimeout: any;
uiLoggerInitialized: boolean = false; uiLoggerInitialized: boolean = false;
@ -52,13 +74,13 @@ export default class UWServer {
this.settings = new Settings({logger: this.logger}); this.settings = new Settings({logger: this.logger});
await this.settings.init(); await this.settings.init();
this.eventBus = new EventBus();
this.comms = new CommsServer(this); this.comms = new CommsServer(this);
this.comms.subscribe('show-logger', async () => await this.initUiAndShowLogger());
this.comms.subscribe('init-vue', async () => await this.initUi());
this.comms.subscribe('uwui-vue-initialized', () => this.uiLoggerInitialized = true);
this.comms.subscribe('emit-logs', () => {}); // we don't need to do anything, this gets forwarded to UI content script as is this.comms.subscribe('emit-logs', () => {}); // we don't need to do anything, this gets forwarded to UI content script as is
browser.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)}); browser.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
} catch (e) { } catch (e) {
console.error(`Ultrawidify [server]: failed to start. Reason:`, e); console.error(`Ultrawidify [server]: failed to start. Reason:`, e);
} }
@ -84,7 +106,7 @@ export default class UWServer {
async removeCss(css, sender) { async removeCss(css, sender) {
try { try {
browser.tabs.removeCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId}); browser.tabs.removeCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
} catch (e) { } catch (e) {
this.logger.log('error','debug', '[UwServer::injectCss] Error while removing css:', {error: e, css, sender}); this.logger.log('error','debug', '[UwServer::injectCss] Error while removing css:', {error: e, css, sender});
} }
} }
@ -98,22 +120,22 @@ export default class UWServer {
extractHostname(url){ extractHostname(url){
var hostname; var hostname;
if (!url) { if (!url) {
return "<no url>"; return "<no url>";
} }
// extract hostname // extract hostname
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
hostname = url.split('/')[2]; hostname = url.split('/')[2];
} }
else { else {
hostname = url.split('/')[0]; hostname = url.split('/')[0];
} }
hostname = hostname.split(':')[0]; //find & remove port number hostname = hostname.split(':')[0]; //find & remove port number
hostname = hostname.split('?')[0]; //find & remove "?" hostname = hostname.split('?')[0]; //find & remove "?"
return hostname; return hostname;
} }
@ -209,14 +231,14 @@ export default class UWServer {
allFrames: true, allFrames: true,
}); });
} else if (BrowserDetect.anyChromium) { } else if (BrowserDetect.anyChromium) {
await new Promise<void>( resolve => await new Promise<void>( resolve =>
chrome.tabs.executeScript({ chrome.tabs.executeScript({
file: '/ext/uw-ui.js', file: '/ext/uw-ui.js',
allFrames: true, allFrames: true,
}, () => resolve()) }, () => resolve())
); );
} }
} catch (e) { } catch (e) {
console.warn('Ultrawidify [server]: UI setup failed. While problematic, this problem shouldn\'t completely crash the extension.'); console.warn('Ultrawidify [server]: UI setup failed. While problematic, this problem shouldn\'t completely crash the extension.');
this.logger.log('ERROR', 'uwbg', 'UI initialization failed. Reason:', e); this.logger.log('ERROR', 'uwbg', 'UI initialization failed. Reason:', e);
@ -232,16 +254,16 @@ export default class UWServer {
await this.initUi(); await this.initUi();
await new Promise<void>( async (resolve, reject) => { await new Promise<void>( async (resolve, reject) => {
// if content script doesn't give us a response within 5 seconds, something is // if content script doesn't give us a response within 5 seconds, something is
// obviously wrong and we stop waiting, // obviously wrong and we stop waiting,
// oh and btw, resolve/reject do not break the loops, so we need to do that // oh and btw, resolve/reject do not break the loops, so we need to do that
// ourselves: // ourselves:
// https://stackoverflow.com/questions/55207256/will-resolve-in-promise-loop-break-loop-iteration // https://stackoverflow.com/questions/55207256/will-resolve-in-promise-loop-break-loop-iteration
let isRejected = false; let isRejected = false;
setTimeout( async () => {isRejected = true; reject()}, 5000); setTimeout( async () => {isRejected = true; reject()}, 5000);
// check whether UI has been initiated on the FE. If it was, we resolve the // check whether UI has been initiated on the FE. If it was, we resolve the
// promise and off we go // promise and off we go
while (!isRejected) { while (!isRejected) {
if (this.uiLoggerInitialized) { if (this.uiLoggerInitialized) {
@ -261,7 +283,7 @@ export default class UWServer {
} }
async getVideoTab() { async getVideoTab() {
// friendly reminder: if current tab doesn't have a video, // friendly reminder: if current tab doesn't have a video,
// there won't be anything in this.videoTabs[this.currentTabId] // there won't be anything in this.videoTabs[this.currentTabId]
const ctab = await this.getCurrentTab(); const ctab = await this.getCurrentTab();
@ -295,11 +317,11 @@ export default class UWServer {
return { return {
...this.videoTabs[ctab.id], ...this.videoTabs[ctab.id],
host: this.extractHostname(ctab.url), host: this.extractHostname(ctab.url),
selected: this.selectedSubitem selected: this.selectedSubitem
}; };
} }
// return something more or less empty if this tab doesn't have // return something more or less empty if this tab doesn't have
// a video registered for it // a video registered for it
return { return {
host: this.extractHostname(ctab.url), host: this.extractHostname(ctab.url),
@ -308,7 +330,7 @@ export default class UWServer {
} }
} }
// chrome shitiness mitigation // chrome shitiness mitigation
sendUnmarkPlayer(message) { sendUnmarkPlayer(message) {
this.comms.sendUnmarkPlayer(message); this.comms.sendUnmarkPlayer(message);
} }

View File

@ -1,7 +1,21 @@
import CommsClient from './comms/CommsClient';
import CommsServer from './comms/CommsServer';
export interface EventBusCommand { export interface EventBusCommand {
isGlobal?: boolean, isGlobal?: boolean,
function: (commandConfig: any) => void | Promise<void> function: (commandConfig: any, context?: any) => void | Promise<void>
}
export interface EventBusContext {
stopPropagation?: boolean,
// Context stuff added by Comms
fromComms?: boolean,
comms?: {
sender?: any,
port?: any,
forwardTo?: 'all' | 'active' | 'contentScript' | 'sameOrigin',
}
} }
export default class EventBus { export default class EventBus {
@ -9,6 +23,20 @@ export default class EventBus {
private commands: { [x: string]: EventBusCommand[]} = {}; private commands: { [x: string]: EventBusCommand[]} = {};
private downstreamBuses: EventBus[] = []; private downstreamBuses: EventBus[] = [];
private upstreamBus?: EventBus; private upstreamBus?: EventBus;
private comms?: CommsClient;
//#region lifecycle
destroy() {
this.commands = null;
for (const bus of this.downstreamBuses) {
bus.destroy();
}
}
//#endregion
setComms(comms: CommsClient): void {
this.comms = comms;
}
setUpstreamBus(eventBus: EventBus, stopRecursing: boolean = false) { setUpstreamBus(eventBus: EventBus, stopRecursing: boolean = false) {
this.upstreamBus = eventBus; this.upstreamBus = eventBus;
@ -49,18 +77,18 @@ export default class EventBus {
} }
} }
send(command: string, config: any, stopPropagation?: boolean) { send(command: string, config: any, context?: EventBusContext) {
if (!this.commands ||!this.commands[command]) { if (!this.commands ||!this.commands[command]) {
// ensure send is not being called for commands that we have no subscriptions for // ensure send is not being called for commands that we have no subscriptions for
return; return;
} }
for (const eventBusCommand of this.commands[command]) { for (const eventBusCommand of this.commands[command]) {
eventBusCommand.function(config); eventBusCommand.function(config, context);
if (eventBusCommand.isGlobal && !stopPropagation) { if (eventBusCommand.isGlobal && !context?.stopPropagation) {
this.sendUpstream(command, config); this.sendUpstream(command, config, context);
this.sendDownstream(command, config); this.sendDownstream(command, config, context);
} }
} }
} }
@ -81,14 +109,14 @@ export default class EventBus {
} }
sendGlobal(command: string, config: any) { sendGlobal(command: string, config: any, context?: EventBusContext) {
this.send(command, config); this.send(command, config);
this.sendUpstream(command, config); this.sendUpstream(command, config);
this.sendDownstream(command, config); this.sendDownstream(command, config);
} }
sendDownstream(command: string, config: any, sourceEventBus?: EventBus) { sendDownstream(command: string, config: any, context?: EventBusContext, sourceEventBus?: EventBus) {
for (const eventBus of this.downstreamBuses) { for (const eventBus of this.downstreamBuses) {
if (eventBus !== sourceEventBus) { if (eventBus !== sourceEventBus) {
eventBus.send(command, config); eventBus.send(command, config);
@ -97,11 +125,14 @@ export default class EventBus {
} }
} }
sendUpstream(command: string, config: any) { sendUpstream(command: string, config: any, context?: EventBusContext) {
if (this.upstreamBus) { if (this.upstreamBus) {
this.upstreamBus.send(command, config); this.upstreamBus.send(command, config, context);
this.upstreamBus.sendUpstream(command, config); this.upstreamBus.sendUpstream(command, config, context);
this.upstreamBus.sendDownstream(command, config, this); this.upstreamBus.sendDownstream(command, config, context, this);
}
if (!this.upstreamBus && this.comms && !context?.fromComms) {
this.comms.sendMessage({command, config});
} }
} }
} }

View File

@ -3,124 +3,111 @@ import BrowserDetect from '../../conf/BrowserDetect';
import Logger from '../Logger'; import Logger from '../Logger';
import { browser } from 'webextension-polyfill-ts'; import { browser } from 'webextension-polyfill-ts';
import Settings from '../Settings'; import Settings from '../Settings';
import EventBus from '../EventBus';
if (process.env.CHANNEL !== 'stable'){ if (process.env.CHANNEL !== 'stable'){
console.info("Loading CommsClient"); console.info("Loading CommsClient");
} }
/**
* Ultrawidify communication spans a few different "domains" that require a few different
* means of communication. The four isolated domains are:
*
* > content script event bus (CS)
* > player UI event bus (UI)
* > UWServer event bus (BG)
* > popup event bus
*
* It is our goal to route messages between various domains. It is our goal that eventBus
* instances in different parts of our script are at least somewhat interoperable between
* each other. As such, scripts sending commands should be unaware that Comms object even
* exists.
*
* EventBus is started first. Other components (including commsClient) follow later.
*
*
* fig 0. ULTRAWIDIFY COMMUNICATION MAP
*
* CS EVENT BUS
* (accessible within tab scripts)
* | NOT EVENT BUS
* PageInfo x (accessible within popup)
* x |
* : : x UWServer
* x CommsClient <---------------x CommsServer x
* | (Connect to popup)
* |
* x eventBus.sendToTunnel()
* <iframe tunnel>
* A
* |
* V
* x <iframe tunnel>
* |
* PlayerUIBase x
* : :
* |
* UI EVENT BUS
* (accessible within player UI)
*/
class CommsClient { class CommsClient {
commsId: string; commsId: string;
logger: Logger; logger: Logger;
settings: any; // sus? settings: any; // sus?
eventBus: EventBus;
commands: {[x: string]: any[]};
_listener: (m: any) => void; _listener: (m: any) => void;
port: any; port: any;
//#region lifecycle
constructor(name, logger, commands) { constructor(name: string, logger: Logger, eventBus: EventBus) {
try { try {
this.logger = logger; this.logger = logger;
this.eventBus = eventBus;
this.eventBus.setComms(this);
this.port = browser.runtime.connect(null, {name: name}); this.port = browser.runtime.connect(null, {name: name});
this.logger.onLogEnd( this.logger.onLogEnd(
(history) => { (history) => {
this.logger.log('info', 'comms', 'Sending logging-stop-and-save to background script ...'); this.logger.log('info', 'comms', 'Sending logging-stop-and-save to background script ...');
try { try {
this.port.postMessage({cmd: 'logging-stop-and-save', host: window.location.hostname, history}) this.port.postMessage({cmd: 'logging-stop-and-save', host: window.location.hostname, history})
} catch (e) { } catch (e) {
this.logger.log('error', 'comms', 'Failed to send message to background script. Error:', e); this.logger.log('error', 'comms', 'Failed to send message to background script. Error:', e);
}
} }
} );
);
this._listener = m => this.processReceivedMessage(m); this._listener = m => this.processReceivedMessage(m);
this.port.onMessage.addListener(this._listener); this.port.onMessage.addListener(this._listener);
this.commsId = (Math.random() * 20).toFixed(0); this.commsId = (Math.random() * 20).toFixed(0);
this.commands = commands;
} catch (e) { } catch (e) {
console.error("CONSTRUCOTR FAILED:", e) console.error("CONSTRUCOTR FAILED:", e)
} }
} }
destroy() { destroy() {
if (!BrowserDetect.edge) { // edge is a very special browser made by outright morons. if (!BrowserDetect.edge) { // edge is a very special browser made by outright morons.
this.port.onMessage.removeListener(this._listener); this.port.onMessage.removeListener(this._listener);
} }
} }
//#endregion
subscribe(command, callback) { async sendMessage(message){
if (!this.commands[command]) {
this.commands[command] = [callback];
} else {
this.commands[command].push(callback);
}
}
processReceivedMessage(message){
this.logger.log('info', 'comms', `[CommsClient.js::processMessage] <${this.commsId}> Received message from background script!`, message);
if (this.commands[message.cmd]) {
for (const c of this.commands[message.cmd]) {
c(message);
}
}
}
async sendMessage_nonpersistent(message){
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
return browser.runtime.sendMessage(null, message, null); return browser.runtime.sendMessage(null, message, null);
} }
processReceivedMessage(message){
// TODO: sus function — does it get any use? this.eventBus.send(message.command, message.config, {fromComms: true});
async requestSettings(){
this.logger.log('info', 'comms', "%c[CommsClient::requestSettings] sending request for congif!", "background: #11D; color: #aad");
var response = await this.sendMessage_nonpersistent({cmd: 'get-config'});
this.logger.log('info', 'comms', "%c[CommsClient::requestSettings] received settings response!", "background: #11D; color: #aad", response);
if(! response || response.extensionConf){
return Promise.resolve(false);
}
this.settings = {active: JSON.parse(response.extensionConf)};
return Promise.resolve(true);
} }
async sendMessage(message) {
return this.sendMessage_nonpersistent(message);
}
registerVideo(){
this.logger.log('info', 'comms', `[CommsClient::registerVideo] <${this.commsId}>`, "Registering video for current page.");
this.port.postMessage({cmd: "has-video"});
}
sendPerformanceUpdate(message){
this.port.postMessage({cmd: 'performance-update', message: message});
}
unregisterVideo(){
this.logger.log('info', 'comms', `[CommsClient::unregisterVideo] <${this.commsId}>`, "Unregistering video for current page.");
this.port.postMessage({cmd: "noVideo"}); // ayymd
}
announceZoom(scale){
this.port.postMessage({cmd: "announce-zoom", zoom: scale});
this.registerVideo();
}
} }
if (process.env.CHANNEL !== 'stable'){ if (process.env.CHANNEL !== 'stable'){

View File

@ -4,12 +4,14 @@ import Logger from '../Logger';
import Settings from '../Settings'; import Settings from '../Settings';
import { browser } from 'webextension-polyfill-ts'; import { browser } from 'webextension-polyfill-ts';
import ExtensionMode from '../../../common/enums/ExtensionMode.enum'; import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
import EventBus from '../EventBus';
class CommsServer { class CommsServer {
server: any; server: any;
logger: Logger; logger: Logger;
settings: Settings; settings: Settings;
eventBus: EventBus;
ports: { ports: {
@ -21,7 +23,7 @@ class CommsServer {
/** /**
* commands functions that handle incoming messages * commands functions that handle incoming messages
* functions can have the following arguments, which are, * functions can have the following arguments, which are,
* in this order: * in this order:
* message the message we received * message the message we received
* port|sender on persistent channels, second argument is port on which the server * port|sender on persistent channels, second argument is port on which the server
@ -30,19 +32,6 @@ class CommsServer {
* sendResponse callback function on messages received via non-persistent channel * sendResponse callback function on messages received via non-persistent channel
*/ */
commands: {[x: string]: ((a: any, b: any) => void | Promise<void>)[]} = { commands: {[x: string]: ((a: any, b: any) => void | Promise<void>)[]} = {
'announce-zoom': [
(message) => {
try {
// forward message to the popup
this.popupPort.postMessage({cmd: 'set-current-zoom', zoom: message.zoom});
} catch (e) {
// if popup is closed, this will/may fail. This is okay, so we just ignore this error
}
},
],
'get-current-zoom': [
(message) => this.sendToActive(message),
],
'get-current-site': [ 'get-current-site': [
async (message, port) => { async (message, port) => {
port.postMessage({ port.postMessage({
@ -52,68 +41,7 @@ class CommsServer {
}); });
}, },
], ],
'popup-set-selected-tab': [
(message) => this.server.setSelectedTab(message.selectedMenu, message.selectedSubitem),
],
'has-video': [
(message, port) => this.server.registerVideo(port.sender),
],
'noVideo': [
(message, port) => this.server.unregisterVideo(port.sender),
],
'inject-css': [
(message, sender) => this.server.injectCss(message.cssString, sender),
],
'eject-css': [
(message, sender) => this.server.removeCss(message.cssString, sender),
],
'replace-css': [
(message, sender) => this.server.replaceCss(message.oldCssString, message.newCssString, sender),
],
// 'get-config': [
// (message, port) => {
// this.logger.log('info', 'comms', "CommsServer: received get-config. Active settings?", this.settings.active, "\n(settings:", this.settings, ")");
// port.postMessage(
// {cmd: "set-config", conf: this.settings.active, site: this.server.currentSite}
// );
// },
// ],
'get-config': [
(message, sender) => {
var ret = {extensionConf: JSON.stringify(this.settings.active)};
this.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Returning this:", "background-color: #11D; color: #aad", ret);
Promise.resolve(ret);
}
],
'autoar-enable': [
() => {
this.settings.active.sites['@global'].autoar = ExtensionMode.Enabled;
this.settings.save();
this.logger.log('info', 'comms', "[uw-bg] autoar set to enabled (blacklist). evidenz:", this.settings.active);
}
],
'autoar-disable': [
(message) => {
this.settings.active.sites['@global'].autoar = ExtensionMode.Disabled;
if (message.reason){
this.settings.active.arDetect.disabledReason = message.reason;
} else {
this.settings.active.arDetect.disabledReason = 'User disabled';
}
this.settings.save();
this.logger.log('info', 'comms', "[uw-bg] autoar set to disabled. evidenz:", this.settings.active);
}
],
'autoar-set-interval': [
(message) => {
this.logger.log('info', 'comms', `[uw-bg] trying to set new interval for autoAr. New interval is, ${message.timeout} ms`);
// set fairly liberal limit
var timeout = message.timeout < 4 ? 4 : message.timeout;
this.settings.active.arDetect.timers.playing = timeout;
this.settings.save();
}
],
'logging-stop-and-save': [ // TODO: possibly never used/superseded — check 'logging-stop-and-save': [ // TODO: possibly never used/superseded — check
(message, sender) => { (message, sender) => {
this.logger.log('info', 'comms', "Received command to stop logging and export the received input"); this.logger.log('info', 'comms', "Received command to stop logging and export the received input");
@ -140,6 +68,7 @@ class CommsServer {
this.server = server; this.server = server;
this.logger = server.logger; this.logger = server.logger;
this.settings = server.settings; this.settings = server.settings;
this.eventBus = server.eventBus;
browser.runtime.onConnect.addListener(p => this.onConnect(p)); browser.runtime.onConnect.addListener(p => this.onConnect(p));
browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender)); browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender));
@ -170,14 +99,26 @@ class CommsServer {
else { else {
hostname = url.split('/')[0]; hostname = url.split('/')[0];
} }
hostname = hostname.split(':')[0]; //find & remove port number hostname = hostname.split(':')[0]; //find & remove port number
hostname = hostname.split('?')[0]; //find & remove "?" hostname = hostname.split('?')[0]; //find & remove "?"
return hostname; return hostname;
} }
sendToAll(message){ sendMessage(message, context) {
if (context?.forwardTo === 'all') {
return this.sendToAll(message);
}
if (context?.forwardTo === 'active') {
return this.sendToActive(message);
}
if (context?.forwardTo === 'contentScript') {
return this.sendToFrame(message, context.tab, context.frame, context.port);
}
}
private sendToAll(message){
for(const tab of this.ports){ for(const tab of this.ports){
for(const frame in tab){ for(const frame in tab){
for (const port in tab[frame]) { for (const port in tab[frame]) {
@ -187,7 +128,6 @@ class CommsServer {
} }
} }
/** /**
* Sends a message to addon content scripts. * Sends a message to addon content scripts.
* @param message message * @param message message
@ -195,7 +135,7 @@ class CommsServer {
* @param frame the frame within that tab that we want to send the message to * @param frame the frame within that tab that we want to send the message to
* @param port if defined, message will only be sent to that specific script, otherwise it gets sent to all scripts of a given frame * @param port if defined, message will only be sent to that specific script, otherwise it gets sent to all scripts of a given frame
*/ */
async sendToFrameContentScripts(message, tab, frame, port?) { private async sendToFrameContentScripts(message, tab, frame, port?) {
if (port !== undefined) { if (port !== undefined) {
this.ports[tab][frame][port].postMessage(message); this.ports[tab][frame][port].postMessage(message);
return; return;
@ -205,7 +145,7 @@ class CommsServer {
} }
} }
async sendToFrame(message, tab, frame, port?) { private async sendToFrame(message, tab, frame, port?) {
this.logger.log('info', 'comms', `%c[CommsServer::sendToFrame] attempting to send message to tab ${tab}, frame ${frame}`, "background: #dda; color: #11D", message); this.logger.log('info', 'comms', `%c[CommsServer::sendToFrame] attempting to send message to tab ${tab}, frame ${frame}`, "background: #dda; color: #11D", message);
if (isNaN(tab)) { if (isNaN(tab)) {
@ -229,13 +169,8 @@ class CommsServer {
} }
} }
async sendToAllFrames(message, tab, port) {
for (const frame in this.ports[tab]) {
this.sendToFrameContentScripts(message, tab, frame, port);
}
}
async sendToActive(message) { 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); this.logger.log('info', 'comms', "%c[CommsServer::sendToActive] trying to send a message to active tab. Message:", "background: #dda; color: #11D", message);
const tabs = await this.activeTab; const tabs = await this.activeTab;
@ -251,7 +186,7 @@ class CommsServer {
} }
onConnect(port){ onConnect(port){
// poseben primer | special case // special case
if (port.name === 'popup-port') { if (port.name === 'popup-port') {
this.popupPort = port; this.popupPort = port;
this.popupPort.onMessage.addListener( (m,p) => this.processReceivedMessage(m,p)); this.popupPort.onMessage.addListener( (m,p) => this.processReceivedMessage(m,p));
@ -261,7 +196,7 @@ class CommsServer {
var tabId = port.sender.tab.id; var tabId = port.sender.tab.id;
var frameId = port.sender.frameId; var frameId = port.sender.frameId;
if (! this.ports[tabId]){ if (! this.ports[tabId]){
this.ports[tabId] = {}; this.ports[tabId] = {};
} }
if (! this.ports[tabId][frameId]) { if (! this.ports[tabId][frameId]) {
this.ports[tabId][frameId] = {}; this.ports[tabId][frameId] = {};
@ -271,9 +206,9 @@ class CommsServer {
this.ports[tabId][frameId][port.name].onDisconnect.addListener( (p) => { this.ports[tabId][frameId][port.name].onDisconnect.addListener( (p) => {
try { try {
delete this.ports[p.sender.tab.id][p.sender.frameId][port.name]; delete this.ports[p.sender.tab.id][p.sender.frameId][port.name];
} catch (e) { } catch (e) {
// no biggie if the thing above doesn't exist. // no biggie if the thing above doesn't exist.
} }
if (Object.keys(this.ports[tabId][frameId].length === 0)) { if (Object.keys(this.ports[tabId][frameId].length === 0)) {
delete this.ports[tabId][frameId]; delete this.ports[tabId][frameId];
@ -284,57 +219,16 @@ class CommsServer {
}); });
} }
// TODO: sendResponse seems redundant — it used to be a callback for
// chrome-based browsers, but browser polyfill doesn't do callback. Just
// awaits.
async execCmd(message, portOrSender, sendResponse?) {
this.logger.log(
'info', 'comms', '[CommsServer.js::execCmd] Received message', message,
". Port/sender:", portOrSender, "sendResponse:", sendResponse, "\nThere is ", this.commands[message.cmd]?.length ?? 0,
" command(s) for action", message.cmd
);
if (this.commands[message.cmd]) {
for (const c of this.commands[message.cmd]) {
try {
await c(message, portOrSender);
} catch (e) {
this.logger.log('error', 'debug', "[CommsServer.js::execCmd] failed to execute command.", e)
}
}
}
}
async handleMessage(message, portOrSender) {
await this.execCmd(message, portOrSender);
if (message.forwardToSameFramePort) {
this.sendToFrameContentScripts(message, portOrSender.tab.id, portOrSender.frameId, message.port);
}
if (message.forwardToContentScript) {
this.logger.log('info', 'comms', "[CommsServer.js::processReceivedMessage] Message has 'forward to content script' flag set. Forwarding message as is. Message:", message);
this.sendToFrame(message, message.targetTab, message.targetFrame);
}
if (message.forwardToAll) {
this.logger.log('info', 'comms', "[CommsServer.js::processReceivedMessage] Message has 'forward to all' flag set. Forwarding message as is. Message:", message);
this.sendToAll(message);
}
if (message.forwardToActive) {
this.logger.log('info', 'comms', "[CommsServer.js::processReceivedMessage] Message has 'forward to active' flag set. Forwarding message as is. Message:", message);
this.sendToActive(message);
}
}
async processReceivedMessage(message, port){ async processReceivedMessage(message, port){
this.logger.log('info', 'comms', "[CommsServer.js::processReceivedMessage] Received message from popup/content script!", message, "port", port); this.logger.log('info', 'comms', "[CommsServer.js::processReceivedMessage] Received message from popup/content script!", message, "port", port);
this.handleMessage(message, port) this.eventBus.send(message, {port, fromComms: true});
} }
processReceivedMessage_nonpersistent(message, sender){ 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.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Received message from background script!", "background-color: #11D; color: #aad", message, sender);
this.handleMessage(message, sender); this.eventBus.send(message, {sender, fromComms: true});
} }
// chrome shitiness mitigation // chrome shitiness mitigation

View File

@ -75,7 +75,6 @@ class PageInfo {
this.extensionMode = extensionMode; this.extensionMode = extensionMode;
this.readOnly = readOnly; this.readOnly = readOnly;
this.eventBus = new EventBus();
if (comms){ if (comms){
this.comms = comms; this.comms = comms;
@ -84,10 +83,7 @@ class PageInfo {
try { try {
// request inject css immediately // request inject css immediately
const playerStyleString = this.settings.active.sites[window.location.hostname].css.replace('\\n', ''); const playerStyleString = this.settings.active.sites[window.location.hostname].css.replace('\\n', '');
this.comms.sendMessage({ this.eventBus.send('inject-css', {cssString: playerStyleString});
cmd: 'inject-css',
cssString: playerStyleString
});
} catch (e) { } catch (e) {
// do nothing. It's ok if there's no special settings for the player element or crop persistence // do nothing. It's ok if there's no special settings for the player element or crop persistence
} }
@ -110,28 +106,6 @@ class PageInfo {
this.scheduleUrlCheck(); this.scheduleUrlCheck();
} }
async injectCss(cssString) {
await this.comms.sendMessage({
cmd: 'inject-css',
cssString: cssString
});
}
async ejectCss(cssString) {
await this.comms.sendMessage({
cmd: 'eject-css',
cssString: cssString
});
}
async replaceCss(oldCssString, newCssString) {
await this.comms.sendMessage({
cmd: 'replace-css',
newCssString,
oldCssString
});
}
destroy() { destroy() {
this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!") this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!")
if(this.rescanTimer){ if(this.rescanTimer){
@ -139,7 +113,7 @@ class PageInfo {
} }
for (let video of this.videos) { for (let video of this.videos) {
try { try {
(this.comms.unregisterVideo as any)(video.videoData.vdid) this.eventBus.send('noVideo', undefined);
video.videoData.destroy(); video.videoData.destroy();
} catch (e) { } catch (e) {
this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e); this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e);
@ -148,12 +122,7 @@ class PageInfo {
try { try {
const playerStyleString = this.settings.active.sites[window.location.hostname].css; const playerStyleString = this.settings.active.sites[window.location.hostname].css;
if (playerStyleString) { this.eventBus.send('eject-css', {cssString: playerStyleString});
this.comms.sendMessage({
cmd: 'eject-css',
cssString: playerStyleString
});
}
} catch (e) { } catch (e) {
// do nothing. It's ok if there's no special settings for the player element // do nothing. It's ok if there's no special settings for the player element
} }
@ -300,10 +269,10 @@ class PageInfo {
if (this.videos.length > 0) { if (this.videos.length > 0) {
// this.comms.registerVideo({host: window.location.hostname, location: window.location}); // this.comms.registerVideo({host: window.location.hostname, location: window.location});
this.comms.registerVideo(); this.eventBus.send('has-video', null);
} else { } else {
// this.comms.unregisterVideo({host: window.location.hostname, location: window.location}); // this.comms.unregisterVideo({host: window.location.hostname, location: window.location});
this.comms.unregisterVideo(); this.eventBus.send('noVideo', null);
} }
} }
@ -415,15 +384,6 @@ class PageInfo {
} }
} }
announceZoom(scale) {
if (this.announceZoomTimeout) {
clearTimeout(this.announceZoomTimeout);
}
this.currentZoomScale = scale;
const ths = this;
this.announceZoomTimeout = setTimeout(() => ths.comms.announceZoom(scale), this.settings.active.zoom.announceDebounce);
}
setArPersistence(persistenceMode) { setArPersistence(persistenceMode) {
// name of this function is mildly misleading — we don't really _set_ ar persistence. (Ar persistence // name of this function is mildly misleading — we don't really _set_ ar persistence. (Ar persistence
// mode is set and saved via popup or keyboard shortcuts, if user defined them) We just save the current // mode is set and saved via popup or keyboard shortcuts, if user defined them) We just save the current

View File

@ -143,16 +143,19 @@ class VideoData {
if (!this.mutationObserver) { if (!this.mutationObserver) {
this.setupMutationObserver(); this.setupMutationObserver();
} }
await this.pageInfo.injectCss(` this.eventBus.send(
.uw-ultrawidify-base-wide-screen { 'inject-css',
margin: 0px 0px 0px 0px !important; `
width: initial !important; .uw-ultrawidify-base-wide-screen {
align-self: start !important; margin: 0px 0px 0px 0px !important;
justify-self: start !important; width: initial !important;
max-height: initial !important; align-self: start !important;
max-width: initial !important; justify-self: start !important;
} max-height: initial !important;
`); max-width: initial !important;
}
`
);
} catch (e) { } catch (e) {
console.error('Failed to inject base css!', e); console.error('Failed to inject base css!', e);
} }

View File

@ -132,16 +132,16 @@ class Resizer {
} }
} }
injectCss(css) { injectCss(cssString) {
this.conf.pageInfo.injectCss(css); this.eventBus.send('inject-css', {cssString});
} }
ejectCss(css) { ejectCss(cssString) {
this.conf.pageInfo.ejectCss(css); this.eventBus.send('eject-css', {cssString});
} }
replaceCss(oldCss, newCss) { replaceCss(oldCssString, newCssString) {
this.conf.pageInfo.replaceCss(oldCss, newCss); this.eventBus.send('replace-css', {oldCss: oldCssString, newCss: newCssString});
} }
prepareCss(css) { prepareCss(css) {