diff --git a/README.md b/README.md index 88a1db51..1ad1d2ae 100644 --- a/README.md +++ b/README.md @@ -77,13 +77,16 @@ local storage). It will require some manual configuration: Mullvad Browser Extension requires the following permissions: - `management` to be able to recommend third party extensions -- `privacy` to disable webRTC -- `proxy` to configure and use Mullvad proxy servers +- `privacy` to disable webRTC and check HTTPS-Only status - `storage` to save preferences - `search` to recommend other search engines -- `tabs` to be able to show proxy settings based on the active tab - `*://*.mullvad.net/*` to get proxy servers list and display your connection information (See `Network requests` for details) + +The following permissions are optional, but are needed to use the proxy feature: + +- `proxy` to configure and use Mullvad proxy servers +- `tabs` to show proxy settings from active tab - `` to have granular proxy settings _Permissions are automatically accepted when testing the extension._ diff --git a/src/background/main.ts b/src/background/main.ts index 0439a5c9..5b9d03f6 100644 --- a/src/background/main.ts +++ b/src/background/main.ts @@ -1,6 +1,5 @@ import { addExtensionsListeners } from '@/helpers/extensions'; -import { initBrowserAction } from '@/helpers/browserAction'; -import { initProxyRequests } from '@/helpers/socksProxy'; +import { cleanProxyListeners, initProxyListeners } from '@/helpers/socksProxy'; // only on dev mode if (import.meta.hot) { @@ -11,8 +10,9 @@ if (import.meta.hot) { // Add listeners on extension actions addExtensionsListeners(); -// Update browserAction for tabs and add listeners -initBrowserAction(); +// Init proxy listeners +initProxyListeners(); -// Add listener for proxy requests -initProxyRequests(); +// Listeners for permissions changes +browser.permissions.onAdded.addListener(initProxyListeners); +browser.permissions.onRemoved.addListener(cleanProxyListeners); diff --git a/src/composables/useActiveTab.ts b/src/composables/useActiveTab.ts index 87b5bf38..d32a6699 100644 --- a/src/composables/useActiveTab.ts +++ b/src/composables/useActiveTab.ts @@ -1,15 +1,19 @@ import { ref } from 'vue'; +import useProxyPermissions from '@/composables/useProxyPermissions'; +const { proxyPermissionsGranted } = useProxyPermissions(); const activeTabHost = ref(''); const isAboutPage = ref(false); const getActiveTab = async () => { - const activeWindow = await browser.windows.getCurrent({ populate: true }); - const activeTab = activeWindow.tabs!.find((tab) => tab.active); + if (proxyPermissionsGranted.value) { + const activeWindow = await browser.windows.getCurrent({ populate: true }); + const activeTab = activeWindow.tabs!.find((tab) => tab.active); - const activeTabURL = new URL(activeTab!.url!); - activeTabHost.value = activeTabURL.hostname; - isAboutPage.value = activeTabURL.protocol === 'about:'; + const activeTabURL = new URL(activeTab!.url!); + activeTabHost.value = activeTabURL.hostname; + isAboutPage.value = activeTabURL.protocol === 'about:'; + } }; const useActiveTab = () => { diff --git a/src/composables/useProxyPermissions.ts b/src/composables/useProxyPermissions.ts new file mode 100644 index 00000000..f9f74b45 --- /dev/null +++ b/src/composables/useProxyPermissions.ts @@ -0,0 +1,20 @@ +import { ref } from 'vue'; +import { getProxyPermissions, requestProxyPermissions } from '@/helpers/permissions'; + +const useProxyPermissions = () => { + const proxyPermissionsGranted = ref(false); + + const checkProxyPermissions = async () => { + proxyPermissionsGranted.value = await getProxyPermissions(); + }; + + const triggerProxyPermissions = async () => { + proxyPermissionsGranted.value = await requestProxyPermissions(); + }; + + checkProxyPermissions(); + + return { proxyPermissionsGranted, triggerProxyPermissions }; +}; + +export default useProxyPermissions; diff --git a/src/helpers/browserAction.ts b/src/helpers/browserAction.ts index 3a120bba..4c039ff8 100644 --- a/src/helpers/browserAction.ts +++ b/src/helpers/browserAction.ts @@ -44,7 +44,7 @@ export const updateTabsProxyBadges = async () => { } }; -const updatedTabListener = async ( +export const updatedTabListener = async ( _tabId: number, _changeInfo: browser.tabs._OnUpdatedChangeInfo, tab: browser.tabs.Tab, diff --git a/src/helpers/permissions.ts b/src/helpers/permissions.ts new file mode 100644 index 00000000..a18ec08c --- /dev/null +++ b/src/helpers/permissions.ts @@ -0,0 +1,13 @@ +export const getProxyPermissions = async () => { + return await browser.permissions.contains({ + permissions: ['proxy', 'tabs'], + origins: [''], + }); +}; + +export const requestProxyPermissions = async () => { + return await browser.permissions.request({ + permissions: ['proxy', 'tabs'], + origins: [''], + }); +}; diff --git a/src/helpers/socksProxy.ts b/src/helpers/socksProxy.ts index 9dc99b52..1447bdbb 100644 --- a/src/helpers/socksProxy.ts +++ b/src/helpers/socksProxy.ts @@ -1,6 +1,9 @@ -import { RequestDetails, ProxyDetails } from './socksProxy.types'; import ipaddr from 'ipaddr.js'; +import { RequestDetails, ProxyDetails } from './socksProxy.types'; +import { getProxyPermissions } from './permissions'; +import { initBrowserAction, updatedTabListener } from './browserAction'; + const getGlobalProxyDetails = async (): Promise => { const response = await browser.storage.local.get('globalProxyDetails'); @@ -39,6 +42,32 @@ export const initProxyRequests = () => { browser.proxy.onRequest.addListener(handleProxyRequest, { urls: [''] }); }; +export const initProxyListeners = async () => { + const proxyPermissionsGranted = await getProxyPermissions(); + if (proxyPermissionsGranted) { + await removeProxyListeners(); + await addProxyListeners(); + } +}; + +export const cleanProxyListeners = async () => { + const proxyPermissionsGranted = await getProxyPermissions(); + + if (!proxyPermissionsGranted) { + await removeProxyListeners(); + } +}; + +const addProxyListeners = async () => { + initBrowserAction(); + initProxyRequests(); +}; + +const removeProxyListeners = async () => { + browser.tabs.onUpdated.removeListener(updatedTabListener); + browser.proxy.onRequest.removeListener(handleProxyRequest); +}; + // TODO decide what how to handle fallback proxy (if proxy is invalid, it will fallback to Firefox proxy if configured) // https://bugzilla.mozilla.org/show_bug.cgi?id=1750561 diff --git a/src/manifest.ts b/src/manifest.ts index 664518de..e224b01d 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -27,16 +27,8 @@ export async function getManifest() { '48': './assets/mullvad-logo.svg', '96': './assets/mullvad-logo.svg', }, - permissions: [ - 'management', - 'privacy', - 'proxy', - 'search', - 'storage', - 'tabs', - '*://*.mullvad.net/*', - '', - ], + permissions: ['management', 'privacy', 'search', 'storage', '*://*.mullvad.net/*'], + optional_permissions: ['proxy', 'tabs', ''], browser_specific_settings: { gecko: { strict_min_version: '91.1.0', diff --git a/src/popup/views/Home.vue b/src/popup/views/Home.vue index 532914b0..ca65225b 100644 --- a/src/popup/views/Home.vue +++ b/src/popup/views/Home.vue @@ -1,5 +1,5 @@