Skip to content

Commit 3386768

Browse files
committed
Show tab-based connection information in the popup
1 parent 7837eec commit 3386768

File tree

6 files changed

+115
-69
lines changed

6 files changed

+115
-69
lines changed

src/components/Proxy/HomeProxyStatus.vue

+8-32
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,15 @@ import IconLabel from '@/components/IconLabel.vue';
88
import TitleCategory from '@/components/TitleCategory.vue';
99
1010
import useActiveTab from '@/composables/useActiveTab';
11-
import useConnection, { ConnectionKey, defaultConnection } from '@/composables/useConnection';
11+
import { ConnectionKey, defaultConnection } from '@/composables/useConnection';
1212
import useLocations from '@/composables/useLocations';
1313
import useProxyPermissions from '@/composables/useProxyPermissions';
14-
import useSocksProxies from '@/composables/useSocksProxies';
1514
import useSocksProxy from '@/composables/useSocksProxy';
1615
import { checkDomain } from '@/helpers/domain';
1716
18-
const { activeTabHost, isBrowserPage } = useActiveTab();
19-
const { updateConnection } = useConnection();
17+
const { activeTabHost, isAboutPage } = useActiveTab();
2018
const { proxySelect } = useLocations();
2119
const { isGranted, requestPermissions } = useProxyPermissions();
22-
const { getSocksProxies } = useSocksProxies();
2320
const {
2421
allowProxy,
2522
currentHostProxyEnabled,
@@ -105,20 +102,6 @@ const handleTabClick = (tabName: string) => {
105102
lastClickedTab.value = tabName;
106103
};
107104
108-
const handleProxySelect = async (host?: string) => {
109-
proxySelect(host);
110-
await getSocksProxies();
111-
};
112-
const handleRemoveGlobalProxy = () => {
113-
removeGlobalProxy();
114-
updateConnection();
115-
};
116-
117-
const handleToggleGlobalProxy = () => {
118-
toggleGlobalProxy();
119-
updateConnection();
120-
};
121-
122105
const handleRemoveProxy = (host: string) => {
123106
removeCustomProxy(host);
124107
// Force switch to default tab otherwise it doesn't work
@@ -128,13 +111,6 @@ const handleRemoveProxy = (host: string) => {
128111
watch([currentHostProxyEnabled, subDomainProxyEnabled, domainProxyDetails, excludedHosts], () => {
129112
lastClickedTab.value = null;
130113
});
131-
132-
watch(isGranted, () => {
133-
// This is to make sure there's always a proxy list when the user starts using the proxy feature
134-
if (isGranted.value) {
135-
getSocksProxies();
136-
}
137-
});
138114
</script>
139115

140116
<template>
@@ -161,7 +137,7 @@ watch(isGranted, () => {
161137
</div>
162138
</div>
163139

164-
<n-switch :value="globalProxyEnabled" @update-value="handleToggleGlobalProxy()" />
140+
<n-switch :value="globalProxyEnabled" @update-value="toggleGlobalProxy()" />
165141
</div>
166142

167143
<IconLabel v-if="globalProxyEnabled && !connection.isMullvad" type="warning" class="mb-2">
@@ -175,21 +151,21 @@ watch(isGranted, () => {
175151
</p>
176152

177153
<div class="flex justify-between">
178-
<Button size="small" @click="handleProxySelect()">
154+
<Button size="small" @click="proxySelect()">
179155
{{ globalProxyDetails.server ? 'Change location' : 'Select location' }}
180156
</Button>
181157
<Button
182158
v-if="globalProxyDetails.server"
183159
size="small"
184160
color="error"
185-
@click="handleRemoveGlobalProxy"
161+
@click="removeGlobalProxy"
186162
>
187163
Remove proxy
188164
</Button>
189165
</div>
190166
</n-tab-pane>
191167

192-
<n-tab-pane v-if="!isBrowserPage" name="current-domain" :tab="truncatedDomain">
168+
<n-tab-pane v-if="!isAboutPage" name="current-domain" :tab="truncatedDomain">
193169
<div>
194170
<h3 class="font-bold mb-2 break-words">{{ tabDomain.domain }}</h3>
195171
<div v-if="excludedHosts.includes(tabDomain.domain)">
@@ -213,7 +189,7 @@ watch(isGranted, () => {
213189
/>
214190
</div>
215191
<div class="flex justify-between">
216-
<Button size="small" @click="handleProxySelect(tabDomain.domain)">
192+
<Button size="small" @click="proxySelect(tabDomain.domain)">
217193
{{ domainProxyDetails ? 'Change location' : 'Select location' }}
218194
</Button>
219195
<SplitButton
@@ -265,7 +241,7 @@ watch(isGranted, () => {
265241
/>
266242
</div>
267243
<div class="flex justify-between">
268-
<Button size="small" @click="handleProxySelect(tabDomain.subDomain)">
244+
<Button size="small" @click="proxySelect(tabDomain.subDomain)">
269245
{{ subDomainProxyDetails ? 'Change location' : 'Select location' }}
270246
</Button>
271247
<SplitButton

src/composables/useActiveTab.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ import { ref } from 'vue';
22
import { getActiveTabDetails } from '@/helpers/tabs';
33

44
const activeTabHost = ref('');
5-
const isBrowserPage = ref(false);
5+
const isAboutPage = ref(false);
66

77
const getActiveTab = async () => {
8-
const { protocol, host } = await getActiveTabDetails();
8+
const { host, isAboutPage: isAboutPageValue } = await getActiveTabDetails();
99
activeTabHost.value = host;
10-
isBrowserPage.value = protocol === 'about:' || protocol === 'moz-extension:';
10+
isAboutPage.value = isAboutPageValue;
1111
};
1212

1313
const useActiveTab = () => {
1414
getActiveTab();
1515

16-
return { activeTabHost, isBrowserPage };
16+
return { activeTabHost, isAboutPage };
1717
};
1818

1919
export default useActiveTab;

src/composables/useProxyPermissions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ref, readonly } from 'vue';
22
import { getProxyPermissions, requestProxyPermissions } from '@/helpers/permissions';
33

4-
export const useProxyPermissions = () => {
4+
const useProxyPermissions = () => {
55
const isGranted = ref(false);
66

77
const checkProxyPermissions = async () => {

src/composables/useSocksProxy.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { computed } from 'vue';
1+
import { computed, watch } from 'vue';
22

33
import {
44
ProxyDetails,
@@ -171,7 +171,6 @@ const setGlobalProxy = ({
171171
globalProxy.value = newGlobalProxy;
172172
globalProxyDetails.value = newGlobalProxyDetails;
173173

174-
updateConnection();
175174
reloadOptions();
176175
if (proxyAutoReload.value) {
177176
reloadGlobalProxiedTabs(combinedHosts.value);
@@ -217,7 +216,6 @@ const setCustomProxy = (
217216
const removeCustomProxy = (host: string) => {
218217
delete hostProxies.value[host];
219218
delete hostProxiesDetails.value[host];
220-
updateConnection();
221219
updateCurrentTabProxyBadge();
222220
reloadOptions();
223221
if (proxyAutoReload.value) {
@@ -253,6 +251,14 @@ const neverProxyHost = (host: string) => {
253251
}
254252
};
255253

254+
watch(
255+
[() => globalProxyDetails.value, () => hostProxiesDetails.value],
256+
() => {
257+
updateConnection();
258+
},
259+
{ deep: true, immediate: false },
260+
);
261+
256262
const useSocksProxy = () => {
257263
return {
258264
allowProxy,

src/helpers/socksProxy/socksProxy.ts

+90-28
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import ipaddr from 'ipaddr.js';
22

3-
import { RequestDetails, ProxyDetails, ProxyInfoMap } from '@/helpers/socksProxy/socksProxy.types';
3+
import {
4+
RequestDetails,
5+
ProxyDetails,
6+
ProxyInfoMap,
7+
ProxyInfo,
8+
} from '@/helpers/socksProxy/socksProxy.types';
49
import { checkDomain } from '@/helpers/domain';
510
import { getRandomSessionProxy } from '@/helpers/socksProxy/getRandomSessionProxy';
11+
import { getActiveTabDetails } from '@/helpers/tabs';
612

713
// TODO decide what how to handle fallback proxy (if proxy is invalid, it will fallback to Firefox proxy if configured)
814
// https://bugzilla.mozilla.org/show_bug.cgi?id=1750561
@@ -16,60 +22,72 @@ export const handleProxyRequest = async (details: browser.proxy._OnRequestDetail
1622
const { hostProxies } = await browser.storage.local.get('hostProxies');
1723
const { hostProxiesDetails } = await browser.storage.local.get('hostProxiesDetails');
1824

19-
const globalConfigParsed = JSON.parse(globalProxy);
20-
const randomProxyModeParsed = JSON.parse(randomProxyMode);
25+
const globalConfigParsed: ProxyInfo = JSON.parse(globalProxy);
26+
const randomProxyModeParsed: boolean = JSON.parse(randomProxyMode);
2127
const globalProxyDetailsParsed: ProxyDetails = JSON.parse(globalProxyDetails);
2228
const excludedHostsParsed: string[] = JSON.parse(excludedHosts);
2329
const hostProxiesParsed: ProxyInfoMap = JSON.parse(hostProxies);
2430
const hostProxiesDetailsParsed: Record<string, ProxyDetails> = JSON.parse(hostProxiesDetails);
2531

2632
const currentHost = getCurrentHost(details);
2733
const { hasSubdomain, domain, subDomain } = checkDomain(currentHost);
34+
const currentDomain = hasSubdomain ? subDomain : domain;
2835

29-
// Block speculative requests, since we can't identify their origins
36+
const isDomainExcluded = excludedHostsParsed.includes(currentDomain);
37+
const isDomainProxied = Object.hasOwn(hostProxiesParsed, currentDomain);
38+
const isDomainProxydEnabled = !!hostProxiesDetailsParsed[currentDomain]?.socksEnabled;
39+
const isGlobalProxyEnabled = globalProxyDetailsParsed.socksEnabled;
40+
41+
// 1. Block speculative requests, since we can't identify their origins
3042
if (details.type === 'speculative') {
3143
return { cancel: true };
3244
}
3345

34-
// Skip proxy for local/reserved IPs
46+
// 2. Skip proxy for local/reserved IPs
3547
if (isLocalOrReservedIP(currentHost)) {
3648
return { type: 'direct' };
3749
}
3850

39-
// 0. If random proxy is enabled, get a random proxy per domain
40-
if (randomProxyModeParsed) {
41-
const randomProxy = await getRandomSessionProxy(domain);
42-
return randomProxy;
51+
// 3. When the request if a conncheck/DNS check originating from the extension,
52+
// we want to use the same proxy as the active tab, to get a consistent conncheck result
53+
const isExtensionRequest = details.documentUrl?.startsWith('moz-extension://');
54+
const isConnCheck = details.url?.endsWith('am.i.mullvad.net/json');
55+
const isDNSCheck = details.url?.endsWith('dnsleak.am.i.mullvad.net/');
56+
57+
const isExtConnCheck = isExtensionRequest && (isConnCheck || isDNSCheck);
58+
59+
if (isExtConnCheck) {
60+
return getProxyForExtensionConnectionCheck(
61+
isGlobalProxyEnabled,
62+
globalConfigParsed,
63+
randomProxyModeParsed,
64+
excludedHostsParsed,
65+
hostProxiesParsed,
66+
hostProxiesDetailsParsed,
67+
);
4368
}
4469

45-
// 1. Check subdomain level
46-
if (hasSubdomain) {
47-
if (excludedHostsParsed.includes(subDomain)) {
48-
return { type: 'direct' };
49-
}
50-
51-
if (
52-
Object.hasOwn(hostProxiesParsed, subDomain) &&
53-
hostProxiesDetailsParsed[currentHost].socksEnabled
54-
) {
55-
return hostProxiesParsed[subDomain];
56-
}
70+
// 4. Check for random proxy mode
71+
// For now, overrides all other proxy settings
72+
if (randomProxyModeParsed) {
73+
return getRandomSessionProxy(domain);
5774
}
5875

59-
// 2. Check domain level
60-
if (excludedHostsParsed.includes(domain)) {
76+
// 5. Check domain/subdomain level
77+
if (isDomainExcluded) {
6178
return { type: 'direct' };
6279
}
63-
if (Object.hasOwn(hostProxiesParsed, domain) && hostProxiesDetailsParsed[domain].socksEnabled) {
64-
return hostProxiesParsed[domain];
80+
81+
if (isDomainProxied && isDomainProxydEnabled) {
82+
return hostProxiesParsed[currentDomain];
6583
}
6684

67-
// 3. Check global proxy
68-
if (globalProxyDetailsParsed.socksEnabled) {
85+
// 6. Check global proxy
86+
if (isGlobalProxyEnabled) {
6987
return globalConfigParsed;
7088
}
7189

72-
// 4. Default: no proxy
90+
// 7. Default: no proxy
7391
return { type: 'direct' };
7492
} catch (error) {
7593
console.log(error);
@@ -117,3 +135,47 @@ export const isLocalOrReservedIP = (hostname: string) => {
117135
return false;
118136
}
119137
};
138+
139+
const getProxyForExtensionConnectionCheck = async (
140+
isGlobalProxyEnabled: boolean,
141+
globalConfigParsed: ProxyInfo,
142+
randomProxyModeParsed: boolean,
143+
excludedHostsParsed: string[],
144+
hostProxiesParsed: ProxyInfoMap,
145+
hostProxiesDetailsParsed: Record<string, ProxyDetails>,
146+
) => {
147+
const { isAboutPage, host } = await getActiveTabDetails();
148+
const { domain, hasSubdomain, subDomain } = checkDomain(host);
149+
const tabDomain = hasSubdomain ? subDomain : domain;
150+
151+
const isTabDomainExcluded = excludedHostsParsed.includes(tabDomain);
152+
const isTabDomainProxied = Object.hasOwn(hostProxiesParsed, tabDomain);
153+
const isTabProxyEnabled = !!hostProxiesDetailsParsed[tabDomain]?.socksEnabled;
154+
155+
// a) If the current tab is an about page, we only need to check for a global proxy
156+
if (isAboutPage) {
157+
return isGlobalProxyEnabled ? globalConfigParsed : { type: 'direct' };
158+
}
159+
160+
// b) If random proxy mode is enabled, we need to check for the current tab's proxy
161+
if (randomProxyModeParsed) {
162+
return getRandomSessionProxy(tabDomain);
163+
}
164+
165+
// c) If current tab domain is excluded, connection is direct
166+
if (isTabDomainExcluded) {
167+
return { type: 'direct' };
168+
}
169+
170+
// d) If current tab is proxied, we need to check for the current tab's proxy
171+
if (isTabDomainProxied && isTabProxyEnabled) {
172+
return hostProxiesParsed[tabDomain];
173+
}
174+
175+
// e) If global proxy is enabled
176+
if (isGlobalProxyEnabled) {
177+
return globalConfigParsed;
178+
}
179+
180+
return { type: 'direct' };
181+
};

src/helpers/tabs.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ export const getActiveTabDetails = async () => {
3939

4040
// activeTab will be null if tabs permission has not been granted
4141
if (!activeTab?.url) {
42-
return { host: '', protocol: '' };
42+
return { host: '', protocol: '', isAboutPage: false };
4343
}
4444

4545
const activeTabURL = new URL(activeTab.url);
46+
4647
return {
4748
host: activeTabURL.hostname,
4849
protocol: activeTabURL.protocol,
50+
isAboutPage: activeTabURL.protocol === 'about:',
4951
};
5052
};
5153

0 commit comments

Comments
 (0)