mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 01:29:58 +00:00
prettier: Re-format .ts files
This commit is contained in:
@@ -6,15 +6,15 @@ import logger from "../lib/logger";
|
||||
import messaging, { Message, Port } from "../messaging";
|
||||
import options from "../lib/options";
|
||||
|
||||
import { ReceiverSelectionActionType
|
||||
, ReceiverSelectorMediaType } from "./receiverSelector";
|
||||
import {
|
||||
ReceiverSelectionActionType,
|
||||
ReceiverSelectorMediaType
|
||||
} from "./receiverSelector";
|
||||
|
||||
import ReceiverSelectorManager
|
||||
from "./receiverSelector/ReceiverSelectorManager";
|
||||
import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager";
|
||||
|
||||
import receiverDevices from "./receiverDevices";
|
||||
|
||||
|
||||
type AnyPort = Port | MessagePort;
|
||||
|
||||
export interface Shim {
|
||||
@@ -25,8 +25,7 @@ export interface Shim {
|
||||
appId?: string;
|
||||
}
|
||||
|
||||
|
||||
export default new class ShimManager {
|
||||
export default new (class ShimManager {
|
||||
private activeShims = new Set<Shim>();
|
||||
|
||||
public async init() {
|
||||
@@ -40,8 +39,8 @@ export default new class ShimManager {
|
||||
receiverDevices.addEventListener("receiverDeviceUp", ev => {
|
||||
for (const shim of this.activeShims) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:serviceUp"
|
||||
, data: { receiverDevice: ev.detail.receiverDevice }
|
||||
subject: "shim:serviceUp",
|
||||
data: { receiverDevice: ev.detail.receiverDevice }
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -49,8 +48,8 @@ export default new class ShimManager {
|
||||
receiverDevices.addEventListener("receiverDeviceDown", ev => {
|
||||
for (const shim of this.activeShims) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:serviceDown"
|
||||
, data: { receiverDeviceId: ev.detail.receiverDeviceId }
|
||||
subject: "shim:serviceDown",
|
||||
data: { receiverDeviceId: ev.detail.receiverDeviceId }
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -74,19 +73,19 @@ export default new class ShimManager {
|
||||
: this.createShimFromContent(port));
|
||||
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:initialized"
|
||||
, data: await bridge.getInfo()
|
||||
subject: "shim:initialized",
|
||||
data: await bridge.getInfo()
|
||||
});
|
||||
|
||||
this.activeShims.add(shim);
|
||||
}
|
||||
|
||||
private async createShimFromBackground(
|
||||
contentPort: MessagePort): Promise<Shim> {
|
||||
|
||||
contentPort: MessagePort
|
||||
): Promise<Shim> {
|
||||
const shim: Shim = {
|
||||
bridgePort: await bridge.connect()
|
||||
, contentPort
|
||||
bridgePort: await bridge.connect(),
|
||||
contentPort
|
||||
};
|
||||
|
||||
shim.bridgePort.onDisconnect.addListener(() => {
|
||||
@@ -105,12 +104,14 @@ export default new class ShimManager {
|
||||
return shim;
|
||||
}
|
||||
|
||||
private async createShimFromContent(
|
||||
contentPort: Port): Promise<Shim> {
|
||||
|
||||
if (contentPort.sender?.tab?.id === undefined
|
||||
|| contentPort.sender?.frameId === undefined) {
|
||||
throw logger.error("Content shim created with an invalid port context.");
|
||||
private async createShimFromContent(contentPort: Port): Promise<Shim> {
|
||||
if (
|
||||
contentPort.sender?.tab?.id === undefined ||
|
||||
contentPort.sender?.frameId === undefined
|
||||
) {
|
||||
throw logger.error(
|
||||
"Content shim created with an invalid port context."
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,20 +119,21 @@ export default new class ShimManager {
|
||||
* tab/frame ID, disconnect it.
|
||||
*/
|
||||
for (const activeShim of this.activeShims) {
|
||||
if (activeShim.contentTabId === contentPort.sender.tab.id
|
||||
&& activeShim.contentFrameId === contentPort.sender.frameId) {
|
||||
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
|
||||
bridgePort: await bridge.connect(),
|
||||
contentPort,
|
||||
contentTabId: contentPort.sender.tab.id,
|
||||
contentFrameId: contentPort.sender.frameId
|
||||
};
|
||||
|
||||
|
||||
const onContentPortMessage = (message: Message) => {
|
||||
this.handleContentMessage(shim, message);
|
||||
};
|
||||
@@ -150,7 +152,6 @@ export default new class ShimManager {
|
||||
this.activeShims.delete(shim);
|
||||
};
|
||||
|
||||
|
||||
shim.bridgePort.onDisconnect.addListener(onDisconnect);
|
||||
shim.bridgePort.onMessage.addListener(onBridgePortMessage);
|
||||
|
||||
@@ -161,7 +162,7 @@ export default new class ShimManager {
|
||||
}
|
||||
|
||||
private async handleContentMessage(shim: Shim, message: Message) {
|
||||
const [ destination ] = message.subject.split(":");
|
||||
const [destination] = message.subject.split(":");
|
||||
if (destination === "bridge") {
|
||||
shim.bridgePort.postMessage(message);
|
||||
}
|
||||
@@ -172,8 +173,8 @@ export default new class ShimManager {
|
||||
|
||||
for (const receiverDevice of receiverDevices.getDevices()) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:serviceUp"
|
||||
, data: { receiverDevice }
|
||||
subject: "shim:serviceUp",
|
||||
data: { receiverDevice }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -181,15 +182,21 @@ export default new class ShimManager {
|
||||
}
|
||||
|
||||
case "main:selectReceiver": {
|
||||
if (shim.contentTabId === undefined
|
||||
|| shim.contentFrameId === undefined) {
|
||||
throw logger.error("Shim associated with content sender missing tab/frame ID");
|
||||
if (
|
||||
shim.contentTabId === undefined ||
|
||||
shim.contentFrameId === undefined
|
||||
) {
|
||||
throw logger.error(
|
||||
"Shim associated with content sender missing tab/frame ID"
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const selection =
|
||||
await ReceiverSelectorManager.getSelection(
|
||||
shim.contentTabId, shim.contentFrameId);
|
||||
await ReceiverSelectorManager.getSelection(
|
||||
shim.contentTabId,
|
||||
shim.contentFrameId
|
||||
);
|
||||
|
||||
// Handle cancellation
|
||||
if (!selection) {
|
||||
@@ -207,25 +214,26 @@ export default new class ShimManager {
|
||||
* been changed, we need to cancel the current
|
||||
* sender and switch it out for the right one.
|
||||
*/
|
||||
if (selection.mediaType !==
|
||||
ReceiverSelectorMediaType.App) {
|
||||
|
||||
if (
|
||||
selection.mediaType !==
|
||||
ReceiverSelectorMediaType.App
|
||||
) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:selectReceiver/cancelled"
|
||||
});
|
||||
|
||||
loadSender({
|
||||
tabId: shim.contentTabId
|
||||
, frameId: shim.contentFrameId
|
||||
, selection
|
||||
tabId: shim.contentTabId,
|
||||
frameId: shim.contentFrameId,
|
||||
selection
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:selectReceiver/selected"
|
||||
, data: selection
|
||||
subject: "shim:selectReceiver/selected",
|
||||
data: selection
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -233,8 +241,8 @@ export default new class ShimManager {
|
||||
|
||||
case ReceiverSelectionActionType.Stop: {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:selectReceiver/stopped"
|
||||
, data: selection
|
||||
subject: "shim:selectReceiver/stopped",
|
||||
data: selection
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -257,7 +265,8 @@ export default new class ShimManager {
|
||||
case "main:sessionCreated": {
|
||||
const selector = await ReceiverSelectorManager.getSelector();
|
||||
const shouldClose = await options.get(
|
||||
"receiverSelectorWaitForConnection");
|
||||
"receiverSelectorWaitForConnection"
|
||||
);
|
||||
|
||||
if (selector.isOpen && shouldClose) {
|
||||
selector.close();
|
||||
@@ -267,4 +276,4 @@ export default new class ShimManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -7,8 +7,7 @@ import messaging from "../messaging";
|
||||
import options from "../lib/options";
|
||||
import bridge, { BridgeInfo } from "../lib/bridge";
|
||||
|
||||
import ReceiverSelectorManager
|
||||
from "./receiverSelector/ReceiverSelectorManager";
|
||||
import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager";
|
||||
|
||||
import ShimManager from "./ShimManager";
|
||||
|
||||
@@ -17,10 +16,8 @@ import receiverDevices from "./receiverDevices";
|
||||
import { initMenus } from "./menus";
|
||||
import { initWhitelist } from "./whitelist";
|
||||
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
|
||||
/**
|
||||
* On install, set the default options before initializing the
|
||||
* extension. On update, handle any unset values and set to
|
||||
@@ -36,7 +33,7 @@ browser.runtime.onInstalled.addListener(async details => {
|
||||
init();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case "update": {
|
||||
// Set new defaults
|
||||
await options.update(defaultOptions);
|
||||
@@ -45,7 +42,6 @@ browser.runtime.onInstalled.addListener(async details => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Sets up media overlay content script and handles toggling
|
||||
* on options change.
|
||||
@@ -62,10 +58,10 @@ async function initMediaOverlay() {
|
||||
|
||||
try {
|
||||
contentScript = await browser.contentScripts.register({
|
||||
allFrames: true
|
||||
, js: [{ file: "senders/media/overlay/overlayContentLoader.js" }]
|
||||
, matches: [ "<all_urls>" ]
|
||||
, runAt: "document_start"
|
||||
allFrames: true,
|
||||
js: [{ file: "senders/media/overlay/overlayContentLoader.js" }],
|
||||
matches: ["<all_urls>"],
|
||||
runAt: "document_start"
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error("Failed to register media overlay");
|
||||
@@ -76,7 +72,6 @@ async function initMediaOverlay() {
|
||||
await contentScript?.unregister();
|
||||
}
|
||||
|
||||
|
||||
registerMediaOverlayContentScript();
|
||||
|
||||
// Update if toggled
|
||||
@@ -90,7 +85,6 @@ async function initMediaOverlay() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the bridge can be reached and is compatible
|
||||
* with the current version of the extension. If not, triggers
|
||||
@@ -113,10 +107,11 @@ async function notifyBridgeCompat() {
|
||||
logger.info("... bridge incompatible!");
|
||||
|
||||
const updateNotificationId = await browser.notifications.create({
|
||||
type: "basic"
|
||||
, title: `${
|
||||
_("extensionName")} — ${_("optionsBridgeIssueStatusTitle")}`
|
||||
, message: info.isVersionOlder
|
||||
type: "basic",
|
||||
title: `${_("extensionName")} — ${_(
|
||||
"optionsBridgeIssueStatusTitle"
|
||||
)}`,
|
||||
message: info.isVersionOlder
|
||||
? _("optionsBridgeOlderAction")
|
||||
: _("optionsBridgeNewerAction")
|
||||
});
|
||||
@@ -127,14 +122,12 @@ async function notifyBridgeCompat() {
|
||||
}
|
||||
|
||||
browser.tabs.create({
|
||||
url: `https://github.com/hensm/fx_cast/releases/tag/v${
|
||||
info.expectedVersion}`
|
||||
url: `https://github.com/hensm/fx_cast/releases/tag/v${info.expectedVersion}`
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let isInitialized = false;
|
||||
|
||||
async function init() {
|
||||
@@ -163,14 +156,13 @@ async function init() {
|
||||
await initWhitelist();
|
||||
await initMediaOverlay();
|
||||
|
||||
|
||||
/**
|
||||
* 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 => {
|
||||
browser.browserAction.onClicked.addListener(async tab => {
|
||||
if (tab.id === undefined) {
|
||||
throw logger.error("Tab ID not found in browser action handler.");
|
||||
}
|
||||
@@ -178,9 +170,9 @@ async function init() {
|
||||
const selection = await ReceiverSelectorManager.getSelection(tab.id);
|
||||
if (selection) {
|
||||
loadSender({
|
||||
tabId: tab.id
|
||||
, frameId: 0
|
||||
, selection
|
||||
tabId: tab.id,
|
||||
frameId: 0,
|
||||
selection
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,22 +6,21 @@ import options from "../lib/options";
|
||||
|
||||
import { stringify } from "../lib/utils";
|
||||
|
||||
import { ReceiverSelectionActionType
|
||||
, ReceiverSelectorMediaType } from "./receiverSelector";
|
||||
import {
|
||||
ReceiverSelectionActionType,
|
||||
ReceiverSelectorMediaType
|
||||
} from "./receiverSelector";
|
||||
|
||||
import ReceiverSelectorManager
|
||||
from "./receiverSelector/ReceiverSelectorManager";
|
||||
import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
|
||||
const URL_PATTERN_HTTP = "http://*/*";
|
||||
const URL_PATTERN_HTTPS = "https://*/*";
|
||||
const URL_PATTERN_FILE = "file://*/*";
|
||||
|
||||
const URL_PATTERNS_REMOTE = [ URL_PATTERN_HTTP, URL_PATTERN_HTTPS ];
|
||||
const URL_PATTERNS_ALL = [ ...URL_PATTERNS_REMOTE, URL_PATTERN_FILE ];
|
||||
|
||||
const URL_PATTERNS_REMOTE = [URL_PATTERN_HTTP, URL_PATTERN_HTTPS];
|
||||
const URL_PATTERNS_ALL = [...URL_PATTERNS_REMOTE, URL_PATTERN_FILE];
|
||||
|
||||
type MenuId = string | number;
|
||||
|
||||
@@ -39,44 +38,44 @@ export async function initMenus() {
|
||||
|
||||
// Global "Cast..." menu item
|
||||
menuIdCast = browser.menus.create({
|
||||
contexts: [ "browser_action", "page", "tools_menu" ]
|
||||
, title: _("contextCast")
|
||||
contexts: ["browser_action", "page", "tools_menu"],
|
||||
title: _("contextCast")
|
||||
});
|
||||
|
||||
// <video>/<audio> "Cast..." context menu item
|
||||
menuIdMediaCast = browser.menus.create({
|
||||
contexts: [ "audio", "video", "image" ]
|
||||
, title: _("contextCast")
|
||||
, visible: opts.mediaEnabled
|
||||
, targetUrlPatterns: opts.localMediaEnabled
|
||||
contexts: ["audio", "video", "image"],
|
||||
title: _("contextCast"),
|
||||
visible: opts.mediaEnabled,
|
||||
targetUrlPatterns: opts.localMediaEnabled
|
||||
? URL_PATTERNS_ALL
|
||||
: URL_PATTERNS_REMOTE
|
||||
});
|
||||
|
||||
|
||||
menuIdWhitelist = browser.menus.create({
|
||||
contexts: [ "browser_action" ]
|
||||
, title: _("contextAddToWhitelist")
|
||||
, enabled: false
|
||||
contexts: ["browser_action"],
|
||||
title: _("contextAddToWhitelist"),
|
||||
enabled: false
|
||||
});
|
||||
|
||||
menuIdWhitelistRecommended = browser.menus.create({
|
||||
title: _("contextAddToWhitelistRecommended")
|
||||
, parentId: menuIdWhitelist
|
||||
title: _("contextAddToWhitelistRecommended"),
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
|
||||
browser.menus.create({
|
||||
type: "separator"
|
||||
, parentId: menuIdWhitelist
|
||||
type: "separator",
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
browser.menus.onClicked.addListener(async (info, tab) => {
|
||||
if (info.parentMenuItemId === menuIdWhitelist) {
|
||||
const pattern = whitelistChildMenuPatterns.get(info.menuItemId);
|
||||
if (!pattern) {
|
||||
throw logger.error(`Whitelist pattern not found for menu item ID ${info.menuItemId}.`);
|
||||
throw logger.error(
|
||||
`Whitelist pattern not found for menu item ID ${info.menuItemId}.`
|
||||
);
|
||||
}
|
||||
|
||||
const whitelist = await options.get("userAgentWhitelist");
|
||||
@@ -99,7 +98,9 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
||||
switch (info.menuItemId) {
|
||||
case menuIdCast: {
|
||||
const selection = await ReceiverSelectorManager.getSelection(
|
||||
tab.id, info.frameId);
|
||||
tab.id,
|
||||
info.frameId
|
||||
);
|
||||
|
||||
// Selection cancelled
|
||||
if (!selection) {
|
||||
@@ -107,9 +108,9 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
||||
}
|
||||
|
||||
loadSender({
|
||||
tabId: tab.id
|
||||
, frameId: info.frameId
|
||||
, selection
|
||||
tabId: tab.id,
|
||||
frameId: info.frameId,
|
||||
selection
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -117,7 +118,10 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
||||
|
||||
case menuIdMediaCast: {
|
||||
const selection = await ReceiverSelectorManager.getSelection(
|
||||
tab.id, info.frameId, true);
|
||||
tab.id,
|
||||
info.frameId,
|
||||
true
|
||||
);
|
||||
|
||||
// Selection cancelled
|
||||
if (!selection) {
|
||||
@@ -130,30 +134,26 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
||||
* If the selected media type is App, that refers to the
|
||||
* media sender in this context, so load media sender.
|
||||
*/
|
||||
if (selection.mediaType ===
|
||||
ReceiverSelectorMediaType.App) {
|
||||
|
||||
if (selection.mediaType === ReceiverSelectorMediaType.App) {
|
||||
await browser.tabs.executeScript(tab.id, {
|
||||
code: stringify`
|
||||
window.receiver = ${selection.receiver};
|
||||
window.mediaUrl = ${info.srcUrl};
|
||||
window.targetElementId = ${
|
||||
info.targetElementId};
|
||||
`
|
||||
, frameId: info.frameId
|
||||
window.targetElementId = ${info.targetElementId};
|
||||
`,
|
||||
frameId: info.frameId
|
||||
});
|
||||
|
||||
await browser.tabs.executeScript(tab.id, {
|
||||
file: "senders/media/index.js"
|
||||
, frameId: info.frameId
|
||||
file: "senders/media/index.js",
|
||||
frameId: info.frameId
|
||||
});
|
||||
} else {
|
||||
|
||||
// Handle other responses
|
||||
loadSender({
|
||||
tabId: tab.id
|
||||
, frameId: info.frameId
|
||||
, selection
|
||||
tabId: tab.id,
|
||||
frameId: info.frameId,
|
||||
selection
|
||||
});
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ browser.menus.onShown.addListener(async info => {
|
||||
enabled: true
|
||||
});
|
||||
|
||||
for (const [ menuId ] of whitelistChildMenuPatterns) {
|
||||
for (const [menuId] of whitelistChildMenuPatterns) {
|
||||
// Clear all page-specific temporary menus
|
||||
if (menuId !== menuIdWhitelistRecommended) {
|
||||
browser.menus.remove(menuId);
|
||||
@@ -235,9 +235,10 @@ browser.menus.onShown.addListener(async info => {
|
||||
}
|
||||
|
||||
// If there is more than one subdomain, get the base domain
|
||||
const baseDomain = (url.hostname.match(/\./g) || []).length > 1
|
||||
? url.hostname.substring(url.hostname.indexOf(".") + 1)
|
||||
: url.hostname;
|
||||
const baseDomain =
|
||||
(url.hostname.match(/\./g) || []).length > 1
|
||||
? url.hostname.substring(url.hostname.indexOf(".") + 1)
|
||||
: url.hostname;
|
||||
|
||||
const portlessOrigin = `${url.protocol}//${url.hostname}`;
|
||||
|
||||
@@ -253,17 +254,17 @@ browser.menus.onShown.addListener(async info => {
|
||||
});
|
||||
|
||||
whitelistChildMenuPatterns.set(
|
||||
menuIdWhitelistRecommended, patternRecommended);
|
||||
|
||||
menuIdWhitelistRecommended,
|
||||
patternRecommended
|
||||
);
|
||||
|
||||
if (url.search) {
|
||||
const whitelistSearchMenuId = browser.menus.create({
|
||||
title: _("contextAddToWhitelistAdvancedAdd", patternSearch)
|
||||
, parentId: menuIdWhitelist
|
||||
title: _("contextAddToWhitelistAdvancedAdd", patternSearch),
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
|
||||
whitelistChildMenuPatterns.set(
|
||||
whitelistSearchMenuId, patternSearch);
|
||||
whitelistChildMenuPatterns.set(whitelistSearchMenuId, patternSearch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,65 +276,63 @@ browser.menus.onShown.addListener(async info => {
|
||||
? url.pathname.substring(0, url.pathname.length - 1)
|
||||
: url.pathname;
|
||||
|
||||
const pathSegments = pathTrimmed.split("/")
|
||||
.filter(segment => segment)
|
||||
.reverse();
|
||||
const pathSegments = pathTrimmed
|
||||
.split("/")
|
||||
.filter(segment => segment)
|
||||
.reverse();
|
||||
|
||||
if (pathSegments.length) {
|
||||
for (let i = 0; i < pathSegments.length; i++) {
|
||||
const partialPath = pathSegments
|
||||
.slice(i)
|
||||
.reverse()
|
||||
.join("/");
|
||||
const partialPath = pathSegments.slice(i).reverse().join("/");
|
||||
|
||||
const pattern = `${portlessOrigin}/${partialPath}/*`;
|
||||
|
||||
const partialPathMenuId = browser.menus.create({
|
||||
title: _("contextAddToWhitelistAdvancedAdd", pattern)
|
||||
, parentId: menuIdWhitelist
|
||||
title: _("contextAddToWhitelistAdvancedAdd", pattern),
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
|
||||
whitelistChildMenuPatterns.set(
|
||||
partialPathMenuId, pattern);
|
||||
whitelistChildMenuPatterns.set(partialPathMenuId, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wildcardProtocolMenuId = browser.menus.create({
|
||||
title: _("contextAddToWhitelistAdvancedAdd"
|
||||
, patternWildcardProtocol)
|
||||
, parentId: menuIdWhitelist
|
||||
title: _("contextAddToWhitelistAdvancedAdd", patternWildcardProtocol),
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
|
||||
whitelistChildMenuPatterns.set(
|
||||
wildcardProtocolMenuId, patternWildcardProtocol);
|
||||
|
||||
wildcardProtocolMenuId,
|
||||
patternWildcardProtocol
|
||||
);
|
||||
|
||||
const wildcardSubdomainMenuId = browser.menus.create({
|
||||
title: _("contextAddToWhitelistAdvancedAdd"
|
||||
, patternWildcardSubdomain)
|
||||
, parentId: menuIdWhitelist
|
||||
title: _("contextAddToWhitelistAdvancedAdd", patternWildcardSubdomain),
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
|
||||
whitelistChildMenuPatterns.set(
|
||||
wildcardSubdomainMenuId, patternWildcardSubdomain);
|
||||
|
||||
wildcardSubdomainMenuId,
|
||||
patternWildcardSubdomain
|
||||
);
|
||||
|
||||
const wildcardProtocolAndSubdomainMenuId = browser.menus.create({
|
||||
title: _("contextAddToWhitelistAdvancedAdd"
|
||||
, patternWildcardProtocolAndSubdomain)
|
||||
, parentId: menuIdWhitelist
|
||||
title: _(
|
||||
"contextAddToWhitelistAdvancedAdd",
|
||||
patternWildcardProtocolAndSubdomain
|
||||
),
|
||||
parentId: menuIdWhitelist
|
||||
});
|
||||
|
||||
whitelistChildMenuPatterns.set(
|
||||
wildcardProtocolAndSubdomainMenuId
|
||||
, patternWildcardProtocolAndSubdomain);
|
||||
|
||||
wildcardProtocolAndSubdomainMenuId,
|
||||
patternWildcardProtocolAndSubdomain
|
||||
);
|
||||
|
||||
await browser.menus.refresh();
|
||||
});
|
||||
|
||||
|
||||
options.addEventListener("changed", async ev => {
|
||||
const alteredOpts = ev.detail;
|
||||
const newOpts = await options.getAll();
|
||||
|
||||
@@ -8,17 +8,16 @@ import { Message, Port } from "../messaging";
|
||||
import { ReceiverDevice } from "../types";
|
||||
import { ReceiverStatus } from "../shim/cast/types";
|
||||
|
||||
|
||||
interface EventMap {
|
||||
"receiverDeviceUp": { receiverDevice: ReceiverDevice }
|
||||
, "receiverDeviceDown": { receiverDeviceId: string }
|
||||
, "receiverDeviceUpdated": {
|
||||
receiverDeviceId: string
|
||||
, status: ReceiverStatus
|
||||
}
|
||||
receiverDeviceUp: { receiverDevice: ReceiverDevice };
|
||||
receiverDeviceDown: { receiverDeviceId: string };
|
||||
receiverDeviceUpdated: {
|
||||
receiverDeviceId: string;
|
||||
status: ReceiverStatus;
|
||||
};
|
||||
}
|
||||
|
||||
export default new class extends TypedEventTarget<EventMap> {
|
||||
export default new (class extends TypedEventTarget<EventMap> {
|
||||
/**
|
||||
* Map of receiver device IDs to devices. Updated as
|
||||
* receiverDevice messages are received from the bridge.
|
||||
@@ -27,7 +26,6 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
|
||||
private bridgePort?: Port;
|
||||
|
||||
|
||||
async init() {
|
||||
if (!this.bridgePort) {
|
||||
await this.refresh();
|
||||
@@ -47,8 +45,8 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
port.onDisconnect.addListener(this.onBridgeDisconnect);
|
||||
|
||||
port.postMessage({
|
||||
subject: "bridge:startDiscovery"
|
||||
, data: {
|
||||
subject: "bridge:startDiscovery",
|
||||
data: {
|
||||
// Also send back status messages
|
||||
shouldWatchStatus: true
|
||||
}
|
||||
@@ -69,15 +67,17 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
*/
|
||||
stopReceiverApp(receiverDeviceId: string) {
|
||||
if (!this.bridgePort) {
|
||||
logger.error("Failed to stop receiver device, no bridge connection");
|
||||
logger.error(
|
||||
"Failed to stop receiver device, no bridge connection"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const receiverDevice = this.receiverDevices.get(receiverDeviceId);
|
||||
if (receiverDevice) {
|
||||
this.bridgePort.postMessage({
|
||||
subject: "bridge:stopCastApp"
|
||||
, data: { receiverDevice }
|
||||
subject: "bridge:stopCastApp",
|
||||
data: { receiverDevice }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -89,10 +89,10 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
|
||||
this.receiverDevices.set(receiverDevice.id, receiverDevice);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("receiverDeviceUp"
|
||||
, {
|
||||
detail: { receiverDevice }
|
||||
}));
|
||||
new CustomEvent("receiverDeviceUp", {
|
||||
detail: { receiverDevice }
|
||||
})
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -104,10 +104,10 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
this.receiverDevices.delete(receiverDeviceId);
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("receiverDeviceDown"
|
||||
, {
|
||||
detail: { receiverDeviceId }
|
||||
}));
|
||||
new CustomEvent("receiverDeviceDown", {
|
||||
detail: { receiverDeviceId }
|
||||
})
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -115,10 +115,12 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
case "main:receiverDeviceUpdated": {
|
||||
const { receiverDeviceId, status } = message.data;
|
||||
const receiverDevice =
|
||||
this.receiverDevices.get(receiverDeviceId);
|
||||
this.receiverDevices.get(receiverDeviceId);
|
||||
|
||||
if (!receiverDevice) {
|
||||
logger.error(`Receiver ID \`${receiverDeviceId}\` not found!`);
|
||||
logger.error(
|
||||
`Receiver ID \`${receiverDeviceId}\` not found!`
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -129,27 +131,27 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
|
||||
if (status.applications) {
|
||||
receiverDevice.status.applications =
|
||||
status.applications;
|
||||
status.applications;
|
||||
}
|
||||
} else {
|
||||
receiverDevice.status = status;
|
||||
}
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("receiverDeviceUpdated"
|
||||
, {
|
||||
detail: {
|
||||
receiverDeviceId
|
||||
, status: receiverDevice.status
|
||||
}
|
||||
}));
|
||||
new CustomEvent("receiverDeviceUpdated", {
|
||||
detail: {
|
||||
receiverDeviceId,
|
||||
status: receiverDevice.status
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private onBridgeDisconnect = () => {
|
||||
// Notify listeners of device availablility
|
||||
for (const [ , receiverDevice ] of this.receiverDevices) {
|
||||
for (const [, receiverDevice] of this.receiverDevices) {
|
||||
const event = new CustomEvent("receiverDeviceDown", {
|
||||
detail: { receiverDeviceId: receiverDevice.id }
|
||||
});
|
||||
@@ -163,5 +165,5 @@ export default new class extends TypedEventTarget<EventMap> {
|
||||
window.setTimeout(() => {
|
||||
this.refresh();
|
||||
}, 10000);
|
||||
}
|
||||
};
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -8,25 +8,22 @@ import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
import { ReceiverSelectionCast
|
||||
, ReceiverSelectionStop
|
||||
, ReceiverSelectorMediaType } from "./index";
|
||||
|
||||
|
||||
import {
|
||||
ReceiverSelectionCast,
|
||||
ReceiverSelectionStop,
|
||||
ReceiverSelectorMediaType
|
||||
} from "./index";
|
||||
|
||||
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
|
||||
|
||||
|
||||
interface ReceiverSelectorEvents {
|
||||
"selected": ReceiverSelectionCast;
|
||||
"error": string;
|
||||
"cancelled": void;
|
||||
"stop": ReceiverSelectionStop;
|
||||
selected: ReceiverSelectionCast;
|
||||
error: string;
|
||||
cancelled: void;
|
||||
stop: ReceiverSelectionStop;
|
||||
}
|
||||
|
||||
export default class ReceiverSelector
|
||||
extends TypedEventTarget<ReceiverSelectorEvents> {
|
||||
|
||||
export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorEvents> {
|
||||
private windowId?: number;
|
||||
|
||||
private messagePort?: Port;
|
||||
@@ -65,11 +62,11 @@ export default class ReceiverSelector
|
||||
}
|
||||
|
||||
public async open(
|
||||
receivers: ReceiverDevice[]
|
||||
, defaultMediaType: ReceiverSelectorMediaType
|
||||
, availableMediaTypes: ReceiverSelectorMediaType
|
||||
, appId?: string): Promise<void> {
|
||||
|
||||
receivers: ReceiverDevice[],
|
||||
defaultMediaType: ReceiverSelectorMediaType,
|
||||
availableMediaTypes: ReceiverSelectorMediaType,
|
||||
appId?: string
|
||||
): Promise<void> {
|
||||
this.appId = appId;
|
||||
|
||||
// If popup already exists, close it
|
||||
@@ -81,27 +78,28 @@ export default class ReceiverSelector
|
||||
this.defaultMediaType = defaultMediaType;
|
||||
this.availableMediaTypes = availableMediaTypes;
|
||||
|
||||
|
||||
let centeredProps: WindowCenteredProps = {
|
||||
left: 100
|
||||
, top: 100
|
||||
, width: 350
|
||||
, height: 200
|
||||
left: 100,
|
||||
top: 100,
|
||||
width: 350,
|
||||
height: 200
|
||||
};
|
||||
|
||||
try {
|
||||
// Calculate centered size/position based on current window
|
||||
centeredProps = getWindowCenteredProps(
|
||||
await browser.windows.getCurrent()
|
||||
, centeredProps.width, centeredProps.height);
|
||||
await browser.windows.getCurrent(),
|
||||
centeredProps.width,
|
||||
centeredProps.height
|
||||
);
|
||||
} catch {
|
||||
// Shouldn't ever hit this, but defaults are provided in case
|
||||
}
|
||||
|
||||
const popup = await browser.windows.create({
|
||||
url: POPUP_URL
|
||||
, type: "popup"
|
||||
, ...centeredProps
|
||||
url: POPUP_URL,
|
||||
type: "popup",
|
||||
...centeredProps
|
||||
});
|
||||
|
||||
if (popup?.id === undefined) {
|
||||
@@ -116,22 +114,23 @@ export default class ReceiverSelector
|
||||
...centeredProps
|
||||
});
|
||||
|
||||
|
||||
const closeIfFocusLost = await options.get(
|
||||
"receiverSelectorCloseIfFocusLost");
|
||||
"receiverSelectorCloseIfFocusLost"
|
||||
);
|
||||
|
||||
if (closeIfFocusLost) {
|
||||
// Add focus listener
|
||||
browser.windows.onFocusChanged.addListener(
|
||||
this.onWindowsFocusChanged);
|
||||
this.onWindowsFocusChanged
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public update(receivers: ReceiverDevice[]) {
|
||||
this.receivers = receivers;
|
||||
this.messagePort?.postMessage({
|
||||
subject: "popup:update"
|
||||
, data: {
|
||||
subject: "popup:update",
|
||||
data: {
|
||||
receivers: this.receivers
|
||||
}
|
||||
});
|
||||
@@ -167,23 +166,25 @@ export default class ReceiverSelector
|
||||
this.messagePortDisconnected = true;
|
||||
});
|
||||
|
||||
if (!this.receivers
|
||||
|| !this.defaultMediaType
|
||||
|| !this.availableMediaTypes) {
|
||||
if (
|
||||
!this.receivers ||
|
||||
!this.defaultMediaType ||
|
||||
!this.availableMediaTypes
|
||||
) {
|
||||
throw logger.error("Popup receiver data not found.");
|
||||
}
|
||||
|
||||
this.messagePort.postMessage({
|
||||
subject: "popup:init"
|
||||
, data: { appId: this.appId }
|
||||
subject: "popup:init",
|
||||
data: { appId: this.appId }
|
||||
});
|
||||
|
||||
this.messagePort.postMessage({
|
||||
subject: "popup:update"
|
||||
, data: {
|
||||
receivers: this.receivers
|
||||
, defaultMediaType: this.defaultMediaType
|
||||
, availableMediaTypes: this.availableMediaTypes
|
||||
subject: "popup:update",
|
||||
data: {
|
||||
receivers: this.receivers,
|
||||
defaultMediaType: this.defaultMediaType,
|
||||
availableMediaTypes: this.availableMediaTypes
|
||||
}
|
||||
});
|
||||
|
||||
@@ -197,17 +198,21 @@ export default class ReceiverSelector
|
||||
switch (message.subject) {
|
||||
case "receiverSelector:selected": {
|
||||
this.wasReceiverSelected = true;
|
||||
this.dispatchEvent(new CustomEvent("selected", {
|
||||
detail: message.data
|
||||
}));
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("selected", {
|
||||
detail: message.data
|
||||
})
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "receiverSelector:stop": {
|
||||
this.dispatchEvent(new CustomEvent("stop", {
|
||||
detail: message.data
|
||||
}));
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("stop", {
|
||||
detail: message.data
|
||||
})
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -226,7 +231,8 @@ export default class ReceiverSelector
|
||||
|
||||
browser.windows.onRemoved.removeListener(this.onWindowsRemoved);
|
||||
browser.windows.onFocusChanged.removeListener(
|
||||
this.onWindowsFocusChanged);
|
||||
this.onWindowsFocusChanged
|
||||
);
|
||||
|
||||
if (!this.wasReceiverSelected) {
|
||||
this.dispatchEvent(new CustomEvent("cancelled"));
|
||||
@@ -247,12 +253,14 @@ export default class ReceiverSelector
|
||||
* `WINDOW_ID_NONE` or if the popup window is re-focused.
|
||||
*/
|
||||
private onWindowsFocusChanged(windowId: number) {
|
||||
if (windowId !== browser.windows.WINDOW_ID_NONE
|
||||
&& windowId !== this.windowId) {
|
||||
|
||||
if (
|
||||
windowId !== browser.windows.WINDOW_ID_NONE &&
|
||||
windowId !== this.windowId
|
||||
) {
|
||||
// Only run once
|
||||
browser.windows.onFocusChanged.removeListener(
|
||||
this.onWindowsFocusChanged);
|
||||
this.onWindowsFocusChanged
|
||||
);
|
||||
|
||||
if (this.windowId) {
|
||||
browser.windows.remove(this.windowId);
|
||||
|
||||
@@ -8,18 +8,18 @@ import receiverDevices from "../receiverDevices";
|
||||
|
||||
import { getMediaTypesForPageUrl } from "../../lib/utils";
|
||||
|
||||
import { ReceiverSelection
|
||||
, ReceiverSelectionActionType
|
||||
, ReceiverSelectorMediaType } from "./index";
|
||||
import {
|
||||
ReceiverSelection,
|
||||
ReceiverSelectionActionType,
|
||||
ReceiverSelectorMediaType
|
||||
} from "./index";
|
||||
|
||||
import ReceiverSelector from "./ReceiverSelector";
|
||||
|
||||
|
||||
async function createSelector() {
|
||||
return new ReceiverSelector();
|
||||
}
|
||||
|
||||
|
||||
let sharedSelector: ReceiverSelector;
|
||||
|
||||
async function getSelector() {
|
||||
@@ -34,7 +34,6 @@ async function getSelector() {
|
||||
return sharedSelector;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Opens a receiver selector with the specified
|
||||
* default/available media types.
|
||||
@@ -46,21 +45,18 @@ async function getSelector() {
|
||||
* - Rejects if the selection fails.
|
||||
*/
|
||||
async function getSelection(
|
||||
contextTabId: number
|
||||
, contextFrameId = 0
|
||||
, withMediaSender = false)
|
||||
: Promise<ReceiverSelection | null> {
|
||||
|
||||
contextTabId: number,
|
||||
contextFrameId = 0,
|
||||
withMediaSender = false
|
||||
): Promise<ReceiverSelection | null> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let currentShim = ShimManager.getShim(
|
||||
contextTabId, contextFrameId);
|
||||
let currentShim = ShimManager.getShim(contextTabId, contextFrameId);
|
||||
|
||||
/**
|
||||
* If the current context is running the mirroring app, pretend
|
||||
* it doesn't exist because it shouldn't be launched like this.
|
||||
*/
|
||||
if (currentShim?.appId ===
|
||||
await options.get("mirroringAppId")) {
|
||||
if (currentShim?.appId === (await options.get("mirroringAppId"))) {
|
||||
currentShim = undefined;
|
||||
}
|
||||
|
||||
@@ -69,13 +65,15 @@ async function getSelection(
|
||||
|
||||
try {
|
||||
const { url } = await browser.webNavigation.getFrame({
|
||||
tabId: contextTabId
|
||||
, frameId: contextFrameId
|
||||
tabId: contextTabId,
|
||||
frameId: contextFrameId
|
||||
});
|
||||
|
||||
availableMediaTypes = getMediaTypesForPageUrl(url);
|
||||
} catch {
|
||||
logger.error("Failed to locate frame, falling back to default available media types.");
|
||||
logger.error(
|
||||
"Failed to locate frame, falling back to default available media types."
|
||||
);
|
||||
availableMediaTypes = ReceiverSelectorMediaType.File;
|
||||
}
|
||||
|
||||
@@ -90,8 +88,8 @@ async function getSelection(
|
||||
// Remove mirroring media types if mirroring is not enabled
|
||||
if (!opts.mirroringEnabled) {
|
||||
availableMediaTypes &= ~(
|
||||
ReceiverSelectorMediaType.Tab
|
||||
| ReceiverSelectorMediaType.Screen);
|
||||
ReceiverSelectorMediaType.Tab | ReceiverSelectorMediaType.Screen
|
||||
);
|
||||
}
|
||||
|
||||
// Remove file media type if local media is not enabled
|
||||
@@ -107,26 +105,28 @@ async function getSelection(
|
||||
// Get a new selector for each selection
|
||||
sharedSelector = await createSelector();
|
||||
|
||||
|
||||
function onReceiverChange() {
|
||||
sharedSelector.update(receiverDevices.getDevices());
|
||||
}
|
||||
|
||||
receiverDevices.addEventListener("receiverDeviceUp", onReceiverChange);
|
||||
receiverDevices.addEventListener(
|
||||
"receiverDeviceUp", onReceiverChange);
|
||||
"receiverDeviceDown",
|
||||
onReceiverChange
|
||||
);
|
||||
receiverDevices.addEventListener(
|
||||
"receiverDeviceDown", onReceiverChange);
|
||||
receiverDevices.addEventListener(
|
||||
"receiverDeviceUpdated", onReceiverChange);
|
||||
|
||||
"receiverDeviceUpdated",
|
||||
onReceiverChange
|
||||
);
|
||||
|
||||
let onSelected: any;
|
||||
let onCancelled: any;
|
||||
let onError: any;
|
||||
let onStop: any;
|
||||
|
||||
type EvParamsType =
|
||||
Parameters<typeof sharedSelector.addEventListener>[0];
|
||||
type EvParamsType = Parameters<
|
||||
typeof sharedSelector.addEventListener
|
||||
>[0];
|
||||
|
||||
function storeListener<T>(type: EvParamsType, fn: T) {
|
||||
if (type === "selected") {
|
||||
@@ -149,67 +149,78 @@ async function getSelection(
|
||||
sharedSelector.removeEventListener("stop", onStop);
|
||||
|
||||
receiverDevices.removeEventListener(
|
||||
"receiverDeviceUp", onReceiverChange);
|
||||
"receiverDeviceUp",
|
||||
onReceiverChange
|
||||
);
|
||||
receiverDevices.removeEventListener(
|
||||
"receiverDeviceDown", onReceiverChange);
|
||||
"receiverDeviceDown",
|
||||
onReceiverChange
|
||||
);
|
||||
receiverDevices.removeEventListener(
|
||||
"receiverDeviceUpdated", onReceiverChange);
|
||||
"receiverDeviceUpdated",
|
||||
onReceiverChange
|
||||
);
|
||||
}
|
||||
|
||||
sharedSelector.addEventListener("selected"
|
||||
, storeListener("selected", ev => {
|
||||
sharedSelector.addEventListener(
|
||||
"selected",
|
||||
storeListener("selected", ev => {
|
||||
logger.info("Selected receiver", ev.detail);
|
||||
resolve({
|
||||
actionType: ReceiverSelectionActionType.Cast,
|
||||
receiver: ev.detail.receiver,
|
||||
mediaType: ev.detail.mediaType,
|
||||
filePath: ev.detail.filePath
|
||||
});
|
||||
removeListeners();
|
||||
})
|
||||
);
|
||||
|
||||
logger.info("Selected receiver", ev.detail);
|
||||
resolve({
|
||||
actionType: ReceiverSelectionActionType.Cast
|
||||
, receiver: ev.detail.receiver
|
||||
, mediaType: ev.detail.mediaType
|
||||
, filePath: ev.detail.filePath
|
||||
});
|
||||
removeListeners();
|
||||
}));
|
||||
sharedSelector.addEventListener(
|
||||
"cancelled",
|
||||
storeListener("cancelled", () => {
|
||||
logger.info("Cancelled receiver selection");
|
||||
resolve(null);
|
||||
removeListeners();
|
||||
})
|
||||
);
|
||||
|
||||
sharedSelector.addEventListener("cancelled"
|
||||
, storeListener("cancelled", () => {
|
||||
sharedSelector.addEventListener(
|
||||
"error",
|
||||
storeListener("error", () => {
|
||||
reject();
|
||||
removeListeners();
|
||||
})
|
||||
);
|
||||
|
||||
logger.info("Cancelled receiver selection");
|
||||
resolve(null);
|
||||
removeListeners();
|
||||
}));
|
||||
sharedSelector.addEventListener(
|
||||
"stop",
|
||||
storeListener("stop", async ev => {
|
||||
logger.info("Stopping receiver app...", ev.detail);
|
||||
|
||||
sharedSelector.addEventListener("error"
|
||||
, storeListener("error", () => {
|
||||
reject();
|
||||
removeListeners();
|
||||
}));
|
||||
|
||||
sharedSelector.addEventListener("stop"
|
||||
, storeListener("stop", async ev => {
|
||||
|
||||
logger.info("Stopping receiver app...", ev.detail);
|
||||
|
||||
receiverDevices.stopReceiverApp(ev.detail.receiver.id);
|
||||
|
||||
resolve({
|
||||
actionType: ReceiverSelectionActionType.Stop
|
||||
, receiver: ev.detail.receiver
|
||||
});
|
||||
removeListeners();
|
||||
}));
|
||||
receiverDevices.stopReceiverApp(ev.detail.receiver.id);
|
||||
|
||||
resolve({
|
||||
actionType: ReceiverSelectionActionType.Stop,
|
||||
receiver: ev.detail.receiver
|
||||
});
|
||||
removeListeners();
|
||||
})
|
||||
);
|
||||
|
||||
// Ensure status manager is initialized
|
||||
await receiverDevices.init();
|
||||
|
||||
sharedSelector.open(
|
||||
receiverDevices.getDevices()
|
||||
, defaultMediaType
|
||||
, availableMediaTypes
|
||||
, currentShim?.appId);
|
||||
receiverDevices.getDevices(),
|
||||
defaultMediaType,
|
||||
availableMediaTypes,
|
||||
currentShim?.appId
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
getSelection
|
||||
, getSelector
|
||||
getSelection,
|
||||
getSelector
|
||||
};
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
export enum ReceiverSelectorType {
|
||||
Popup
|
||||
, Native
|
||||
Popup,
|
||||
Native
|
||||
}
|
||||
|
||||
export enum ReceiverSelectorMediaType {
|
||||
App = 1
|
||||
, Tab = 2
|
||||
, Screen = 4
|
||||
, File = 8
|
||||
App = 1,
|
||||
Tab = 2,
|
||||
Screen = 4,
|
||||
File = 8
|
||||
}
|
||||
|
||||
export enum ReceiverSelectionActionType {
|
||||
Cast = 1
|
||||
, Stop = 2
|
||||
Cast = 1,
|
||||
Stop = 2
|
||||
}
|
||||
|
||||
export interface ReceiverSelectionCast {
|
||||
|
||||
@@ -5,21 +5,23 @@ import options from "../lib/options";
|
||||
|
||||
import { getChromeUserAgent } from "../lib/userAgents";
|
||||
|
||||
import { CAST_FRAMEWORK_LOADER_SCRIPT_URL
|
||||
, CAST_LOADER_SCRIPT_URL } from "../lib/endpoints";
|
||||
|
||||
import {
|
||||
CAST_FRAMEWORK_LOADER_SCRIPT_URL,
|
||||
CAST_LOADER_SCRIPT_URL
|
||||
} from "../lib/endpoints";
|
||||
|
||||
// Missing on @types/firefox-webext-browser
|
||||
type OnBeforeSendHeadersDetails = Parameters<Parameters<
|
||||
typeof browser.webRequest.onBeforeSendHeaders.addListener>[0]>[0] & {
|
||||
frameAncestors?: Array<{ url: string, frameId: number }>
|
||||
type OnBeforeSendHeadersDetails = Parameters<
|
||||
Parameters<typeof browser.webRequest.onBeforeSendHeaders.addListener>[0]
|
||||
>[0] & {
|
||||
frameAncestors?: Array<{ url: string; frameId: number }>;
|
||||
};
|
||||
type OnBeforeRequestDetails = Parameters<Parameters<
|
||||
typeof browser.webRequest.onBeforeRequest.addListener>[0]>[0] & {
|
||||
frameAncestors?: Array<{ url: string, frameId: number }>
|
||||
type OnBeforeRequestDetails = Parameters<
|
||||
Parameters<typeof browser.webRequest.onBeforeRequest.addListener>[0]
|
||||
>[0] & {
|
||||
frameAncestors?: Array<{ url: string; frameId: number }>;
|
||||
};
|
||||
|
||||
|
||||
const originUrlCache: string[] = [];
|
||||
|
||||
let platform: string;
|
||||
@@ -51,40 +53,43 @@ export async function initWhitelist() {
|
||||
options.addEventListener("changed", ev => {
|
||||
const alteredOpts = ev.detail;
|
||||
|
||||
if (alteredOpts.includes("userAgentWhitelist")
|
||||
|| alteredOpts.includes("userAgentWhitelistEnabled")) {
|
||||
if (
|
||||
alteredOpts.includes("userAgentWhitelist") ||
|
||||
alteredOpts.includes("userAgentWhitelistEnabled")
|
||||
) {
|
||||
unregisterUserAgentWhitelist();
|
||||
registerUserAgentWhitelist();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Web apps usually only load the sender library and
|
||||
* provide cast functionality if the browser is detected
|
||||
* as Chrome, so we should rewrite the User-Agent header
|
||||
* to reflect this on whitelisted sites.
|
||||
*/
|
||||
async function onWhitelistedBeforeSendHeaders(
|
||||
details: OnBeforeSendHeadersDetails) {
|
||||
|
||||
async function onWhitelistedBeforeSendHeaders(
|
||||
details: OnBeforeSendHeadersDetails
|
||||
) {
|
||||
if (!details.requestHeaders) {
|
||||
throw logger.error("OnBeforeSendHeaders handler details missing requestHeaders.");
|
||||
throw logger.error(
|
||||
"OnBeforeSendHeaders handler details missing requestHeaders."
|
||||
);
|
||||
}
|
||||
|
||||
if (details.originUrl && !originUrlCache.includes(details.originUrl)) {
|
||||
originUrlCache.push(details.originUrl);
|
||||
}
|
||||
|
||||
const host = details.requestHeaders.find(
|
||||
header => header.name === "Host");
|
||||
const host = details.requestHeaders.find(header => header.name === "Host");
|
||||
|
||||
for (const header of details.requestHeaders) {
|
||||
if (header.name === "User-Agent") {
|
||||
header.value = host?.value === "www.youtube.com"
|
||||
? chromeUserAgentHybrid
|
||||
: chromeUserAgent;
|
||||
header.value =
|
||||
host?.value === "www.youtube.com"
|
||||
? chromeUserAgentHybrid
|
||||
: chromeUserAgent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -101,8 +106,8 @@ export async function initWhitelist() {
|
||||
* main site is whitelisted.
|
||||
*/
|
||||
function onWhitelistedChildBeforeSendHeaders(
|
||||
details: OnBeforeSendHeadersDetails) {
|
||||
|
||||
details: OnBeforeSendHeadersDetails
|
||||
) {
|
||||
if (!details.requestHeaders || !details.frameAncestors) {
|
||||
return;
|
||||
}
|
||||
@@ -110,13 +115,15 @@ function onWhitelistedChildBeforeSendHeaders(
|
||||
for (const ancestor of details.frameAncestors) {
|
||||
if (originUrlCache.includes(ancestor.url)) {
|
||||
const host = details.requestHeaders.find(
|
||||
header => header.name === "Host");
|
||||
header => header.name === "Host"
|
||||
);
|
||||
|
||||
for (const header of details.requestHeaders) {
|
||||
if (header.name === "User-Agent") {
|
||||
header.value = host?.value === "www.youtube.com"
|
||||
? chromeUserAgentHybrid
|
||||
: chromeUserAgent;
|
||||
header.value =
|
||||
host?.value === "www.youtube.com"
|
||||
? chromeUserAgentHybrid
|
||||
: chromeUserAgent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +135,6 @@ function onWhitelistedChildBeforeSendHeaders(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sender applications load a cast_sender.js script that
|
||||
* functions as a loader for the internal chrome-extension:
|
||||
@@ -165,16 +171,17 @@ async function onBeforeCastSDKRequest(details: OnBeforeRequestDetails) {
|
||||
await browser.tabs.executeScript(details.tabId, {
|
||||
code: `
|
||||
window.isFramework = ${
|
||||
details.url === CAST_FRAMEWORK_LOADER_SCRIPT_URL};
|
||||
`
|
||||
, frameId: details.frameId
|
||||
, runAt: "document_start"
|
||||
details.url === CAST_FRAMEWORK_LOADER_SCRIPT_URL
|
||||
};
|
||||
`,
|
||||
frameId: details.frameId,
|
||||
runAt: "document_start"
|
||||
});
|
||||
|
||||
await browser.tabs.executeScript(details.tabId, {
|
||||
file: "shim/contentBridge.js"
|
||||
, frameId: details.frameId
|
||||
, runAt: "document_start"
|
||||
file: "shim/contentBridge.js",
|
||||
frameId: details.frameId,
|
||||
runAt: "document_start"
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -182,40 +189,41 @@ async function onBeforeCastSDKRequest(details: OnBeforeRequestDetails) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async function registerUserAgentWhitelist() {
|
||||
const { userAgentWhitelist
|
||||
, userAgentWhitelistEnabled } = await options.getAll();
|
||||
const { userAgentWhitelist, userAgentWhitelistEnabled } =
|
||||
await options.getAll();
|
||||
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
onBeforeCastSDKRequest
|
||||
, { urls: [
|
||||
CAST_LOADER_SCRIPT_URL
|
||||
, CAST_FRAMEWORK_LOADER_SCRIPT_URL ]}
|
||||
, [ "blocking" ]);
|
||||
onBeforeCastSDKRequest,
|
||||
{ urls: [CAST_LOADER_SCRIPT_URL, CAST_FRAMEWORK_LOADER_SCRIPT_URL] },
|
||||
["blocking"]
|
||||
);
|
||||
|
||||
if (!userAgentWhitelistEnabled || !userAgentWhitelist.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||
onWhitelistedBeforeSendHeaders
|
||||
, { urls: userAgentWhitelist }
|
||||
, [ "blocking", "requestHeaders" ]);
|
||||
onWhitelistedBeforeSendHeaders,
|
||||
{ urls: userAgentWhitelist },
|
||||
["blocking", "requestHeaders"]
|
||||
);
|
||||
|
||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||
onWhitelistedChildBeforeSendHeaders
|
||||
, { urls: [ "<all_urls>" ]}
|
||||
, [ "blocking", "requestHeaders" ]);
|
||||
onWhitelistedChildBeforeSendHeaders,
|
||||
{ urls: ["<all_urls>"] },
|
||||
["blocking", "requestHeaders"]
|
||||
);
|
||||
}
|
||||
|
||||
function unregisterUserAgentWhitelist() {
|
||||
originUrlCache.length = 0;
|
||||
|
||||
browser.webRequest.onBeforeSendHeaders
|
||||
.removeListener(onWhitelistedBeforeSendHeaders);
|
||||
browser.webRequest.onBeforeSendHeaders
|
||||
.removeListener(onWhitelistedChildBeforeSendHeaders);
|
||||
browser.webRequest.onBeforeRequest
|
||||
.removeListener(onBeforeCastSDKRequest);
|
||||
browser.webRequest.onBeforeSendHeaders.removeListener(
|
||||
onWhitelistedBeforeSendHeaders
|
||||
);
|
||||
browser.webRequest.onBeforeSendHeaders.removeListener(
|
||||
onWhitelistedChildBeforeSendHeaders
|
||||
);
|
||||
browser.webRequest.onBeforeRequest.removeListener(onBeforeCastSDKRequest);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user