Repository navigation
Expand file tree
/
Copy pathbackground.js
More file actions
148 lines (118 loc) · 4.58 KB
/
Copy pathbackground.js
File metadata and controls
148 lines (118 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// background.js — HeaderForge service worker
const RULE_ID_BASE = 1000;
// ── Storage helpers ────────────────────────────────────────────────────────────
async function getData() {
return new Promise(resolve => {
chrome.storage.local.get(['sites', 'activeProfiles'], result => {
resolve({
sites: result.sites || {}, // { hostname: { profiles: [{name, headers:[{name,value}]}], enabled } }
activeProfiles: result.activeProfiles || {} // { hostname: profileName }
});
});
});
}
async function saveData(data) {
return new Promise(resolve => chrome.storage.local.set(data, resolve));
}
// ── Rule builder ───────────────────────────────────────────────────────────────
function buildRules(sites, activeProfiles) {
const rules = [];
let ruleId = RULE_ID_BASE;
for (const [hostname, siteData] of Object.entries(sites)) {
if (!siteData.enabled) continue;
const profileName = activeProfiles[hostname];
if (!profileName) continue;
const profile = (siteData.profiles || []).find(p => p.name === profileName);
if (!profile || !profile.headers?.length) continue;
const requestHeaders = profile.headers
.filter(h => h.name && h.value)
.map(h => ({ header: h.name, operation: 'set', value: h.value }));
if (!requestHeaders.length) continue;
// Match the hostname and all subdomains / paths
const urlFilter = `||${hostname}^`;
rules.push({
id: ruleId++,
priority: 1,
action: { type: 'modifyHeaders', requestHeaders },
condition: {
urlFilter,
resourceTypes: [
'main_frame','sub_frame','xmlhttprequest','script',
'stylesheet','image','font','other'
]
}
});
}
return rules;
}
// ── Apply rules ────────────────────────────────────────────────────────────────
async function applyRules() {
const { sites, activeProfiles } = await getData();
const newRules = buildRules(sites, activeProfiles);
// Remove all existing dynamic rules first
const existing = await chrome.declarativeNetRequest.getDynamicRules();
const removeIds = existing.map(r => r.id);
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: removeIds,
addRules: newRules
});
console.log(`[HeaderForge] Applied ${newRules.length} rule(s).`);
}
// ── Message handler (from popup) ───────────────────────────────────────────────
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
(async () => {
const { sites, activeProfiles } = await getData();
switch (msg.type) {
case 'GET_DATA':
sendResponse({ sites, activeProfiles });
break;
case 'SAVE_SITE': {
// msg.hostname, msg.siteData
sites[msg.hostname] = msg.siteData;
await saveData({ sites, activeProfiles });
await applyRules();
sendResponse({ ok: true });
break;
}
case 'DELETE_SITE': {
delete sites[msg.hostname];
delete activeProfiles[msg.hostname];
await saveData({ sites, activeProfiles });
await applyRules();
sendResponse({ ok: true });
break;
}
case 'SET_ACTIVE_PROFILE': {
// msg.hostname, msg.profileName (null = none)
if (msg.profileName) {
activeProfiles[msg.hostname] = msg.profileName;
} else {
delete activeProfiles[msg.hostname];
}
await saveData({ sites, activeProfiles });
await applyRules();
sendResponse({ ok: true });
break;
}
case 'TOGGLE_SITE': {
if (sites[msg.hostname]) {
sites[msg.hostname].enabled = msg.enabled;
await saveData({ sites, activeProfiles });
await applyRules();
}
sendResponse({ ok: true });
break;
}
case 'APPLY_RULES':
await applyRules();
sendResponse({ ok: true });
break;
default:
sendResponse({ error: 'Unknown message type' });
}
})();
return true; // keep channel open for async
});
// Re-apply on startup
chrome.runtime.onStartup.addListener(applyRules);
chrome.runtime.onInstalled.addListener(applyRules);