"use strict"; import messageRouter from "./messageRouter"; const _ = browser.i18n.getMessage; browser.runtime.onInstalled.addListener(async details => { const initialOptions = { option_localMediaEnabled: true , option_localMediaServerPort: 9555 , option_uaWhitelistEnabled: true , option_uaWhitelist: [ "https://www.netflix.com/*" ] }; switch (details.reason) { // Set initial options case "install": browser.storage.sync.set({ options: initialOptions }); break; // Set newly added options case "update": const { options } = await browser.storage.sync.get("options"); const newOptions = {}; // Find options not already in storage for (const [ key, val ] of Object.entries(initialOptions)) { if (!options.hasOwnProperty(key)) { newOptions[key] = val; } } // Update storage with default values of new options browser.storage.sync.set({ options: { ...options , ...newOptions } }); break; } }); // Google-hosted API loader script const SENDER_SCRIPT_URL = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"; const SENDER_SCRIPT_FRAMEWORK_URL = `${SENDER_SCRIPT_URL}?loadCastFramework=1`; /** * Sender applications load a cast_sender.js script that * functions as a loader for the internal chrome-extension: * hosted script. * * We can redirect this and inject our own script to setup * the API shim. */ browser.webRequest.onBeforeRequest.addListener( async details => { switch (details.url) { case SENDER_SCRIPT_URL: // Content/Page script bridge await browser.tabs.executeScript(details.tabId, { file: "content.js" , frameId: details.frameId , runAt: "document_start" }); return { redirectUrl: browser.runtime.getURL("shim/bundle.js") }; case SENDER_SCRIPT_FRAMEWORK_URL: // TODO: implement cast.framework return { cancel: true }; } } , { urls: [ SENDER_SCRIPT_URL , SENDER_SCRIPT_FRAMEWORK_URL ]} , [ "blocking" ]); /** * Returns a Chrome user agent string with the provided platform. */ function getChromeUA (platform) { return `Mozilla/5.0 (${platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36`; } // Desktop platform Chrome UA strings const UA_STRINGS = { "mac" : getChromeUA("Macintosh; Intel Mac OS X 10_14_1") , "win" : getChromeUA("Windows NT 10.0; Win64; x64") , "linux" : getChromeUA("Mozilla/5.0 (X11; Linux x86_64") }; // Current user agent string for all whitelisted requests let currentUAString; /** * 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. * * TODO: Inject script to change navigator.userAgent * property. */ async function onBeforeSendHeaders (details) { const { options } = await browser.storage.sync.get("options"); // Create Chrome UA from platform info on first run if (!currentUAString) { currentUAString = UA_STRINGS[ (await browser.runtime.getPlatformInfo()).os] } // Find and rewrite the User-Agent header for (const header of details.requestHeaders) { if (header.name.toLowerCase() === "user-agent") { header.value = currentUAString; break; } } return { requestHeaders: details.requestHeaders }; } async function registerWebRequestListeners (alteredOptions) { const { options } = await browser.storage.sync.get("options"); // If options aren't set yet, return if (!options) return; const registerFunctions = { onBeforeSendHeaders () { browser.webRequest.onBeforeSendHeaders.addListener( onBeforeSendHeaders , { urls: options.option_uaWhitelistEnabled ? options.option_uaWhitelist : [] } , [ "blocking", "requestHeaders" ]); } }; if (!alteredOptions) { // If no altered properties specified, register all listeners for (const func of Object.values(registerFunctions)) { func(); } } else { if ( alteredOptions.includes("option_uaWhitelist") || alteredOptions.includes("option_uaWhitelistEnabled")) { browser.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders); registerFunctions.onBeforeSendHeaders(); } } } registerWebRequestListeners(); browser.runtime.onMessage.addListener(message => { switch (message.subject) { case "optionsUpdated": const { alteredOptions } = message.data; registerWebRequestListeners(alteredOptions); break; } }); // Defines window.chrome for site compatibility browser.contentScripts.register({ allFrames: true , js: [{ file: "contentSetup.js" }] , matches: [ "" ] , runAt: "document_start" }); // Screen/Tab mirroring "Cast..." context menu item browser.menus.create({ contexts: [ "browser_action", "page" ] , id: "contextCast" , title: _("context_media_cast") }); //