Skip to content

Commit cf035d2

Browse files
authored
Merge pull request #103 from Tensai75/v1.4.5
v1.4.5
2 parents 3a28e7a + 98b0e77 commit cf035d2

File tree

5 files changed

+38
-88
lines changed

5 files changed

+38
-88
lines changed

src/components/interception/editDomainDialog.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function reset() {
6060
v-slot="$field"
6161
:name="i18n.t('settings.interception.domains.domain.title')"
6262
:initial-value="domain.domain"
63-
:resolver="requiredUniqueBaseDomainResolver"
63+
:resolver="isAdd ? requiredUniqueBaseDomainResolver : undefined"
6464
:validate-on-blur="true"
6565
:validate-on-value-update="true"
6666
:validate-on-mount="true"
@@ -73,7 +73,7 @@ function reset() {
7373
size="small"
7474
autocomplete="off"
7575
type="text"
76-
:disabled="domain.isDefault"
76+
:disabled="domain.isDefault || isAdd === false"
7777
/>
7878
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple" class="flex-auto">{{
7979
$field.error?.message
Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,30 @@
11
import { browser } from '#imports'
2-
import {
3-
getSettings as getInterceptionSettings,
4-
Settings as InterceptionSettings,
5-
watchSettings as watchInterceptionSettings,
6-
} from '@/services/interception'
2+
import { getActiveDomains, watchSettings as watchInterceptionSettings } from '@/services/interception'
73
import log from '@/services/logger/debugLogger'
84

95
const scriptId: string = 'interception-content-script'
106
const scriptSource: string = '/content-scripts/interception.js'
117

128
export default function (): void {
13-
getInterceptionSettings().then((settings) => {
14-
registerContentScript(settings)
15-
})
16-
watchInterceptionSettings((settings) => {
17-
log.info('interception settings have changed')
18-
registerContentScript(settings)
9+
// async setup
10+
log.info('Setting up interception content script registration')
11+
setupInterceptionContentScriptRegistration()
12+
// synchronous listener for settings changes
13+
watchInterceptionSettings(() => {
14+
log.info('Interception settings have changed, updating interception content script registration')
15+
setupInterceptionContentScriptRegistration()
1916
})
2017
}
2118

22-
function registerContentScript(settings: InterceptionSettings): void {
23-
const domains = getActiveDomainsMatchPatternArray(settings)
24-
browser.scripting.getRegisteredContentScripts().then((scripts) => {
25-
const isRegistered = scripts.some((script) => script.id === scriptId)
26-
if (domains.length === 0) {
27-
handleNoDomains(isRegistered)
28-
} else {
29-
handleDomainsRegistration(isRegistered, domains)
30-
}
31-
})
19+
async function setupInterceptionContentScriptRegistration(): Promise<void> {
20+
const domains = await getActiveDomainsMatchPatternArray()
21+
const scripts = await browser.scripting.getRegisteredContentScripts()
22+
const isRegistered = scripts.some((script) => script.id === scriptId)
23+
if (domains.length === 0) {
24+
handleNoDomains(isRegistered)
25+
} else {
26+
handleDomainsRegistration(isRegistered, domains)
27+
}
3228
}
3329

3430
function handleNoDomains(isRegistered: boolean): void {
@@ -46,7 +42,7 @@ function handleDomainsRegistration(isRegistered: boolean, domains: Array<string>
4642
updateContentScript(domains)
4743
} else {
4844
log.info('Registering the interception content script')
49-
registerNewContentScript(domains)
45+
registerContentScript(domains)
5046
}
5147
}
5248

@@ -64,15 +60,14 @@ function updateContentScript(domains: Array<string>): void {
6460
.catch((e: Error) => log.error('Error while updating the registration of the interception content script', e))
6561
}
6662

67-
function registerNewContentScript(domains: Array<string>): void {
63+
function registerContentScript(domains: Array<string>): void {
6864
browser.scripting
6965
.registerContentScripts([{ id: scriptId, js: [scriptSource], matches: domains }])
7066
.then(() => log.info('Registration of the interception content script completed successfully'))
7167
.catch((e: Error) => log.error('Error while registering the interception content script', e))
7268
}
7369

74-
function getActiveDomainsMatchPatternArray(settings: InterceptionSettings): Array<string> {
75-
return settings.enabled
76-
? settings.domains.filter((domain) => domain.isActive).map((domain) => `*://*.${domain.domain}/*`)
77-
: []
70+
async function getActiveDomainsMatchPatternArray(): Promise<Array<string>> {
71+
const activeDomains = await getActiveDomains()
72+
return activeDomains.map((domain) => `*://*.${domain.domain}/*`)
7873
}

src/entrypoints/background/interception/declarativeNetRequestHandler.ts

Lines changed: 12 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ const redirectURLs = [
2929
// Redirect URL to be used in declarativeNetRequest rule
3030
const redirectURL: string = 'https://connectivity-check.ubuntu.com/204'
3131
// In-memory cache for request details
32-
export const requestCache = new Map<string, RequestDetails>()
3332
export const tabRelationships = new Map<number, number>() // key: new tab ID, value: opener tab ID
3433

3534
// Request methods for declarativeNetRequest rules
@@ -122,10 +121,6 @@ async function registerInterceptionListener(): Promise<void> {
122121
log.info('removing onBeforeRequest listener for interception')
123122
browser.webRequest.onBeforeRequest.removeListener(onBeforeRequestListener)
124123
}
125-
if (browser.webRequest.onHeadersReceived.hasListener(onRedirectHeadersReceivedListener)) {
126-
log.info('removing onRedirectHeadersReceived listener for interception')
127-
browser.webRequest.onHeadersReceived.removeListener(onRedirectHeadersReceivedListener)
128-
}
129124
if (browser.tabs.onCreated.hasListener(onTabCreatedListener)) {
130125
log.info('removing onTabCreated listener for interception')
131126
browser.tabs.onCreated.removeListener(onTabCreatedListener)
@@ -146,14 +141,6 @@ async function registerInterceptionListener(): Promise<void> {
146141
const error = e instanceof Error ? e : new Error(String(e))
147142
log.error('failed to register onBeforeRequest listener for interception:', error)
148143
}
149-
try {
150-
log.info('registering onRedirectHeadersReceived listener for interception')
151-
browser.webRequest.onHeadersReceived.addListener(onRedirectHeadersReceivedListener, { urls: [redirectURL] })
152-
log.info('registration of the onRedirectHeadersReceived listener for interception was successful')
153-
} catch (e) {
154-
const error = e instanceof Error ? e : new Error(String(e))
155-
log.error('failed to register onRedirectHeadersReceived listener for interception:', error)
156-
}
157144
try {
158145
log.info('registering onTabCreated listener for interception')
159146
browser.tabs.onCreated.addListener(onTabCreatedListener)
@@ -179,60 +166,26 @@ function onBeforeRequestListener(
179166
details: Browser.webRequest.OnBeforeRequestDetails
180167
): Browser.webRequest.BlockingResponse | undefined {
181168
isURLTracked(details.url).then((isTracked) => {
182-
if (!isTracked) return
183-
log.info(`request ${details.requestId} to ${details.url} is tracked, caching request details`)
169+
// If the tabId is negative, it means the request comes from the background script itself and hence should be ignored
170+
if (!isTracked || details.tabId < 0) return
171+
log.info(`request ${details.requestId} to ${details.url} is going to be blocked, gathering request details`)
184172
let bodyData: RequestDetails['body']
185173
if (details.method !== 'GET' && details.requestBody) {
186174
if (details.requestBody.formData) {
187175
bodyData = details.requestBody.formData as RequestDetails['body']
188176
}
189177
}
190-
requestCache.set(details.requestId, {
178+
const requestDeatils = {
191179
tabId: details.tabId,
192180
requestId: details.requestId,
193181
url: details.url,
194182
method: details.method,
195183
body: bodyData,
196184
// @ts-expect-error: Property 'originUrl' does not exist on type 'WebRequestBodyDetails'
197185
source: details.originUrl ?? details.initiator ?? '',
198-
})
199-
setTimeout(() => {
200-
if (requestCache.delete(details.requestId)) {
201-
log.info(`request cache for request ${details.requestId} was cleared after 5 seconds`)
202-
}
203-
}, 5000)
204-
})
205-
return undefined
206-
}
207-
208-
function onRedirectHeadersReceivedListener(
209-
details: Browser.webRequest.OnHeadersReceivedDetails
210-
): Browser.webRequest.BlockingResponse | undefined {
211-
;(async () => {
212-
log.info(`interception redirect detected for request ${details.requestId}`)
213-
// Wait for the request details to be available in the cache.
214-
// This is necessary because onBeforeRedirectRequestListener may be called
215-
// before onBeforeRequestListener has finished caching the details.
216-
let timeout = false
217-
setTimeout(() => {
218-
timeout = true
219-
}, 1000)
220-
while (!requestCache.has(details.requestId)) {
221-
log.info(`waiting for cached request details of request ${details.requestId}`)
222-
await new Promise((resolve) => setTimeout(resolve, 100))
223-
if (timeout) {
224-
log.warn(`waiting for request details of request ${details.requestId} timed out, skipping interception`)
225-
return
226-
}
227186
}
228-
// Once the request details are available or the timeout is reached,
229-
// either process the request if found in cache or ignore it
230-
if (requestCache.has(details.requestId)) {
231-
log.info(`request details of intercepted request ${details.requestId} found in cache`)
232-
interceptRequest(requestCache.get(details.requestId)!)
233-
requestCache.delete(details.requestId)
234-
}
235-
})()
187+
interceptRequest(requestDeatils)
188+
})
236189
return undefined
237190
}
238191

@@ -249,7 +202,7 @@ function constructRuleSet(activeDomains: DomainSettings[]): Browser.declarativeN
249202
priority: 1,
250203
action: { type: 'redirect', redirect: { url: redirectURL } },
251204
condition: {
252-
regexFilter: `${domain.domain}${normalizeRegexStart(domain.pathRegExp)}`,
205+
regexFilter: getDomainRegExp(domain),
253206
resourceTypes,
254207
requestMethods,
255208
initiatorDomains: [domain.domain],
@@ -284,10 +237,11 @@ async function createUrlsFilter(): Promise<string[]> {
284237
}
285238

286239
async function isURLTracked(url: string): Promise<boolean> {
240+
if (url.includes('x-nzbdonkey')) return false // avoid intercepting our own requests from the content script
287241
const activeDomains = await getActiveDomains()
288242
for (const domain of activeDomains) {
289243
try {
290-
const regex = new RegExp(`${domain.domain}${normalizeRegexStart(domain.pathRegExp)}`, 'i')
244+
const regex = new RegExp(getDomainRegExp(domain), 'i')
291245
if (regex.test(url)) return true
292246
} catch (e) {
293247
const error = e instanceof Error ? e : new Error(String(e))
@@ -297,6 +251,7 @@ async function isURLTracked(url: string): Promise<boolean> {
297251
return false
298252
}
299253

300-
function normalizeRegexStart(input: string): string {
301-
return input.startsWith('^') ? input.slice(1) : '.*?' + input
254+
function getDomainRegExp(domain: DomainSettings): string {
255+
const pathRegExp = domain.pathRegExp.startsWith('^') ? domain.pathRegExp.slice(1) : '.*?' + domain.pathRegExp
256+
return `${domain.domain}${pathRegExp}`
302257
}

src/entrypoints/background/interception/helperFunction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@ export function addTimestampToURL(request: Request, ruleId: number): Request {
6969
const url = new URL(request.url)
7070
// Use a combination of timestamp and counter for uniqueness
7171
const uniqueId = `${Date.now()}${ruleId}`
72-
url.searchParams.append('nzbdonkey', uniqueId)
72+
url.searchParams.append('x-nzbdonkey', uniqueId)
7373
return new Request(url.toString(), request)
7474
}

src/services/interception/functions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export async function extractArchive(blob: Blob, source: string): Promise<NZBFil
4141

4242
export async function getActiveDomains(): Promise<DomainSettings[]> {
4343
const settings = await getSettings()
44-
return settings.domains.filter((domain) => domain.isActive)
44+
return settings.enabled ? settings.domains.filter((domain) => domain.isActive) : []
4545
}
4646

4747
export async function processNzbFiles(nzbFiles: NZBFileObject[], filename: string): Promise<void> {

0 commit comments

Comments
 (0)