| Index: lib/scriptInjection.js | 
| =================================================================== | 
| rename from lib/cssInjection.js | 
| rename to lib/scriptInjection.js | 
| --- a/lib/cssInjection.js | 
| +++ b/lib/scriptInjection.js | 
| @@ -10,39 +10,43 @@ | 
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| * GNU General Public License for more details. | 
| * | 
| * You should have received a copy of the GNU General Public License | 
| * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| */ | 
|  | 
| -/** @module cssInjection */ | 
| +/** @module scriptInjection */ | 
|  | 
| "use strict"; | 
|  | 
| const {RegExpFilter} = require("../adblockpluscore/lib/filterClasses"); | 
| const {ElemHide} = require("../adblockpluscore/lib/elemHide"); | 
| const {ElemHideEmulation} = require("../adblockpluscore/lib/elemHideEmulation"); | 
| +const {Snippets, compileScript} = require("../adblockpluscore/lib/snippets"); | 
| const {checkWhitelisted} = require("./whitelisting"); | 
| const {extractHostFromFrame} = require("./url"); | 
| const {port} = require("./messaging"); | 
| const {HitLogger} = require("./hitLogger"); | 
| const info = require("info"); | 
|  | 
| // Chromium's support for tabs.removeCSS is still a work in progress and the | 
| // API is likely to be different from Firefox's; for now we just don't use it | 
| // at all, even if it's available. | 
| // See https://crbug.com/608854 | 
| const styleSheetRemovalSupported = info.platform == "gecko"; | 
|  | 
| const selectorGroupSize = 1024; | 
|  | 
| let userStyleSheetsSupported = true; | 
|  | 
| +let snippetsLibrarySource = ""; | 
| +let executableCode = new Map(); | 
| + | 
| function* splitSelectors(selectors) | 
| { | 
| // Chromium's Blink engine supports only up to 8,192 simple selectors, and | 
| // even fewer compound selectors, in a rule. The exact number of selectors | 
| // that would work depends on their sizes (e.g. "#foo .bar" has a size of 2). | 
| // Since we don't know the sizes of the selectors here, we simply split them | 
| // into groups of 1,024, based on the reasonable assumption that the average | 
| // selector won't have a size greater than 8. The alternative would be to | 
| @@ -90,17 +94,17 @@ | 
| userStyleSheetsSupported = false; | 
|  | 
| // For other errors, we simply return false to indicate failure. | 
| // | 
| // One common error that occurs frequently is when a frame is not found | 
| // (e.g. "Error: No frame with id 574 in tab 266"), which can happen when | 
| // the code in the parent document has removed the frame before the | 
| // background page has had a chance to respond to the content script's | 
| -    // "elemhide.getSelectors" message. We simply ignore such errors, because | 
| +    // "content.applyFilters" message. We simply ignore such errors, because | 
| // otherwise they show up in the log too often and make debugging | 
| // difficult. | 
| // | 
| // Also note that the missing frame error is thrown synchronously on | 
| // Firefox, while on Chromium it is an asychronous promise rejection. In | 
| // the latter case, we cannot indicate failure to the caller, but we still | 
| // explicitly ignore the error. | 
| return false; | 
| @@ -155,35 +159,84 @@ | 
| // style sheet now. | 
| if (oldStyleSheet && oldStyleSheet != styleSheet) | 
| removeStyleSheet(tabId, frameId, oldStyleSheet); | 
|  | 
| frame.injectedStyleSheets.set(groupName, styleSheet); | 
| return true; | 
| } | 
|  | 
| -port.on("elemhide.getSelectors", (message, sender) => | 
| +function getExecutableCode(script) | 
| +{ | 
| +  let code = executableCode.get(script); | 
| +  if (code) | 
| +    return code; | 
| + | 
| +  code = compileScript(script, [snippetsLibrarySource]); | 
| + | 
| +  executableCode.set(script, code); | 
| +  return code; | 
| +} | 
| + | 
| +function executeScript(script, tabId, frameId) | 
| +{ | 
| +  try | 
| +  { | 
| +    browser.tabs.executeScript(tabId, { | 
| +      code: getExecutableCode(script), | 
| +      frameId, | 
| +      matchAboutBlank: true, | 
| +      runAt: "document_start" | 
| +    }) | 
| +    .catch(error => | 
| +    { | 
| +      // Sometimes a frame is added and removed very quickly, in such cases we | 
| +      // simply ignore the error. | 
| +      if (error.message == "The frame was removed.") | 
| +        return; | 
| + | 
| +      throw error; | 
| +    }); | 
| +  } | 
| +  catch (error) | 
| +  { | 
| +  } | 
| +} | 
| + | 
| +port.on("content.applyFilters", (message, sender) => | 
| { | 
| let selectors = []; | 
| let emulatedPatterns = []; | 
| let trace = HitLogger.hasListener(sender.page.id); | 
| let inline = !userStyleSheetsSupported; | 
|  | 
| +  let {elemhide, snippets} = message.filterTypes || | 
| +                             {elemhide: true, snippets: true}; | 
| + | 
| if (!checkWhitelisted(sender.page, sender.frame, null, | 
| -                        RegExpFilter.typeMap.DOCUMENT | | 
| -                        RegExpFilter.typeMap.ELEMHIDE)) | 
| +                        RegExpFilter.typeMap.DOCUMENT)) | 
| { | 
| let hostname = extractHostFromFrame(sender.frame); | 
| -    let specificOnly = checkWhitelisted(sender.page, sender.frame, null, | 
| -                                        RegExpFilter.typeMap.GENERICHIDE); | 
| + | 
| +    if (snippets) | 
| +    { | 
| +      for (let script of Snippets.getScriptsForDomain(hostname)) | 
| +        executeScript(script, sender.page.id, sender.frame.id); | 
| +    } | 
|  | 
| -    selectors = ElemHide.getSelectorsForDomain(hostname, specificOnly); | 
| +    if (elemhide && !checkWhitelisted(sender.page, sender.frame, null, | 
| +                                      RegExpFilter.typeMap.ELEMHIDE)) | 
| +    { | 
| +      let specificOnly = checkWhitelisted(sender.page, sender.frame, null, | 
| +                                          RegExpFilter.typeMap.GENERICHIDE); | 
| +      selectors = ElemHide.getSelectorsForDomain(hostname, specificOnly); | 
|  | 
| -    for (let filter of ElemHideEmulation.getRulesForDomain(hostname)) | 
| -      emulatedPatterns.push({selector: filter.selector, text: filter.text}); | 
| +      for (let filter of ElemHideEmulation.getRulesForDomain(hostname)) | 
| +        emulatedPatterns.push({selector: filter.selector, text: filter.text}); | 
| +    } | 
| } | 
|  | 
| if (!inline && !updateFrameStyles(sender.page.id, sender.frame.id, | 
| selectors, "standard")) | 
| { | 
| inline = true; | 
| } | 
|  | 
| @@ -201,8 +254,15 @@ | 
| return response; | 
| }); | 
|  | 
| port.on("elemhide.injectSelectors", (message, sender) => | 
| { | 
| updateFrameStyles(sender.page.id, sender.frame.id, message.selectors, | 
| message.groupName, message.appendOnly); | 
| }); | 
| + | 
| +fetch(browser.extension.getURL("/snippets.js"), {cache: "no-cache"}) | 
| +.then(response => response.ok ? response.text() : "") | 
| +.then(text => | 
| +{ | 
| +  snippetsLibrarySource = text; | 
| +}); | 
|  |