Support both firefox and chrome

This commit is contained in:
mrjvs
2024-01-10 20:30:12 +01:00
parent ba83059d43
commit f831dea5d6
6 changed files with 133 additions and 54 deletions

View File

@@ -7,7 +7,9 @@
"scripts": { "scripts": {
"dev": "plasmo dev", "dev": "plasmo dev",
"build": "plasmo build", "build": "plasmo build",
"build:firefox": "plasmo build --target=firefox-mv2",
"package": "plasmo package", "package": "plasmo package",
"package:firefox": "plasmo package --target=firefox-mv2",
"lint": "eslint --ext .tsx,.ts src", "lint": "eslint --ext .tsx,.ts src",
"lint:fix": "eslint --fix --ext .tsx,.ts src", "lint:fix": "eslint --fix --ext .tsx,.ts src",
"lint:report": "eslint --ext .tsx,.ts --output-file eslint_report.json --format json src", "lint:report": "eslint --ext .tsx,.ts --output-file eslint_report.json --format json src",
@@ -22,6 +24,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/chrome": "0.0.251", "@types/chrome": "0.0.251",
"@types/firefox-webext-browser": "^120.0.0",
"@types/node": "20.9.0", "@types/node": "20.9.0",
"@types/react": "18.2.37", "@types/react": "18.2.37",
"@types/react-dom": "18.2.15", "@types/react-dom": "18.2.15",

7
pnpm-lock.yaml generated
View File

@@ -25,6 +25,9 @@ devDependencies:
'@types/chrome': '@types/chrome':
specifier: 0.0.251 specifier: 0.0.251
version: 0.0.251 version: 0.0.251
'@types/firefox-webext-browser':
specifier: ^120.0.0
version: 120.0.0
'@types/node': '@types/node':
specifier: 20.9.0 specifier: 20.9.0
version: 20.9.0 version: 20.9.0
@@ -2767,6 +2770,10 @@ packages:
resolution: {integrity: sha512-Kpi2GXQyYJdjL8mFclL1eDgihn1SIzorMZjD94kdPZh9E4VxGOeyjPxi5LpsM4Zku7P0reqegZTt2GxhmA9VBg==} resolution: {integrity: sha512-Kpi2GXQyYJdjL8mFclL1eDgihn1SIzorMZjD94kdPZh9E4VxGOeyjPxi5LpsM4Zku7P0reqegZTt2GxhmA9VBg==}
dev: true dev: true
/@types/firefox-webext-browser@120.0.0:
resolution: {integrity: sha512-L+tDlwNeq0kQGfAYc2sNfKhRWJz9CNRvlbq9HnLibKUiJ3VTThG8sj7xrJF4CtKpEA9eBAr91Z2nnKIAy+xUJg==}
dev: true
/@types/har-format@1.2.15: /@types/har-format@1.2.15:
resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==} resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==}
dev: true dev: true

View File

@@ -13,10 +13,11 @@ interface Request extends BaseRequest {
const mapHeadersToDeclarativeNetRequestHeaders = ( const mapHeadersToDeclarativeNetRequestHeaders = (
headers: Record<string, string>, headers: Record<string, string>,
): chrome.declarativeNetRequest.ModifyHeaderInfo[] => { op: string,
): { header: string; operation: any; value: string }[] => {
return Object.entries(headers).map(([name, value]) => ({ return Object.entries(headers).map(([name, value]) => ({
header: name, header: name,
operation: chrome.declarativeNetRequest.HeaderOperation.SET, operation: op,
value, value,
})); }));
}; };
@@ -25,6 +26,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
try { try {
await assertDomainWhitelist(req.body.requestDomain); await assertDomainWhitelist(req.body.requestDomain);
if (chrome) {
await chrome.declarativeNetRequest.updateDynamicRules({ await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [req.body.ruleId], removeRuleIds: [req.body.ruleId],
addRules: [ addRules: [
@@ -35,9 +37,14 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
}, },
action: { action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS, type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
...(req.body.requestHeaders && { ...(req.body.requestHeaders
requestHeaders: mapHeadersToDeclarativeNetRequestHeaders(req.body.requestHeaders), ? {
}), requestHeaders: mapHeadersToDeclarativeNetRequestHeaders(
req.body.requestHeaders,
chrome.declarativeNetRequest.HeaderOperation.SET,
),
}
: {}),
responseHeaders: [ responseHeaders: [
{ {
header: 'Access-Control-Allow-Origin', header: 'Access-Control-Allow-Origin',
@@ -54,14 +61,56 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
operation: chrome.declarativeNetRequest.HeaderOperation.SET, operation: chrome.declarativeNetRequest.HeaderOperation.SET,
value: '*', value: '*',
}, },
...mapHeadersToDeclarativeNetRequestHeaders(req.body.responseHeaders ?? {}), ...mapHeadersToDeclarativeNetRequestHeaders(
req.body.responseHeaders ?? {},
chrome.declarativeNetRequest.HeaderOperation.SET,
),
], ],
}, },
}, },
], ],
}); });
if (chrome.runtime.lastError?.message) throw new Error(chrome.runtime.lastError.message); if (chrome.runtime.lastError?.message) throw new Error(chrome.runtime.lastError.message);
} else {
browser.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [req.body.ruleId],
addRules: [
{
id: req.body.ruleId,
condition: {
requestDomains: req.body.targetDomains,
},
action: {
type: 'modifyHeaders',
...(req.body.requestHeaders
? {
requestHeaders: mapHeadersToDeclarativeNetRequestHeaders(req.body.requestHeaders, 'set'),
}
: {}),
responseHeaders: [
{
header: 'Access-Control-Allow-Origin',
operation: 'set',
value: '*',
},
{
header: 'Access-Control-Allow-Methods',
operation: 'set',
value: 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
},
{
header: 'Access-Control-Allow-Headers',
operation: 'set',
value: '*',
},
...mapHeadersToDeclarativeNetRequestHeaders(req.body.responseHeaders ?? {}, 'set'),
],
},
},
],
});
if (browser.runtime.lastError?.message) throw new Error(browser.runtime.lastError.message);
}
res.send({ res.send({
success: true, success: true,

View File

@@ -1,28 +1,17 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { makeUrlIntoDomain } from '~utils/domains'; import { makeUrlIntoDomain } from '~utils/domains';
import { listenToTabChanges, queryCurrentDomain, stopListenToTabChanges } from '~utils/tabs';
function queryCurrentDomain(cb: (domain: string | null) => void) {
chrome.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
const url = tabs[0]?.url;
if (!url) cb(null);
else cb(url);
});
}
export function useDomain(): null | string { export function useDomain(): null | string {
const [domain, setDomain] = useState<string | null>(null); const [domain, setDomain] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
queryCurrentDomain(setDomain); const listen = () => queryCurrentDomain(setDomain);
function listen() { listen();
queryCurrentDomain(setDomain); listenToTabChanges(listen);
}
chrome.tabs.onActivated.addListener(listen);
chrome.tabs.onUpdated.addListener(listen);
return () => { return () => {
chrome.tabs.onActivated.removeListener(listen); stopListenToTabChanges(listen);
chrome.tabs.onUpdated.removeListener(listen);
}; };
}, []); }, []);

View File

@@ -1,6 +1,7 @@
export function getVersion(ops?: { prefixed?: boolean }) { export function getVersion(ops?: { prefixed?: boolean }) {
const prefix = ops?.prefixed ? 'v' : ''; const prefix = ops?.prefixed ? 'v' : '';
return `${prefix}${chrome.runtime.getManifest().version}`; const manifest = (chrome || browser).runtime.getManifest();
return `${prefix}${manifest.version}`;
} }
export function useVersion(ops?: { prefixed?: boolean }) { export function useVersion(ops?: { prefixed?: boolean }) {

30
src/utils/tabs.ts Normal file
View File

@@ -0,0 +1,30 @@
export function queryCurrentDomain(cb: (domain: string | null) => void) {
const handle = (tabUrl: string | null) => {
if (!tabUrl) cb(null);
else cb(tabUrl);
};
const ops = { active: true, currentWindow: true } as const;
if (chrome) chrome.tabs.query(ops).then((tabs) => handle(tabs[0]?.url));
else browser.tabs.query(ops).then((tabs) => handle(tabs[0]?.url));
}
export function listenToTabChanges(cb: () => void) {
if (chrome) {
chrome.tabs.onActivated.addListener(cb);
chrome.tabs.onUpdated.addListener(cb);
} else if (browser) {
browser.tabs.onActivated.addListener(cb);
browser.tabs.onUpdated.addListener(cb);
}
}
export function stopListenToTabChanges(cb: () => void) {
if (chrome) {
chrome.tabs.onActivated.removeListener(cb);
chrome.tabs.onUpdated.removeListener(cb);
} else if (browser) {
browser.tabs.onActivated.removeListener(cb);
browser.tabs.onUpdated.removeListener(cb);
}
}