redirect dynamically via injected content script instead of checking

each request
This commit is contained in:
2026-07-07 17:36:01 +02:00
parent 71593f6762
commit 4c9825ebd5
5 changed files with 66 additions and 18 deletions
+42 -7
View File
@@ -4,19 +4,54 @@ import { HostSettings, UrlReferer } from '@/lib/settings';
export default defineBackground(() => { export default defineBackground(() => {
browser.runtime.onMessage.addListener(async (message, sender) => { browser.runtime.onMessage.addListener(async (message, sender) => {
const messageType = message.type as BackgroundMessageType; const type = message.type as BackgroundMessageType;
const messageData = message.data as BackgroundMessageData<typeof messageType>; let data = message.data;
let response; let response;
switch (messageType) {
case BackgroundMessageType.Ff2mpv: switch (type) {
await browser.runtime.sendNativeMessage('ff2mpv', { url: messageData!.url }); case BackgroundMessageType.Ff2mpv: {
data = data as BackgroundMessageData<typeof type>;
await browser.runtime.sendNativeMessage('ff2mpv', { url: data.url });
break; break;
case BackgroundMessageType.RequestTabUrl: }
case BackgroundMessageType.RequestTabUrl: {
response = sender.tab?.url ?? null; response = sender.tab?.url ?? null;
break; break;
}
case BackgroundMessageType.RegisterContentScript: {
// requires "dynamic" host permissions, which is only available in mv2
if (import.meta.env.MANIFEST_VERSION === 3) break;
data = data as BackgroundMessageData<typeof type>;
const contentScript: Browser.scripting.RegisteredContentScript = {
id: 'temporary-hosts',
js: ['content-scripts/content.js'],
matches: [`*://${data.domain}/*`],
persistAcrossSessions: false,
allFrames: true,
runAt: 'document_end'
};
const contentScripts = await browser.scripting.getRegisteredContentScripts({ ids: [contentScript.id] });
if (contentScripts.length !== 0) {
const registeredContentScript = contentScripts[0];
// do not double register for same domain
if (registeredContentScript.matches!.indexOf(contentScript.matches![0]) !== -1) break;
contentScript.matches!.push(...contentScripts[0].matches!);
await browser.scripting.updateContentScripts([contentScript]);
} else {
await browser.scripting.registerContentScripts([contentScript]);
}
break;
}
default: default:
return; break;
} }
return response; return response;
+1 -5
View File
@@ -3,11 +3,7 @@ import { getHost, HostMatchType, hosts, type HostMatch } from '@/lib/host';
import { FF2MPVSettings, PerDomainSettings } from '@/lib/settings'; import { FF2MPVSettings, PerDomainSettings } from '@/lib/settings';
export default defineContentScript({ export default defineContentScript({
matches: [ matches: [...Object.values(hosts).flatMap((h) => h.domains.map((d) => `*://${d}/*`))],
...Object.values(hosts).flatMap((h) => h.domains.map((d) => `*://${d}/*`)),
// only mv2 allows to match all urls
...(import.meta.env.MANIFEST_VERSION === 2 ? ['<all_urls>'] : [])
],
allFrames: true, allFrames: true,
runAt: 'document_end', runAt: 'document_end',
main main
+8 -3
View File
@@ -1,6 +1,6 @@
// --- tab communication --- // // --- tab communication --- //
export enum TabMessageType { export enum TabMessageType {
RequestActiveMatch, RequestActiveMatch = 10,
NotifyActiveMatch NotifyActiveMatch
} }
@@ -35,8 +35,9 @@ export function listenTabMessages(listener: (type: TabMessageType, data: any) =>
// --- background script communication --- // // --- background script communication --- //
export enum BackgroundMessageType { export enum BackgroundMessageType {
Ff2mpv, Ff2mpv = 20,
RequestTabUrl RequestTabUrl,
RegisterContentScript
} }
export type BackgroundMessageData<T extends BackgroundMessageType> = { export type BackgroundMessageData<T extends BackgroundMessageType> = {
@@ -44,11 +45,15 @@ export type BackgroundMessageData<T extends BackgroundMessageType> = {
url: string; url: string;
}; };
[BackgroundMessageType.RequestTabUrl]: undefined; [BackgroundMessageType.RequestTabUrl]: undefined;
[BackgroundMessageType.RegisterContentScript]: {
domain: string;
};
}[T]; }[T];
export type BackgroundMessageReply<T extends BackgroundMessageType> = { export type BackgroundMessageReply<T extends BackgroundMessageType> = {
[BackgroundMessageType.Ff2mpv]: undefined; [BackgroundMessageType.Ff2mpv]: undefined;
[BackgroundMessageType.RequestTabUrl]: string | null; [BackgroundMessageType.RequestTabUrl]: string | null;
[BackgroundMessageType.RegisterContentScript]: undefined;
}[T]; }[T];
export async function sendBackgroundMessage<T extends BackgroundMessageType>( export async function sendBackgroundMessage<T extends BackgroundMessageType>(
+11 -2
View File
@@ -1,4 +1,5 @@
import { storage } from '#imports'; import { storage } from '#imports';
import { BackgroundMessageType, sendBackgroundMessage } from './communication';
import type { HostId } from '@/lib/host'; import type { HostId } from '@/lib/host';
class Setting<T> { class Setting<T> {
@@ -38,11 +39,19 @@ export class HostSettings {
/* tmp */ /* tmp */
private static temporaryHostDomain = new Setting<Record<string, string>>('local:temporaryHostDomain', {}); private static temporaryHostDomain = new Setting<Record<string, string>>('local:temporaryHostDomain', {});
static addTemporaryHostDomain = (hostId: HostId, domain: string) => static addTemporaryHostDomain = async (hostId: HostId, domain: string) => {
this.temporaryHostDomain.update((val) => { // only has an effect with mv2
const backgroundScriptInject =
import.meta.env.MANIFEST_VERSION === 2
? sendBackgroundMessage(BackgroundMessageType.RegisterContentScript, { domain: domain })
: Promise.resolve();
const temporaryHostDomainUpdate = this.temporaryHostDomain.update((val) => {
val[domain] = hostId; val[domain] = hostId;
return val; return val;
}); });
return Promise.all([backgroundScriptInject, temporaryHostDomainUpdate]);
};
static checkTemporaryHostDomain = (domain: string) => this.temporaryHostDomain.get().then((val) => val[domain]); static checkTemporaryHostDomain = (domain: string) => this.temporaryHostDomain.get().then((val) => val[domain]);
} }
+4 -1
View File
@@ -26,7 +26,10 @@ export default defineConfig({
gecko_android: {} gecko_android: {}
} }
: undefined, : undefined,
permissions: ['storage', ...(manifestVersion === 2 ? ['webRequest', 'webRequestBlocking', '<all_urls>'] : [])], permissions: [
'storage',
...(manifestVersion === 2 ? ['scripting', 'webRequest', 'webRequestBlocking', '<all_urls>'] : [])
],
optional_permissions: ['nativeMessaging'], optional_permissions: ['nativeMessaging'],
web_accessible_resources: [ web_accessible_resources: [
{ {