/
background.ts
157 lines (139 loc) · 3.81 KB
/
background.ts
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
149
150
151
152
153
154
155
156
157
import * as adblocker from '../index';
/**
* Initialize the adblocker using lists of filters and resources. It returns a
* Promise resolving on the `Engine` that we will use to decide what requests
* should be blocked or altered.
*/
function loadAdblocker() {
const engine = new adblocker.FiltersEngine({
loadCosmeticFilters: true,
loadNetworkFilters: true,
optimizeAOT: false,
version: 1,
});
return Promise.all([
adblocker.fetchLists(),
adblocker.fetchResources(),
]).then(([responses, resources]) => {
const lists: Array<{ filters: string, checksum: string, asset: string }> = [];
for (let i = 0; i < responses.length; i += 1) {
lists.push({
asset: '' + i,
checksum: '',
filters: responses[i],
});
}
engine.onUpdateResource([{ filters: resources, checksum: '' }]);
engine.onUpdateFilters(lists, new Set());
return engine;
});
}
/**
* Because the WebRequest API does not give us access to the URL of the page
* each request comes from (but we know from which tab they originate), we need
* to independently keep a mapping from tab ids to source URLs. This information
* is needed by the adblocker (some filters only apply on specific domains).
*/
const tabs = new Map();
function resetState(tabId, source) {
tabs.set(tabId, { source, count: 0 });
}
function updateBadgeCount(tabId) {
if (tabs.has(tabId)) {
const { count } = tabs.get(tabId);
chrome.browserAction.setBadgeText({ text: '' + count });
}
}
function incrementBlockedCounter(tabId) {
if (tabs.has(tabId)) {
const tabStats = tabs.get(tabId);
tabStats.count += 1;
updateBadgeCount(tabId);
}
}
chrome.tabs.onCreated.addListener((tab) => {
resetState(tab.id, tab.url);
updateBadgeCount(tab.id);
});
chrome.tabs.onUpdated.addListener((_0, _1, tab) => {
if (tabs.has(tab.id)) {
const { source } = tabs.get(tab.id);
if (source !== tab.url) {
resetState(tab.id, tab.url);
updateBadgeCount(tab.id);
}
}
});
chrome.tabs.onActivated.addListener(({ tabId }) => {
updateBadgeCount(tabId);
});
const types = {
// maps string (web-ext) to int (FF cpt)
beacon: 19,
csp_report: 17,
font: 14,
image: 3,
imageset: 21,
main_frame: 6,
media: 15,
object: 5,
object_subrequest: 12,
other: 1,
ping: 10,
script: 2,
stylesheet: 4,
sub_frame: 7,
web_manifest: 22,
websocket: 16,
xbl: 9,
xml_dtd: 13,
xmlhttprequest: 11,
xslt: 18,
};
loadAdblocker().then((engine) => {
function listener(details) {
let source;
if (tabs.has(details.tabId)) {
source = tabs.get(details.tabId).source;
}
const result = engine.match({
cpt: types[details.type],
sourceUrl: source,
url: details.url,
});
if (result.redirect) {
incrementBlockedCounter(details.tabId);
return { redirectUrl: result.redirect };
} else if (result.match) {
incrementBlockedCounter(details.tabId);
return { cancel: true };
}
return {};
}
// Start listening to requests, and allow 'blocking' so that we can cancel
// some of them (or redirect).
chrome.webRequest.onBeforeRequest.addListener(
listener,
{
urls: [
'*://*/*',
],
},
['blocking'],
);
// Start listening to messages coming from the content-script
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
// Extract hostname from sender's URL
const url = sender.url;
let hostname = '';
if (url !== undefined) {
hostname = (new URL(url)).hostname;
}
// Answer to content-script with a list of nodes
if (msg.action === 'getCosmeticsForDomain') {
sendResponse(engine.getDomainFilters(hostname));
} else if (msg.action === 'getCosmeticsForNodes') {
sendResponse(engine.getCosmeticsFilters(hostname, msg.args[0]));
}
});
});