Index: lib/contentFiltering.js
===================================================================
--- a/lib/contentFiltering.js
+++ b/lib/contentFiltering.js
@@ -37,16 +37,18 @@
 
 const selectorGroupSize = 1024;
 
 let userStyleSheetsSupported = true;
 
 let snippetsLibrarySource = "";
 let executableCode = new Map();
 
+let registeredContentScripts = 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
@@ -224,17 +226,17 @@
   let {elemhide, snippets} = message.filterTypes ||
                              {elemhide: true, snippets: true};
 
   if (!checkWhitelisted(sender.page, sender.frame, null,
                         RegExpFilter.typeMap.DOCUMENT))
   {
     let hostname = extractHostFromFrame(sender.frame);
 
-    if (snippets)
+    if (snippets && !browser.contentScripts)
     {
       for (let script of Snippets.getScriptsForDomain(hostname))
         executeScript(script, sender.page.id, sender.frame.id);
     }
 
     if (elemhide && !checkWhitelisted(sender.page, sender.frame, null,
                                       RegExpFilter.typeMap.ELEMHIDE))
     {
@@ -274,8 +276,59 @@
 });
 
 fetch(browser.extension.getURL("/snippets.js"), {cache: "no-cache"})
 .then(response => response.ok ? response.text() : "")
 .then(text =>
 {
   snippetsLibrarySource = text;
 });
+
+if (browser.contentScripts)
+{
+  Snippets.on("snippets.filterAdded", ({script, domains, text}) =>
+  {
+    let details = {
+      js: [{code: getExecutableCode(script)}],
+      allFrames: true,
+      matchAboutBlank: true,
+      runAt: "document_start",
+      matches: []
+    };
+
+    for (let [domain, include] of domains)
+    {
+      if (domain == "")
+        continue;
+
+      if (!include && !details.excludeMatches)
+        details.excludeMatches = [];
+
+      let matches = include ? details.matches : details.excludeMatches;
+
+      matches.push(`http://*.${domain}/*`);
+      matches.push(`https://*.${domain}/*`);
+    }
+
+    browser.contentScripts.register(details).then(contentScript =>
+    {
+      registeredContentScripts.set(text, contentScript);
+    });
+  });
+
+  Snippets.on("snippets.filterRemoved", ({text}) =>
+  {
+    let contentScript = registeredContentScripts.get(text);
+    if (contentScript)
+    {
+      contentScript.unregister();
+      registeredContentScripts.delete(text);
+    }
+  });
+
+  Snippets.on("snippets.filtersCleared", () =>
+  {
+    for (let contentScript of registeredContentScripts.values())
+      contentScript.unregister();
+
+    registeredContentScripts.clear();
+  });
+}
