More receiver selector refactoring

This commit is contained in:
hensm
2022-04-29 01:14:06 +01:00
parent 70ac18511a
commit b1100bd258
15 changed files with 123 additions and 145 deletions

View File

@@ -1,35 +1,45 @@
"use strict";
import logger from "../../lib/logger";
import messaging, { Port, Message } from "../../messaging";
import options from "../../lib/options";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { ReceiverDevice } from "../../types";
import { SessionRequest } from "../../cast/sdk/classes";
import logger from "../lib/logger";
import messaging, { Port, Message } from "../messaging";
import options from "../lib/options";
import { TypedEventTarget } from "../lib/TypedEventTarget";
import { SessionRequest } from "../cast/sdk/classes";
import {
ReceiverSelectionCast,
ReceiverSelectionStop,
ReceiverDevice,
ReceiverSelectionActionType,
ReceiverSelectorMediaType
} from "./index";
} from "../types";
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
export interface PageInfo {
/** Info about sender page context. */
export interface ReceiverSelectorPageInfo {
url: string;
tabId: number;
frameId: number;
sessionRequest?: SessionRequest;
}
export interface ReceiverSelectionCast {
actionType: ReceiverSelectionActionType.Cast;
receiverDevice: ReceiverDevice;
mediaType: ReceiverSelectorMediaType;
filePath?: string;
}
export interface ReceiverSelectionStop {
actionType: ReceiverSelectionActionType.Stop;
receiverDevice: ReceiverDevice;
}
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
interface ReceiverSelectorEvents {
selected: ReceiverSelectionCast;
error: string;
cancelled: void;
stop: ReceiverSelectionStop;
}
/**
* Manages the receiver selector popup window and communication with the
* extension page hosted within.
@@ -38,7 +48,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
/** Popup window ID. */
private windowId?: number;
/** Message port to extension page within popup window. */
/** Message port to extension page. */
private messagePort?: Port;
private messagePortDisconnected?: boolean;
@@ -50,9 +60,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
private wasReceiverSelected = false;
private appId?: string;
private pageInfo?: PageInfo;
#isOpen = false;
private pageInfo?: ReceiverSelectorPageInfo;
constructor() {
super();
@@ -63,6 +71,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
this.onWindowsFocusChanged = this.onWindowsFocusChanged.bind(this);
browser.windows.onRemoved.addListener(this.onWindowsRemoved);
browser.windows.onFocusChanged.addListener(this.onWindowsFocusChanged);
/**
* Handle incoming message channel connection from popup
@@ -73,7 +82,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
/** Is receiver selector window currently open. */
get isOpen() {
return this.#isOpen;
return this.windowId !== undefined;
}
/**
@@ -84,7 +93,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
defaultMediaType: ReceiverSelectorMediaType;
availableMediaTypes: ReceiverSelectorMediaType;
appId?: string;
pageInfo?: PageInfo;
pageInfo?: ReceiverSelectorPageInfo;
}) {
this.appId = opts.appId;
this.pageInfo = opts.pageInfo;
@@ -139,15 +148,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
...popupSizePosition
});
this.#isOpen = true;
this.windowId = popup.id;
// Add focus listener
if (await options.get("receiverSelectorCloseIfFocusLost")) {
browser.windows.onFocusChanged.addListener(
this.onWindowsFocusChanged
);
}
}
/** Updates receiver devices displayed in the receiver selector. */
@@ -258,8 +259,6 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
return;
}
this.#isOpen = false;
browser.windows.onRemoved.removeListener(this.onWindowsRemoved);
browser.windows.onFocusChanged.removeListener(
this.onWindowsFocusChanged
@@ -279,21 +278,18 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
}
/**
* Closes popup window if another browser window is brought
* into focus. Doesn't apply if no window is focused
* `WINDOW_ID_NONE` or if the popup window is re-focused.
* Closes popup window if another browser window is brought into
* focus. Doesn't apply if no window is focused `WINDOW_ID_NONE`
* or if the popup window is re-focused.
*/
private onWindowsFocusChanged(windowId: number) {
private async onWindowsFocusChanged(windowId: number) {
if (!this.windowId) return;
if (
windowId !== browser.windows.WINDOW_ID_NONE &&
windowId !== this.windowId
) {
// Only run once
browser.windows.onFocusChanged.removeListener(
this.onWindowsFocusChanged
);
if (this.windowId) {
if (await options.get("receiverSelectorCloseIfFocusLost")) {
browser.windows.remove(this.windowId);
}
}

View File

@@ -5,9 +5,9 @@ import logger from "../lib/logger";
import options from "../lib/options";
import bridge, { BridgeInfo } from "../lib/bridge";
import CastManager from "../cast/CastManager";
import receiverDevices from "./receiverDevices";
import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager";
import castManager from "./castManager";
import deviceManager from "./deviceManager";
import selectorManager from "./selectorManager";
import { initMenus } from "./menus";
import { initWhitelist } from "./whitelist";
@@ -101,8 +101,8 @@ async function init() {
await notifyBridgeCompat();
await receiverDevices.init();
await CastManager.init();
await deviceManager.init();
await castManager.init();
await initMenus();
await initWhitelist();
@@ -119,9 +119,9 @@ async function init() {
return;
}
const selection = await ReceiverSelectorManager.getSelection(tab.id);
const selection = await selectorManager.getSelection(tab.id);
if (selection) {
CastManager.loadSender({
castManager.loadSender({
tabId: tab.id,
frameId: 0,
selection

View File

@@ -7,13 +7,14 @@ import options from "../lib/options";
import { stringify } from "../lib/utils";
import {
ReceiverSelection,
ReceiverSelectionActionType,
ReceiverSelectorMediaType
} from "../background/receiverSelector";
} from "../types";
import ReceiverSelectorManager from "../background/receiverSelector/ReceiverSelectorManager";
import receiverDevices from "../background/receiverDevices";
import { ReceiverSelection } from "./ReceiverSelector";
import deviceManager from "./deviceManager";
import selectorManager from "./selectorManager";
type AnyPort = Port | MessagePort;
@@ -26,7 +27,7 @@ export interface CastInstance {
}
/** Keeps track of cast API instances and provides bridge messaging. */
export default new (class CastManager {
export default new (class {
private activeInstances = new Set<CastInstance>();
public async init() {
@@ -38,7 +39,7 @@ export default new (class CastManager {
});
// Forward receiver eventes to cast instances
receiverDevices.addEventListener("receiverDeviceUp", ev => {
deviceManager.addEventListener("receiverDeviceUp", ev => {
for (const instance of this.activeInstances) {
instance.contentPort.postMessage({
subject: "cast:receiverDeviceUp",
@@ -46,7 +47,7 @@ export default new (class CastManager {
});
}
});
receiverDevices.addEventListener("receiverDeviceDown", ev => {
deviceManager.addEventListener("receiverDeviceDown", ev => {
for (const instance of this.activeInstances) {
instance.contentPort.postMessage({
subject: "cast:receiverDeviceDown",
@@ -202,7 +203,7 @@ export default new (class CastManager {
case "main:initializeCast": {
instance.appId = message.data.appId;
for (const receiverDevice of receiverDevices.getDevices()) {
for (const receiverDevice of deviceManager.getDevices()) {
instance.contentPort.postMessage({
subject: "cast:receiverDeviceUp",
data: { receiverDevice }
@@ -224,12 +225,11 @@ export default new (class CastManager {
}
try {
const selection =
await ReceiverSelectorManager.getSelection(
instance.contentTabId,
instance.contentFrameId,
{ sessionRequest: message.data.sessionRequest }
);
const selection = await selectorManager.getSelection(
instance.contentTabId,
instance.contentFrameId,
{ sessionRequest: message.data.sessionRequest }
);
// Handle cancellation
if (!selection) {
@@ -297,7 +297,7 @@ export default new (class CastManager {
* same one that caused the session creation.
*/
case "main:closeReceiverSelector": {
const selector = await ReceiverSelectorManager.getSelector();
const selector = await selectorManager.getSelector();
const shouldClose = await options.get(
"receiverSelectorWaitForConnection"
);
@@ -366,7 +366,7 @@ export default new (class CastManager {
case ReceiverSelectorMediaType.File: {
const fileUrl = new URL(`file://${opts.selection.filePath}`);
const { init } = await import("./senders/media");
const { init } = await import("../cast/senders/media");
init({
mediaUrl: fileUrl.href,

View File

@@ -2,17 +2,17 @@
import logger from "../lib/logger";
import options from "../lib/options";
import { stringify } from "../lib/utils";
import {
ReceiverSelection,
ReceiverSelectionActionType,
ReceiverSelectorMediaType
} from "./receiverSelector";
} from "../types";
import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager";
import CastManager from "../cast/CastManager";
import { ReceiverSelection } from "./ReceiverSelector";
import selectorManager from "./selectorManager";
import castManager from "./castManager";
const _ = browser.i18n.getMessage;
@@ -142,7 +142,7 @@ async function onMenuClicked(
let selection: Nullable<ReceiverSelection> = null;
try {
selection = await ReceiverSelectorManager.getSelection(
selection = await selectorManager.getSelection(
tab.id,
info.frameId,
{ withMediaSender: castMediaMenuClicked }
@@ -160,7 +160,7 @@ async function onMenuClicked(
}
if (castMenuClicked) {
CastManager.loadSender({
castManager.loadSender({
tabId: tab.id,
frameId: info.frameId,
selection
@@ -187,7 +187,7 @@ async function onMenuClicked(
});
} else {
// Handle other responses
CastManager.loadSender({
castManager.loadSender({
tabId: tab.id,
frameId: info.frameId,
selection

View File

@@ -1,29 +0,0 @@
"use strict";
import { ReceiverDevice } from "../../types";
export enum ReceiverSelectorMediaType {
None = 0,
App = 1,
Tab = 2,
Screen = 4,
File = 8
}
export enum ReceiverSelectionActionType {
Cast = 1,
Stop = 2
}
export interface ReceiverSelectionCast {
actionType: ReceiverSelectionActionType.Cast;
receiverDevice: ReceiverDevice;
mediaType: ReceiverSelectorMediaType;
filePath?: string;
}
export interface ReceiverSelectionStop {
actionType: ReceiverSelectionActionType.Stop;
receiverDevice: ReceiverDevice;
}
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;

View File

@@ -1,23 +1,24 @@
"use strict";
import options from "../../lib/options";
import logger from "../../lib/logger";
import options from "../lib/options";
import logger from "../lib/logger";
import CastManager from "../../cast/CastManager";
import { SessionRequest } from "../../cast/sdk/classes";
import receiverDevices from "../receiverDevices";
import { getMediaTypesForPageUrl } from "../lib/utils";
import { SessionRequest } from "../cast/sdk/classes";
import { getMediaTypesForPageUrl } from "../../lib/utils";
import castManager from "./castManager";
import deviceManager from "./deviceManager";
import ReceiverSelector, {
ReceiverSelection,
ReceiverSelectionCast,
ReceiverSelectionStop
} from "./ReceiverSelector";
import {
ReceiverSelection,
ReceiverSelectionActionType,
ReceiverSelectionCast,
ReceiverSelectionStop,
ReceiverSelectorMediaType
} from "./index";
import ReceiverSelector from "./ReceiverSelector";
} from "../types";
let sharedSelector: ReceiverSelector;
async function getSelector() {
@@ -46,12 +47,12 @@ async function getSelection(
contextTabId: number,
contextFrameId = 0,
selectionOpts?: {
sessionRequest: SessionRequest;
sessionRequest?: SessionRequest;
withMediaSender?: boolean;
}
): Promise<ReceiverSelection | null> {
return new Promise(async (resolve, reject) => {
let castInstance = CastManager.getInstance(
let castInstance = castManager.getInstance(
contextTabId,
contextFrameId
);
@@ -112,15 +113,12 @@ async function getSelection(
sharedSelector = new ReceiverSelector();
function onReceiverChange() {
sharedSelector.update(receiverDevices.getDevices());
sharedSelector.update(deviceManager.getDevices());
}
receiverDevices.addEventListener("receiverDeviceUp", onReceiverChange);
receiverDevices.addEventListener(
"receiverDeviceDown",
onReceiverChange
);
receiverDevices.addEventListener(
deviceManager.addEventListener("receiverDeviceUp", onReceiverChange);
deviceManager.addEventListener("receiverDeviceDown", onReceiverChange);
deviceManager.addEventListener(
"receiverDeviceUpdated",
onReceiverChange
);
@@ -139,7 +137,7 @@ async function getSelection(
function onSelectorStop(ev: CustomEvent<ReceiverSelectionStop>) {
logger.info("Stopping receiver app...", ev.detail);
receiverDevices.stopReceiverApp(ev.detail.receiverDevice.id);
deviceManager.stopReceiverApp(ev.detail.receiverDevice.id);
removeListeners();
resolve({
@@ -172,25 +170,25 @@ async function getSelection(
);
sharedSelector.removeEventListener("error", onSelectorError);
receiverDevices.removeEventListener(
deviceManager.removeEventListener(
"receiverDeviceUp",
onReceiverChange
);
receiverDevices.removeEventListener(
deviceManager.removeEventListener(
"receiverDeviceDown",
onReceiverChange
);
receiverDevices.removeEventListener(
deviceManager.removeEventListener(
"receiverDeviceUpdated",
onReceiverChange
);
}
// Ensure status manager is initialized
await receiverDevices.init();
await deviceManager.init();
sharedSelector.open({
receiverDevices: receiverDevices.getDevices(),
receiverDevices: deviceManager.getDevices(),
defaultMediaType,
availableMediaTypes,
appId: castInstance?.appId,

View File

@@ -32,7 +32,7 @@ Reflect.defineProperty(HTMLScriptElement.prototype.wrappedJSObject, "src", {
enumerable: true,
get: desc?.get,
set: exportFunction(function setFunc(this: HTMLScriptElement, value) {
set: exportFunction(function (this: HTMLScriptElement, value: string) {
if (CAST_SCRIPT_URLS.includes(value)) {
return desc?.set?.call(this, CAST_LOADER_SCRIPT_URL);
}

View File

@@ -48,11 +48,13 @@ export function ensureInit(): Promise<TypedMessagePort<Message>> {
* URL.
*/
if (window.location.protocol === "moz-extension:") {
const { default: CastManager } = await import("./CastManager");
const { default: castManager } = await import(
"../background/castManager"
);
// port2 will post bridge messages to port 1
await CastManager.init();
await CastManager.createInstance(channel.port2);
await castManager.init();
await castManager.createInstance(channel.port2);
// bridge -> cast instance
channel.port1.onmessage = ev => {

View File

@@ -2,10 +2,7 @@
import logger from "../../lib/logger";
import {
ReceiverDevice,
ReceiverDeviceCapabilities as ReceiverDeviceCapabilities
} from "../../types";
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../../types";
import { ErrorCallback, SuccessCallback } from "../types";
import eventMessaging from "../eventMessaging";

View File

@@ -3,8 +3,7 @@
import options from "../../lib/options";
import cast, { ensureInit } from "../export";
import { ReceiverSelectorMediaType } from "../../background/receiverSelector";
import { ReceiverDevice } from "../../types";
import { ReceiverDevice, ReceiverSelectorMediaType } from "../../types";
import type Session from "../sdk/Session";

View File

@@ -1,6 +1,6 @@
"use strict";
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
import { ReceiverSelectorMediaType } from "../types";
export function getNextEllipsis(ellipsis: string): string {
if (ellipsis === "") return ".";

View File

@@ -3,7 +3,6 @@
import { TypedPort } from "./lib/TypedPort";
import { BridgeInfo } from "./lib/bridge";
import { ReceiverSelectorMediaType } from "./background/receiverSelector";
import {
ReceiverSelection,
ReceiverSelectionCast,
@@ -19,7 +18,7 @@ import {
} from "./cast/sdk/types";
import { SessionRequest } from "./cast/sdk/classes";
import { ReceiverDevice } from "./types";
import { ReceiverDevice, ReceiverSelectorMediaType } from "./types";
/**
* Messages are JSON objects with a `subject` string key and a

View File

@@ -20,3 +20,15 @@ export interface ReceiverDevice {
port: number;
status?: ReceiverStatus;
}
export enum ReceiverSelectorMediaType {
None = 0,
App = 1,
Tab = 2,
Screen = 4,
File = 8
}
export enum ReceiverSelectionActionType {
Cast = 1,
Stop = 2
}

View File

@@ -11,14 +11,15 @@ import messaging, { Message, Port } from "../../messaging";
import { getNextEllipsis } from "../../lib/utils";
import { RemoteMatchPattern } from "../../lib/matchPattern";
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../../types";
import { Capability } from "../../cast/sdk/enums";
import {
ReceiverDevice,
ReceiverDeviceCapabilities,
ReceiverSelectionActionType,
ReceiverSelectorMediaType
} from "../../background/receiverSelector";
import { PageInfo } from "../../background/receiverSelector/ReceiverSelector";
} from "../../types";
import { ReceiverSelectorPageInfo } from "../../background/ReceiverSelector";
import { Capability } from "../../cast/sdk/enums";
const _ = browser.i18n.getMessage;
@@ -73,7 +74,7 @@ interface PopupAppState {
filePath?: string;
appId?: string;
pageInfo?: PageInfo;
pageInfo?: ReceiverSelectorPageInfo;
mirroringEnabled: boolean;
userAgentWhitelistEnabled: boolean;
@@ -405,7 +406,10 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
);
}
private async onAddToWhitelist(app: KnownApp, pageInfo: PageInfo) {
private async onAddToWhitelist(
app: KnownApp,
pageInfo: ReceiverSelectorPageInfo
) {
if (!app.matches) {
return;
}