From 8c9ac7b1d516e3a4eadcace7e2d567ff809fc1e8 Mon Sep 17 00:00:00 2001 From: hensm Date: Sun, 28 Jul 2019 06:07:57 +0100 Subject: [PATCH] Move some background modules to a separate folder and fix init order --- ext/src/background/ShimManager.ts | 237 ++++++++++++++++++ ext/src/{ => background}/StatusManager.ts | 32 ++- ext/src/{ => background}/createMenus.ts | 4 +- .../NativeReceiverSelector.ts} | 14 +- .../PopupReceiverSelector.ts | 8 +- .../receiverSelector}/ReceiverSelector.ts | 4 +- .../ReceiverSelectorManager.ts} | 56 +++-- ext/src/background/receiverSelector/index.ts | 17 ++ ext/src/createShim.ts | 230 ----------------- ext/src/defaultOptions.ts | 2 +- ext/src/lib/loadSender.ts | 2 +- ext/src/lib/nativeMessaging.ts | 2 +- ext/src/lib/options.ts | 48 ++-- ext/src/main.ts | 87 ++++--- ext/src/receiver_selectors/index.ts | 34 --- ext/src/senders/mirroringCast.ts | 4 +- ext/src/shim/cast/index.ts | 3 +- ext/src/shim/export.ts | 11 +- ext/src/ui/options/index.tsx | 4 +- ext/src/ui/popup/index.tsx | 3 +- 20 files changed, 412 insertions(+), 390 deletions(-) create mode 100644 ext/src/background/ShimManager.ts rename ext/src/{ => background}/StatusManager.ts (83%) rename ext/src/{ => background}/createMenus.ts (98%) rename ext/src/{receiver_selectors/NativeMacReceiverSelector.ts => background/receiverSelector/NativeReceiverSelector.ts} (94%) rename ext/src/{receiver_selectors => background/receiverSelector}/PopupReceiverSelector.ts (96%) rename ext/src/{receiver_selectors => background/receiverSelector}/ReceiverSelector.ts (87%) rename ext/src/{SelectorManager.ts => background/receiverSelector/ReceiverSelectorManager.ts} (60%) create mode 100644 ext/src/background/receiverSelector/index.ts delete mode 100644 ext/src/createShim.ts delete mode 100644 ext/src/receiver_selectors/index.ts diff --git a/ext/src/background/ShimManager.ts b/ext/src/background/ShimManager.ts new file mode 100644 index 0000000..9ea528e --- /dev/null +++ b/ext/src/background/ShimManager.ts @@ -0,0 +1,237 @@ +"use strict"; + +import bridge from "../lib/bridge"; +import loadSender from "../lib/loadSender"; +import options from "../lib/options"; + +import { TypedEventTarget } from "../lib/typedEvents"; +import { Message } from "../types"; + +import { ReceiverSelectorMediaType } from "./receiverSelector"; + +import ReceiverSelectorManager + from "./receiverSelector/ReceiverSelectorManager"; + +import StatusManager from "./StatusManager"; + + +type Port = browser.runtime.Port | MessagePort; + +export interface Shim { + bridgePort: browser.runtime.Port; + contentPort: Port; + contentTabId?: number; + contentFrameId?: number; +} + + +// tslint:disable-next-line:new-parens +export default new class ShimManager { + private activeShims = new Set(); + + public async init () { + await StatusManager.init(); + await this.initStatusListeners(); + } + + public async createShim (port: Port) { + const shim = await (port instanceof MessagePort + ? this.createShimFromBackground(port) + : this.createShimFromContent(port)); + + shim.contentPort.postMessage({ + subject: "shim:/initialized" + , data: await bridge.getInfo() + }); + + this.activeShims.add(shim); + } + + private async createShimFromBackground ( + contentPort: MessagePort): Promise { + + const shim: Shim = { + bridgePort: await bridge.connect() + , contentPort + }; + + shim.bridgePort.onDisconnect.addListener(() => { + contentPort.close(); + this.activeShims.delete(shim); + }); + + shim.bridgePort.onMessage.addListener((message: Message) => { + contentPort.postMessage(message); + }); + + contentPort.onmessage = ev => { + const message = ev.data as Message; + this.handleContentMessage(shim, message); + }; + + return shim; + } + + private async createShimFromContent ( + contentPort: browser.runtime.Port): Promise { + + /** + * If there's already an active shim for the sender + * tab/frame ID, disconnect it. + */ + for (const activeShim of this.activeShims) { + if (activeShim.contentTabId === contentPort.sender.tab.id + && activeShim.contentFrameId === contentPort.sender.frameId) { + activeShim.bridgePort.disconnect(); + } + } + + const shim: Shim = { + bridgePort: await bridge.connect() + , contentPort + , contentTabId: contentPort.sender.tab.id + , contentFrameId: contentPort.sender.frameId + }; + + + const onContentPortMessage = (message: Message) => { + this.handleContentMessage(shim, message); + }; + + const onBridgePortMessage = (message: Message) => { + contentPort.postMessage(message); + }; + + const onDisconnect = () => { + shim.bridgePort.onMessage.removeListener(onBridgePortMessage); + contentPort.onMessage.removeListener(onContentPortMessage); + + shim.bridgePort.disconnect(); + contentPort.disconnect(); + + this.activeShims.delete(shim); + }; + + + shim.bridgePort.onDisconnect.addListener(onDisconnect); + shim.bridgePort.onMessage.addListener(onBridgePortMessage); + + contentPort.onDisconnect.addListener(onDisconnect); + contentPort.onMessage.addListener(onContentPortMessage); + + return shim; + } + + private async handleContentMessage (shim: Shim, message: Message) { + const [ destination ] = message.subject.split(":/"); + if (destination === "bridge") { + shim.bridgePort.postMessage(message); + } + + switch (message.subject) { + case "main:/shimInitialized": { + for (const receiver of StatusManager.getReceivers()) { + shim.contentPort.postMessage({ + subject: "shim:/serviceUp" + , data: { id: receiver.id } + }); + } + + break; + } + + case "main:/selectReceiverBegin": { + const allMediaTypes = + ReceiverSelectorMediaType.App + | ReceiverSelectorMediaType.Tab + | ReceiverSelectorMediaType.Screen + | ReceiverSelectorMediaType.File; + + try { + const selection = await ReceiverSelectorManager + .getSelection( + ReceiverSelectorMediaType.App + , allMediaTypes); + + // Handle cancellation + if (!selection) { + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverCancelled" + }); + + break; + } + + /** + * If the media type returned from the selector has been + * changed, we need to cancel the current sender and switch + * it out for the right one. + */ + if (selection.mediaType !== ReceiverSelectorMediaType.App) { + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverCancelled" + }); + + loadSender({ + tabId: shim.contentTabId + , frameId: shim.contentFrameId + , selection + }); + + break; + } + + // Pass selection back to shim + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverEnd" + , data: selection + }); + + } catch (err) { + // TODO: Report errors properly + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverCancelled" + }); + } + + break; + } + + /** + * TODO: If we're closing a selector, make sure it's the + * same one that caused the session creation. + */ + case "main:/sessionCreated": { + const selector = await ReceiverSelectorManager.getSelector(); + const shouldClose = await options.get( + "receiverSelectorWaitForConnection"); + + if (selector.isOpen && shouldClose) { + selector.close(); + } + + break; + } + } + } + + private async initStatusListeners () { + StatusManager.addEventListener("serviceUp", ev => { + for (const shim of this.activeShims) { + shim.contentPort.postMessage({ + subject: "shim:/serviceUp" + , data: { id: ev.detail.id } + }); + } + }); + + StatusManager.addEventListener("serviceDown", ev => { + for (const shim of this.activeShims) { + shim.contentPort.postMessage({ + subject: "shim:/serviceDown" + , data: { id: ev.detail.id } + }); + } + }); + } +}; diff --git a/ext/src/StatusManager.ts b/ext/src/background/StatusManager.ts similarity index 83% rename from ext/src/StatusManager.ts rename to ext/src/background/StatusManager.ts index d95d8b8..9f816c3 100644 --- a/ext/src/StatusManager.ts +++ b/ext/src/background/StatusManager.ts @@ -1,10 +1,10 @@ "use strict"; -import bridge from "./lib/bridge"; -import options from "./lib/options"; +import bridge from "../lib/bridge"; +import options from "../lib/options"; -import { TypedEventTarget } from "./lib/typedEvents"; -import { Message, Receiver, ReceiverStatus } from "./types"; +import { TypedEventTarget } from "../lib/typedEvents"; +import { Message, Receiver, ReceiverStatus } from "../types"; interface ReceiverStatusMessage extends Message { @@ -36,7 +36,9 @@ interface EventMap { } // tslint:disable-next-line:new-parens -export default new class extends TypedEventTarget { +export default new class StatusManager + extends TypedEventTarget { + private bridgePort: browser.runtime.Port; private receivers = new Map(); @@ -46,25 +48,31 @@ export default new class extends TypedEventTarget { // Bind listeners this.onBridgePortMessage = this.onBridgePortMessage.bind(this); this.onBridgePortDisconnect = this.onBridgePortDisconnect.bind(this); + } - this.initBridgePort(); + public async init () { + if (!this.bridgePort) { + await this.createBridgePort(); + } } public getReceivers () { return Array.from(this.receivers.values()); } - private async initBridgePort () { - this.bridgePort = await bridge.connect(); - this.bridgePort.onMessage.addListener(this.onBridgePortMessage); - this.bridgePort.onDisconnect.addListener(this.onBridgePortDisconnect); + private async createBridgePort () { + const bridgePort = await bridge.connect(); + bridgePort.onMessage.addListener(this.onBridgePortMessage); + bridgePort.onDisconnect.addListener(this.onBridgePortDisconnect); - this.bridgePort.postMessage({ + bridgePort.postMessage({ subject: "bridge:/initialize" , data: { shouldWatchStatus: true } }); + + return bridgePort; } /** @@ -148,7 +156,7 @@ export default new class extends TypedEventTarget { this.bridgePort = null; window.setTimeout(async () => { - this.initBridgePort(); + this.bridgePort = await this.createBridgePort(); }, 10000); } }; diff --git a/ext/src/createMenus.ts b/ext/src/background/createMenus.ts similarity index 98% rename from ext/src/createMenus.ts rename to ext/src/background/createMenus.ts index 6bae74d..6d952bf 100644 --- a/ext/src/createMenus.ts +++ b/ext/src/background/createMenus.ts @@ -1,7 +1,7 @@ "use strict"; -import options from "./lib/options"; -import { TypedEventTarget } from "./lib/typedEvents"; +import options from "../lib/options"; +import { TypedEventTarget } from "../lib/typedEvents"; const _ = browser.i18n.getMessage; diff --git a/ext/src/receiver_selectors/NativeMacReceiverSelector.ts b/ext/src/background/receiverSelector/NativeReceiverSelector.ts similarity index 94% rename from ext/src/receiver_selectors/NativeMacReceiverSelector.ts rename to ext/src/background/receiverSelector/NativeReceiverSelector.ts index 34b46ba..90a17e2 100644 --- a/ext/src/receiver_selectors/NativeMacReceiverSelector.ts +++ b/ext/src/background/receiverSelector/NativeReceiverSelector.ts @@ -1,17 +1,17 @@ "use strict"; -import bridge from "../lib/bridge"; -import options from "../lib/options"; +import bridge from "../../lib/bridge"; +import options from "../../lib/options"; + +import { TypedEventTarget } from "../../lib/typedEvents"; +import { getWindowCenteredProps } from "../../lib/utils"; +import { Message, Receiver } from "../../types"; import ReceiverSelector, { ReceiverSelection , ReceiverSelectorEvents , ReceiverSelectorMediaType } from "./ReceiverSelector"; -import { TypedEventTarget } from "../lib/typedEvents"; -import { getWindowCenteredProps } from "../lib/utils"; -import { Message, Receiver } from "../types"; - const _ = browser.i18n.getMessage; @@ -33,7 +33,7 @@ interface NativeReceiverSelectorErrorMessage extends Message { // TODO: Figure out lifetime properly -export default class NativeMacReceiverSelector +export default class NativeReceiverSelector extends TypedEventTarget implements ReceiverSelector { diff --git a/ext/src/receiver_selectors/PopupReceiverSelector.ts b/ext/src/background/receiverSelector/PopupReceiverSelector.ts similarity index 96% rename from ext/src/receiver_selectors/PopupReceiverSelector.ts rename to ext/src/background/receiverSelector/PopupReceiverSelector.ts index 8b110e9..b7a3590 100644 --- a/ext/src/receiver_selectors/PopupReceiverSelector.ts +++ b/ext/src/background/receiverSelector/PopupReceiverSelector.ts @@ -4,11 +4,11 @@ import ReceiverSelector, { ReceiverSelectorEvents , ReceiverSelectorMediaType } from "./ReceiverSelector"; -import options from "../lib/options"; +import options from "../../lib/options"; -import { TypedEventTarget } from "../lib/typedEvents"; -import { getWindowCenteredProps } from "../lib/utils"; -import { Message, Receiver } from "../types"; +import { TypedEventTarget } from "../../lib/typedEvents"; +import { getWindowCenteredProps } from "../../lib/utils"; +import { Message, Receiver } from "../../types"; export default class PopupReceiverSelector diff --git a/ext/src/receiver_selectors/ReceiverSelector.ts b/ext/src/background/receiverSelector/ReceiverSelector.ts similarity index 87% rename from ext/src/receiver_selectors/ReceiverSelector.ts rename to ext/src/background/receiverSelector/ReceiverSelector.ts index 99b839c..856b353 100644 --- a/ext/src/receiver_selectors/ReceiverSelector.ts +++ b/ext/src/background/receiverSelector/ReceiverSelector.ts @@ -1,7 +1,7 @@ "use strict"; -import { TypedEventTarget } from "../lib/typedEvents"; -import { Receiver } from "../types"; +import { TypedEventTarget } from "../../lib/typedEvents"; +import { Receiver } from "../../types"; export enum ReceiverSelectorMediaType { diff --git a/ext/src/SelectorManager.ts b/ext/src/background/receiverSelector/ReceiverSelectorManager.ts similarity index 60% rename from ext/src/SelectorManager.ts rename to ext/src/background/receiverSelector/ReceiverSelectorManager.ts index 32eb4d1..c88ff8f 100644 --- a/ext/src/SelectorManager.ts +++ b/ext/src/background/receiverSelector/ReceiverSelectorManager.ts @@ -1,31 +1,43 @@ "use strict"; -import options from "./lib/options"; +import options from "../../lib/options"; -import { getReceiverSelector - , ReceiverSelection - , ReceiverSelector - , ReceiverSelectorMediaType - , ReceiverSelectorType } from "./receiver_selectors"; +import StatusManager from "../StatusManager"; -import StatusManager from "./StatusManager"; +import { ReceiverSelector + , ReceiverSelectorType } from "./"; +import { ReceiverSelection + , ReceiverSelectorMediaType } from "./ReceiverSelector"; + +import NativeReceiverSelector from "./NativeReceiverSelector"; +import PopupReceiverSelector from "./PopupReceiverSelector"; + + +async function createSelector () { + const type = await options.get("receiverSelectorType"); + + switch (type) { + case ReceiverSelectorType.Native: { + return new NativeReceiverSelector(); + } + case ReceiverSelectorType.Popup: { + return new PopupReceiverSelector(); + } + } +} let sharedSelector: ReceiverSelector; async function getSelector () { - return getReceiverSelector( - await options.get("receiverSelectorType")); -} - -async function getSharedSelector () { if (!sharedSelector) { - sharedSelector = await getSelector(); + sharedSelector = await createSelector(); } return sharedSelector; } + /** * Opens a receiver selector with the specified * default/available media types. @@ -46,16 +58,14 @@ async function getSelection ( : Promise { return new Promise(async (resolve, reject) => { - /** - * Close any existing selector, and renew to minimize issues - * with bridge failing. - */ - await getSharedSelector(); - if (sharedSelector.isOpen) { + + // Close an existing open selector + if (sharedSelector && sharedSelector.isOpen) { sharedSelector.close(); } - sharedSelector = await getSelector(); + // Get a new selector for each selection + sharedSelector = await createSelector(); sharedSelector.addEventListener("selected", ev => { console.info("fx_cast (Debug): Selected receiver", ev.detail); @@ -72,6 +82,10 @@ async function getSelection ( reject(); }); + + // Ensure status manager is initialized + await StatusManager.init(); + sharedSelector.open( StatusManager.getReceivers() , defaultMediaType @@ -81,5 +95,5 @@ async function getSelection ( export default { getSelection - , getSharedSelector + , getSelector }; diff --git a/ext/src/background/receiverSelector/index.ts b/ext/src/background/receiverSelector/index.ts new file mode 100644 index 0000000..1789fc5 --- /dev/null +++ b/ext/src/background/receiverSelector/index.ts @@ -0,0 +1,17 @@ +"use strict"; + +import NativeReceiverSelector from "./NativeReceiverSelector"; +import PopupReceiverSelector from "./PopupReceiverSelector"; + + +export type ReceiverSelector = + NativeReceiverSelector + | PopupReceiverSelector; + +export enum ReceiverSelectorType { + Popup + , Native +} + +export { ReceiverSelection + , ReceiverSelectorMediaType } from "./ReceiverSelector"; diff --git a/ext/src/createShim.ts b/ext/src/createShim.ts deleted file mode 100644 index d5a9db7..0000000 --- a/ext/src/createShim.ts +++ /dev/null @@ -1,230 +0,0 @@ -"use strict"; - -import bridge from "./lib/bridge"; -import loadSender from "./lib/loadSender"; -import options from "./lib/options"; - -import { TypedEventTarget } from "./lib/typedEvents"; -import { Message } from "./types"; - -import { ReceiverSelectorMediaType } from "./receiver_selectors"; - -import SelectorManager from "./SelectorManager"; -import StatusManager from "./StatusManager"; - - -type Port = browser.runtime.Port | MessagePort; - -export interface Shim { - bridgePort: browser.runtime.Port; - contentPort: Port; - contentTabId?: number; - contentFrameId?: number; -} - - -const activeShims = new Set(); - -StatusManager.addEventListener("serviceUp", ev => { - for (const shim of activeShims) { - shim.contentPort.postMessage({ - subject: "shim:/serviceUp" - , data: { id: ev.detail.id } - }); - } -}); - -StatusManager.addEventListener("serviceDown", ev => { - for (const shim of activeShims) { - shim.contentPort.postMessage({ - subject: "shim:/serviceDown" - , data: { id: ev.detail.id } - }); - } -}); - - -async function createShim (port: Port): Promise { - const shim = await (port instanceof MessagePort - ? createShimFromBackground(port) - : createShimFromContent(port)); - - shim.contentPort.postMessage({ - subject: "shim:/initialized" - , data: await bridge.getInfo() - }); - - activeShims.add(shim); -} - - -async function createShimFromBackground ( - contentPort: MessagePort): Promise { - - const shim: Shim = { - bridgePort: await bridge.connect() - , contentPort - }; - - shim.bridgePort.onDisconnect.addListener(() => { - contentPort.close(); - activeShims.delete(shim); - }); - - shim.bridgePort.onMessage.addListener((message: Message) => { - contentPort.postMessage(message); - }); - - contentPort.onmessage = ev => { - const message = ev.data as Message; - handleContentMessage(shim, message); - }; - - return shim; -} - - -async function createShimFromContent ( - contentPort: browser.runtime.Port): Promise { - - /** - * If there's already an active shim for the sender - * tab/frame ID, disconnect it. - */ - for (const activeShim of activeShims) { - if (activeShim.contentTabId === contentPort.sender.tab.id - && activeShim.contentFrameId === contentPort.sender.frameId) { - activeShim.bridgePort.disconnect(); - } - } - - const shim: Shim = { - bridgePort: await bridge.connect() - , contentPort - , contentTabId: contentPort.sender.tab.id - , contentFrameId: contentPort.sender.frameId - }; - - function onContentPortMessage (message: Message) { - handleContentMessage(shim, message); - } - function onBridgePortMessage (message: Message) { - contentPort.postMessage(message); - } - - function onDisconnect () { - shim.bridgePort.onMessage.removeListener(onBridgePortMessage); - contentPort.onMessage.removeListener(onContentPortMessage); - - shim.bridgePort.disconnect(); - contentPort.disconnect(); - - activeShims.delete(shim); - } - - - shim.bridgePort.onDisconnect.addListener(onDisconnect); - shim.bridgePort.onMessage.addListener(onBridgePortMessage); - - contentPort.onDisconnect.addListener(onDisconnect); - contentPort.onMessage.addListener(onContentPortMessage); - - - return shim; -} - - -async function handleContentMessage (shim: Shim, message: Message) { - const [ destination ] = message.subject.split(":/"); - if (destination === "bridge") { - shim.bridgePort.postMessage(message); - } - - switch (message.subject) { - case "main:/shimInitialized": { - for (const receiver of StatusManager.getReceivers()) { - shim.contentPort.postMessage({ - subject: "shim:/serviceUp" - , data: { id: receiver.id } - }); - } - - break; - } - - case "main:/selectReceiverBegin": { - const allMediaTypes = - ReceiverSelectorMediaType.App - | ReceiverSelectorMediaType.Tab - | ReceiverSelectorMediaType.Screen - | ReceiverSelectorMediaType.File; - - try { - const selection = await SelectorManager.getSelection( - ReceiverSelectorMediaType.App - , allMediaTypes); - - // Handle cancellation - if (!selection) { - shim.contentPort.postMessage({ - subject: "shim:/selectReceiverCancelled" - }); - - break; - } - - /** - * If the media type returned from the selector has been - * changed, we need to cancel the current sender and switch - * it out for the right one. - */ - if (selection.mediaType !== ReceiverSelectorMediaType.App) { - shim.contentPort.postMessage({ - subject: "shim:/selectReceiverCancelled" - }); - - loadSender({ - tabId: shim.contentTabId - , frameId: shim.contentFrameId - , selection - }); - - break; - } - - // Pass selection back to shim - shim.contentPort.postMessage({ - subject: "shim:/selectReceiverEnd" - , data: selection - }); - - } catch (err) { - // TODO: Report errors properly - shim.contentPort.postMessage({ - subject: "shim:/selectReceiverCancelled" - }); - } - - break; - } - - /** - * TODO: If we're closing a selector, make sure it's the - * same one that caused the session creation. - */ - case "main:/sessionCreated": { - const selector = await SelectorManager.getSharedSelector(); - - const shouldClose = await options.get( - "receiverSelectorWaitForConnection"); - - if (selector.isOpen && shouldClose) { - selector.close(); - } - - break; - } - } -} - -export default createShim; diff --git a/ext/src/defaultOptions.ts b/ext/src/defaultOptions.ts index 1bfd55d..a0bea00 100644 --- a/ext/src/defaultOptions.ts +++ b/ext/src/defaultOptions.ts @@ -1,7 +1,7 @@ "use strict"; +import { ReceiverSelectorType } from "./background/receiverSelector"; import { Options } from "./lib/options"; -import { ReceiverSelectorType } from "./receiver_selectors"; export default { diff --git a/ext/src/lib/loadSender.ts b/ext/src/lib/loadSender.ts index 6b5a469..b93e33c 100644 --- a/ext/src/lib/loadSender.ts +++ b/ext/src/lib/loadSender.ts @@ -3,7 +3,7 @@ import { stringify } from "./utils"; import { ReceiverSelection - , ReceiverSelectorMediaType } from "../receiver_selectors"; + , ReceiverSelectorMediaType } from "../background/receiverSelector"; interface LoadSenderOptions { diff --git a/ext/src/lib/nativeMessaging.ts b/ext/src/lib/nativeMessaging.ts index 7c15910..33ef4f7 100644 --- a/ext/src/lib/nativeMessaging.ts +++ b/ext/src/lib/nativeMessaging.ts @@ -6,7 +6,7 @@ import { Message } from "../types"; const WEBSOCKET_DAEMON_URL = "ws://localhost:9556"; -type DisconnectListener = (port: browser.runtime.Port) => void;; +type DisconnectListener = (port: browser.runtime.Port) => void; type MessageListener = (message: any) => void; function connectNative (application: string) { diff --git a/ext/src/lib/options.ts b/ext/src/lib/options.ts index a640f47..aea83d4 100644 --- a/ext/src/lib/options.ts +++ b/ext/src/lib/options.ts @@ -2,7 +2,7 @@ import defaultOptions from "../defaultOptions"; -import { ReceiverSelectorType } from "../receiver_selectors/"; +import { ReceiverSelectorType } from "../background/receiverSelector"; import { Message } from "../types"; import { TypedEventTarget } from "./typedEvents"; @@ -16,13 +16,13 @@ export interface Options { localMediaServerPort: number; mirroringEnabled: boolean; mirroringAppId: string; - receiverSelectorType: ReceiverSelectorType.Popup; + receiverSelectorType: ReceiverSelectorType; receiverSelectorCloseIfFocusLost: boolean; receiverSelectorWaitForConnection: boolean; userAgentWhitelistEnabled: boolean; userAgentWhitelist: string[]; - [key: string]: Options[keyof Options] + [key: string]: Options[keyof Options]; } @@ -49,28 +49,30 @@ export default new class extends TypedEventTarget { const { oldValue, newValue } = _changes.options; const changedKeys = []; - for (const key in newValue) { - // Don't track added keys - if (!(key in oldValue)) { - continue; - } - - const oldKeyValue = oldValue[key]; - const newKeyValue = newValue[key]; - - // Equality comparison - if (oldKeyValue === newKeyValue) { - continue; - } - - // Array comparison - if (oldKeyValue instanceof Array - && newKeyValue instanceof Array) { - if (oldKeyValue.length === newKeyValue.length - && oldKeyValue.every((value, index) => - value === newKeyValue[index])) { + for (const key of Object.keys(newValue)) { + if (oldValue) { + // Don't track added keys + if (!(key in oldValue)) { continue; } + + const oldKeyValue = oldValue[key]; + const newKeyValue = newValue[key]; + + // Equality comparison + if (oldKeyValue === newKeyValue) { + continue; + } + + // Array comparison + if (oldKeyValue instanceof Array + && newKeyValue instanceof Array) { + if (oldKeyValue.length === newKeyValue.length + && oldKeyValue.every((value, index) => + value === newKeyValue[index])) { + continue; + } + } } changedKeys.push(key); diff --git a/ext/src/main.ts b/ext/src/main.ts index 3133501..ac82491 100755 --- a/ext/src/main.ts +++ b/ext/src/main.ts @@ -8,20 +8,19 @@ import options, { Options } from "./lib/options"; import { getChromeUserAgent } from "./lib/userAgents"; import { stringify } from "./lib/utils"; -import { ReceiverSelection - , ReceiverSelectorMediaType } from "./receiver_selectors"; - import { Message } from "./types"; import { CAST_FRAMEWORK_LOADER_SCRIPT_URL , CAST_LOADER_SCRIPT_URL } from "./lib/endpoints"; +import { ReceiverSelection + , ReceiverSelectorMediaType } from "./background/receiverSelector"; -import SelectorManager from "./SelectorManager"; -import StatusManager from "./StatusManager"; +import ReceiverSelectorManager + from "./background/receiverSelector/ReceiverSelectorManager"; -import createMenus from "./createMenus"; -import createShim, { Shim } from "./createShim"; +import createMenus from "./background/createMenus"; +import ShimManager from "./background/ShimManager"; const _ = browser.i18n.getMessage; @@ -48,33 +47,6 @@ browser.runtime.onInstalled.addListener(async details => { }); -/** - * When a message port connection with the name "shim" is - * established, pass it to createShim to handle the setup - * and maintenance. - */ -browser.runtime.onConnect.addListener(async port => { - if (port.name === "shim") { - createShim(port); - } -}); - -/** - * When the browser action is clicked, open a receiver - * selector and load a sender for the response. The - * mirroring sender is loaded into the current tab at the - * top-level frame. - */ -browser.browserAction.onClicked.addListener(async tab => { - const selection = await SelectorManager.getSelection(); - - loadSender({ - tabId: tab.id - , frameId: 0 - , selection - }); -}); - async function initMenus () { console.info("fx_cast (Debug): init (menus)"); @@ -93,7 +65,7 @@ async function initMenus () { | ReceiverSelectorMediaType.Screen | ReceiverSelectorMediaType.File; - const selection = await SelectorManager.getSelection( + const selection = await ReceiverSelectorManager.getSelection( ReceiverSelectorMediaType.App , allMediaTypes); @@ -134,7 +106,7 @@ async function initMenus () { } case menuIdMirroringCast: { - const selection = await SelectorManager.getSelection(); + const selection = await ReceiverSelectorManager.getSelection(); loadSender({ tabId: tab.id @@ -268,8 +240,13 @@ function initWhitelist () { } +let isInitialized = false; async function init () { + if (isInitialized) { + return; + } + /** * If options haven't been set yet, we can't properly * initialize, so wait until init is called again in the @@ -281,10 +258,42 @@ async function init () { console.info("fx_cast (Debug): init"); + isInitialized = true; - initMenus(); - initRequestListener(); - initWhitelist(); + + await initMenus(); + await initRequestListener(); + await initWhitelist(); + + await ShimManager.init(); + + + /** + * When a message port connection with the name "shim" is + * established, pass it to createShim to handle the setup + * and maintenance. + */ + browser.runtime.onConnect.addListener(async port => { + if (port.name === "shim") { + ShimManager.createShim(port); + } + }); + + /** + * When the browser action is clicked, open a receiver + * selector and load a sender for the response. The + * mirroring sender is loaded into the current tab at the + * top-level frame. + */ + browser.browserAction.onClicked.addListener(async tab => { + const selection = await ReceiverSelectorManager.getSelection(); + + loadSender({ + tabId: tab.id + , frameId: 0 + , selection + }); + }); } init(); diff --git a/ext/src/receiver_selectors/index.ts b/ext/src/receiver_selectors/index.ts deleted file mode 100644 index 55fa8cb..0000000 --- a/ext/src/receiver_selectors/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; - -import NativeMacReceiverSelector - from "./NativeMacReceiverSelector"; -import PopupReceiverSelector - from "./PopupReceiverSelector"; - - -import { ReceiverSelection - , ReceiverSelectorMediaType } from "./ReceiverSelector"; - -type ReceiverSelector = ReturnType; - -export { - ReceiverSelector - , ReceiverSelection - , ReceiverSelectorMediaType -}; - -export enum ReceiverSelectorType { - Popup - , NativeMac -} - -export function getReceiverSelector (type: ReceiverSelectorType) { - switch (type) { - case ReceiverSelectorType.Popup: { - return new PopupReceiverSelector(); - } - case ReceiverSelectorType.NativeMac: { - return new NativeMacReceiverSelector(); - } - } -} diff --git a/ext/src/senders/mirroringCast.ts b/ext/src/senders/mirroringCast.ts index 0fbd7c5..a581d98 100644 --- a/ext/src/senders/mirroringCast.ts +++ b/ext/src/senders/mirroringCast.ts @@ -3,9 +3,7 @@ import options from "../lib/options"; import cast, { ensureInit } from "../shim/export"; -import { ReceiverSelectorMediaType } - from "../receiver_selectors/ReceiverSelector"; - +import { ReceiverSelectorMediaType } from "../background/receiverSelector"; import { Receiver } from "../types"; diff --git a/ext/src/shim/cast/index.ts b/ext/src/shim/cast/index.ts index f2aa891..1197347 100755 --- a/ext/src/shim/cast/index.ts +++ b/ext/src/shim/cast/index.ts @@ -27,8 +27,7 @@ import { AutoJoinPolicy import * as media from "./media"; -import { ReceiverSelectorMediaType } - from "../../receiver_selectors/ReceiverSelector"; +import { ReceiverSelectorMediaType } from "../../background/receiverSelector"; import { Receiver } from "../../types"; import { onMessage, sendMessageResponse } from "../eventMessageChannel"; diff --git a/ext/src/shim/export.ts b/ext/src/shim/export.ts index 7b4f7ac..76a421d 100644 --- a/ext/src/shim/export.ts +++ b/ext/src/shim/export.ts @@ -5,7 +5,11 @@ import * as cast from "./cast"; import { BridgeInfo } from "../lib/bridge"; import { Message } from "../types"; -import { onMessage, onMessageResponse, sendMessage } from "./eventMessageChannel"; +import { onMessage + , onMessageResponse + , sendMessage } from "./eventMessageChannel"; + +import ShimManager from "../background/ShimManager"; let initializedBridgeInfo: BridgeInfo; @@ -42,10 +46,9 @@ export function ensureInit (): Promise { * URL. */ if (window.location.protocol === "moz-extension:") { - const { default: createShim } = await import("../createShim"); - // port2 will post bridge messages to port 1 - await createShim(channel.port2); + await ShimManager.init(); + await ShimManager.createShim(channel.port2); // bridge -> shim channel.port1.onmessage = ev => { diff --git a/ext/src/ui/options/index.tsx b/ext/src/ui/options/index.tsx index 9a95bdb..7dc3c5e 100644 --- a/ext/src/ui/options/index.tsx +++ b/ext/src/ui/options/index.tsx @@ -13,7 +13,7 @@ import bridge, { BridgeInfo } from "../../lib/bridge"; import options, { Options } from "../../lib/options"; import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils"; -import { ReceiverSelectorType } from "../../receiver_selectors"; +import { ReceiverSelectorType } from "../../background/receiverSelector"; const _ = browser.i18n.getMessage; @@ -250,7 +250,7 @@ class OptionsApp extends Component<{}, OptionsAppState> { - diff --git a/ext/src/ui/popup/index.tsx b/ext/src/ui/popup/index.tsx index 7251366..3340a41 100755 --- a/ext/src/ui/popup/index.tsx +++ b/ext/src/ui/popup/index.tsx @@ -7,8 +7,7 @@ import ReactDOM from "react-dom"; import { getNextEllipsis } from "../../lib/utils"; import { Message, Receiver } from "../../types"; -import { ReceiverSelectorMediaType } - from "../../receiver_selectors/ReceiverSelector"; +import { ReceiverSelectorMediaType } from "../../background/receiverSelector"; const _ = browser.i18n.getMessage;