| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-present eyeo GmbH | 
|  | 4  * | 
|  | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 6  * it under the terms of the GNU General Public License version 3 as | 
|  | 7  * published by the Free Software Foundation. | 
|  | 8  * | 
|  | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
|  | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12  * GNU General Public License for more details. | 
|  | 13  * | 
|  | 14  * You should have received a copy of the GNU General Public License | 
|  | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 16  */ | 
|  | 17 | 
|  | 18 "use strict"; | 
|  | 19 | 
|  | 20 const {defaultMatcher} = require("matcher"); | 
|  | 21 const {RegExpFilter, WhitelistFilter} = require("filterClasses"); | 
|  | 22 const {verifySignature} = require("rsa"); | 
|  | 23 const {extractHostFromFrame, isThirdParty, stringifyURL} = require("url"); | 
|  | 24 const {checkWhitelisted} = require("whitelisting"); | 
|  | 25 const {FilterNotifier} = require("filterNotifier"); | 
|  | 26 const devtools = require("devtools"); | 
|  | 27 const info = require("info"); | 
|  | 28 | 
|  | 29 const {typeMap} = RegExpFilter; | 
|  | 30 | 
|  | 31 const remoteURL = "https://easylist-downloads.adblockplus.org/snippets.js"; | 
|  | 32 | 
|  | 33 let libraries = {local: "", remote: ""}; | 
|  | 34 let executableCode = new Map(); | 
|  | 35 | 
|  | 36 function fetchText(url) | 
|  | 37 { | 
|  | 38   return fetch(url, {cache: "no-cache"}).then( | 
|  | 39     response => response.ok ? response.text() : "" | 
|  | 40   ); | 
|  | 41 } | 
|  | 42 | 
|  | 43 function updateLibrary(name, text) | 
|  | 44 { | 
|  | 45   libraries[name] = text; | 
|  | 46 | 
|  | 47   executableCode.clear(); | 
|  | 48 } | 
|  | 49 | 
|  | 50 function checkLibrarySignature(url, text) | 
|  | 51 { | 
|  | 52   return fetchText(`${url}.sig`).then( | 
|  | 53     signature => fetchText(browser.extension.getURL("/key")).then( | 
|  | 54       key => verifySignature(key.replace(/=/g, ""), signature, text) | 
|  | 55     ) | 
|  | 56   ); | 
|  | 57 } | 
|  | 58 | 
|  | 59 function loadLibrary(name, url, {verify = true} = {}) | 
|  | 60 { | 
|  | 61   fetchText(url).then(text => | 
|  | 62   { | 
|  | 63     if (text != libraries[name]) | 
|  | 64     { | 
|  | 65       let check = verify ? checkLibrarySignature(url, text) : | 
|  | 66                     Promise.resolve(true); | 
|  | 67       check.then(ok => | 
|  | 68       { | 
|  | 69         if (ok) | 
|  | 70           updateLibrary(name, text); | 
|  | 71       }); | 
|  | 72     } | 
|  | 73   }); | 
|  | 74 } | 
|  | 75 | 
|  | 76 function loadLocalLibrary() | 
|  | 77 { | 
|  | 78   loadLibrary("local", browser.extension.getURL("/snippets.js"), | 
|  | 79               {verify: false}); | 
|  | 80 } | 
|  | 81 | 
|  | 82 function loadRemoteLibrary() | 
|  | 83 { | 
|  | 84   loadLibrary("remote", remoteURL); | 
|  | 85 } | 
|  | 86 | 
|  | 87 function getExecutableCode(snippet) | 
|  | 88 { | 
|  | 89   let code = executableCode.get(snippet); | 
|  | 90   if (code) | 
|  | 91     return code; | 
|  | 92 | 
|  | 93   code = ` | 
|  | 94     "use strict"; | 
|  | 95     { | 
|  | 96       let localImports = Object.create(null); | 
|  | 97       let remoteImports = Object.create(null); | 
|  | 98       new Function("exports", ${JSON.stringify(libraries.local)})( | 
|  | 99         localImports | 
|  | 100       ); | 
|  | 101       new Function("exports", ${JSON.stringify(libraries.remote)})( | 
|  | 102         remoteImports | 
|  | 103       ); | 
|  | 104       let imports = Object.assign(Object.create(null), | 
|  | 105                                   localImports, | 
|  | 106                                   remoteImports); | 
|  | 107       let key = ${JSON.stringify(snippet)}; | 
|  | 108       if (Object.prototype.hasOwnProperty.call(imports, key)) | 
|  | 109       { | 
|  | 110         let value = imports[key]; | 
|  | 111         if (typeof value == "function") | 
|  | 112           value(); | 
|  | 113       } | 
|  | 114     } | 
|  | 115   `; | 
|  | 116 | 
|  | 117   executableCode.set(snippet, code); | 
|  | 118   return code; | 
|  | 119 } | 
|  | 120 | 
|  | 121 function injectCode(snippet, tabId, frameId) | 
|  | 122 { | 
|  | 123   try | 
|  | 124   { | 
|  | 125     browser.tabs.executeScript(tabId, { | 
|  | 126       code: getExecutableCode(snippet), | 
|  | 127       frameId, | 
|  | 128       matchAboutBlank: true, | 
|  | 129       runAt: "document_start" | 
|  | 130     }); | 
|  | 131   } | 
|  | 132   catch (error) | 
|  | 133   { | 
|  | 134   } | 
|  | 135 } | 
|  | 136 | 
|  | 137 loadLocalLibrary(); | 
|  | 138 | 
|  | 139 // Only Chrome supports dynamic loading of JS. | 
|  | 140 if (info.platform == "chromium") | 
|  | 141 { | 
|  | 142   loadRemoteLibrary(); | 
|  | 143 | 
|  | 144   // Download every 24 hours. | 
|  | 145   setInterval(loadRemoteLibrary, 24 * 60 * 60 * 1000); | 
|  | 146 } | 
|  | 147 | 
|  | 148 browser.webNavigation.onCommitted.addListener(({tabId, frameId, url}) => | 
|  | 149 { | 
|  | 150   // There's a bug in Chrome that causes webNavigation.onCommitted to get | 
|  | 151   // dispatched twice if there's a URL filter present, therefore we must listen | 
|  | 152   // for all URLs and do an explicit check here. | 
|  | 153   // https://crbug.com/827855 | 
|  | 154   if (!/^(https?:\/\/|about:blank\b|about:srcdoc\b)/.test(url)) | 
|  | 155     return; | 
|  | 156 | 
|  | 157   let urlObject = new URL(url); | 
|  | 158   let urlString = stringifyURL(urlObject); | 
|  | 159   let frame = ext.getFrame(tabId, frameId); | 
|  | 160   let hostname = extractHostFromFrame(frame); | 
|  | 161   let thirdParty = isThirdParty(urlObject, hostname); | 
|  | 162 | 
|  | 163   let filter = defaultMatcher.matchesAny(urlString, typeMap.SNIPPET, hostname, | 
|  | 164                                          thirdParty, null, true); | 
|  | 165   if (!filter) | 
|  | 166     return; | 
|  | 167 | 
|  | 168   let page = new ext.Page({id: tabId, url}); | 
|  | 169 | 
|  | 170   if (checkWhitelisted(page, frame)) | 
|  | 171     return; | 
|  | 172 | 
|  | 173   devtools.logRequest(page, urlString, "SNIPPET", hostname, thirdParty, null, | 
|  | 174                       true, filter); | 
|  | 175   FilterNotifier.emit("filter.hitCount", filter, 0, 0, page); | 
|  | 176 | 
|  | 177   if (filter instanceof WhitelistFilter) | 
|  | 178     return; | 
|  | 179 | 
|  | 180   for (let snippet of filter.snippets) | 
|  | 181     injectCode(snippet, tabId, frameId); | 
|  | 182 }); | 
| OLD | NEW | 
|---|