diff --git a/ext/src/_locales/en/messages.json b/ext/src/_locales/en/messages.json index 9377bb7..b3c196b 100755 --- a/ext/src/_locales/en/messages.json +++ b/ext/src/_locales/en/messages.json @@ -29,6 +29,20 @@ , "options_option_localMediaServerPort": { "message": "HTTP server port" } + + , "options_category_uaWhitelist": { + "message": "User agent whitelist" + } + , "options_category_uaWhitelist_description": { + "message": "Sites for which to replace the user agent with a Chrome version for compatibility." + } + , "options_option_uaWhitelistEnabled": { + "message": "Enabled" + } + , "options_option_uaWhitelist": { + "message": "Site list (newline-separated)" + } + , "options_submit": { "message": "Submit" } diff --git a/ext/src/main.js b/ext/src/main.js index 4f7b2cc..a9d917a 100755 --- a/ext/src/main.js +++ b/ext/src/main.js @@ -9,6 +9,8 @@ browser.runtime.onInstalled.addListener(async details => { const initialOptions = { option_localMediaEnabled: true , option_localMediaServerPort: 9555 + , option_uaWhitelistEnabled: true + , option_uaWhitelist: [ "www.netflix.com" ].join("\n") }; switch (details.reason) { @@ -89,6 +91,67 @@ browser.webRequest.onBeforeRequest.addListener( ]} , [ "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. + */ +browser.webRequest.onBeforeSendHeaders.addListener( + async details => { + const { options } = await browser.storage.sync.get("options"); + + // Cancel if feature is disabled + if (!options.option_uaWhitelistEnabled) return; + + // Cancel if not on whitelist + // TODO: Do this with the built in filter + const { hostname } = new URL(details.url); + if (!options.option_uaWhitelist.split("\n").includes(hostname)) return; + + // 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 + }; + } + , { urls: [ "" ]} + , [ "blocking", "requestHeaders" ]); + + // Defines window.chrome for site compatibility browser.contentScripts.register({ allFrames: true diff --git a/ext/src/options/index.jsx b/ext/src/options/index.jsx index ee3e973..a1c5f48 100644 --- a/ext/src/options/index.jsx +++ b/ext/src/options/index.jsx @@ -28,6 +28,8 @@ class OptionsApp extends Component { options: { option_localMediaEnabled: this.state.option_localMediaEnabled , option_localMediaServerPort: this.state.option_localMediaServerPort + , option_uaWhitelistEnabled: this.state.option_uaWhitelistEnabled + , option_uaWhitelist: this.state.option_uaWhitelist } }); } @@ -37,6 +39,7 @@ class OptionsApp extends Component { */ async componentDidMount () { const { options } = await browser.storage.sync.get("options"); + if (options) { this.setState({ ...options @@ -80,8 +83,6 @@ class OptionsApp extends Component { } }; - console.log(ev.target.name); - this.setState({ [ ev.target.name ]: val }); @@ -124,6 +125,36 @@ class OptionsApp extends Component { +
+ + { _("options_category_uaWhitelist") } + +

+ { _("options_category_uaWhitelist_description") } +

+ + + + +
+