mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-11 10:09:59 +00:00
Restructure background script (#70)
Splits some background script functionality into separate modules: - Receiver selector handling is moved to ./SelectorManager. - Status bridge handling is moved to ./StatusManager. - Menu creation and updates are handled in ./createMenus. - Shim creation is handled in ./createShim. TypedEventTarget allows EventTarget-derived classes to export typed events. Options type definition is moved to ./lib/options, module assumes more responsibility for update handling and provides a "changed" event. Private cast._requestSession method allows bypassing receiver selector.
This commit is contained in:
@@ -6,9 +6,6 @@ import SessionRequest from "./SessionRequest";
|
||||
import { AutoJoinPolicy
|
||||
, DefaultActionPolicy } from "../enums";
|
||||
|
||||
import { ReceiverSelectorMediaType }
|
||||
from "../../../receiver_selectors/ReceiverSelector";
|
||||
|
||||
|
||||
export default class ApiConfig {
|
||||
public additionalSessionRequests: any[] = [];
|
||||
@@ -23,15 +20,5 @@ export default class ApiConfig {
|
||||
, public autoJoinPolicy: string
|
||||
= AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED
|
||||
, public defaultActionPolicy: string
|
||||
= DefaultActionPolicy.CREATE_SESSION
|
||||
|
||||
// TODO: Remove awful hack for mirror casting
|
||||
, public _defaultMediaType: ReceiverSelectorMediaType
|
||||
= ReceiverSelectorMediaType.App
|
||||
, public _availableMediaTypes: ReceiverSelectorMediaType
|
||||
= ReceiverSelectorMediaType.App
|
||||
| ReceiverSelectorMediaType.Tab
|
||||
| ReceiverSelectorMediaType.Screen
|
||||
| ReceiverSelectorMediaType.File) {
|
||||
}
|
||||
= DefaultActionPolicy.CREATE_SESSION) {}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import ApiConfig from "./classes/ApiConfig";
|
||||
import DialRequest from "./classes/DialRequest";
|
||||
import Error_ from "./classes/Error";
|
||||
import Image_ from "./classes/Image";
|
||||
import Receiver from "./classes/Receiver";
|
||||
import Receiver_ from "./classes/Receiver";
|
||||
import ReceiverDisplayStatus from "./classes/ReceiverDisplayStatus";
|
||||
import SenderApplication from "./classes/SenderApplication";
|
||||
import Session from "./classes/Session";
|
||||
@@ -26,18 +26,18 @@ import { AutoJoinPolicy
|
||||
|
||||
import * as media from "./media";
|
||||
|
||||
|
||||
import { ReceiverSelectorMediaType }
|
||||
from "../../receiver_selectors/ReceiverSelector";
|
||||
from "../../receiver_selectors/ReceiverSelector";
|
||||
import { Receiver } from "../../types";
|
||||
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
||||
|
||||
|
||||
type ReceiverActionListener = (
|
||||
receiver: Receiver
|
||||
receiver: Receiver_
|
||||
, receiverAction: string) => void;
|
||||
|
||||
type RequestSessionSuccessCallback = (
|
||||
session: Session
|
||||
, selectedMedia: ReceiverSelectorMediaType) => void;
|
||||
type RequestSessionSuccessCallback = (session: Session) => void;
|
||||
|
||||
type SuccessCallback = () => void;
|
||||
type ErrorCallback = (err: Error_) => void;
|
||||
@@ -61,12 +61,13 @@ export {
|
||||
, SenderPlatform, SessionStatus, VolumeControlType
|
||||
|
||||
// Classes
|
||||
, ApiConfig, DialRequest, Receiver, ReceiverDisplayStatus
|
||||
, ApiConfig, DialRequest, ReceiverDisplayStatus
|
||||
, SenderApplication, Session, SessionRequest, Timeout
|
||||
, Volume
|
||||
|
||||
, Error_ as Error
|
||||
, Image_ as Image
|
||||
, Receiver_ as Receiver
|
||||
|
||||
, media
|
||||
};
|
||||
@@ -84,14 +85,17 @@ export function addReceiverActionListener (
|
||||
|
||||
export function initialize (
|
||||
newApiConfig: ApiConfig
|
||||
, successCallback: SuccessCallback
|
||||
, errorCallback: ErrorCallback): void {
|
||||
, successCallback?: SuccessCallback
|
||||
, errorCallback?: ErrorCallback): void {
|
||||
|
||||
console.info("fx_cast (Debug): cast.initialize");
|
||||
|
||||
// Already initialized
|
||||
if (apiConfig) {
|
||||
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER));
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +110,9 @@ export function initialize (
|
||||
? ReceiverAvailability.AVAILABLE
|
||||
: ReceiverAvailability.UNAVAILABLE);
|
||||
|
||||
successCallback();
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export function logMessage (message: string): void {
|
||||
@@ -125,29 +131,37 @@ export function removeReceiverActionListener (
|
||||
}
|
||||
|
||||
export function requestSession (
|
||||
successCallback: RequestSessionSuccessCallback
|
||||
, errorCallback: ErrorCallback
|
||||
, sessionRequest: SessionRequest
|
||||
= apiConfig.sessionRequest): void {
|
||||
successCallback?: RequestSessionSuccessCallback
|
||||
, errorCallback?: ErrorCallback
|
||||
, sessionRequest: SessionRequest = apiConfig.sessionRequest): void {
|
||||
|
||||
console.info("fx_cast (Debug): cast.requestSession");
|
||||
|
||||
// Called before initialization
|
||||
if (!apiConfig) {
|
||||
errorCallback(new Error_(ErrorCode.API_NOT_INITIALIZED));
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.API_NOT_INITIALIZED));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Already requesting session
|
||||
if (sessionRequestInProgress) {
|
||||
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER
|
||||
, "Session request already in progress."));
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER
|
||||
, "Session request already in progress."));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// No available receivers
|
||||
if (!receiverList.length) {
|
||||
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -159,21 +173,94 @@ export function requestSession (
|
||||
// Open destination chooser
|
||||
sendMessageResponse({
|
||||
subject: "main:/selectReceiverBegin"
|
||||
, data: {
|
||||
defaultMediaType: apiConfig._defaultMediaType
|
||||
, availableMediaTypes: apiConfig._availableMediaTypes
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function _requestSession (
|
||||
_receiver: Receiver
|
||||
, successCallback?: RequestSessionSuccessCallback
|
||||
, errorCallback?: ErrorCallback): void {
|
||||
|
||||
console.info("fx_cast (Debug): cast._requestSession");
|
||||
|
||||
if (!apiConfig) {
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.API_NOT_INITIALIZED));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionRequestInProgress) {
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER
|
||||
, "Session request already in progress."));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!receiverList.length) {
|
||||
if (errorCallback) {
|
||||
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sessionRequestInProgress = true;
|
||||
|
||||
sessionSuccessCallback = successCallback;
|
||||
sessionErrorCallback = errorCallback;
|
||||
|
||||
|
||||
const selectedReceiver = new Receiver_(
|
||||
_receiver.id
|
||||
, _receiver.friendlyName);
|
||||
|
||||
(selectedReceiver as any)._address = _receiver.host;
|
||||
(selectedReceiver as any)._port = _receiver.port;
|
||||
|
||||
function createSession () {
|
||||
sessionList.push(new Session(
|
||||
sessionList.length.toString() // sessionId
|
||||
, apiConfig.sessionRequest.appId // appId
|
||||
, _receiver.friendlyName // displayName
|
||||
, [] // appImages
|
||||
, selectedReceiver // receiver
|
||||
, (session: Session) => {
|
||||
sendMessageResponse({
|
||||
subject: "main:/sessionCreated"
|
||||
});
|
||||
|
||||
sessionRequestInProgress = false;
|
||||
|
||||
if (sessionSuccessCallback) {
|
||||
sessionSuccessCallback(session);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// If an existing session is active, stop it and start new one
|
||||
if (sessionList.length) {
|
||||
const lastSession = sessionList[sessionList.length - 1];
|
||||
|
||||
if (lastSession.status !== SessionStatus.STOPPED) {
|
||||
lastSession.stop(createSession, null);
|
||||
}
|
||||
} else {
|
||||
createSession();
|
||||
}
|
||||
}
|
||||
|
||||
export function requestSessionById (sessionId: string): void {
|
||||
console.info("STUB :: cast.requestSessionById");
|
||||
}
|
||||
|
||||
export function setCustomReceivers (
|
||||
receivers: Receiver[]
|
||||
, successCallback: SuccessCallback
|
||||
, errorCallback: ErrorCallback): void {
|
||||
receivers: Receiver_[]
|
||||
, successCallback?: SuccessCallback
|
||||
, errorCallback?: ErrorCallback): void {
|
||||
|
||||
console.info("STUB :: cast.setCustomReceivers");
|
||||
}
|
||||
@@ -240,7 +327,7 @@ onMessage(async message => {
|
||||
case "shim:/selectReceiverEnd": {
|
||||
console.info("fx_cast (Debug): Selected receiver");
|
||||
|
||||
const selectedReceiver = new Receiver(
|
||||
const selectedReceiver = new Receiver_(
|
||||
message.data.receiver.id
|
||||
, message.data.receiver.friendlyName);
|
||||
|
||||
@@ -261,9 +348,9 @@ onMessage(async message => {
|
||||
|
||||
sessionRequestInProgress = false;
|
||||
|
||||
sessionSuccessCallback(
|
||||
session
|
||||
, message.data.mediaType);
|
||||
if (sessionSuccessCallback) {
|
||||
sessionSuccessCallback(session);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -287,7 +374,10 @@ onMessage(async message => {
|
||||
case "shim:/selectReceiverCancelled": {
|
||||
if (sessionRequestInProgress) {
|
||||
sessionRequestInProgress = false;
|
||||
sessionErrorCallback(new Error_(ErrorCode.CANCEL));
|
||||
|
||||
if (sessionErrorCallback) {
|
||||
sessionErrorCallback(new Error_(ErrorCode.CANCEL));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
import { CAST_LOADER_SCRIPT_URL
|
||||
, CAST_SCRIPT_URLS } from "../endpoints";
|
||||
, CAST_SCRIPT_URLS } from "../lib/endpoints";
|
||||
|
||||
|
||||
(window.wrappedJSObject as any).chrome = cloneInto({}, window);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
import { loadScript } from "../lib/utils";
|
||||
import { Message } from "../types";
|
||||
import { onMessageResponse, sendMessage } from "./eventMessageChannel";
|
||||
|
||||
|
||||
@@ -17,12 +18,17 @@ if (isFramework) {
|
||||
|
||||
|
||||
// Message port to background script
|
||||
const backgroundPort = browser.runtime.connect({ name: "shim" });
|
||||
export const backgroundPort = browser.runtime.connect({ name: "shim" });
|
||||
|
||||
// Forward background messages to shim
|
||||
backgroundPort.onMessage.addListener(sendMessage);
|
||||
const forwardToShim = (message: Message) => sendMessage(message);
|
||||
const forwardToMain = (message: Message) => backgroundPort.postMessage(message);
|
||||
|
||||
// Forward shim messages to background
|
||||
onMessageResponse(message => {
|
||||
backgroundPort.postMessage(message);
|
||||
// Add message listeners
|
||||
backgroundPort.onMessage.addListener(forwardToShim);
|
||||
const listener = onMessageResponse(forwardToMain);
|
||||
|
||||
// Remove listeners
|
||||
backgroundPort.onDisconnect.addListener(() => {
|
||||
backgroundPort.onMessage.removeListener(forwardToShim);
|
||||
listener.disconnect();
|
||||
});
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
import * as cast from "./cast";
|
||||
|
||||
import { BridgeInfo } from "../lib/getBridgeInfo";
|
||||
import { BridgeInfo } from "../lib/bridge";
|
||||
import { Message } from "../types";
|
||||
import { onMessage } from "./eventMessageChannel";
|
||||
|
||||
|
||||
let initializedBridgeInfo: BridgeInfo;
|
||||
let initializedBackgroundPort: browser.runtime.Port;
|
||||
|
||||
/**
|
||||
* To support exporting an API from a module, we need to
|
||||
* retain the event-based message passing despite not
|
||||
@@ -14,17 +17,44 @@ import { onMessage } from "./eventMessageChannel";
|
||||
* for and emits these messages, and changing that behavior
|
||||
* is too messy.
|
||||
*/
|
||||
export function init (): Promise<BridgeInfo> {
|
||||
export function ensureInit (): Promise<browser.runtime.Port> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
|
||||
// Trigger message port setup side-effects
|
||||
import("./contentBridge");
|
||||
// If already initialized, just return existing bridge info
|
||||
if (initializedBridgeInfo) {
|
||||
if (initializedBridgeInfo.isVersionCompatible) {
|
||||
resolve(initializedBackgroundPort);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the module is imported into a background script
|
||||
* context, the location will be the internal extension URL,
|
||||
* whereas in a content script, it will be the content page
|
||||
* URL.
|
||||
*/
|
||||
if (window.location.protocol === "moz-extension:") {
|
||||
//
|
||||
} else {
|
||||
// Trigger message port setup side-effects
|
||||
const { backgroundPort } = await import("./contentBridge");
|
||||
initializedBackgroundPort = backgroundPort;
|
||||
}
|
||||
|
||||
onMessage(message => {
|
||||
switch (message.subject) {
|
||||
case "shim:/initialized": {
|
||||
const bridgeInfo: BridgeInfo = message.data;
|
||||
resolve(bridgeInfo);
|
||||
initializedBridgeInfo = message.data;
|
||||
|
||||
if (initializedBridgeInfo.isVersionCompatible) {
|
||||
resolve(initializedBackgroundPort);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import * as cast from "./cast";
|
||||
|
||||
import { CAST_FRAMEWORK_SCRIPT_URL } from "../endpoints";
|
||||
import { CAST_FRAMEWORK_SCRIPT_URL } from "../lib/endpoints";
|
||||
import { loadScript } from "../lib/utils";
|
||||
import { onMessage } from "./eventMessageChannel";
|
||||
|
||||
@@ -13,6 +13,10 @@ if (!_window.chrome) {
|
||||
_window.chrome = {};
|
||||
}
|
||||
|
||||
|
||||
// Remove private APIs
|
||||
delete cast._requestSession;
|
||||
|
||||
// Create page-accessible API object
|
||||
_window.chrome.cast = cast;
|
||||
|
||||
@@ -61,7 +65,6 @@ if (document.currentScript) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMessage(message => {
|
||||
switch (message.subject) {
|
||||
case "shim:/initialized": {
|
||||
|
||||
Reference in New Issue
Block a user