diff --git a/app/NativeMacReceiverSelector/ViewController.swift b/app/NativeMacReceiverSelector/ViewController.swift index 2d2ae1a..413848d 100644 --- a/app/NativeMacReceiverSelector/ViewController.swift +++ b/app/NativeMacReceiverSelector/ViewController.swift @@ -66,14 +66,43 @@ class ViewController : NSViewController { self.mediaTypePopUpButton = NSPopUpButton() + self.mediaTypePopUpButton.autoenablesItems = false self.mediaTypePopUpButton.addItems(withTitles: [ initData.i18n_mediaTypeApp , initData.i18n_mediaTypeTab , initData.i18n_mediaTypeScreen ]) + let appItem = self.mediaTypePopUpButton + .item(withTitle: initData.i18n_mediaTypeApp)! + let tabItem = self.mediaTypePopUpButton + .item(withTitle: initData.i18n_mediaTypeTab)! + let screenItem = self.mediaTypePopUpButton + .item(withTitle: initData.i18n_mediaTypeScreen)! + + // Set tags to enum value + appItem.tag = MediaType.app.rawValue + tabItem.tag = MediaType.tab.rawValue + screenItem.tag = MediaType.screen.rawValue + + if (initData.availableMediaTypes & appItem.tag) == 0 { + self.mediaTypePopUpButton + .item(withTitle: initData.i18n_mediaTypeApp)? + .isEnabled = false + } + if (initData.availableMediaTypes & tabItem.tag) == 0 { + self.mediaTypePopUpButton + .item(withTitle: initData.i18n_mediaTypeTab)? + .isEnabled = false + } + if (initData.availableMediaTypes & screenItem.tag) == 0 { + self.mediaTypePopUpButton + .item(withTitle: initData.i18n_mediaTypeScreen)? + .isEnabled = false + } + self.mediaTypePopUpButton.selectItem( - at: initData.defaultMediaType.rawValue) + withTag: initData.defaultMediaType.rawValue) let mediaTypeStackView = NSStackView(views: [ @@ -140,7 +169,7 @@ extension ViewController : ReceiverViewDelegate { do { let mediaType = MediaType( - rawValue: self.mediaTypePopUpButton.indexOfSelectedItem)! + rawValue: self.mediaTypePopUpButton.selectedItem!.tag)! let selection = ReceiverSelection( receiver: receiver diff --git a/app/NativeMacReceiverSelector/models/InitData.swift b/app/NativeMacReceiverSelector/models/InitData.swift index 41cca09..a7f7b18 100644 --- a/app/NativeMacReceiverSelector/models/InitData.swift +++ b/app/NativeMacReceiverSelector/models/InitData.swift @@ -1,6 +1,7 @@ struct InitData : Codable { let receivers: [Receiver] let defaultMediaType: MediaType + let availableMediaTypes: Int let i18n_extensionName: String let i18n_castButtonTitle: String diff --git a/app/NativeMacReceiverSelector/models/MediaType.swift b/app/NativeMacReceiverSelector/models/MediaType.swift index 65b9dbc..38df504 100644 --- a/app/NativeMacReceiverSelector/models/MediaType.swift +++ b/app/NativeMacReceiverSelector/models/MediaType.swift @@ -1,3 +1,8 @@ -enum MediaType : Int, Codable { - case app, tab, screen +import Foundation + + +enum MediaType: Int, Codable { + case app = 1 + case tab = 2 + case screen = 4 } diff --git a/ext/src/global.d.ts b/ext/src/global.d.ts index 93b2d16..48bb392 100644 --- a/ext/src/global.d.ts +++ b/ext/src/global.d.ts @@ -37,7 +37,8 @@ declare interface RTCPeerConnection { } declare interface MediaDevices { - getDisplayMedia(constraints: MediaStreamConstraints): Promise; + getDisplayMedia (constraints: MediaStreamConstraints) + : Promise; } diff --git a/ext/src/main.ts b/ext/src/main.ts index edf79cb..1cf1054 100755 --- a/ext/src/main.ts +++ b/ext/src/main.ts @@ -541,7 +541,7 @@ async function initCreateStatusBridge (opts: Options) { */ function onStatusBridgeDisconnect () { // Notify shims for receiver availability - for (const [ , receiver ] of statusBridgeReceivers) { + for (const [, receiver ] of statusBridgeReceivers) { for (const [, shim ] of shimMap) { shim.port.postMessage({ subject: "shim:/serviceDown" @@ -752,7 +752,8 @@ async function onConnectShim (port: browser.runtime.Port) { case "main:/selectReceiverBegin": { receiverSelector.open( Array.from(statusBridgeReceivers.values()) - , message.data.defaultMediaType); + , message.data.defaultMediaType + , message.data.availableMediaTypes); break; } diff --git a/ext/src/receiver_selectors/NativeMacReceiverSelector.ts b/ext/src/receiver_selectors/NativeMacReceiverSelector.ts index a37860f..7026902 100644 --- a/ext/src/receiver_selectors/NativeMacReceiverSelector.ts +++ b/ext/src/receiver_selectors/NativeMacReceiverSelector.ts @@ -28,7 +28,8 @@ export default class NativeMacReceiverSelector public async open ( receivers: Receiver[] - , defaultMediaType: ReceiverSelectorMediaType): Promise { + , defaultMediaType: ReceiverSelectorMediaType + , availableMediaTypes: ReceiverSelectorMediaType): Promise { const applicationName = await options.get("bridgeApplicationName"); this.bridgePort = nativeMessaging.connectNative(applicationName); @@ -62,6 +63,7 @@ export default class NativeMacReceiverSelector , data: JSON.stringify({ receivers , defaultMediaType + , availableMediaTypes , i18n_extensionName: _("extensionName") , i18n_castButtonTitle: _("popupCastButtonTitle") diff --git a/ext/src/receiver_selectors/PopupReceiverSelector.ts b/ext/src/receiver_selectors/PopupReceiverSelector.ts index 90bf062..a86691a 100644 --- a/ext/src/receiver_selectors/PopupReceiverSelector.ts +++ b/ext/src/receiver_selectors/PopupReceiverSelector.ts @@ -17,6 +17,7 @@ export default class PopupReceiverSelector private receivers: Receiver[]; private defaultMediaType: ReceiverSelectorMediaType; + private availableMediaTypes: ReceiverSelectorMediaType; private wasReceiverSelected: boolean = false; @@ -53,6 +54,7 @@ export default class PopupReceiverSelector , data: { receivers: this.receivers , defaultMediaType: this.defaultMediaType + , availableMediaTypes: this.availableMediaTypes } }); }); @@ -61,7 +63,8 @@ export default class PopupReceiverSelector public async open ( receivers: Receiver[] - , defaultMediaType: ReceiverSelectorMediaType): Promise { + , defaultMediaType: ReceiverSelectorMediaType + , availableMediaTypes: ReceiverSelectorMediaType): Promise { // If popup already exists, close it if (this.windowId) { @@ -70,6 +73,7 @@ export default class PopupReceiverSelector this.receivers = receivers; this.defaultMediaType = defaultMediaType; + this.availableMediaTypes = availableMediaTypes; // Current window to base centered position on const openerWindow = await browser.windows.getCurrent(); diff --git a/ext/src/receiver_selectors/ReceiverSelector.ts b/ext/src/receiver_selectors/ReceiverSelector.ts index ab8f558..f146864 100644 --- a/ext/src/receiver_selectors/ReceiverSelector.ts +++ b/ext/src/receiver_selectors/ReceiverSelector.ts @@ -4,9 +4,9 @@ import { Receiver } from "../types"; export enum ReceiverSelectorMediaType { - App - , Tab - , Screen + App = 1 + , Tab = 2 + , Screen = 4 } export interface ReceiverSelection { @@ -21,7 +21,8 @@ export type ReceiverSelectorCancelledEvent = CustomEvent; export default interface ReceiverSelector extends EventTarget { open (receivers: Receiver[] - , defaultMediaType: ReceiverSelectorMediaType): void; + , defaultMediaType: ReceiverSelectorMediaType + , availableMediaTypes: ReceiverSelectorMediaType): void; close (): void; } diff --git a/ext/src/senders/mirroringCast.ts b/ext/src/senders/mirroringCast.ts index 4b07476..eae61e9 100644 --- a/ext/src/senders/mirroringCast.ts +++ b/ext/src/senders/mirroringCast.ts @@ -22,6 +22,21 @@ let peerConnection: RTCPeerConnection; let drawWindowIntervalId: number; +let availableMediaTypes = + ReceiverSelectorMediaType.Screen + | ReceiverSelectorMediaType.Tab; + +/** + * Remove "Screen" option when on an insecure origin as + * MediaDevices.getDisplayMedia will not exist (and legacy + * MediaDevices.getUserMedia mediaSource constraint will + * fail). + */ +if (typeof navigator.mediaDevices.getDisplayMedia === "undefined") { + availableMediaTypes &= ~ReceiverSelectorMediaType.Screen; +} + + /** * Sends a message to the fx_cast app running on the * receiver device. @@ -70,7 +85,6 @@ async function onRequestSessionSuccess ( switch (newSelectedMedia) { case ReceiverSelectorMediaType.Tab: { - const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); @@ -118,7 +132,6 @@ async function onRequestSessionSuccess ( } case ReceiverSelectorMediaType.Screen: { - const stream = await navigator.mediaDevices.getDisplayMedia({ video: { cursor: "motion" } , audio: false @@ -142,8 +155,11 @@ async function onRequestSessionSuccess ( function receiverListener (availability: string) { cast.logMessage("receiverListener"); - if (!wasSessionRequested - && availability === cast.ReceiverAvailability.AVAILABLE) { + if (wasSessionRequested) { + return; + } + + if (availability === cast.ReceiverAvailability.AVAILABLE) { wasSessionRequested = true; cast.requestSession( onRequestSessionSuccess @@ -181,7 +197,8 @@ init().then(async bridgeInfo => { , sessionListener , receiverListener , undefined, undefined - , selectedMedia); + , selectedMedia + , availableMediaTypes); cast.initialize(apiConfig , onInitializeSuccess diff --git a/ext/src/shim/cast/classes/ApiConfig.ts b/ext/src/shim/cast/classes/ApiConfig.ts index 409c71d..304d61e 100755 --- a/ext/src/shim/cast/classes/ApiConfig.ts +++ b/ext/src/shim/cast/classes/ApiConfig.ts @@ -26,7 +26,11 @@ export default class ApiConfig { = DefaultActionPolicy.CREATE_SESSION // TODO: Remove awful hack for mirror casting - , public _selectedMedia: ReceiverSelectorMediaType - = ReceiverSelectorMediaType.App) { + , public _defaultMediaType: ReceiverSelectorMediaType + = ReceiverSelectorMediaType.App + , public _availableMediaTypes: ReceiverSelectorMediaType + = ReceiverSelectorMediaType.App + | ReceiverSelectorMediaType.Tab + | ReceiverSelectorMediaType.Screen) { } } diff --git a/ext/src/shim/cast/index.ts b/ext/src/shim/cast/index.ts index 5e619e3..210c54f 100755 --- a/ext/src/shim/cast/index.ts +++ b/ext/src/shim/cast/index.ts @@ -160,7 +160,8 @@ export function requestSession ( sendMessageResponse({ subject: "main:/selectReceiverBegin" , data: { - defaultMediaType: apiConfig._selectedMedia + defaultMediaType: apiConfig._defaultMediaType + , availableMediaTypes: apiConfig._availableMediaTypes } }); } diff --git a/ext/src/shim/contentBridge.ts b/ext/src/shim/contentBridge.ts index 50c7bc1..248db07 100644 --- a/ext/src/shim/contentBridge.ts +++ b/ext/src/shim/contentBridge.ts @@ -1,7 +1,7 @@ "use strict"; -import { onMessageResponse, sendMessage } from "./eventMessageChannel"; import { loadScript } from "../lib/utils"; +import { onMessageResponse, sendMessage } from "./eventMessageChannel"; const { isFramework } diff --git a/ext/src/ui/popup/index.tsx b/ext/src/ui/popup/index.tsx index 8f869d2..1da51e0 100755 --- a/ext/src/ui/popup/index.tsx +++ b/ext/src/ui/popup/index.tsx @@ -28,6 +28,7 @@ browser.runtime.getPlatformInfo() interface PopupAppState { receivers: Receiver[]; mediaType: ReceiverSelectorMediaType; + availableMediaTypes: ReceiverSelectorMediaType; isLoading: boolean; } @@ -41,6 +42,7 @@ class PopupApp extends Component<{}, PopupAppState> { this.state = { receivers: [] , mediaType: ReceiverSelectorMediaType.App + , availableMediaTypes: ReceiverSelectorMediaType.App , isLoading: false }; @@ -64,6 +66,7 @@ class PopupApp extends Component<{}, PopupAppState> { this.setState({ receivers: message.data.receivers , mediaType: message.data.defaultMediaType + , availableMediaTypes: message.data.availableMediaTypes }); break; @@ -90,10 +93,6 @@ class PopupApp extends Component<{}, PopupAppState> { } public render () { - const shareMedia = - this.state.mediaType === ReceiverSelectorMediaType.Tab - || this.state.mediaType === ReceiverSelectorMediaType.Screen; - return (
@@ -102,15 +101,18 @@ class PopupApp extends Component<{}, PopupAppState> { onChange={ this.onSelectChange } className="media-select-dropdown"> diff --git a/ext/tslint.json b/ext/tslint.json index 2df5ee8..eda91d2 100644 --- a/ext/tslint.json +++ b/ext/tslint.json @@ -2,4 +2,7 @@ "extends": [ "../common/tslint.json" ] + , "rules": { + "no-bitwise": false + } }