background.js
1 'use strict'; 2 3 /* global importScripts, chrome */ 4 5 importScripts('common.js', 'sites.js', 'dnr_manager.js'); 6 7 // chrome.browserAction changed to chrome.action in MV3 8 const actionApi = chrome.action || chrome.browserAction; 9 10 const restrictions = { 11 'adweek.com': /^((?!\.adweek\.com\/(.+\/)?(amp|agencyspy|tvnewser|tvspy)\/).)*$/, 12 'barrons.com': /.+\.barrons\.com\/(amp\/)?article(s)?\/.+/, 13 'economist.com': /.+economist\.com\/.+\/\d{1,4}\/\d{1,2}\/\d{2}\/.+/, 14 'seekingalpha.com': /.+seekingalpha\.com\/article\/.+/, 15 'techinasia.com': /\.techinasia\.com\/.+/, 16 'ft.com': /.+\.ft.com\/content\// 17 }; 18 19 // Don't remove cookies before page load 20 const allowCookies = [ 21 'brisbanetimes.com.au', 22 'canberratimes.com.au', 23 'cen.acs.org', 24 'demorgen.be', 25 'denverpost.com', 26 'examiner.com.au', 27 'gelocal.it', 28 'grubstreet.com', 29 'harpers.org', 30 'hbr.org', 31 'humo.be', 32 'lesechos.fr', 33 'lrb.co.uk', 34 'medium.com', 35 'mercurynews.com', 36 'newstatesman.com', 37 'nrc.nl', 38 'nymag.com', 39 'ocregister.com', 40 'parool.nl', 41 'qz.com', 42 'scientificamerican.com', 43 'seattletimes.com', 44 'seekingalpha.com', 45 'sofrep.com', 46 'spectator.co.uk', 47 'speld.nl', 48 'techinasia.com', 49 'telegraaf.nl', 50 'the-american-interest.com', 51 'theadvocate.com.au', 52 'theage.com.au', 53 'theatlantic.com', 54 'theaustralian.com.au', 55 'thecut.com', 56 'thediplomat.com', 57 'themercury.com.au', 58 'towardsdatascience.com', 59 'trouw.nl', 60 'vn.nl', 61 'volkskrant.nl', 62 'vulture.com', 63 'nzz.ch', 64 'thehindu.com', 65 'financialpost.com', 66 'haaretz.co.il', 67 'haaretz.com', 68 'themarker.com', 69 'sueddeutsche.de', 70 'gelocal.it', 71 'elmundo.es', 72 'time.com', 73 'zeit.de', 74 'expansion.com', 75 'dailytelegraph.com.au', 76 'washingtonpost.com' 77 ]; 78 79 // Removes cookies after page load 80 const removeCookies = [ 81 'bloomberg.com', 82 'bloombergquint.com', 83 'brisbanetimes.com.au', 84 'canberratimes.com.au', 85 'cen.acs.org', 86 'demorgen.be', 87 'denverpost.com', 88 'examiner.com.au', 89 'globes.co.il', 90 'grubstreet.com', 91 'harpers.org', 92 'hbr.org', 93 'humo.be', 94 'lesechos.fr', 95 'mercurynews.com', 96 'newstatesman.com', 97 'nrc.nl', 98 'nymag.com', 99 'ocregister.com', 100 'qz.com', 101 'scientificamerican.com', 102 'seattletimes.com', 103 'sofrep.com', 104 'spectator.co.uk', 105 'speld.nl', 106 'telegraaf.nl', 107 'theadvocate.com.au', 108 'theage.com.au', 109 'theatlantic.com', 110 'thecut.com', 111 'thediplomat.com', 112 'towardsdatascience.com', 113 'vn.nl', 114 'vulture.com', 115 'wsj.com', 116 'medium.com', 117 'washingtonpost.com', 118 'japantimes.co.jp' 119 ]; 120 121 // Contains remove cookie sites above plus any custom sites 122 let _removeCookies = removeCookies; 123 124 // select specific cookie(s) to hold from removeCookies domains 125 const removeCookiesSelectHold = { 126 'qz.com': ['gdpr'], 127 'wsj.com': ['wsjregion'], 128 'seattletimes.com': ['st_newsletter_splash_seen'] 129 }; 130 131 // select only specific cookie(s) to drop from removeCookies domains 132 const removeCookiesSelectDrop = { 133 'ambito.com': ['TDNotesRead'], 134 'demorgen.be': ['TID_ID'], 135 'fd.nl': ['socialread'], 136 'humo.be': ['TID_ID'], 137 'nrc.nl': ['counter'], 138 'speld.nl': ['speld-paywall'] 139 }; 140 141 // Override User-Agent with Googlebot 142 const useGoogleBotSites = [ 143 'adelaidenow.com.au', 144 'barrons.com', 145 'couriermail.com.au', 146 'fd.nl', 147 'genomeweb.com', 148 'heraldsun.com.au', 149 'lavoixdunord.fr', 150 'ntnews.com.au', 151 'quora.com', 152 'seekingalpha.com', 153 'telegraph.co.uk', 154 'theaustralian.com.au', 155 'themercury.com.au', 156 'thenational.scot', 157 'wsj.com', 158 'kansascity.com', 159 'republic.ru', 160 'nzz.ch', 161 'df.cl', 162 'ft.com', 163 'wired.com', 164 'zeit.de' 165 ]; 166 167 // Override User-Agent with Bingbot 168 const useBingBot = []; 169 170 const useGoogleAdsBotMobile = [ 171 'haaretz.co.il', 172 'haaretz.com', 173 'themarker.com' 174 ]; 175 176 // Contains google bot sites above plus any custom sites 177 let _useGoogleBotSites = useGoogleBotSites; 178 179 function setDefaultOptions () { 180 extensionApi.storage.sync.set({ 181 sites: defaultSites 182 }, function () { 183 extensionApi.runtime.openOptionsPage(); 184 }); 185 } 186 187 // Block external scripts 188 const blockedRegexes = { 189 'adweek.com': /.+\.lightboxcdn\.com\/.+/, 190 'afr.com': /afr\.com\/assets\/vendorsReactRedux_client.+\.js/, 191 'businessinsider.com': /(.+\.tinypass\.com\/.+|cdn\.onesignal\.com\/sdks\/.+\.js)/, 192 'chicagotribune.com': /.+:\/\/.+\.tribdss\.com\//, 193 'economist.com': /(.+\.tinypass\.com\/.+|economist\.com\/engassets\/_next\/static\/chunks\/framework.+\.js)/, 194 'editorialedomani.it': /(js\.pelcro\.com\/.+|editorialedomani.it\/pelcro\.js)/, 195 'foreignpolicy.com': /(cdn\.cxense\.com\/|\.tinypass\.com\/)/, 196 'fortune.com': /.+\.tinypass\.com\/.+/, 197 'haaretz.co.il': /haaretz\.co\.il\/htz\/js\/inter\.js/, 198 'haaretz.com': /haaretz\.com\/hdc\/web\/js\/minified\/header-scripts-int.js.+/, 199 'inquirer.com': /.+\.tinypass\.com\/.+/, 200 'lastampa.it': /.+\.repstatic\.it\/minify\/sites\/lastampa\/.+\/config\.cache\.php\?name=social_js/, 201 'lrb.co.uk': /.+\.tinypass\.com\/.+/, 202 'medscape.com': /.+\.medscapestatic\.com\/.*medscape-library\.js/, 203 'interest.co.nz': /(.+\.presspatron\.com.+|.+interest\.co\.nz.+pp-ablock-banner\.js)/, 204 'repubblica.it': /scripts\.repubblica\.it\/pw\/pw\.js.+/, 205 'spectator.co.uk': /.+\.tinypass\.com\/.+/, 206 'spectator.com.au': /.+\.tinypass\.com\/.+/, 207 'telegraph.co.uk': /.+telegraph\.co\.uk.+martech.+/, 208 'thecourier.com.au': /.+cdn-au\.piano\.io\/api\/tinypass.+\.js/, 209 'thenation.com': /thenation\.com\/.+\/paywall-script\.php/, 210 'thenational.scot': /(.+\.tinypass\.com\/.+|.+thenational\.scot.+omniture\.js|.+thenational\.scot.+responsive-sync.+)/, 211 'thewrap.com': /thewrap\.com\/.+\/wallkit\.js/, 212 'wsj.com': /cdn\.ampproject\.org\/v\d\/amp-access-.+\.js/, 213 'historyextra.com': /.+\.evolok\.net\/.+\/authorize\/.+/, 214 'barrons.com': /cdn\.ampproject\.org\/v\d\/amp-access-.+\.js/, 215 'irishtimes.com': /cdn\.ampproject\.org\/v\d\/amp-access-.+\.js/, 216 'elmercurio.com': /(merreader\.emol\.cl\/assets\/js\/merPramV2.js|staticmer\.emol\.cl\/js\/inversiones\/PramModal.+\.js)/, 217 'sloanreview.mit.edu': /(.+\.tinypass\.com\/.+|.+\.netdna-ssl\.com\/wp-content\/themes\/smr\/assets\/js\/libs\/welcome-ad\.js)/, 218 'latercera.com': /.+\.cxense\.com\/+/, 219 'lesechos.fr': /.+\.tinypass\.com\/.+/, 220 'thehindu.com': /ajax\.cloudflare\.com\/cdn-cgi\/scripts\/.+\/cloudflare-static\/rocket-loader\.min\.js/, 221 'technologyreview.com': /.+\.blueconic\.net\/.+/, 222 'spectator.us': /(cdn\.cxense\.com\/.+|\.tinypass\.com\/.+)/, 223 'gelocal.it': /(\.repstatic\.it\/minify\/sites\/gelocal\/.+\/config\.cache(_\d)?\.php|cdn\.ampproject\.org\/v\d\/amp-(access|ad)-.+\.js)/, 224 'elmundo.es': /cdn\.ampproject\.org\/v\d\/amp-(access|ad|consent)-.+\.js/, 225 'time.com': /\/time\.com\/dist\/meter-wall-client-js\..+\.js/, 226 'thestar.com': /\.com\/api\/overlaydata/, 227 'elpais.com': /(\.epimg\.net\/js\/.+\/(noticia|user)\.min\.js|\/elpais\.com\/arc\/subs\/p\.min\.js|cdn\.ampproject\.org\/v\d\/amp-(access|(sticky-)?ad|consent)-.+\.js)/, 228 'expansion.com': /cdn\.ampproject\.org\/v\d\/amp-(access|ad|consent)-.+\.js/, 229 'chicagobusiness.com': /(\.tinypass\.com\/|\.chicagobusiness\.com\/.+\/js\/js_.+\.js)/, 230 'dailytelegraph.com.au': /cdn\.ampproject\.org\/v\d\/amp-(access|ad|consent)-.+\.js/, 231 'theglobeandmail.com': /(\.theglobeandmail\.com\/pf\/dist\/engine\/react\.js|smartwall\.theglobeandmail\.com\/)/, 232 'nytimes.com': /(meter-svc\.nytimes\.com\/meter\.js|mwcm\.nyt\.com\/.+\.js|cooking\.nytimes\.com\/api\/.+\/access)/, 233 'latimes.com': /(metering\.platform\.latimes\.com\/|cdn\.ampproject\.org\/v\d\/amp-(access|subscriptions)-.+\.js)/, 234 'theathletic.com': /cdn\.ampproject\.org\/v\d\/amp-(access|subscriptions)-.+\.js/, 235 'japantimes.co.jp': /cdn\.cxense\.com\//, 236 'scmp.com': /(\.tinypass\.com\/|cdn\.ampproject\.org\/v\d\/amp-access-.+\.js)/, 237 'ilmessaggero.it': /(utils\.cedsdigital\.it\/js\/PaywallMeter\.js)/, 238 'washingtonpost.com': /\.washingtonpost\.com\/tetro\/metering\/evaluate/ 239 }; 240 241 const userAgentDesktop = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'; 242 const userAgentMobile = 'Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible ; Googlebot/2.1 ; +http://www.google.com/bot.html)'; 243 const userAgentDesktopBingBot = 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'; 244 const userAgentMobileBingBot = 'Chrome/80.0.3987.92 Mobile Safari/537.36 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'; 245 const userAgentGoogleAdsBotMobile = 'Mozilla/5.0 (Linux; Android 5.0; SM-G920A) AppleWebKit (KHTML, like Gecko) Chrome Mobile Safari (compatible; AdsBot-Google-Mobile; +http://www.google.com/mobile/adsbot.html)'; 246 247 let enabledSites = []; 248 249 // Get the enabled sites 250 extensionApi.storage.sync.get({ 251 sites: {}, 252 customSites: [] 253 }, function (items) { 254 enabledSites = Object.values(items.sites).concat(items.customSites); 255 256 // Remove cookies for custom sites 257 _removeCookies = removeCookies.concat(items.customSites); 258 259 // Update DNR rules 260 dnrManager.updateRules(enabledSites, items.customSites, _useGoogleBotSites, useBingBot, useGoogleAdsBotMobile, blockedRegexes, allowCookies); 261 262 263 // GA removed in MV3 264 265 }); 266 267 // Listen for changes to options 268 extensionApi.storage.onChanged.addListener(function (changes, namespace) { 269 if (changes.sites && changes.sites.newValue) { 270 const sites = changes.sites.newValue; 271 enabledSites = Object.values(sites); 272 273 // Refresh DNR rules when settings change 274 extensionApi.storage.sync.get(['customSites'], function(items) { 275 dnrManager.updateRules(enabledSites, items.customSites || [], _useGoogleBotSites, useBingBot, useGoogleAdsBotMobile, blockedRegexes, allowCookies); 276 }); 277 } 278 }); 279 280 // Set and show default options on install 281 extensionApi.runtime.onInstalled.addListener(function (details) { 282 if (details.reason === 'install') { 283 setDefaultOptions(); 284 } else if (details.reason === 'update') { 285 // User updated extension 286 } 287 }); 288 289 extensionApi.tabs.onUpdated.addListener(function (tabId, info, tab) { 290 updateBadge(tab); 291 if (info.status === 'loading' && tab.url && isSiteEnabled({ url: tab.url })) { 292 chrome.scripting.executeScript({ 293 target: { tabId: tabId }, 294 files: ['src/js/contentScript.js'], 295 injectImmediately: true 296 }).catch(err => { 297 // Silently fail if tab is closed or restricted 298 }); 299 } 300 }); 301 extensionApi.tabs.onActivated.addListener(function (activeInfo) { 302 extensionApi.tabs.get(activeInfo.tabId, updateBadge); 303 }); 304 305 function updateBadge (activeTab) { 306 if (extensionApi.runtime.lastError || !activeTab) { return; } 307 const badgeText = getBadgeText(activeTab.url); 308 actionApi.setBadgeBackgroundColor({ color: 'blue' }); 309 actionApi.setBadgeText({ text: badgeText }); 310 } 311 312 function getBadgeText (currentUrl) { 313 return currentUrl && isSiteEnabled({ url: currentUrl }) ? 'ON' : ''; 314 } 315 316 // Redirects are now handled via DNR in dnr_manager.js 317 318 319 // Script blocking is now handled via DNR in dnr_manager.js 320 321 322 323 324 // remove cookies after page load 325 extensionApi.webRequest.onCompleted.addListener(function (details) { 326 let domainToRemove; 327 for (const domain of _removeCookies) { 328 if (enabledSites.includes(domain) && matchUrlDomain(domain, details.url)) { 329 domainToRemove = domain; 330 break; 331 } 332 } 333 if (domainToRemove) { 334 extensionApi.cookies.getAll({ domain: domainToRemove }, function (cookies) { 335 for (const ck of cookies) { 336 const cookie = { 337 url: (ck.secure ? 'https://' : 'http://') + ck.domain + ck.path, 338 name: ck.name, 339 storeId: ck.storeId 340 }; 341 // .firstPartyDomain = undefined on Chrome (doesn't support it) 342 if (ck.firstPartyDomain !== undefined) { 343 cookie.firstPartyDomain = ck.firstPartyDomain; 344 } 345 const cookieDomain = ck.domain; 346 const rcDomain = cookieDomain.replace(/^(\.?www\.|\.)/, ''); 347 // hold specific cookie(s) from removeCookies domains 348 if ((rcDomain in removeCookiesSelectHold) && removeCookiesSelectHold[rcDomain].includes(ck.name)) { 349 continue; // don't remove specific cookie 350 } 351 // drop only specific cookie(s) from removeCookies domains 352 if ((rcDomain in removeCookiesSelectDrop) && !(removeCookiesSelectDrop[rcDomain].includes(ck.name))) { 353 continue; // only remove specific cookie 354 } 355 extensionApi.cookies.remove(cookie); 356 } 357 }); 358 } 359 }, { 360 urls: ['<all_urls>'] 361 }); 362 363 // Google Analytics removed in MV3 364 365 function isSiteEnabled (details) { 366 const enabledSite = matchUrlDomain(enabledSites, details.url); 367 if (enabledSite in restrictions) { 368 return restrictions[enabledSite].test(details.url); 369 } 370 return !!enabledSite; 371 } 372 373 function matchUrlDomain (domains, url) { 374 return matchDomain(domains, urlHost(url)); 375 } 376 377 function matchDomain (domains, hostname) { 378 let matchedDomain = false; 379 if (!hostname) { hostname = window.location.hostname; } 380 if (typeof domains === 'string') { domains = [domains]; } 381 domains.some(domain => (hostname === domain || hostname.endsWith('.' + domain)) && (matchedDomain = domain)); 382 return matchedDomain; 383 } 384 385 function urlHost (url) { 386 if (url && url.startsWith('http')) { 387 try { 388 return new URL(url).hostname; 389 } catch (e) { 390 console.log(`url not valid: ${url} error: ${e}`); 391 } 392 } 393 return url; 394 }