mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 17:49:58 +00:00
prettier: Re-format .ts files
This commit is contained in:
@@ -10,14 +10,18 @@ interface TypedEvents {
|
||||
export class TypedEventTarget<T extends TypedEvents> extends EventTarget {
|
||||
// @ts-ignore
|
||||
public addEventListener<K extends keyof T>(
|
||||
type: K, listener: (ev: CustomEvent<T[K]>) => void): void {
|
||||
type: K,
|
||||
listener: (ev: CustomEvent<T[K]>) => void
|
||||
): void {
|
||||
// @ts-ignore
|
||||
super.addEventListener(type as string, listener);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
public removeEventListener<K extends keyof T>(
|
||||
type: K, listener: (ev: CustomEvent<T[K]>) => void): void {
|
||||
type: K,
|
||||
listener: (ev: CustomEvent<T[K]>) => void
|
||||
): void {
|
||||
// @ts-ignore
|
||||
super.removeEventListener(type as string, listener);
|
||||
}
|
||||
|
||||
@@ -4,24 +4,21 @@
|
||||
* Provides a typed interface to runtime.Port objects.
|
||||
*/
|
||||
export interface TypedPort<T>
|
||||
extends Omit<browser.runtime.Port
|
||||
, "onDisconnect"
|
||||
| "onMessage"
|
||||
| "postMessage"> {
|
||||
|
||||
extends Omit<
|
||||
browser.runtime.Port,
|
||||
"onDisconnect" | "onMessage" | "postMessage"
|
||||
> {
|
||||
onDisconnect: {
|
||||
addListener (cb: (port: TypedPort<T>) => void): void | Promise<void>
|
||||
, removeListener (cb: (port: TypedPort<T>) => void): void | Promise<void>
|
||||
, hasListener (cb: (port: TypedPort<T>) => void): boolean
|
||||
, hasListeners (): boolean
|
||||
}
|
||||
|
||||
, onMessage: {
|
||||
addListener (cb: (message: T) => void): void | Promise<void>
|
||||
, removeListener (cb: (message: T) => void): void | Promise<void>
|
||||
, hasListener (cb: (message: T) => void): boolean
|
||||
, hasListeners (): boolean
|
||||
}
|
||||
|
||||
, postMessage (message: T): void
|
||||
addListener(cb: (port: TypedPort<T>) => void): void | Promise<void>;
|
||||
removeListener(cb: (port: TypedPort<T>) => void): void | Promise<void>;
|
||||
hasListener(cb: (port: TypedPort<T>) => void): boolean;
|
||||
hasListeners(): boolean;
|
||||
};
|
||||
onMessage: {
|
||||
addListener(cb: (message: T) => void): void | Promise<void>;
|
||||
removeListener(cb: (message: T) => void): void | Promise<void>;
|
||||
hasListener(cb: (message: T) => void): boolean;
|
||||
hasListeners(): boolean;
|
||||
};
|
||||
postMessage(message: T): void;
|
||||
}
|
||||
|
||||
@@ -13,21 +13,18 @@ export class TypedStorageArea<Schema extends { [key: string]: any }> {
|
||||
this.storageArea = storageArea;
|
||||
}
|
||||
|
||||
public async get<SchemaKey extends keyof Schema
|
||||
, SchemaPartial extends Partial<Schema>>(
|
||||
keys?: SchemaKey
|
||||
| SchemaKey[]
|
||||
| SchemaPartial
|
||||
| null | undefined)
|
||||
: Promise<Pick<Schema, Extract<
|
||||
keyof SchemaPartial, SchemaKey>>> {
|
||||
|
||||
public async get<
|
||||
SchemaKey extends keyof Schema,
|
||||
SchemaPartial extends Partial<Schema>
|
||||
>(
|
||||
keys?: SchemaKey | SchemaKey[] | SchemaPartial | null | undefined
|
||||
): Promise<Pick<Schema, Extract<keyof SchemaPartial, SchemaKey>>> {
|
||||
return await this.storageArea.get(keys);
|
||||
}
|
||||
|
||||
public async getBytesInUse<SchemaKey extends keyof Schema>(
|
||||
keys?: Schema | SchemaKey[]): Promise<number> {
|
||||
|
||||
keys?: Schema | SchemaKey[]
|
||||
): Promise<number> {
|
||||
return await this.storageArea.getBytesInUse(keys);
|
||||
}
|
||||
|
||||
@@ -36,8 +33,8 @@ export class TypedStorageArea<Schema extends { [key: string]: any }> {
|
||||
}
|
||||
|
||||
public async remove<SchemaKey extends keyof Schema>(
|
||||
keys: SchemaKey | SchemaKey[]): Promise<void> {
|
||||
|
||||
keys: SchemaKey | SchemaKey[]
|
||||
): Promise<void> {
|
||||
await this.storageArea.remove(keys);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Port } from "../messaging";
|
||||
import nativeMessaging from "./nativeMessaging";
|
||||
import options from "./options";
|
||||
|
||||
|
||||
export const BRIDGE_TIMEOUT = 5000;
|
||||
|
||||
/**
|
||||
@@ -15,13 +14,16 @@ export const BRIDGE_TIMEOUT = 5000;
|
||||
*/
|
||||
async function connect(): Promise<Port> {
|
||||
const applicationName = await options.get("bridgeApplicationName");
|
||||
const bridgePort = nativeMessaging.connectNative(applicationName) as
|
||||
unknown as Port;
|
||||
const bridgePort = nativeMessaging.connectNative(
|
||||
applicationName
|
||||
) as unknown as Port;
|
||||
|
||||
bridgePort.onDisconnect.addListener(() => {
|
||||
if (bridgePort.error) {
|
||||
console.error(`${applicationName} disconnected:`
|
||||
, bridgePort.error.message);
|
||||
console.error(
|
||||
`${applicationName} disconnected:`,
|
||||
bridgePort.error.message
|
||||
);
|
||||
} else {
|
||||
console.info(`${applicationName} disconnected`);
|
||||
}
|
||||
@@ -30,7 +32,6 @@ async function connect(): Promise<Port> {
|
||||
return bridgePort;
|
||||
}
|
||||
|
||||
|
||||
export interface BridgeInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
@@ -50,77 +51,81 @@ export class BridgeTimedOutError extends Error {}
|
||||
* rules to determine compatiblity, then returns a
|
||||
* BridgeInfo object.
|
||||
*/
|
||||
const getInfo = () => new Promise<BridgeInfo>(async (resolve, reject) => {
|
||||
const applicationName = await options.get("bridgeApplicationName");
|
||||
if (!applicationName) {
|
||||
reject(logger.error("Bridge application name not found."));
|
||||
return;
|
||||
}
|
||||
const getInfo = () =>
|
||||
new Promise<BridgeInfo>(async (resolve, reject) => {
|
||||
const applicationName = await options.get("bridgeApplicationName");
|
||||
if (!applicationName) {
|
||||
reject(logger.error("Bridge application name not found."));
|
||||
return;
|
||||
}
|
||||
|
||||
const bridgeTimeoutId = setTimeout(() => {
|
||||
logger.error("Bridge timed out.");
|
||||
reject(new BridgeTimedOutError());
|
||||
}, BRIDGE_TIMEOUT);
|
||||
const bridgeTimeoutId = setTimeout(() => {
|
||||
logger.error("Bridge timed out.");
|
||||
reject(new BridgeTimedOutError());
|
||||
}, BRIDGE_TIMEOUT);
|
||||
|
||||
let applicationVersion: string;
|
||||
try {
|
||||
const { version } = browser.runtime.getManifest();
|
||||
let applicationVersion: string;
|
||||
try {
|
||||
const { version } = browser.runtime.getManifest();
|
||||
|
||||
applicationVersion = await nativeMessaging.sendNativeMessage(
|
||||
applicationName,
|
||||
{ subject: "bridge:/getInfo", data: version }
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error("Bridge connection failed.");
|
||||
reject(new BridgeConnectionError());
|
||||
clearTimeout(bridgeTimeoutId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
applicationVersion = await nativeMessaging.sendNativeMessage(
|
||||
applicationName
|
||||
, { subject: "bridge:/getInfo"
|
||||
, data: version });
|
||||
} catch (err) {
|
||||
logger.error("Bridge connection failed.");
|
||||
reject(new BridgeConnectionError());
|
||||
clearTimeout(bridgeTimeoutId);
|
||||
|
||||
return;
|
||||
}
|
||||
const extensionVersion = browser.runtime.getManifest().version;
|
||||
const extensionVersionMajor = semver.major(extensionVersion);
|
||||
|
||||
clearTimeout(bridgeTimeoutId);
|
||||
const versionDiff = semver.diff(applicationVersion, extensionVersion);
|
||||
|
||||
const extensionVersion = browser.runtime.getManifest().version;
|
||||
const extensionVersionMajor = semver.major(extensionVersion);
|
||||
/**
|
||||
* If the target version is above 0.x.x range, API is stable
|
||||
* and versions with minor or patch level changes should be
|
||||
* compatible.
|
||||
*/
|
||||
const isVersionCompatible =
|
||||
semver.eq(applicationVersion, extensionVersion) ||
|
||||
(versionDiff !== "major" && extensionVersionMajor !== 0) ||
|
||||
(versionDiff === "patch" && extensionVersionMajor === 0);
|
||||
|
||||
const versionDiff = semver.diff(applicationVersion, extensionVersion);
|
||||
const isVersionExact = semver.eq(applicationVersion, extensionVersion);
|
||||
const isVersionOlder = semver.lt(applicationVersion, extensionVersion);
|
||||
const isVersionNewer = semver.gt(applicationVersion, extensionVersion);
|
||||
|
||||
/**
|
||||
* If the target version is above 0.x.x range, API is stable
|
||||
* and versions with minor or patch level changes should be
|
||||
* compatible.
|
||||
*/
|
||||
const isVersionCompatible =
|
||||
semver.eq(applicationVersion, extensionVersion)
|
||||
|| (versionDiff !== "major" && extensionVersionMajor !== 0)
|
||||
|| (versionDiff === "patch" && extensionVersionMajor === 0);
|
||||
// Print compatibility info to console
|
||||
if (!isVersionCompatible) {
|
||||
logger.error(
|
||||
`Expecting ${applicationName} v${BRIDGE_VERSION}, found v${applicationVersion}. ${
|
||||
isVersionOlder
|
||||
? "Try updating the native app to the latest version."
|
||||
: "Try updating the extension to the latest version"
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
const isVersionExact = semver.eq(applicationVersion, extensionVersion);
|
||||
const isVersionOlder = semver.lt(applicationVersion, extensionVersion);
|
||||
const isVersionNewer = semver.gt(applicationVersion, extensionVersion);
|
||||
resolve({
|
||||
name: applicationName,
|
||||
version: applicationVersion,
|
||||
expectedVersion: BRIDGE_VERSION,
|
||||
|
||||
// Print compatibility info to console
|
||||
if (!isVersionCompatible) {
|
||||
logger.error(`Expecting ${applicationName} v${BRIDGE_VERSION}, found v${applicationVersion}. ${
|
||||
isVersionOlder
|
||||
? "Try updating the native app to the latest version."
|
||||
: "Try updating the extension to the latest version"}`);
|
||||
}
|
||||
|
||||
resolve({
|
||||
name: applicationName
|
||||
, version: applicationVersion
|
||||
, expectedVersion: BRIDGE_VERSION
|
||||
|
||||
// Version info
|
||||
, isVersionExact
|
||||
, isVersionCompatible
|
||||
, isVersionOlder
|
||||
, isVersionNewer
|
||||
// Version info
|
||||
isVersionExact,
|
||||
isVersionCompatible,
|
||||
isVersionOlder,
|
||||
isVersionNewer
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export default {
|
||||
connect
|
||||
, getInfo
|
||||
connect,
|
||||
getInfo
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* UA string checking.
|
||||
*/
|
||||
export const CAST_LOADER_SCRIPT_URL =
|
||||
"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js";
|
||||
"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js";
|
||||
|
||||
/**
|
||||
* Cast Chrome Sender Framework API loader script.
|
||||
@@ -18,8 +18,7 @@ export const CAST_LOADER_SCRIPT_URL =
|
||||
* the framework API script is conditionally loaded in
|
||||
* addition to the regular SDK script.
|
||||
*/
|
||||
export const CAST_FRAMEWORK_LOADER_SCRIPT_URL =
|
||||
`${CAST_LOADER_SCRIPT_URL}?loadCastFramework=1`;
|
||||
export const CAST_FRAMEWORK_LOADER_SCRIPT_URL = `${CAST_LOADER_SCRIPT_URL}?loadCastFramework=1`;
|
||||
|
||||
/**
|
||||
* Cast extension URLs.
|
||||
@@ -29,8 +28,8 @@ export const CAST_FRAMEWORK_LOADER_SCRIPT_URL =
|
||||
* chrome-extension: URLs for compatibility reasons (?).
|
||||
*/
|
||||
export const CAST_SCRIPT_URLS = [
|
||||
"chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/cast_sender.js"
|
||||
, "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/cast_sender.js"
|
||||
"chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/cast_sender.js",
|
||||
"chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/cast_sender.js"
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -41,4 +40,4 @@ export const CAST_SCRIPT_URLS = [
|
||||
* opposed to within the cast extension.
|
||||
*/
|
||||
export const CAST_FRAMEWORK_SCRIPT_URL =
|
||||
"https://www.gstatic.com/cast/sdk/libs/sender/1.0/cast_framework.js";
|
||||
"https://www.gstatic.com/cast/sdk/libs/sender/1.0/cast_framework.js";
|
||||
|
||||
@@ -15,21 +15,30 @@ interface KnownApp {
|
||||
*/
|
||||
export default {
|
||||
// Web-supported
|
||||
"CA5E8412": { name: "Netflix" , matches: "https://www.netflix.com/*" }
|
||||
, "233637DE": { name: "YouTube" , matches: "https://www.youtube.com/*" }
|
||||
, "CC32E753": { name: "Spotify" , matches: "https://open.spotify.com/*" }
|
||||
, "5E81F6DB": { name: "BBC iPlayer" , matches: "https://www.bbc.co.uk/iplayer/*" }
|
||||
, "03977A48": { name: "BBC Sounds" , matches: "https://www.bbc.co.uk/sounds/*" }
|
||||
, "AA666EDD": { name: "Crunchyroll" , matches: "https://crunchyroll.com/*" }
|
||||
, "10AAD887": { name: "All 4" , matches: "https://www.channel4.com/*" }
|
||||
, "B3DCF968": { name: "Twitch" , matches: "https://www.twitch.tv/*" }
|
||||
, "B88B034A": { name: "Dailymotion" , matches: "https://www.dailymotion.com/*" }
|
||||
, "C3DE6BC2": { name: "Disney+" , matches: "https://www.disneyplus.com/*" }
|
||||
"CA5E8412": { name: "Netflix", matches: "https://www.netflix.com/*" },
|
||||
"233637DE": { name: "YouTube", matches: "https://www.youtube.com/*" },
|
||||
"CC32E753": { name: "Spotify", matches: "https://open.spotify.com/*" },
|
||||
"5E81F6DB": {
|
||||
name: "BBC iPlayer",
|
||||
matches: "https://www.bbc.co.uk/iplayer/*"
|
||||
},
|
||||
"03977A48": {
|
||||
name: "BBC Sounds",
|
||||
matches: "https://www.bbc.co.uk/sounds/*"
|
||||
},
|
||||
"AA666EDD": { name: "Crunchyroll", matches: "https://crunchyroll.com/*" },
|
||||
"10AAD887": { name: "All 4", matches: "https://www.channel4.com/*" },
|
||||
"B3DCF968": { name: "Twitch", matches: "https://www.twitch.tv/*" },
|
||||
"B88B034A": {
|
||||
name: "Dailymotion",
|
||||
matches: "https://www.dailymotion.com/*"
|
||||
},
|
||||
"C3DE6BC2": { name: "Disney+", matches: "https://www.disneyplus.com/*" },
|
||||
|
||||
// Misc
|
||||
, "17608BC8": { name: "Prime Video" }
|
||||
, "9AC194DC": { name: "Plex" }
|
||||
, "CD7B9F59": { name: "Global Player Live" }
|
||||
"17608BC8": { name: "Prime Video" },
|
||||
"9AC194DC": { name: "Plex" },
|
||||
"CD7B9F59": { name: "Global Player Live" },
|
||||
|
||||
, "CC1AD845": { name: _("popupMediaTypeAppMedia") }
|
||||
"CC1AD845": { name: _("popupMediaTypeAppMedia") }
|
||||
} as Record<string, KnownApp>;
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
import logger from "./logger";
|
||||
import { stringify } from "./utils";
|
||||
|
||||
import { ReceiverSelection
|
||||
, ReceiverSelectionActionType
|
||||
, ReceiverSelectorMediaType } from "../background/receiverSelector";
|
||||
import {
|
||||
ReceiverSelection,
|
||||
ReceiverSelectionActionType,
|
||||
ReceiverSelectorMediaType
|
||||
} from "../background/receiverSelector";
|
||||
|
||||
import ShimManager from "../background/ShimManager";
|
||||
|
||||
|
||||
interface LoadSenderOptions {
|
||||
tabId: number;
|
||||
frameId?: number;
|
||||
@@ -34,13 +35,14 @@ export default async function loadSender(opts: LoadSenderOptions) {
|
||||
case ReceiverSelectorMediaType.App: {
|
||||
const shim = ShimManager.getShim(opts.tabId, opts.frameId);
|
||||
if (!shim) {
|
||||
throw logger.error(`Shim not found at tabId ${
|
||||
opts.tabId} / frameId ${opts.frameId}`);
|
||||
throw logger.error(
|
||||
`Shim not found at tabId ${opts.tabId} / frameId ${opts.frameId}`
|
||||
);
|
||||
}
|
||||
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:launchApp"
|
||||
, data: { receiver: opts.selection.receiver }
|
||||
subject: "shim:launchApp",
|
||||
data: { receiver: opts.selection.receiver }
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -52,13 +54,13 @@ export default async function loadSender(opts: LoadSenderOptions) {
|
||||
code: stringify`
|
||||
window.selectedMedia = ${opts.selection.mediaType};
|
||||
window.selectedReceiver = ${opts.selection.receiver};
|
||||
`
|
||||
, frameId: opts.frameId
|
||||
`,
|
||||
frameId: opts.frameId
|
||||
});
|
||||
|
||||
await browser.tabs.executeScript(opts.tabId, {
|
||||
file: "senders/mirroring.js"
|
||||
, frameId: opts.frameId
|
||||
file: "senders/mirroring.js",
|
||||
frameId: opts.frameId
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -69,8 +71,8 @@ export default async function loadSender(opts: LoadSenderOptions) {
|
||||
const { init } = await import("../senders/media");
|
||||
|
||||
init({
|
||||
mediaUrl: fileUrl.href
|
||||
, receiver: opts.selection.receiver
|
||||
mediaUrl: fileUrl.href,
|
||||
receiver: opts.selection.receiver
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
@@ -5,7 +5,6 @@ import options from "./options";
|
||||
|
||||
import { Message, Port } from "../messaging";
|
||||
|
||||
|
||||
type DisconnectListener = (port: Port) => void;
|
||||
type MessageListener = (message: Message) => void;
|
||||
|
||||
@@ -26,7 +25,6 @@ function connectNative(application: string): Port {
|
||||
|
||||
const port = browser.runtime.connectNative(application);
|
||||
|
||||
|
||||
let socket: WebSocket;
|
||||
|
||||
const onDisconnectListeners = new Set<DisconnectListener>();
|
||||
@@ -34,47 +32,47 @@ function connectNative(application: string): Port {
|
||||
|
||||
// Port proxy API
|
||||
const portObject: Port = {
|
||||
error: null as any
|
||||
, name: ""
|
||||
error: null as any,
|
||||
name: "",
|
||||
|
||||
, onDisconnect: {
|
||||
onDisconnect: {
|
||||
addListener(cb: DisconnectListener) {
|
||||
onDisconnectListeners.add(cb);
|
||||
}
|
||||
, removeListener(cb: DisconnectListener) {
|
||||
},
|
||||
removeListener(cb: DisconnectListener) {
|
||||
onDisconnectListeners.delete(cb);
|
||||
}
|
||||
, hasListener(cb: DisconnectListener) {
|
||||
},
|
||||
hasListener(cb: DisconnectListener) {
|
||||
return onDisconnectListeners.has(cb);
|
||||
},
|
||||
hasListeners() {
|
||||
return onDisconnectListeners.size > 0;
|
||||
}
|
||||
, hasListeners() {
|
||||
return onDisconnectListeners.size > 0;
|
||||
}
|
||||
}
|
||||
, onMessage: {
|
||||
},
|
||||
onMessage: {
|
||||
addListener(cb: MessageListener) {
|
||||
onMessageListeners.add(cb);
|
||||
}
|
||||
, removeListener(cb: MessageListener) {
|
||||
},
|
||||
removeListener(cb: MessageListener) {
|
||||
onMessageListeners.delete(cb);
|
||||
}
|
||||
, hasListener(cb: MessageListener) {
|
||||
},
|
||||
hasListener(cb: MessageListener) {
|
||||
return onMessageListeners.has(cb);
|
||||
},
|
||||
hasListeners() {
|
||||
return onMessageListeners.size > 0;
|
||||
}
|
||||
, hasListeners() {
|
||||
return onMessageListeners.size > 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
, disconnect() {
|
||||
disconnect() {
|
||||
if (socket) {
|
||||
socket.close();
|
||||
} else {
|
||||
port.disconnect();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
, postMessage(message) {
|
||||
postMessage(message) {
|
||||
if (socket) {
|
||||
switch (socket.readyState) {
|
||||
case WebSocket.CONNECTING: {
|
||||
@@ -99,11 +97,9 @@ function connectNative(application: string): Port {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
port.onDisconnect.addListener(async () => {
|
||||
const { bridgeBackupEnabled
|
||||
, bridgeBackupHost
|
||||
, bridgeBackupPort } = await options.getAll();
|
||||
const { bridgeBackupEnabled, bridgeBackupHost, bridgeBackupPort } =
|
||||
await options.getAll();
|
||||
|
||||
if (!bridgeBackupEnabled) {
|
||||
portObject.error = {
|
||||
@@ -114,14 +110,17 @@ function connectNative(application: string): Port {
|
||||
listener(portObject);
|
||||
}
|
||||
|
||||
throw logger.error("Bridge connection failed and backup not enabled.");
|
||||
throw logger.error(
|
||||
"Bridge connection failed and backup not enabled."
|
||||
);
|
||||
}
|
||||
|
||||
if (port.error && !isNativeHostStatusKnown) {
|
||||
isNativeHostStatusKnown = true;
|
||||
|
||||
socket = new WebSocket(
|
||||
`ws://${bridgeBackupHost}:${bridgeBackupPort}`);
|
||||
`ws://${bridgeBackupHost}:${bridgeBackupPort}`
|
||||
);
|
||||
|
||||
socket.addEventListener("open", () => {
|
||||
// Send all messages in queue
|
||||
@@ -163,30 +162,28 @@ function connectNative(application: string): Port {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return portObject;
|
||||
}
|
||||
|
||||
async function sendNativeMessage(
|
||||
application: string
|
||||
, message: Message) {
|
||||
|
||||
async function sendNativeMessage(application: string, message: Message) {
|
||||
try {
|
||||
return await browser.runtime.sendNativeMessage(application, message);
|
||||
} catch {
|
||||
const { bridgeBackupEnabled
|
||||
, bridgeBackupHost
|
||||
, bridgeBackupPort } = await options.getAll();
|
||||
const { bridgeBackupEnabled, bridgeBackupHost, bridgeBackupPort } =
|
||||
await options.getAll();
|
||||
|
||||
if (!bridgeBackupEnabled) {
|
||||
throw logger.error("Bridge connection failed and backup not enabled.");
|
||||
throw logger.error(
|
||||
"Bridge connection failed and backup not enabled."
|
||||
);
|
||||
}
|
||||
|
||||
const port = await options.get("bridgeBackupPort");
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
const ws = new WebSocket(
|
||||
`ws://${bridgeBackupHost}:${bridgeBackupPort}`);
|
||||
`ws://${bridgeBackupHost}:${bridgeBackupPort}`
|
||||
);
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
ws.send(JSON.stringify(message));
|
||||
@@ -205,8 +202,7 @@ async function sendNativeMessage(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
connectNative
|
||||
, sendNativeMessage
|
||||
connectNative,
|
||||
sendNativeMessage
|
||||
};
|
||||
|
||||
@@ -8,9 +8,8 @@ import { ReceiverSelectorType } from "../background/receiverSelector";
|
||||
import { TypedEventTarget } from "./TypedEventTarget";
|
||||
import { TypedStorageArea } from "./TypedStorageArea";
|
||||
|
||||
|
||||
const storageArea = new TypedStorageArea<{
|
||||
options: Options
|
||||
options: Options;
|
||||
}>(browser.storage.sync);
|
||||
|
||||
export interface Options {
|
||||
@@ -35,12 +34,11 @@ export interface Options {
|
||||
[key: string]: Options[keyof Options];
|
||||
}
|
||||
|
||||
|
||||
interface EventMap {
|
||||
"changed": Array<keyof Options>;
|
||||
changed: Array<keyof Options>;
|
||||
}
|
||||
|
||||
export default new class extends TypedEventTarget<EventMap> {
|
||||
export default new (class extends TypedEventTarget<EventMap> {
|
||||
constructor() {
|
||||
super();
|
||||
this.onStorageChanged = this.onStorageChanged.bind(this);
|
||||
@@ -53,9 +51,9 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
}
|
||||
|
||||
private onStorageChanged(
|
||||
changes: { [key: string]: browser.storage.StorageChange }
|
||||
, areaName: string) {
|
||||
|
||||
changes: { [key: string]: browser.storage.StorageChange },
|
||||
areaName: string
|
||||
) {
|
||||
if (areaName !== "sync") {
|
||||
return;
|
||||
}
|
||||
@@ -80,11 +78,16 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
}
|
||||
|
||||
// Array comparison
|
||||
if (oldKeyValue instanceof Array
|
||||
&& newKeyValue instanceof Array) {
|
||||
if (oldKeyValue.length === newKeyValue.length
|
||||
&& oldKeyValue.every((value, index) =>
|
||||
value === newKeyValue[index])) {
|
||||
if (
|
||||
oldKeyValue instanceof Array &&
|
||||
newKeyValue instanceof Array
|
||||
) {
|
||||
if (
|
||||
oldKeyValue.length === newKeyValue.length &&
|
||||
oldKeyValue.every(
|
||||
(value, index) => value === newKeyValue[index]
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -93,9 +96,11 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
changedKeys.push(key);
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent("changed", {
|
||||
detail: changedKeys as Array<keyof Options>
|
||||
}));
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("changed", {
|
||||
detail: changedKeys as Array<keyof Options>
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,15 +140,14 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
* promise.
|
||||
*/
|
||||
public async set<T extends keyof Options>(
|
||||
name: T
|
||||
, value: Options[T]): Promise<void> {
|
||||
|
||||
name: T,
|
||||
value: Options[T]
|
||||
): Promise<void> {
|
||||
const options = await this.getAll();
|
||||
options[name] = value;
|
||||
return this.setAll(options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets existing options from storage and compares it
|
||||
* against defaults. Any options in defaults and not in
|
||||
@@ -153,7 +157,7 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
const newOpts = await this.getAll();
|
||||
|
||||
// Find options not already in storage
|
||||
for (const [ optName, optVal ] of Object.entries(defaults)) {
|
||||
for (const [optName, optVal] of Object.entries(defaults)) {
|
||||
if (!newOpts.hasOwnProperty(optName)) {
|
||||
newOpts[optName] = optVal;
|
||||
}
|
||||
@@ -162,4 +166,4 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
// Update storage with default values of new options
|
||||
return this.setAll(newOpts);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -5,15 +5,14 @@ const PLATFORM_MAC_HYBRID = "Macintosh; Intel Mac OS X 10.15; rv:72.0";
|
||||
const PLATFORM_WIN = "Windows NT 10.0; Win64; x64";
|
||||
const PLATFORM_LINUX = "X11; Linux x86_64";
|
||||
|
||||
const UA_CHROME = "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36";
|
||||
const UA_CHROME =
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36";
|
||||
const UA_HYBRID = "Chrome/80.0.3987.87 Gecko/20100101 Firefox/72.0";
|
||||
|
||||
export function getChromeUserAgent(platform: string, hybrid = false) {
|
||||
let platformComponent: string;
|
||||
if (platform === "mac") {
|
||||
platformComponent = hybrid
|
||||
? PLATFORM_MAC_HYBRID
|
||||
: PLATFORM_MAC;
|
||||
platformComponent = hybrid ? PLATFORM_MAC_HYBRID : PLATFORM_MAC;
|
||||
} else if (platform === "win") {
|
||||
platformComponent = PLATFORM_WIN;
|
||||
} else if (platform === "linux") {
|
||||
@@ -22,9 +21,7 @@ export function getChromeUserAgent(platform: string, hybrid = false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browserComponent = hybrid
|
||||
? UA_HYBRID
|
||||
: UA_CHROME;
|
||||
const browserComponent = hybrid ? UA_HYBRID : UA_CHROME;
|
||||
|
||||
return `Mozilla/5.0 (${platformComponent}) ${browserComponent}`;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import logger from "./logger";
|
||||
|
||||
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
|
||||
|
||||
|
||||
export function getNextEllipsis(ellipsis: string): string {
|
||||
if (ellipsis === "") return ".";
|
||||
if (ellipsis === ".") return "..";
|
||||
@@ -18,9 +17,9 @@ export function getNextEllipsis(ellipsis: string): string {
|
||||
* Template literal tag function, JSON-encodes substitutions.
|
||||
*/
|
||||
export function stringify(
|
||||
templateStrings: TemplateStringsArray
|
||||
, ...substitutions: any[]) {
|
||||
|
||||
templateStrings: TemplateStringsArray,
|
||||
...substitutions: any[]
|
||||
) {
|
||||
let formattedString = "";
|
||||
|
||||
for (const templateString of templateStrings) {
|
||||
@@ -35,8 +34,8 @@ export function stringify(
|
||||
}
|
||||
|
||||
export function getMediaTypesForPageUrl(
|
||||
pageUrl: string): ReceiverSelectorMediaType {
|
||||
|
||||
pageUrl: string
|
||||
): ReceiverSelectorMediaType {
|
||||
const url = new URL(pageUrl);
|
||||
let availableMediaTypes = ReceiverSelectorMediaType.File;
|
||||
|
||||
@@ -45,18 +44,18 @@ export function getMediaTypesForPageUrl(
|
||||
* Mozilla domains.
|
||||
*/
|
||||
const blockedHosts = [
|
||||
"accounts-static.cdn.mozilla.net"
|
||||
, "accounts.firefox.com"
|
||||
, "addons.cdn.mozilla.net"
|
||||
, "addons.mozilla.org"
|
||||
, "api.accounts.firefox.com"
|
||||
, "content.cdn.mozilla.net"
|
||||
, "discovery.addons.mozilla.org"
|
||||
, "install.mozilla.org"
|
||||
, "oauth.accounts.firefox.com"
|
||||
, "profile.accounts.firefox.com"
|
||||
, "support.mozilla.org"
|
||||
, "sync.services.mozilla.com"
|
||||
"accounts-static.cdn.mozilla.net",
|
||||
"accounts.firefox.com",
|
||||
"addons.cdn.mozilla.net",
|
||||
"addons.mozilla.org",
|
||||
"api.accounts.firefox.com",
|
||||
"content.cdn.mozilla.net",
|
||||
"discovery.addons.mozilla.org",
|
||||
"install.mozilla.org",
|
||||
"oauth.accounts.firefox.com",
|
||||
"profile.accounts.firefox.com",
|
||||
"support.mozilla.org",
|
||||
"sync.services.mozilla.com"
|
||||
];
|
||||
|
||||
if (blockedHosts.includes(url.host)) {
|
||||
@@ -80,7 +79,6 @@ export function getMediaTypesForPageUrl(
|
||||
return availableMediaTypes;
|
||||
}
|
||||
|
||||
|
||||
export interface WindowCenteredProps {
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -89,33 +87,37 @@ export interface WindowCenteredProps {
|
||||
}
|
||||
|
||||
export function getWindowCenteredProps(
|
||||
refWin: browser.windows.Window
|
||||
, width: number
|
||||
, height: number): WindowCenteredProps {
|
||||
|
||||
if (refWin.left === undefined || refWin.width === undefined
|
||||
|| refWin.top === undefined || refWin.height === undefined) {
|
||||
refWin: browser.windows.Window,
|
||||
width: number,
|
||||
height: number
|
||||
): WindowCenteredProps {
|
||||
if (
|
||||
refWin.left === undefined ||
|
||||
refWin.width === undefined ||
|
||||
refWin.top === undefined ||
|
||||
refWin.height === undefined
|
||||
) {
|
||||
throw logger.error("refWin missing positional attributes.");
|
||||
}
|
||||
|
||||
const centerX = refWin.left + (refWin.width / 2);
|
||||
const centerY = refWin.top + (refWin.height / 3);
|
||||
const centerX = refWin.left + refWin.width / 2;
|
||||
const centerY = refWin.top + refWin.height / 3;
|
||||
|
||||
return {
|
||||
width, height
|
||||
, left: Math.floor(centerX - width / 2)
|
||||
, top: Math.floor(centerY - height / 2)
|
||||
width,
|
||||
height,
|
||||
left: Math.floor(centerX - width / 2),
|
||||
top: Math.floor(centerY - height / 2)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export const REMOTE_MATCH_PATTERN_REGEX = /^(?:(?:(\*|https?|ftp):\/\/(\*|(?:\*\.(?:[^\/\*:]\.?)+(?:[^\.])|[^\/\*:]*))?)(\/.*)|<all_urls>)$/;
|
||||
|
||||
export const REMOTE_MATCH_PATTERN_REGEX =
|
||||
/^(?:(?:(\*|https?|ftp):\/\/(\*|(?:\*\.(?:[^\/\*:]\.?)+(?:[^\.])|[^\/\*:]*))?)(\/.*)|<all_urls>)$/;
|
||||
|
||||
export function loadScript(
|
||||
scriptUrl: string
|
||||
, doc: Document = document): HTMLScriptElement {
|
||||
|
||||
scriptUrl: string,
|
||||
doc: Document = document
|
||||
): HTMLScriptElement {
|
||||
const scriptElement = doc.createElement("script");
|
||||
scriptElement.src = scriptUrl;
|
||||
(doc.head || doc.documentElement).append(scriptElement);
|
||||
|
||||
Reference in New Issue
Block a user