Integrate comms client/server into eventBus
This commit is contained in:
parent
f0120010fe
commit
8806c8ea0c
@ -7,6 +7,7 @@ import CommsClient from './lib/comms/CommsClient';
|
||||
import PageInfo from './lib/video-data/PageInfo';
|
||||
import Logger, { baseLoggingOptions } from './lib/Logger';
|
||||
import UWGlobals from './lib/UWGlobals';
|
||||
import EventBus from './lib/EventBus';
|
||||
|
||||
export default class UWContent {
|
||||
pageInfo: PageInfo;
|
||||
@ -14,6 +15,7 @@ export default class UWContent {
|
||||
settings: Settings;
|
||||
actionHandler: ActionHandler;
|
||||
logger: Logger;
|
||||
eventBus: EventBus;
|
||||
|
||||
commsHandlers: {
|
||||
[x: string]: ((a: any, b?: any) => void | Promise<void>)[]
|
||||
@ -84,16 +86,13 @@ export default class UWContent {
|
||||
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
|
||||
if (this.comms) {
|
||||
this.comms.destroy();
|
||||
}
|
||||
if (this.eventBus) {
|
||||
this.eventBus.destroy();
|
||||
}
|
||||
if (!this.settings) {
|
||||
this.settings = new Settings({
|
||||
onSettingsChanged: () => this.reloadSettings(),
|
||||
@ -102,51 +101,70 @@ export default class UWContent {
|
||||
await this.settings.init();
|
||||
}
|
||||
|
||||
this.comms = new CommsClient('content-main-port', this.logger, this.commsHandlers);
|
||||
|
||||
// če smo razširitev onemogočili v nastavitvah, ne naredimo ničesar
|
||||
// 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) {
|
||||
if (this.settings.getExtensionMode('@global') === ExtensionMode.Disabled) {
|
||||
this.logger.log('info', 'debug', "[uw::init] EXTENSION DISABLED, THEREFORE WONT BE STARTED")
|
||||
return;
|
||||
this.eventBus = new EventBus();
|
||||
this.eventBus.subscribe(
|
||||
'uw-restart',
|
||||
{
|
||||
function: () => this.initPhase2()
|
||||
}
|
||||
}
|
||||
);
|
||||
this.comms = new CommsClient('content-main-port', this.logger, this.eventBus);
|
||||
|
||||
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.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);
|
||||
}
|
||||
this.initPhase2();
|
||||
} catch (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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,13 @@ import Logger, { baseLoggingOptions } from './lib/Logger';
|
||||
import { sleep } from '../common/js/utils';
|
||||
|
||||
import { browser } from 'webextension-polyfill-ts';
|
||||
import EventBus, { EventBusCommand } from './lib/EventBus';
|
||||
|
||||
export default class UWServer {
|
||||
settings: Settings;
|
||||
logger: Logger;
|
||||
comms: CommsServer;
|
||||
eventBus: EventBus;
|
||||
|
||||
ports: any[] = [];
|
||||
hasVideos: boolean;
|
||||
@ -24,6 +26,26 @@ export default class UWServer {
|
||||
'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;
|
||||
uiLoggerInitialized: boolean = false;
|
||||
@ -52,13 +74,13 @@ export default class UWServer {
|
||||
|
||||
this.settings = new Settings({logger: this.logger});
|
||||
await this.settings.init();
|
||||
this.eventBus = new EventBus();
|
||||
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
|
||||
|
||||
browser.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
|
||||
browser.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
|
||||
} catch (e) {
|
||||
console.error(`Ultrawidify [server]: failed to start. Reason:`, e);
|
||||
}
|
||||
@ -84,7 +106,7 @@ export default class UWServer {
|
||||
async removeCss(css, sender) {
|
||||
try {
|
||||
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});
|
||||
}
|
||||
}
|
||||
@ -98,22 +120,22 @@ export default class UWServer {
|
||||
|
||||
extractHostname(url){
|
||||
var hostname;
|
||||
|
||||
|
||||
if (!url) {
|
||||
return "<no url>";
|
||||
}
|
||||
|
||||
// extract hostname
|
||||
// extract 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;
|
||||
}
|
||||
|
||||
@ -209,14 +231,14 @@ export default class UWServer {
|
||||
allFrames: true,
|
||||
});
|
||||
} else if (BrowserDetect.anyChromium) {
|
||||
await new Promise<void>( resolve =>
|
||||
await new Promise<void>( resolve =>
|
||||
chrome.tabs.executeScript({
|
||||
file: '/ext/uw-ui.js',
|
||||
allFrames: true,
|
||||
}, () => resolve())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
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);
|
||||
@ -232,16 +254,16 @@ export default class UWServer {
|
||||
await this.initUi();
|
||||
|
||||
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,
|
||||
|
||||
// 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:
|
||||
// https://stackoverflow.com/questions/55207256/will-resolve-in-promise-loop-break-loop-iteration
|
||||
let isRejected = false;
|
||||
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
|
||||
while (!isRejected) {
|
||||
if (this.uiLoggerInitialized) {
|
||||
@ -261,7 +283,7 @@ export default class UWServer {
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
const ctab = await this.getCurrentTab();
|
||||
@ -295,11 +317,11 @@ export default class UWServer {
|
||||
return {
|
||||
...this.videoTabs[ctab.id],
|
||||
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
|
||||
return {
|
||||
host: this.extractHostname(ctab.url),
|
||||
@ -308,7 +330,7 @@ export default class UWServer {
|
||||
}
|
||||
}
|
||||
|
||||
// chrome shitiness mitigation
|
||||
// chrome shitiness mitigation
|
||||
sendUnmarkPlayer(message) {
|
||||
this.comms.sendUnmarkPlayer(message);
|
||||
}
|
||||
|
@ -1,7 +1,21 @@
|
||||
import CommsClient from './comms/CommsClient';
|
||||
import CommsServer from './comms/CommsServer';
|
||||
|
||||
export interface EventBusCommand {
|
||||
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 {
|
||||
@ -9,6 +23,20 @@ export default class EventBus {
|
||||
private commands: { [x: string]: EventBusCommand[]} = {};
|
||||
private downstreamBuses: 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) {
|
||||
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]) {
|
||||
// ensure send is not being called for commands that we have no subscriptions for
|
||||
return;
|
||||
}
|
||||
|
||||
for (const eventBusCommand of this.commands[command]) {
|
||||
eventBusCommand.function(config);
|
||||
eventBusCommand.function(config, context);
|
||||
|
||||
if (eventBusCommand.isGlobal && !stopPropagation) {
|
||||
this.sendUpstream(command, config);
|
||||
this.sendDownstream(command, config);
|
||||
if (eventBusCommand.isGlobal && !context?.stopPropagation) {
|
||||
this.sendUpstream(command, config, context);
|
||||
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.sendUpstream(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) {
|
||||
if (eventBus !== sourceEventBus) {
|
||||
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) {
|
||||
this.upstreamBus.send(command, config);
|
||||
this.upstreamBus.sendUpstream(command, config);
|
||||
this.upstreamBus.sendDownstream(command, config, this);
|
||||
this.upstreamBus.send(command, config, context);
|
||||
this.upstreamBus.sendUpstream(command, config, context);
|
||||
this.upstreamBus.sendDownstream(command, config, context, this);
|
||||
}
|
||||
if (!this.upstreamBus && this.comms && !context?.fromComms) {
|
||||
this.comms.sendMessage({command, config});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,124 +3,111 @@ import BrowserDetect from '../../conf/BrowserDetect';
|
||||
import Logger from '../Logger';
|
||||
import { browser } from 'webextension-polyfill-ts';
|
||||
import Settings from '../Settings';
|
||||
import EventBus from '../EventBus';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
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 {
|
||||
commsId: string;
|
||||
|
||||
logger: Logger;
|
||||
settings: any; // sus?
|
||||
|
||||
|
||||
commands: {[x: string]: any[]};
|
||||
|
||||
|
||||
eventBus: EventBus;
|
||||
|
||||
_listener: (m: any) => void;
|
||||
port: any;
|
||||
|
||||
|
||||
constructor(name, logger, commands) {
|
||||
//#region lifecycle
|
||||
constructor(name: string, logger: Logger, eventBus: EventBus) {
|
||||
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(
|
||||
(history) => {
|
||||
this.logger.log('info', 'comms', 'Sending logging-stop-and-save to background script ...');
|
||||
try {
|
||||
this.port.postMessage({cmd: 'logging-stop-and-save', host: window.location.hostname, history})
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'comms', 'Failed to send message to background script. Error:', e);
|
||||
this.logger.onLogEnd(
|
||||
(history) => {
|
||||
this.logger.log('info', 'comms', 'Sending logging-stop-and-save to background script ...');
|
||||
try {
|
||||
this.port.postMessage({cmd: 'logging-stop-and-save', host: window.location.hostname, history})
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'comms', 'Failed to send message to background script. Error:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
this._listener = m => this.processReceivedMessage(m);
|
||||
this.port.onMessage.addListener(this._listener);
|
||||
this._listener = m => this.processReceivedMessage(m);
|
||||
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) {
|
||||
console.error("CONSTRUCOTR FAILED:", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
destroy() {
|
||||
if (!BrowserDetect.edge) { // edge is a very special browser made by outright morons.
|
||||
this.port.onMessage.removeListener(this._listener);
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
subscribe(command, callback) {
|
||||
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){
|
||||
async sendMessage(message){
|
||||
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
|
||||
return browser.runtime.sendMessage(null, message, null);
|
||||
}
|
||||
|
||||
|
||||
// TODO: sus function — does it get any use?
|
||||
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);
|
||||
processReceivedMessage(message){
|
||||
this.eventBus.send(message.command, message.config, {fromComms: 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'){
|
||||
|
@ -4,12 +4,14 @@ import Logger from '../Logger';
|
||||
import Settings from '../Settings';
|
||||
import { browser } from 'webextension-polyfill-ts';
|
||||
import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
|
||||
import EventBus from '../EventBus';
|
||||
|
||||
|
||||
class CommsServer {
|
||||
server: any;
|
||||
logger: Logger;
|
||||
settings: Settings;
|
||||
eventBus: EventBus;
|
||||
|
||||
|
||||
ports: {
|
||||
@ -21,7 +23,7 @@ class CommsServer {
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* message — the message we received
|
||||
* 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
|
||||
*/
|
||||
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': [
|
||||
async (message, port) => {
|
||||
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
|
||||
(message, sender) => {
|
||||
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.logger = server.logger;
|
||||
this.settings = server.settings;
|
||||
this.eventBus = server.eventBus;
|
||||
|
||||
browser.runtime.onConnect.addListener(p => this.onConnect(p));
|
||||
browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender));
|
||||
@ -170,14 +99,26 @@ class CommsServer {
|
||||
else {
|
||||
hostname = url.split('/')[0];
|
||||
}
|
||||
|
||||
|
||||
hostname = hostname.split(':')[0]; //find & remove port number
|
||||
hostname = hostname.split('?')[0]; //find & remove "?"
|
||||
|
||||
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 frame in tab){
|
||||
for (const port in tab[frame]) {
|
||||
@ -187,7 +128,6 @@ class CommsServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message to addon content scripts.
|
||||
* @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 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) {
|
||||
this.ports[tab][frame][port].postMessage(message);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
const tabs = await this.activeTab;
|
||||
@ -251,7 +186,7 @@ class CommsServer {
|
||||
}
|
||||
|
||||
onConnect(port){
|
||||
// poseben primer | special case
|
||||
// special case
|
||||
if (port.name === 'popup-port') {
|
||||
this.popupPort = port;
|
||||
this.popupPort.onMessage.addListener( (m,p) => this.processReceivedMessage(m,p));
|
||||
@ -261,7 +196,7 @@ class CommsServer {
|
||||
var tabId = port.sender.tab.id;
|
||||
var frameId = port.sender.frameId;
|
||||
if (! this.ports[tabId]){
|
||||
this.ports[tabId] = {};
|
||||
this.ports[tabId] = {};
|
||||
}
|
||||
if (! this.ports[tabId][frameId]) {
|
||||
this.ports[tabId][frameId] = {};
|
||||
@ -271,9 +206,9 @@ class CommsServer {
|
||||
|
||||
this.ports[tabId][frameId][port.name].onDisconnect.addListener( (p) => {
|
||||
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) {
|
||||
// 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)) {
|
||||
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){
|
||||
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){
|
||||
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
|
||||
|
@ -75,7 +75,6 @@ class PageInfo {
|
||||
this.extensionMode = extensionMode;
|
||||
this.readOnly = readOnly;
|
||||
|
||||
this.eventBus = new EventBus();
|
||||
|
||||
if (comms){
|
||||
this.comms = comms;
|
||||
@ -84,10 +83,7 @@ class PageInfo {
|
||||
try {
|
||||
// request inject css immediately
|
||||
const playerStyleString = this.settings.active.sites[window.location.hostname].css.replace('\\n', '');
|
||||
this.comms.sendMessage({
|
||||
cmd: 'inject-css',
|
||||
cssString: playerStyleString
|
||||
});
|
||||
this.eventBus.send('inject-css', {cssString: playerStyleString});
|
||||
} catch (e) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
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() {
|
||||
this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!")
|
||||
if(this.rescanTimer){
|
||||
@ -139,7 +113,7 @@ class PageInfo {
|
||||
}
|
||||
for (let video of this.videos) {
|
||||
try {
|
||||
(this.comms.unregisterVideo as any)(video.videoData.vdid)
|
||||
this.eventBus.send('noVideo', undefined);
|
||||
video.videoData.destroy();
|
||||
} catch (e) {
|
||||
this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e);
|
||||
@ -148,12 +122,7 @@ class PageInfo {
|
||||
|
||||
try {
|
||||
const playerStyleString = this.settings.active.sites[window.location.hostname].css;
|
||||
if (playerStyleString) {
|
||||
this.comms.sendMessage({
|
||||
cmd: 'eject-css',
|
||||
cssString: playerStyleString
|
||||
});
|
||||
}
|
||||
this.eventBus.send('eject-css', {cssString: playerStyleString});
|
||||
} catch (e) {
|
||||
// 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) {
|
||||
// this.comms.registerVideo({host: window.location.hostname, location: window.location});
|
||||
this.comms.registerVideo();
|
||||
this.eventBus.send('has-video', null);
|
||||
} else {
|
||||
// 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) {
|
||||
// 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
|
||||
|
@ -143,16 +143,19 @@ class VideoData {
|
||||
if (!this.mutationObserver) {
|
||||
this.setupMutationObserver();
|
||||
}
|
||||
await this.pageInfo.injectCss(`
|
||||
.uw-ultrawidify-base-wide-screen {
|
||||
margin: 0px 0px 0px 0px !important;
|
||||
width: initial !important;
|
||||
align-self: start !important;
|
||||
justify-self: start !important;
|
||||
max-height: initial !important;
|
||||
max-width: initial !important;
|
||||
}
|
||||
`);
|
||||
this.eventBus.send(
|
||||
'inject-css',
|
||||
`
|
||||
.uw-ultrawidify-base-wide-screen {
|
||||
margin: 0px 0px 0px 0px !important;
|
||||
width: initial !important;
|
||||
align-self: start !important;
|
||||
justify-self: start !important;
|
||||
max-height: initial !important;
|
||||
max-width: initial !important;
|
||||
}
|
||||
`
|
||||
);
|
||||
} catch (e) {
|
||||
console.error('Failed to inject base css!', e);
|
||||
}
|
||||
|
@ -132,16 +132,16 @@ class Resizer {
|
||||
}
|
||||
}
|
||||
|
||||
injectCss(css) {
|
||||
this.conf.pageInfo.injectCss(css);
|
||||
injectCss(cssString) {
|
||||
this.eventBus.send('inject-css', {cssString});
|
||||
}
|
||||
|
||||
ejectCss(css) {
|
||||
this.conf.pageInfo.ejectCss(css);
|
||||
ejectCss(cssString) {
|
||||
this.eventBus.send('eject-css', {cssString});
|
||||
}
|
||||
|
||||
replaceCss(oldCss, newCss) {
|
||||
this.conf.pageInfo.replaceCss(oldCss, newCss);
|
||||
replaceCss(oldCssString, newCssString) {
|
||||
this.eventBus.send('replace-css', {oldCss: oldCssString, newCss: newCssString});
|
||||
}
|
||||
|
||||
prepareCss(css) {
|
||||
|
Loading…
Reference in New Issue
Block a user