Re-work whitelist feature to allow per-site UA configuration

This commit is contained in:
hensm
2022-05-29 20:38:09 +01:00
parent 32a6b218f8
commit 539d2c60d9
15 changed files with 426 additions and 510 deletions

View File

@@ -35,17 +35,43 @@ Missing/outdated strings:
- `optionsMirroringCategoryDescription` - `optionsMirroringCategoryDescription`
- `optionsMirroringEnabled` - `optionsMirroringEnabled`
- `optionsMirroringAppId` - `optionsMirroringAppId`
- `popupWhitelistNotWhitelisted`
- `popupWhitelistAddToWhitelist`
- `popupMediaTypeAppNotFound`
- `optionsBridgeCompatible` - `optionsBridgeCompatible`
- `optionsBridgeLikelyCompatible` - `optionsBridgeLikelyCompatible`
- `optionsBridgeIncompatible` - `optionsBridgeIncompatible`
- `optionsSiteWhitelistCategoryName`
- `optionsSiteWhitelistCategoryDescription`
- `optionsSiteWhitelistEnabled`
- `optionsSiteWhitelistEnabledDescription`
- `optionsSiteWhitelistContent`
- `optionsSiteWhitelistBasicView`
- `optionsSiteWhitelistRawView`
- `optionsSiteWhitelistSaveRaw`
- `optionsSiteWhitelistAddItem`
- `optionsSiteWhitelistUserAgent`
- `optionsSiteWhitelistEditItem`
- `optionsSiteWhitelistRemoveItem`
- `optionsSiteWhitelistInvalidMatchPattern`
- `popupWhitelistNotWhitelisted`
- `popupWhitelistAddToWhitelist`
- `popupMediaTypeAppNotFound`
- `popupCastMenuTitle` - `popupCastMenuTitle`
- `popupStopMenuTitle` - `popupStopMenuTitle`
- `es` - `es`
- `optionsSiteWhitelistCategoryName`
- `optionsSiteWhitelistCategoryDescription`
- `optionsSiteWhitelistEnabled`
- `optionsSiteWhitelistEnabledDescription`
- `optionsSiteWhitelistContent`
- `optionsSiteWhitelistBasicView`
- `optionsSiteWhitelistRawView`
- `optionsSiteWhitelistSaveRaw`
- `optionsSiteWhitelistAddItem`
- `optionsSiteWhitelistUserAgent`
- `optionsSiteWhitelistEditItem`
- `optionsSiteWhitelistRemoveItem`
- `optionsSiteWhitelistInvalidMatchPattern`
- `popupWhitelistNotWhitelisted` - `popupWhitelistNotWhitelisted`
- `popupWhitelistAddToWhitelist` - `popupWhitelistAddToWhitelist`
- `popupCastMenuTitle` - `popupCastMenuTitle`
@@ -54,23 +80,47 @@ Missing/outdated strings:
- `nl` - `nl`
- `optionsBridgeBackupEnabled` - `optionsBridgeBackupEnabled`
- `optionsUserAgentWhitelistRestrictedEnabled` - `optionsBridgeCompatible`
- `optionsUserAgentWhitelistRestrictedEnabledDescription` - `optionsBridgeLikelyCompatible`
- `optionsBridgeIncompatible`
- `optionsOptionRecommended` - `optionsOptionRecommended`
- `optionsMirroringCategoryName` - `optionsMirroringCategoryName`
- `optionsMirroringCategoryDescription` - `optionsMirroringCategoryDescription`
- `optionsMirroringEnabled` - `optionsMirroringEnabled`
- `optionsMirroringAppId` - `optionsMirroringAppId`
- `optionsSiteWhitelistCategoryName`
- `optionsSiteWhitelistCategoryDescription`
- `optionsSiteWhitelistEnabled`
- `optionsSiteWhitelistEnabledDescription`
- `optionsSiteWhitelistContent`
- `optionsSiteWhitelistBasicView`
- `optionsSiteWhitelistRawView`
- `optionsSiteWhitelistSaveRaw`
- `optionsSiteWhitelistAddItem`
- `optionsSiteWhitelistUserAgent`
- `optionsSiteWhitelistEditItem`
- `optionsSiteWhitelistRemoveItem`
- `optionsSiteWhitelistInvalidMatchPattern`
- `popupWhitelistNotWhitelisted` - `popupWhitelistNotWhitelisted`
- `popupWhitelistAddToWhitelist` - `popupWhitelistAddToWhitelist`
- `popupMediaTypeAppNotFound` - `popupMediaTypeAppNotFound`
- `optionsBridgeCompatible`
- `optionsBridgeLikelyCompatible`
- `optionsBridgeIncompatible`
- `popupCastMenuTitle` - `popupCastMenuTitle`
- `popupStopMenuTitle` - `popupStopMenuTitle`
- `no` - `no`
- `optionsSiteWhitelistCategoryName`
- `optionsSiteWhitelistCategoryDescription`
- `optionsSiteWhitelistEnabled`
- `optionsSiteWhitelistEnabledDescription`
- `optionsSiteWhitelistContent`
- `optionsSiteWhitelistBasicView`
- `optionsSiteWhitelistRawView`
- `optionsSiteWhitelistSaveRaw`
- `optionsSiteWhitelistAddItem`
- `optionsSiteWhitelistUserAgent`
- `optionsSiteWhitelistEditItem`
- `optionsSiteWhitelistRemoveItem`
- `optionsSiteWhitelistInvalidMatchPattern`
- `popupWhitelistNotWhitelisted` - `popupWhitelistNotWhitelisted`
- `popupWhitelistAddToWhitelist` - `popupWhitelistAddToWhitelist`
- `popupCastMenuTitle` - `popupCastMenuTitle`

View File

@@ -259,55 +259,55 @@
"description": "Receiver selector close if focus lost option checkbox label." "description": "Receiver selector close if focus lost option checkbox label."
}, },
"optionsUserAgentWhitelistCategoryName": { "optionsSiteWhitelistCategoryName": {
"message": "Useragent-Whitelist", "message": "Useragent-Whitelist",
"description": "Options page whitelist category title." "description": "Options page whitelist category title."
}, },
"optionsUserAgentWhitelistCategoryDescription": { "optionsSiteWhitelistCategoryDescription": {
"message": "Seiten auf denen der Useragent aus Kompatibilitätsgründen mit einer Chrome-Version ersetzt wird. Suchmuster müssen gültig sein.", "message": "Seiten auf denen der Useragent aus Kompatibilitätsgründen mit einer Chrome-Version ersetzt wird. Suchmuster müssen gültig sein.",
"description": "Options page whitelist category description." "description": "Options page whitelist category description."
}, },
"optionsUserAgentWhitelistEnabled": { "optionsSiteWhitelistEnabled": {
"message": "Webseiten-Whitelist aktivieren", "message": "Webseiten-Whitelist aktivieren",
"description": "Whitelist enabled checkbox label." "description": "Whitelist enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabled": { "optionsSiteWhitelistRestrictedEnabled": {
"message": "Eingeschränkten Modus aktivieren", "message": "Eingeschränkten Modus aktivieren",
"description": "Whitelist restricted mode enabled checkbox label." "description": "Whitelist restricted mode enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabledDescription": { "optionsSiteWhitelistRestrictedEnabledDescription": {
"message": "Whitelist-Einschränkungen auch auf Seiten anwenden, die unabhängig vom aktuellen Useragent versuchen Stream-Funktionen zu laden.", "message": "Whitelist-Einschränkungen auch auf Seiten anwenden, die unabhängig vom aktuellen Useragent versuchen Stream-Funktionen zu laden.",
"description": "Whitelist restricted mode enabled description." "description": "Whitelist restricted mode enabled description."
}, },
"optionsUserAgentWhitelistContent": { "optionsSiteWhitelistContent": {
"message": "Suchmuster:", "message": "Suchmuster:",
"description": "Match patterns editor widget label." "description": "Match patterns editor widget label."
}, },
"optionsUserAgentWhitelistBasicView": { "optionsSiteWhitelistBasicView": {
"message": "Einfache Ansicht", "message": "Einfache Ansicht",
"description": "Switch to basic view button title." "description": "Switch to basic view button title."
}, },
"optionsUserAgentWhitelistRawView": { "optionsSiteWhitelistRawView": {
"message": "Rohdatenansicht", "message": "Rohdatenansicht",
"description": "Switch to raw view button title." "description": "Switch to raw view button title."
}, },
"optionsUserAgentWhitelistSaveRaw": { "optionsSiteWhitelistSaveRaw": {
"message": "Rohdaten speichern", "message": "Rohdaten speichern",
"description": "Save raw view edits button title." "description": "Save raw view edits button title."
}, },
"optionsUserAgentWhitelistAddItem": { "optionsSiteWhitelistAddItem": {
"message": "Eintrag hinzufügen", "message": "Eintrag hinzufügen",
"description": "Add new whitelist item button title." "description": "Add new whitelist item button title."
}, },
"optionsUserAgentWhitelistEditItem": { "optionsSiteWhitelistEditItem": {
"message": "Bearbeiten", "message": "Bearbeiten",
"description": "Edit whitelist item button title. Displayed on each item." "description": "Edit whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistRemoveItem": { "optionsSiteWhitelistRemoveItem": {
"message": "Entfernen", "message": "Entfernen",
"description": "Remove whitelist item button title. Displayed on each item." "description": "Remove whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistInvalidMatchPattern": { "optionsSiteWhitelistInvalidMatchPattern": {
"message": "Ungültiges Suchmuster $matchPattern$", "message": "Ungültiges Suchmuster $matchPattern$",
"description": "Error displayed by input indicating an invalid match pattern.", "description": "Error displayed by input indicating an invalid match pattern.",
"placeholders": { "placeholders": {

View File

@@ -302,55 +302,55 @@
"description": "Receiver selector close if focus lost option checkbox label." "description": "Receiver selector close if focus lost option checkbox label."
}, },
"optionsUserAgentWhitelistCategoryName": { "optionsSiteWhitelistCategoryName": {
"message": "User agent whitelist", "message": "Site whitelist",
"description": "Options page whitelist category title." "description": "Options page whitelist category title."
}, },
"optionsUserAgentWhitelistCategoryDescription": { "optionsSiteWhitelistCategoryDescription": {
"message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns.", "message": "Site where cast functionality will be enabled and the user agent string will be replaced with a Chrome version for compatibility.",
"description": "Options page whitelist category description." "description": "Options page whitelist category description."
}, },
"optionsUserAgentWhitelistEnabled": { "optionsSiteWhitelistEnabled": {
"message": "Enable site whitelist", "message": "Enable site whitelist",
"description": "Whitelist enabled checkbox label." "description": "Whitelist enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabled": { "optionsSiteWhitelistEnabledDescription": {
"message": "Enable restricted mode", "message": "Disabling this option will enable cast functionality on any site, but the user agent string will not be replaced.",
"description": "Whitelist restricted mode enabled checkbox label."
},
"optionsUserAgentWhitelistRestrictedEnabledDescription": {
"message": "Also apply whitelist restrictions to sites attempting to load cast functionality regardless of the current user agent.",
"description": "Whitelist restricted mode enabled description." "description": "Whitelist restricted mode enabled description."
}, },
"optionsUserAgentWhitelistContent": { "optionsSiteWhitelistContent": {
"message": "Match patterns:", "message": "Match patterns:",
"description": "Match patterns editor widget label." "description": "Match patterns editor widget label."
}, },
"optionsUserAgentWhitelistBasicView": { "optionsSiteWhitelistBasicView": {
"message": "Basic View", "message": "Basic View",
"description": "Switch to basic view button title." "description": "Switch to basic view button title."
}, },
"optionsUserAgentWhitelistRawView": { "optionsSiteWhitelistRawView": {
"message": "Raw View", "message": "Raw View",
"description": "Switch to raw view button title." "description": "Switch to raw view button title."
}, },
"optionsUserAgentWhitelistSaveRaw": { "optionsSiteWhitelistSaveRaw": {
"message": "Save Raw", "message": "Save Raw",
"description": "Save raw view edits button title." "description": "Save raw view edits button title."
}, },
"optionsUserAgentWhitelistAddItem": { "optionsSiteWhitelistAddItem": {
"message": "Add Item", "message": "Add Item",
"description": "Add new whitelist item button title." "description": "Add new whitelist item button title."
}, },
"optionsUserAgentWhitelistEditItem": { "optionsSiteWhitelistUserAgent": {
"message": "Enable UA",
"description": "Whitelist item user agent checkbox title."
},
"optionsSiteWhitelistEditItem": {
"message": "Edit", "message": "Edit",
"description": "Edit whitelist item button title. Displayed on each item." "description": "Edit whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistRemoveItem": { "optionsSiteWhitelistRemoveItem": {
"message": "Remove", "message": "Remove",
"description": "Remove whitelist item button title. Displayed on each item." "description": "Remove whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistInvalidMatchPattern": { "optionsSiteWhitelistInvalidMatchPattern": {
"message": "Invalid match pattern $matchPattern$", "message": "Invalid match pattern $matchPattern$",
"description": "Error displayed by input indicating an invalid match pattern.", "description": "Error displayed by input indicating an invalid match pattern.",
"placeholders": { "placeholders": {

View File

@@ -275,55 +275,55 @@
"description": "Receiver selector close if focus lost option checkbox label." "description": "Receiver selector close if focus lost option checkbox label."
}, },
"optionsUserAgentWhitelistCategoryName": { "optionsSiteWhitelistCategoryName": {
"message": "Lista blanca de agentes de usuario", "message": "Lista blanca de agentes de usuario",
"description": "Options page whitelist category title." "description": "Options page whitelist category title."
}, },
"optionsUserAgentWhitelistCategoryDescription": { "optionsSiteWhitelistCategoryDescription": {
"message": "Sitios en los cuales reemplazar el agente de usuario con una versión de Chrome para compatibilidad. Deben ser patrones de coincidencia válidos.", "message": "Sitios en los cuales reemplazar el agente de usuario con una versión de Chrome para compatibilidad. Deben ser patrones de coincidencia válidos.",
"description": "Options page whitelist category description." "description": "Options page whitelist category description."
}, },
"optionsUserAgentWhitelistEnabled": { "optionsSiteWhitelistEnabled": {
"message": "Activar lista blanca de sitios", "message": "Activar lista blanca de sitios",
"description": "Whitelist enabled checkbox label." "description": "Whitelist enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabled": { "optionsSiteWhitelistRestrictedEnabled": {
"message": "Activar modo restringido", "message": "Activar modo restringido",
"description": "Whitelist restricted mode enabled checkbox label." "description": "Whitelist restricted mode enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabledDescription": { "optionsSiteWhitelistRestrictedEnabledDescription": {
"message": "También aplica restricciones de la lista blanca a sitios intentando cargar la funcionalidad de transmisión sin importar el agente de usuario actual.", "message": "También aplica restricciones de la lista blanca a sitios intentando cargar la funcionalidad de transmisión sin importar el agente de usuario actual.",
"description": "Whitelist restricted mode enabled description." "description": "Whitelist restricted mode enabled description."
}, },
"optionsUserAgentWhitelistContent": { "optionsSiteWhitelistContent": {
"message": "Patrones de coincidencia:", "message": "Patrones de coincidencia:",
"description": "Match patterns editor widget label." "description": "Match patterns editor widget label."
}, },
"optionsUserAgentWhitelistBasicView": { "optionsSiteWhitelistBasicView": {
"message": "Vista básica", "message": "Vista básica",
"description": "Switch to basic view button title." "description": "Switch to basic view button title."
}, },
"optionsUserAgentWhitelistRawView": { "optionsSiteWhitelistRawView": {
"message": "Vista en bruto", "message": "Vista en bruto",
"description": "Switch to raw view button title." "description": "Switch to raw view button title."
}, },
"optionsUserAgentWhitelistSaveRaw": { "optionsSiteWhitelistSaveRaw": {
"message": "Guardar archivo en bruto", "message": "Guardar archivo en bruto",
"description": "Save raw view edits button title." "description": "Save raw view edits button title."
}, },
"optionsUserAgentWhitelistAddItem": { "optionsSiteWhitelistAddItem": {
"message": "Añadir elemento", "message": "Añadir elemento",
"description": "Add new whitelist item button title." "description": "Add new whitelist item button title."
}, },
"optionsUserAgentWhitelistEditItem": { "optionsSiteWhitelistEditItem": {
"message": "Editar", "message": "Editar",
"description": "Edit whitelist item button title. Displayed on each item." "description": "Edit whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistRemoveItem": { "optionsSiteWhitelistRemoveItem": {
"message": "Eliminar", "message": "Eliminar",
"description": "Remove whitelist item button title. Displayed on each item." "description": "Remove whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistInvalidMatchPattern": { "optionsSiteWhitelistInvalidMatchPattern": {
"message": "Patrón de coincidencia $matchPattern$ inválido", "message": "Patrón de coincidencia $matchPattern$ inválido",
"description": "Error displayed by input indicating an invalid match pattern.", "description": "Error displayed by input indicating an invalid match pattern.",
"placeholders": { "placeholders": {

View File

@@ -243,47 +243,47 @@
"message": "Sluit na het verliezen van de focus", "message": "Sluit na het verliezen van de focus",
"description": "Receiver selector close if focus lost option checkbox label." "description": "Receiver selector close if focus lost option checkbox label."
}, },
"optionsUserAgentWhitelistCategoryName": { "optionsSiteWhitelistCategoryName": {
"message": "Gebruikersagent - Whitelist", "message": "Gebruikersagent - Whitelist",
"description": "Options page whitelist category title." "description": "Options page whitelist category title."
}, },
"optionsUserAgentWhitelistCategoryDescription": { "optionsSiteWhitelistCategoryDescription": {
"message": "Websites waarvan de gebruikersagent omwille van compatibiliteit moet worden ingesteld op Chrome. De patronen moeten geldig zijn.", "message": "Websites waarvan de gebruikersagent omwille van compatibiliteit moet worden ingesteld op Chrome. De patronen moeten geldig zijn.",
"description": "Options page whitelist category description." "description": "Options page whitelist category description."
}, },
"optionsUserAgentWhitelistEnabled": { "optionsSiteWhitelistEnabled": {
"message": "Whitelist ingeschakeld", "message": "Whitelist ingeschakeld",
"description": "Whitelist enabled checkbox label." "description": "Whitelist enabled checkbox label."
}, },
"optionsUserAgentWhitelistContent": { "optionsSiteWhitelistContent": {
"message": "Patronen:", "message": "Patronen:",
"description": "Match patterns editor widget label." "description": "Match patterns editor widget label."
}, },
"optionsUserAgentWhitelistBasicView": { "optionsSiteWhitelistBasicView": {
"message": "Basisweergave", "message": "Basisweergave",
"description": "Switch to basic view button title." "description": "Switch to basic view button title."
}, },
"optionsUserAgentWhitelistRawView": { "optionsSiteWhitelistRawView": {
"message": "Ruwe weergave", "message": "Ruwe weergave",
"description": "Switch to raw view button title." "description": "Switch to raw view button title."
}, },
"optionsUserAgentWhitelistSaveRaw": { "optionsSiteWhitelistSaveRaw": {
"message": "Ruwe weergave opslaan", "message": "Ruwe weergave opslaan",
"description": "Save raw view edits button title." "description": "Save raw view edits button title."
}, },
"optionsUserAgentWhitelistAddItem": { "optionsSiteWhitelistAddItem": {
"message": "Voeg toe", "message": "Voeg toe",
"description": "Add new whitelist item button title." "description": "Add new whitelist item button title."
}, },
"optionsUserAgentWhitelistEditItem": { "optionsSiteWhitelistEditItem": {
"message": "Bewerken", "message": "Bewerken",
"description": "Edit whitelist item button title. Displayed on each item." "description": "Edit whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistRemoveItem": { "optionsSiteWhitelistRemoveItem": {
"message": "Verwijderen", "message": "Verwijderen",
"description": "Remove whitelist item button title. Displayed on each item." "description": "Remove whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistInvalidMatchPattern": { "optionsSiteWhitelistInvalidMatchPattern": {
"message": "Ongeldig patroon $matchPattern$", "message": "Ongeldig patroon $matchPattern$",
"description": "Error displayed by input indicating an invalid match pattern.", "description": "Error displayed by input indicating an invalid match pattern.",
"placeholders": { "placeholders": {

View File

@@ -263,55 +263,55 @@
"description": "Receiver selector close if focus lost option checkbox label." "description": "Receiver selector close if focus lost option checkbox label."
}, },
"optionsUserAgentWhitelistCategoryName": { "optionsSiteWhitelistCategoryName": {
"message": "Brukeragent whitelist", "message": "Brukeragent whitelist",
"description": "Options page whitelist category title." "description": "Options page whitelist category title."
}, },
"optionsUserAgentWhitelistCategoryDescription": { "optionsSiteWhitelistCategoryDescription": {
"message": "Sider hvor man kan erstatte brukeragent med en Chrome-versjon for kompatibilitet. Må være et gjenkjennbart mønster.", "message": "Sider hvor man kan erstatte brukeragent med en Chrome-versjon for kompatibilitet. Må være et gjenkjennbart mønster.",
"description": "Options page whitelist category description." "description": "Options page whitelist category description."
}, },
"optionsUserAgentWhitelistEnabled": { "optionsSiteWhitelistEnabled": {
"message": "Skru på whitelist", "message": "Skru på whitelist",
"description": "Whitelist enabled checkbox label." "description": "Whitelist enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabled": { "optionsSiteWhitelistRestrictedEnabled": {
"message": "Skru på ", "message": "Skru på ",
"description": "Whitelist restricted mode enabled checkbox label." "description": "Whitelist restricted mode enabled checkbox label."
}, },
"optionsUserAgentWhitelistRestrictedEnabledDescription": { "optionsSiteWhitelistRestrictedEnabledDescription": {
"message": "Legg også til whitelist-begrensninger til side smo prøvde å laste cast-funksjonalitet uavhengig av nåværende brukeragent.", "message": "Legg også til whitelist-begrensninger til side smo prøvde å laste cast-funksjonalitet uavhengig av nåværende brukeragent.",
"description": "Whitelist restricted mode enabled description." "description": "Whitelist restricted mode enabled description."
}, },
"optionsUserAgentWhitelistContent": { "optionsSiteWhitelistContent": {
"message": "Match mønster", "message": "Match mønster",
"description": "Match patterns editor widget label." "description": "Match patterns editor widget label."
}, },
"optionsUserAgentWhitelistBasicView": { "optionsSiteWhitelistBasicView": {
"message": "Standard visning", "message": "Standard visning",
"description": "Switch to basic view button title." "description": "Switch to basic view button title."
}, },
"optionsUserAgentWhitelistRawView": { "optionsSiteWhitelistRawView": {
"message": "Rå visning", "message": "Rå visning",
"description": "Switch to raw view button title." "description": "Switch to raw view button title."
}, },
"optionsUserAgentWhitelistSaveRaw": { "optionsSiteWhitelistSaveRaw": {
"message": "Lagre rå", "message": "Lagre rå",
"description": "Save raw view edits button title." "description": "Save raw view edits button title."
}, },
"optionsUserAgentWhitelistAddItem": { "optionsSiteWhitelistAddItem": {
"message": "Legg til", "message": "Legg til",
"description": "Add new whitelist item button title." "description": "Add new whitelist item button title."
}, },
"optionsUserAgentWhitelistEditItem": { "optionsSiteWhitelistEditItem": {
"message": "Rediger", "message": "Rediger",
"description": "Edit whitelist item button title. Displayed on each item." "description": "Edit whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistRemoveItem": { "optionsSiteWhitelistRemoveItem": {
"message": "Fjern", "message": "Fjern",
"description": "Remove whitelist item button title. Displayed on each item." "description": "Remove whitelist item button title. Displayed on each item."
}, },
"optionsUserAgentWhitelistInvalidMatchPattern": { "optionsSiteWhitelistInvalidMatchPattern": {
"message": "Ugyldig mønster $matchPattern$", "message": "Ugyldig mønster $matchPattern$",
"description": "Error displayed by input indicating an invalid match pattern.", "description": "Error displayed by input indicating an invalid match pattern.",
"placeholders": { "placeholders": {

View File

@@ -137,11 +137,11 @@ async function onMenuClicked(
); );
} }
const whitelist = await options.get("userAgentWhitelist"); const whitelist = await options.get("siteWhitelist");
if (!whitelist.includes(pattern)) { if (!whitelist.find(item => item.pattern === pattern)) {
// Add to whitelist and update options // Add to whitelist and update options
whitelist.push(pattern); whitelist.push({ pattern });
await options.set("userAgentWhitelist", whitelist); await options.set("siteWhitelist", whitelist);
} }
return; return;

View File

@@ -22,6 +22,11 @@ type OnBeforeRequestDetails = Parameters<
frameAncestors?: Array<{ url: string; frameId: number }>; frameAncestors?: Array<{ url: string; frameId: number }>;
}; };
export interface WhitelistItemData {
pattern: string;
isUserAgentDisabled?: boolean;
}
const originUrlCache: string[] = []; const originUrlCache: string[] = [];
let platform: string; let platform: string;
@@ -47,18 +52,18 @@ export async function initWhitelist() {
} }
// Register on first run // Register on first run
await registerUserAgentWhitelist(); await registerSiteWhitelist();
// Re-register when options change // Re-register when options change
options.addEventListener("changed", ev => { options.addEventListener("changed", ev => {
const alteredOpts = ev.detail; const alteredOpts = ev.detail;
if ( if (
alteredOpts.includes("userAgentWhitelist") || alteredOpts.includes("siteWhitelist") ||
alteredOpts.includes("userAgentWhitelistEnabled") alteredOpts.includes("siteWhitelistEnabled")
) { ) {
unregisterUserAgentWhitelist(); unregisterSiteWhitelist();
registerUserAgentWhitelist(); registerSiteWhitelist();
} }
}); });
} }
@@ -189,9 +194,8 @@ async function onBeforeCastSDKRequest(details: OnBeforeRequestDetails) {
}; };
} }
async function registerUserAgentWhitelist() { async function registerSiteWhitelist() {
const { userAgentWhitelist, userAgentWhitelistEnabled } = const { siteWhitelist, siteWhitelistEnabled } = await options.getAll();
await options.getAll();
browser.webRequest.onBeforeRequest.addListener( browser.webRequest.onBeforeRequest.addListener(
onBeforeCastSDKRequest, onBeforeCastSDKRequest,
@@ -199,13 +203,18 @@ async function registerUserAgentWhitelist() {
["blocking"] ["blocking"]
); );
if (!userAgentWhitelistEnabled || !userAgentWhitelist.length) { if (!siteWhitelistEnabled || !siteWhitelist.length) {
return; return;
} }
browser.webRequest.onBeforeSendHeaders.addListener( browser.webRequest.onBeforeSendHeaders.addListener(
onWhitelistedBeforeSendHeaders, onWhitelistedBeforeSendHeaders,
{ urls: userAgentWhitelist }, {
// Filter for items with UA enabled
urls: siteWhitelist.flatMap(item =>
!item.isUserAgentDisabled ? [item.pattern] : []
)
},
["blocking", "requestHeaders"] ["blocking", "requestHeaders"]
); );
@@ -216,7 +225,7 @@ async function registerUserAgentWhitelist() {
); );
} }
function unregisterUserAgentWhitelist() { function unregisterSiteWhitelist() {
originUrlCache.length = 0; originUrlCache.length = 0;
browser.webRequest.onBeforeSendHeaders.removeListener( browser.webRequest.onBeforeSendHeaders.removeListener(

View File

@@ -16,7 +16,6 @@ export default {
mirroringAppId: MIRRORING_APP_ID, mirroringAppId: MIRRORING_APP_ID,
receiverSelectorCloseIfFocusLost: true, receiverSelectorCloseIfFocusLost: true,
receiverSelectorWaitForConnection: true, receiverSelectorWaitForConnection: true,
userAgentWhitelistEnabled: true, siteWhitelistEnabled: true,
userAgentWhitelistRestrictedEnabled: true, siteWhitelist: [{ pattern: "https://www.netflix.com/*" }]
userAgentWhitelist: ["https://www.netflix.com/*"]
} as Options; } as Options;

View File

@@ -1,6 +1,7 @@
"use strict"; "use strict";
import defaultOptions from "../defaultOptions"; import defaultOptions from "../defaultOptions";
import type { WhitelistItemData } from "../background/whitelist";
import logger from "./logger"; import logger from "./logger";
@@ -25,9 +26,8 @@ export interface Options {
mirroringAppId: string; mirroringAppId: string;
receiverSelectorCloseIfFocusLost: boolean; receiverSelectorCloseIfFocusLost: boolean;
receiverSelectorWaitForConnection: boolean; receiverSelectorWaitForConnection: boolean;
userAgentWhitelistEnabled: boolean; siteWhitelistEnabled: boolean;
userAgentWhitelistRestrictedEnabled: boolean; siteWhitelist: WhitelistItemData[];
userAgentWhitelist: string[];
[key: string]: Options[keyof Options]; [key: string]: Options[keyof Options];
} }

View File

@@ -1,316 +0,0 @@
/* eslint-disable max-len */
"use strict";
import React, { Component } from "react";
const _ = browser.i18n.getMessage;
interface EditableListProps {
data: string[];
itemPattern: RegExp;
onChange (data: string[]): void;
itemPatternError (err?: string): string;
}
interface EditableListState {
addingNewItem: boolean;
rawView: boolean;
rawViewValue: string;
}
export default class EditableList extends Component<
EditableListProps, EditableListState> {
private rawViewTextArea: (HTMLTextAreaElement | null) = null;
constructor(props: EditableListProps) {
super(props);
this.state = {
addingNewItem: false
, rawView: false
, rawViewValue: ""
};
this.handleItemRemove = this.handleItemRemove.bind(this);
this.handleItemEdit = this.handleItemEdit.bind(this);
this.handleSwitchView = this.handleSwitchView.bind(this);
this.handleSaveRaw = this.handleSaveRaw.bind(this);
this.handleRawViewTextAreaChange = this.handleRawViewTextAreaChange.bind(this);
this.handleAddItem = this.handleAddItem.bind(this);
this.handleNewItemRemove = this.handleNewItemRemove.bind(this);
this.handleNewItemEdit = this.handleNewItemEdit.bind(this);
}
public render() {
return (
<div className="editable-list">
{ this.state.rawView
? (
<textarea className="editable-list__raw-view"
rows={ Math.min(this.props.data.length, 10) }
value={ this.state.rawViewValue }
onChange={ this.handleRawViewTextAreaChange }
ref={ el => { this.rawViewTextArea = el; }}>
</textarea>
) : (
<ul className="editable-list__items">
{ this.props.data.map((item, i) =>
<EditableListItem text={ item }
itemPattern={ this.props.itemPattern }
itemPatternError={ this.props.itemPatternError }
onRemove={ this.handleItemRemove }
onEdit={ this.handleItemEdit }
key={ i } /> )}
{ this.state.addingNewItem &&
<EditableListItem text=""
itemPattern={ this.props.itemPattern }
itemPatternError={ this.props.itemPatternError }
onRemove={ this.handleNewItemRemove }
onEdit={ this.handleNewItemEdit }
editing={ true } /> }
</ul>
)}
<hr />
<div className="editable-list__view-actions">
{ !this.state.rawView &&
<button className="editable-list__add-button ghost"
title={ _("optionsUserAgentWhitelistAddItem") }
onClick={ this.handleAddItem }
type="button">
<img src="assets/photon_new.svg" alt="icon, add" />
</button> }
{ this.state.rawView &&
<button className="editable-list__save-raw-button"
onClick={ this.handleSaveRaw }
type="button">
{ _("optionsUserAgentWhitelistSaveRaw") }
</button> }
<button className="editable-list__view-button"
onClick={ this.handleSwitchView }
type="button">
{ this.state.rawView
? _("optionsUserAgentWhitelistBasicView")
: _("optionsUserAgentWhitelistRawView") }
</button>
</div>
</div>
);
}
private handleItemRemove(item: string) {
const newItems = new Set(this.props.data);
newItems.delete(item);
this.props.onChange([ ...newItems ]);
}
private handleItemEdit(item: string, newValue: string) {
this.props.onChange(this.props.data.map(
currentItem => currentItem === item
? newValue
: currentItem));
}
private handleSwitchView() {
this.setState(currentState => {
if (currentState.rawView) {
return {
rawView: false
, rawViewValue: ""
};
}
return {
rawView: true
, rawViewValue: this.props.data.join("\n")
};
});
}
private handleSaveRaw() {
this.setState(currentState => {
const newItems = currentState.rawViewValue.split("\n")
.filter(item => item !== "");
if ("itemPattern" in this.props) {
for (const item of newItems) {
if (!this.props.itemPattern.test(item)) {
this.rawViewTextArea?.setCustomValidity(
this.props.itemPatternError(item));
return;
}
}
this.rawViewTextArea?.setCustomValidity("");
}
this.props.onChange(newItems);
});
}
private handleRawViewTextAreaChange(ev: React.ChangeEvent<HTMLTextAreaElement>) {
if (!this.rawViewTextArea) {
return;
}
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
}
this.setState({
rawViewValue: ev.target.value
});
}
private handleAddItem() {
this.setState({
addingNewItem: true
});
}
private handleNewItemRemove() {
this.setState({
addingNewItem: false
});
}
private handleNewItemEdit(_item: string, newItem: string) {
this.setState({
addingNewItem: false
}, () => {
this.props.onChange([ ...this.props.data, newItem ]);
});
}
}
interface EditableListItemProps {
text: string;
itemPattern: RegExp;
editing?: boolean;
itemPatternError (err?: string): string;
onRemove (item: string): void;
onEdit (item: string, newValue: string): void;
}
interface EditableListItemState {
editing: boolean;
editValue: string;
}
class EditableListItem extends Component<
EditableListItemProps, EditableListItemState> {
private input: (HTMLInputElement | null) = null;
constructor(props: EditableListItemProps) {
super(props);
this.state = {
editing: this.props.editing || false
, editValue: ""
};
this.handleRemove = this.handleRemove.bind(this);
this.handleEditBegin = this.handleEditBegin.bind(this);
this.handleEditEnd = this.handleEditEnd.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
}
public render() {
const selected = this.state.editing
? "editable-list__item--selected" : "";
return (
<li className={`editable-list__item ${selected}`}>
<div className="editable-list__title"
onDoubleClick={ this.handleEditBegin }>
{ this.state.editing
? <input className="editable-list__edit-field"
type="text"
ref={ input => this.input = input }
value={ this.state.editValue }
onBlur={ this.handleEditEnd }
onChange={ this.handleInputChange }
onKeyPress={ this.handleInputKeyPress }/>
: this.props.text }
</div>
<button className="editable-list__edit-button ghost"
title={ _("optionsUserAgentWhitelistEditItem") }
onClick={ this.handleEditBegin }
type="button">
<img src="assets/photon_edit.svg" alt="icon, edit" />
</button>
<button className="editable-list__remove-button ghost"
title={ _("optionsUserAgentWhitelistRemoveItem") }
onClick={ this.handleRemove }
type="button">
<img src="assets/photon_delete.svg" alt="icon, remove" />
</button>
</li>
);
}
private stopEditing(input: HTMLInputElement) {
if (this.props.editing
&& !this.props.itemPattern.test(this.state.editValue)) {
input.setCustomValidity(this.props.itemPatternError());
}
if (!input.validity.valid) {
return;
}
this.props.onEdit(this.props.text, this.state.editValue);
this.setState({
editing: false
, editValue: ""
});
}
private handleRemove() {
this.props.onRemove(this.props.text);
}
private handleEditBegin() {
if (!this.state.editing) {
this.setState({
editing: true
, editValue: this.props.text
}, () => {
this.input?.focus();
this.input?.select();
});
}
}
private handleEditEnd(ev: React.FocusEvent<HTMLInputElement>) {
this.stopEditing(ev.target);
}
private handleInputChange(ev: React.ChangeEvent<HTMLInputElement>) {
this.setState({
editValue: ev.target.value
});
// If invalid, set custom error from parent
ev.target.setCustomValidity(!this.props.itemPattern.test(ev.target.value)
? this.props.itemPatternError()
: "");
}
private handleInputKeyPress(ev: React.KeyboardEvent<HTMLInputElement>) {
if (ev.key === "Enter") {
this.stopEditing(ev.target as HTMLInputElement);
}
}
}

View File

@@ -0,0 +1,225 @@
/* eslint-disable max-len */
"use strict";
import React, { Component } from "react";
import type { WhitelistItemData } from "../../background/whitelist";
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/matchPattern";
const _ = browser.i18n.getMessage;
interface WhitelistProps {
items: WhitelistItemData[];
onChange: (items: WhitelistItemData[]) => void;
}
interface WhitelistState {
addingNewItem: boolean;
}
/** Editable list component for site whitelist. */
export default class Whitelist extends Component<
WhitelistProps,
WhitelistState
> {
state: WhitelistState = {
addingNewItem: false
};
render() {
return (
<div className="whitelist">
<ul className="whitelist__items">
{this.props.items.map((item, i) => (
<WhitelistItem
value={item}
onEdit={(oldValue, newValue) => {
// Replace item
this.props.onChange(
this.props.items.map(item =>
item.pattern === oldValue?.pattern
? newValue
: item
)
);
}}
onRemove={value => {
// Remove item
this.props.onChange(
this.props.items.filter(
item => item.pattern !== value?.pattern
)
);
}}
key={i}
/>
))}
{this.state.addingNewItem && (
<WhitelistItem
isEditing={true}
onEdit={(__, newValue) => {
// Add new item
this.setState({ addingNewItem: false }, () => {
this.props.onChange([
...this.props.items,
newValue
]);
});
}}
onRemove={() => {
// Cancel adding new item
this.setState({ addingNewItem: false });
}}
/>
)}
</ul>
<hr />
<div className="whitelist__view-actions">
<button
className="whitelist__add-button ghost"
title={_("optionsSiteWhitelistAddItem")}
onClick={() => this.setState({ addingNewItem: true })}
type="button"
>
<img src="assets/photon_new.svg" alt="icon, add" />
</button>
</div>
</div>
);
}
}
interface WhitelistItemProps {
value?: WhitelistItemData;
/** Initial editing state */
isEditing?: boolean;
onEdit: (
oldValue: WhitelistItemData | undefined,
newValue: WhitelistItemData
) => void;
onRemove: (value?: WhitelistItemData) => void;
}
interface WhitelistItemState {
isEditing: boolean;
editValue?: WhitelistItemData;
}
/** Editable item component for whitelist. */
class WhitelistItem extends Component<WhitelistItemProps, WhitelistItemState> {
private inputElement: HTMLInputElement | null = null;
constructor(props: WhitelistItemProps) {
super(props);
this.state = { isEditing: props.isEditing ?? false };
this.beginEditing = this.beginEditing.bind(this);
this.finishEditing = this.finishEditing.bind(this);
}
/** Sets editing state and focuses input field. */
private beginEditing() {
if (this.state.isEditing) return;
this.setState(
{
isEditing: true,
editValue: this.props.value
},
() => {
this.inputElement?.focus();
this.inputElement?.select();
}
);
}
/** Checks input validity and sends edit update. */
private finishEditing() {
if (!this.state.isEditing || !this.state.editValue) return;
if (this.inputElement?.validity.valid) {
this.props.onEdit(this.props.value, this.state.editValue);
this.setState({
isEditing: false,
editValue: undefined
});
}
}
render() {
const selectedClassName = this.state.isEditing
? "whitelist__item--selected"
: "";
return (
<li className={`whitelist__item ${selectedClassName}`}>
<div
className="whitelist__title"
onDoubleClick={this.beginEditing}
>
{this.state.isEditing ? (
<input
ref={el => (this.inputElement = el)}
type="text"
className="whitelist__input-pattern"
value={this.state.editValue?.pattern}
pattern={REMOTE_MATCH_PATTERN_REGEX.source}
onChange={ev => {
this.setState(prevState => ({
editValue: {
...prevState.editValue,
pattern: ev.target.value
}
}));
}}
onBlur={this.finishEditing}
onKeyPress={ev => {
if (ev.key === "Enter") {
this.finishEditing();
}
}}
/>
) : (
this.props.value?.pattern
)}
</div>
<label className="whitelist__user-agent">
<input
type="checkbox"
checked={!this.props.value?.isUserAgentDisabled}
onChange={ev => {
if (!this.props.value) return;
this.props.onEdit(this.props.value, {
...this.props.value,
isUserAgentDisabled: !ev.target.checked
});
}}
/>
{_("optionsSiteWhitelistUserAgent")}
</label>
<button
className="whitelist__edit-button ghost"
title={_("optionsSiteWhitelistEditItem")}
onClick={this.beginEditing}
type="button"
>
<img src="assets/photon_edit.svg" alt="icon, edit" />
</button>
<button
className="whitelist__remove-button ghost"
title={_("optionsSiteWhitelistRemoveItem")}
onClick={() => {
this.props.onRemove(this.props.value);
}}
type="button"
>
<img src="assets/photon_delete.svg" alt="icon, remove" />
</button>
</li>
);
}
}

View File

@@ -5,14 +5,14 @@ import React, { Component } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import defaultOptions from "../../defaultOptions"; import defaultOptions from "../../defaultOptions";
import type { WhitelistItemData } from "../../background/whitelist";
import Bridge from "./Bridge"; import Bridge from "./Bridge";
import EditableList from "./EditableList"; import Whitelist from "./Whitelist";
import bridge, { BridgeInfo, BridgeTimedOutError } from "../../lib/bridge"; import bridge, { BridgeInfo, BridgeTimedOutError } from "../../lib/bridge";
import logger from "../../lib/logger"; import logger from "../../lib/logger";
import options, { Options } from "../../lib/options"; import options, { Options } from "../../lib/options";
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/matchPattern";
const _ = browser.i18n.getMessage; const _ = browser.i18n.getMessage;
@@ -77,9 +77,6 @@ class OptionsApp extends Component<OptionsAppProps, OptionsAppState> {
this.handleFormChange = this.handleFormChange.bind(this); this.handleFormChange = this.handleFormChange.bind(this);
this.handleInputChange = this.handleInputChange.bind(this); this.handleInputChange = this.handleInputChange.bind(this);
this.handleWhitelistChange = this.handleWhitelistChange.bind(this); this.handleWhitelistChange = this.handleWhitelistChange.bind(this);
this.getWhitelistItemPatternError =
this.getWhitelistItemPatternError.bind(this);
} }
public async componentDidMount() { public async componentDidMount() {
@@ -333,77 +330,43 @@ class OptionsApp extends Component<OptionsAppProps, OptionsAppState> {
<fieldset className="category"> <fieldset className="category">
<legend className="category__name"> <legend className="category__name">
<h2> <h2>{_("optionsSiteWhitelistCategoryName")}</h2>
{_("optionsUserAgentWhitelistCategoryName")}
</h2>
</legend> </legend>
<p className="category__description"> <p className="category__description">
{_("optionsUserAgentWhitelistCategoryDescription")} {_("optionsSiteWhitelistCategoryDescription")}
</p> </p>
<label className="option option--inline"> <label className="option option--inline">
<div className="option__control"> <div className="option__control">
<input <input
name="userAgentWhitelistEnabled" name="siteWhitelistEnabled"
type="checkbox" type="checkbox"
checked={ checked={
this.state.options this.state.options?.siteWhitelistEnabled
?.userAgentWhitelistEnabled
} }
onChange={this.handleInputChange} onChange={this.handleInputChange}
/> />
</div> </div>
<div className="option__label"> <div className="option__label">
{_("optionsUserAgentWhitelistEnabled")} {_("optionsSiteWhitelistEnabled")}
<span className="option__recommended">
{_("optionsOptionRecommended")}
</span>
</div>
</label>
<label className="option option--inline">
<div className="option__control">
<input
name="userAgentWhitelistRestrictedEnabled"
type="checkbox"
checked={
this.state.options
?.userAgentWhitelistRestrictedEnabled
}
onChange={this.handleInputChange}
/>
</div>
<div className="option__label">
{_(
"optionsUserAgentWhitelistRestrictedEnabled"
)}
<span className="option__recommended"> <span className="option__recommended">
{_("optionsOptionRecommended")} {_("optionsOptionRecommended")}
</span> </span>
</div> </div>
<div className="option__description"> <div className="option__description">
{_( {_("optionsSiteWhitelistEnabledDescription")}
"optionsUserAgentWhitelistRestrictedEnabledDescription"
)}
</div> </div>
</label> </label>
<div className="option"> <div className="option">
<div className="option__label"> <div className="option__label">
{_("optionsUserAgentWhitelistContent")} {_("optionsSiteWhitelistContent")}
</div> </div>
<div className="option__control"> <div className="option__control">
{this.state.options?.userAgentWhitelist && ( {this.state.options?.userAgentWhitelist && (
<EditableList <Whitelist
data={ items={this.state.options.siteWhitelist}
this.state.options
.userAgentWhitelist
}
onChange={this.handleWhitelistChange} onChange={this.handleWhitelistChange}
itemPattern={REMOTE_MATCH_PATTERN_REGEX}
itemPatternError={
this.getWhitelistItemPatternError
}
/> />
)} )}
</div> </div>
@@ -485,22 +448,15 @@ class OptionsApp extends Component<OptionsAppProps, OptionsAppState> {
}); });
} }
private handleWhitelistChange(whitelist: string[]) { private handleWhitelistChange(whitelist: WhitelistItemData[]) {
this.setState(currentState => { this.setState(currentState => {
if (currentState.options) { if (currentState.options) {
currentState.options.userAgentWhitelist = whitelist; currentState.options.siteWhitelist = whitelist;
} }
return currentState; return currentState;
}); });
} }
private getWhitelistItemPatternError(info: string): string {
return _("optionsUserAgentWhitelistInvalidMatchPattern", info);
}
} }
ReactDOM.render(<OptionsApp />, document.querySelector("#root"));
ReactDOM.render(
<OptionsApp />
, document.querySelector("#root"));

View File

@@ -316,7 +316,7 @@ button.ghost:not(:hover) {
} }
.editable-list { .whitelist {
background-color: var(--box-background); background-color: var(--box-background);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
color: var(--box-color); color: var(--box-color);
@@ -324,22 +324,18 @@ button.ghost:not(:hover) {
padding: 5px; padding: 5px;
} }
.editable-list__view-actions { .whitelist__view-actions {
display: flex; display: flex;
justify-content: end; justify-content: end;
} }
.editable-list__save-raw-button { .whitelist hr {
margin-inline-end: 5px;
}
.editable-list hr {
border: initial; border: initial;
border-top: 1px solid var(--border-color); border-top: 1px solid var(--border-color);
margin: 5px 0; margin: 5px 0;
} }
.editable-list__items { .whitelist__items {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: initial; margin: initial;
@@ -348,22 +344,22 @@ button.ghost:not(:hover) {
width: calc(100% + 10px); width: calc(100% + 10px);
} }
.editable-list__item { .whitelist__item {
align-items: center; align-items: center;
display: flex; display: flex;
height: 34px; height: 34px;
padding: 0 5px; padding: 0 5px;
} }
.editable-list__item:nth-child(odd) { .whitelist__item:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.05); background-color: rgba(0, 0, 0, 0.05);
} }
.editable-list__item--selected { .whitelist__item--selected {
background-color: var(--blue-50-a30) !important; background-color: var(--blue-50-a30) !important;
} }
.editable-list__title { .whitelist__title {
flex: 1; flex: 1;
font-family: monospace; font-family: monospace;
overflow: hidden; overflow: hidden;
@@ -372,28 +368,24 @@ button.ghost:not(:hover) {
white-space: nowrap; white-space: nowrap;
} }
.editable-list__item:not(.editable-list__item--selected) > .editable-list__title { .whitelist__item:not(.whitelist__item--selected) > .whitelist__title {
padding: 0 8px; padding: 0 8px;
} }
.editable-list__title + button { .whitelist__title + button {
margin-inline-end: 5px; margin-inline-end: 5px;
} }
.editable-list__edit-field { .whitelist__input-pattern {
font: inherit; font: inherit;
margin-inline-end: 1em; margin-inline-end: 1em;
width: -moz-available; width: -moz-available;
} }
.editable-list__raw-view { .whitelist__user-agent {
max-height: 300px; margin-inline-end: 5px;
overflow-y: auto;
resize: vertical;
width: 100%;
} }
.whitelist__add-button {
.editable-list__add-button {
margin-inline-end: auto; margin-inline-end: auto;
} }
@@ -410,7 +402,7 @@ button.ghost:not(:hover) {
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.editable-list__item:nth-child(odd) { .whitelist__item:nth-child(odd) {
background-color: rgba(255, 255, 255, 0.05); background-color: rgba(255, 255, 255, 0.05);
} }
} }

View File

@@ -5,6 +5,8 @@ import React, { Component } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { menuIdPopupCast, menuIdPopupStop } from "../../background/menus"; import { menuIdPopupCast, menuIdPopupStop } from "../../background/menus";
import type { ReceiverSelectorPageInfo } from "../../background/ReceiverSelector";
import type { WhitelistItemData } from "../../background/whitelist";
import knownApps, { KnownApp } from "../../cast/knownApps"; import knownApps, { KnownApp } from "../../cast/knownApps";
import options from "../../lib/options"; import options from "../../lib/options";
@@ -20,7 +22,6 @@ import {
ReceiverSelectorMediaType ReceiverSelectorMediaType
} from "../../types"; } from "../../types";
import { ReceiverSelectorPageInfo } from "../../background/ReceiverSelector";
import { Capability } from "../../cast/sdk/enums"; import { Capability } from "../../cast/sdk/enums";
const _ = browser.i18n.getMessage; const _ = browser.i18n.getMessage;
@@ -92,8 +93,8 @@ interface PopupAppState {
// Options // Options
mirroringEnabled: boolean; mirroringEnabled: boolean;
userAgentWhitelistEnabled: boolean; siteWhitelistEnabled: boolean;
userAgentWhitelist: string[]; siteWhitelist: WhitelistItemData[];
} }
class PopupApp extends Component<PopupAppProps, PopupAppState> { class PopupApp extends Component<PopupAppProps, PopupAppState> {
@@ -114,8 +115,8 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
isPageWhitelisted: false, isPageWhitelisted: false,
isConnecting: false, isConnecting: false,
mirroringEnabled: false, mirroringEnabled: false,
userAgentWhitelistEnabled: true, siteWhitelistEnabled: true,
userAgentWhitelist: [] siteWhitelist: []
}; };
// Store window ref // Store window ref
@@ -229,8 +230,8 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
// Check if target page URL is whitelisted. // Check if target page URL is whitelisted.
if (this.state.pageInfo) { if (this.state.pageInfo) {
for (const patternString of this.state.userAgentWhitelist) { for (const item of this.state.siteWhitelist) {
const pattern = new RemoteMatchPattern(patternString); const pattern = new RemoteMatchPattern(item.pattern);
if (pattern.matches(this.state.pageInfo.url)) { if (pattern.matches(this.state.pageInfo.url)) {
isPageWhitelisted = true; isPageWhitelisted = true;
break; break;
@@ -249,10 +250,10 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
return; return;
} }
const whitelist = await options.get("userAgentWhitelist"); const whitelist = await options.get("siteWhitelist");
if (!whitelist.includes(app.matches)) { if (!whitelist.find(item => item.pattern === app.matches)) {
whitelist.push(app.matches); whitelist.push({ pattern: app.matches });
await options.set("userAgentWhitelist", whitelist); await options.set("siteWhitelist", whitelist);
await browser.tabs.reload(pageInfo.tabId); await browser.tabs.reload(pageInfo.tabId);
window.close(); window.close();
@@ -382,8 +383,8 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
options.getAll().then(opts => { options.getAll().then(opts => {
this.setState({ this.setState({
mirroringEnabled: opts.mirroringEnabled, mirroringEnabled: opts.mirroringEnabled,
userAgentWhitelistEnabled: opts.userAgentWhitelistEnabled, siteWhitelistEnabled: opts.siteWhitelistEnabled,
userAgentWhitelist: opts.userAgentWhitelist siteWhitelist: opts.siteWhitelist
}); });
}); });
@@ -429,9 +430,9 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
// If we don't know the app // If we don't know the app
!this.state.knownApp || !this.state.knownApp ||
// If the whitelist is disabled // If the whitelist is disabled
!this.state.userAgentWhitelistEnabled || !this.state.siteWhitelistEnabled ||
// If the whitelist is enabled, and the page is whitelisted // If the whitelist is enabled, and the page is whitelisted
(this.state.userAgentWhitelistEnabled && (this.state.siteWhitelistEnabled &&
this.state.isPageWhitelisted) || this.state.isPageWhitelisted) ||
// If an app is already loaded on the page // If an app is already loaded on the page
!!( !!(