Bug 1350381 - Change Flash blocking to allow the setting 'Ask to Activate' to control CTA of unknown documents. r=qdot a=lizzard
authorKirk Steuber <ksteuber@mozilla.com>
Fri, 24 Mar 2017 14:15:02 -0700
changeset 395573 004cd303c040b4390fc6c60e5b3b26e34d21d944
parent 395572 05822f3a065705205ba3f021f7daa45cc6774f08
child 395574 1edf2dfa3809bdaaa83a3b8914fe80d4fe91a912
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, lizzard
bugs1350381
milestone54.0a2
Bug 1350381 - Change Flash blocking to allow the setting 'Ask to Activate' to control CTA of unknown documents. r=qdot a=lizzard MozReview-Commit-ID: GxE6iMclCzx
dom/base/nsDocument.cpp
dom/base/nsObjectLoadingContent.cpp
toolkit/components/url-classifier/tests/browser/browser.ini
toolkit/components/url-classifier/tests/browser/browser_flash_block_lists.js
toolkit/components/url-classifier/tests/browser/browser_flashblock_off_with_always_activate.js
toolkit/components/url-classifier/tests/browser/browser_flashblock_off_with_ask_to_activate.js
toolkit/components/url-classifier/tests/browser/browser_flashblock_off_with_never_activate.js
toolkit/components/url-classifier/tests/browser/browser_flashblock_on_with_always_activate.js
toolkit/components/url-classifier/tests/browser/browser_flashblock_on_with_ask_to_activate.js
toolkit/components/url-classifier/tests/browser/browser_flashblock_on_with_never_activate.js
toolkit/components/url-classifier/tests/browser/classifierTester.js
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13041,22 +13041,21 @@ ArrayContainsTable(const nsTArray<nsCStr
  *
  * For more information, see
  * toolkit/components/url-classifier/flash-block-lists.rst
  */
 FlashClassification
 nsDocument::PrincipalFlashClassification()
 {
   nsresult rv;
-  bool isThirdPartyDoc = IsThirdParty();
 
   // If flash blocking is disabled, it is equivalent to all sites being
-  // whitelisted.
+  // on neither list.
   if (!Preferences::GetBool("plugins.flashBlock.enabled")) {
-    return FlashClassification::Allowed;
+    return FlashClassification::Unknown;
   }
 
   nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
   if (principal->GetIsNullPrincipal()) {
     return FlashClassification::Denied;
   }
 
   nsCOMPtr<nsIURI> classificationURI;
@@ -13074,16 +13073,18 @@ nsDocument::PrincipalFlashClassification
   Preferences::GetCString("urlclassifier.flashAllowExceptTable",
                           &allowExceptionsTables);
   MaybeAddTableToTableList(allowExceptionsTables, tables);
   Preferences::GetCString("urlclassifier.flashTable", &denyTables);
   MaybeAddTableToTableList(denyTables, tables);
   Preferences::GetCString("urlclassifier.flashExceptTable",
                           &denyExceptionsTables);
   MaybeAddTableToTableList(denyExceptionsTables, tables);
+
+  bool isThirdPartyDoc = IsThirdParty();
   if (isThirdPartyDoc) {
     Preferences::GetCString("urlclassifier.flashSubDocTable",
                             &subDocDenyTables);
     MaybeAddTableToTableList(subDocDenyTables, tables);
     Preferences::GetCString("urlclassifier.flashSubDocExceptTable",
                             &subDocDenyExceptionsTables);
     MaybeAddTableToTableList(subDocDenyExceptionsTables, tables);
   }
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -3334,17 +3334,17 @@ nsObjectLoadingContent::ShouldPlay(Fallb
     return false;
   }
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetTop();
   NS_ENSURE_TRUE(topWindow, false);
   nsCOMPtr<nsIDocument> topDoc = topWindow->GetDoc();
   NS_ENSURE_TRUE(topDoc, false);
 
   // Check the flash blocking status for this page (this applies to Flash only)
-  FlashClassification documentClassification = FlashClassification::Allowed;
+  FlashClassification documentClassification = FlashClassification::Unknown;
   if (IsFlashMIME(mContentType)) {
     documentClassification = ownerDoc->DocumentFlashClassification();
   }
   if (documentClassification == FlashClassification::Denied) {
     aReason = eFallbackSuppressed;
     return false;
   }
 
@@ -3409,20 +3409,26 @@ nsObjectLoadingContent::ShouldPlay(Fallb
     return false;
   }
 
   if (PreferFallback(enabledState == nsIPluginTag::STATE_CLICKTOPLAY)) {
     aReason = eFallbackAlternate;
     return false;
   }
 
+  // On the following switch we don't need to handle the case where
+  // documentClassification is FlashClassification::Denied because
+  // that's already handled above.
   switch (enabledState) {
   case nsIPluginTag::STATE_ENABLED:
-    return documentClassification == FlashClassification::Allowed;
+    return true;
   case nsIPluginTag::STATE_CLICKTOPLAY:
+    if (documentClassification == FlashClassification::Allowed) {
+      return true;
+    }
     return false;
   }
   MOZ_CRASH("Unexpected enabledState");
 }
 
 bool
 nsObjectLoadingContent::FavorFallbackMode(bool aIsPluginClickToPlay) {
   if (!IsFlashMIME(mContentType)) {
--- a/toolkit/components/url-classifier/tests/browser/browser.ini
+++ b/toolkit/components/url-classifier/tests/browser/browser.ini
@@ -1,6 +1,13 @@
 [DEFAULT]
 support-files =
   flash_block_frame.html
   classifierHelper.js
+  classifierTester.js
 
-[browser_flash_block_lists.js]
+[browser_flashblock_on_with_never_activate.js]
+[browser_flashblock_off_with_never_activate.js]
+[browser_flashblock_on_with_ask_to_activate.js]
+[browser_flashblock_off_with_ask_to_activate.js]
+[browser_flashblock_on_with_always_activate.js]
+[browser_flashblock_off_with_always_activate.js]
+
deleted file mode 100644
--- a/toolkit/components/url-classifier/tests/browser/browser_flash_block_lists.js
+++ /dev/null
@@ -1,364 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-requestLongerTimeout(3);
-
-var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/Task.jsm");
-Cc["@mozilla.org/moz/jssubscript-loader;1"]
-  .getService(Ci.mozIJSSubScriptLoader)
-  .loadSubScript("chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
-                 this);
-
-const URL_PATH = "/browser/toolkit/components/url-classifier/tests/browser/flash_block_frame.html";
-const OBJECT_ID = "testObject";
-const IFRAME_ID = "testFrame";
-const FLASHBLOCK_ENABLE_PREF = "plugins.flashBlock.enabled";
-
-var dbUrls = [
-  {
-    url: "flashallow.example.com/",
-    db: "test-flashallow-simple",
-    pref: "urlclassifier.flashAllowTable"
-  },
-  {
-    url: "exception.flashallow.example.com/",
-    db: "testexcept-flashallow-simple",
-    pref: "urlclassifier.flashAllowExceptTable"
-  },
-  {
-    url: "flashblock.example.com/",
-    db: "test-flash-simple",
-    pref: "urlclassifier.flashTable"
-  },
-  {
-    url: "exception.flashblock.example.com/",
-    db: "testexcept-flash-simple",
-    pref: "urlclassifier.flashExceptTable"
-  },
-  {
-    url: "subdocument.example.com/",
-    db: "test-flashsubdoc-simple",
-    pref: "urlclassifier.flashThirdPartyTable"
-  },
-  {
-    url: "exception.subdocument.example.com/",
-    db: "testexcept-flashsubdoc-simple",
-    pref: "urlclassifier.flashThirdPartyExceptTable"
-  }
-];
-
-function setDBPrefs() {
-  for (let dbData of dbUrls) {
-    Services.prefs.setCharPref(dbData.pref, dbData.db);
-  }
-  Services.prefs.setBoolPref(FLASHBLOCK_ENABLE_PREF, true);
-}
-
-function unsetDBPrefs() {
-  for (let dbData of dbUrls) {
-    Services.prefs.clearUserPref(dbData.pref);
-  }
-  Services.prefs.clearUserPref(FLASHBLOCK_ENABLE_PREF);
-}
-registerCleanupFunction(unsetDBPrefs);
-
-// The |domains| property describes the domains of the nested documents making
-// up the page. |domains[0]| represents the domain in the URL bar. The last
-// domain in the list is the domain of the most deeply nested iframe.
-// Only the plugin in the most deeply nested document will be checked.
-var testCases = [
-  {
-    name: "Unknown domain",
-    domains: ["http://example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Nested unknown domains",
-    domains: ["http://example.com", "http://example.org"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Allowed domain",
-    domains: ["http://flashallow.example.com"],
-    expectedActivated: true,
-    expectedHasRunningPlugin: true,
-    pluginListed: true,
-    expectedFlashClassification: "allowed"
-  },
-  {
-    name: "Allowed nested domain",
-    domains: ["http://example.com", "http://flashallow.example.com"],
-    expectedActivated: true,
-    expectedHasRunningPlugin: true,
-    pluginListed: true,
-    expectedFlashClassification: "allowed"
-  },
-  {
-    name: "Subdocument of allowed domain",
-    domains: ["http://flashallow.example.com", "http://example.com"],
-    expectedActivated: true,
-    expectedHasRunningPlugin: true,
-    pluginListed: true,
-    expectedFlashClassification: "allowed"
-  },
-  {
-    name: "Exception to allowed domain",
-    domains: ["http://exception.flashallow.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Blocked domain",
-    domains: ["http://flashblock.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Nested blocked domain",
-    domains: ["http://example.com", "http://flashblock.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Subdocument of blocked subdocument",
-    domains: ["http://example.com", "http://flashblock.example.com", "http://example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Blocked subdocument nested among in allowed documents",
-    domains: ["http://flashallow.example.com", "http://flashblock.example.com", "http://flashallow.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Exception to blocked domain",
-    domains: ["http://exception.flashblock.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Sub-document blocked domain in top-level context",
-    domains: ["http://subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Sub-document blocked domain",
-    domains: ["http://example.com", "http://subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Sub-document blocked domain in non-Third-Party context",
-    domains: ["http://subdocument.example.com", "http://subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Sub-document blocked domain differing only by scheme",
-    domains: ["http://subdocument.example.com", "https://subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Sub-document blocked subdocument of an allowed domain",
-    domains: ["http://flashallow.example.com", "http://subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Subdocument of Sub-document blocked domain",
-    domains: ["http://example.com", "http://subdocument.example.com", "http://example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: false,
-    expectedFlashClassification: "denied"
-  },
-  {
-    name: "Sub-document exception in top-level context",
-    domains: ["http://exception.subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  },
-  {
-    name: "Sub-document blocked domain exception",
-    domains: ["http://example.com", "http://exception.subdocument.example.com"],
-    expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-    expectedActivated: false,
-    expectedHasRunningPlugin: false,
-    pluginListed: true,
-    expectedFlashClassification: "unknown"
-  }
-];
-
-function buildDocumentStructure(browser, iframeDomains) {
-  return Task.spawn(function* () {
-    let depth = 0;
-    for (let domain of iframeDomains) {
-      // Firefox does not like to load the same page in its own iframe. Put some
-      // bogus query strings in the URL to make it happy.
-      let url = domain + URL_PATH + "?date=" + Date.now() + "rand=" + Math.random();
-      let domainLoaded = BrowserTestUtils.browserLoaded(browser, true, url);
-
-      ContentTask.spawn(browser, {iframeId: IFRAME_ID, url: url, depth: depth},
-                        function*({iframeId, url, depth}) {
-        let doc = content.document;
-        for (let i = 0; i < depth; ++i) {
-          doc = doc.getElementById(iframeId).contentDocument;
-        }
-        doc.getElementById(iframeId).src = url;
-      });
-
-      yield domainLoaded;
-      ++depth;
-    }
-  });
-}
-
-function getPluginInfo(browser, depth) {
-  return ContentTask.spawn(browser,
-                           {iframeId: IFRAME_ID, depth: depth},
-                           function* ({iframeId, depth}) {
-    let doc = content.document;
-    let win = content.window;
-    for (let i = 0; i < depth; ++i) {
-      let frame = doc.getElementById(iframeId);
-      doc = frame.contentDocument;
-      win = frame.contentWindow;
-    }
-
-    let pluginObj = doc.getElementById("testObject");
-    if (!(pluginObj instanceof Ci.nsIObjectLoadingContent)) {
-      throw new Error("Unable to find plugin!");
-    }
-    return {
-      pluginFallbackType: pluginObj.pluginFallbackType,
-      activated: pluginObj.activated,
-      hasRunningPlugin: pluginObj.hasRunningPlugin,
-      listed: ("Shockwave Flash" in win.navigator.plugins),
-      flashClassification: doc.documentFlashClassification
-    };
-  });
-}
-
-add_task(function* checkFlashBlockLists() {
-  setDBPrefs();
-
-  yield classifierHelper.waitForInit();
-  yield classifierHelper.addUrlToDB(dbUrls);
-
-  for (let testCase of testCases) {
-    info(`RUNNING TEST: ${testCase.name}`);
-
-    let iframeDomains = testCase.domains.slice();
-    let pageDomain = iframeDomains.shift();
-    let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
-                                                          pageDomain + URL_PATH);
-
-    yield buildDocumentStructure(tab.linkedBrowser, iframeDomains);
-
-    let pluginInfo = yield getPluginInfo(tab.linkedBrowser, iframeDomains.length);
-
-    if ("expectedPluginFallbackType" in testCase) {
-      is(pluginInfo.pluginFallbackType, testCase.expectedPluginFallbackType,
-        "Plugin should have the correct fallback type");
-    }
-    if ("expectedActivated" in testCase) {
-      is(pluginInfo.activated, testCase.expectedActivated,
-        "Plugin should have the correct activation");
-    }
-    if ("expectedHasRunningPlugin" in testCase) {
-      is(pluginInfo.hasRunningPlugin, testCase.expectedHasRunningPlugin,
-        "Plugin should have the correct 'plugin running' state");
-    }
-    if ("pluginListed" in testCase) {
-      is(pluginInfo.listed, testCase.pluginListed,
-        "Plugin's existance in navigator.plugins should match expected")
-    }
-    if ("expectedFlashClassification" in testCase) {
-      is(pluginInfo.flashClassification, testCase.expectedFlashClassification,
-        "Page's classification should match expected");
-    }
-
-    yield BrowserTestUtils.removeTab(tab);
-  }
-});
-
-add_task(function* checkFlashBlockDisabled() {
-  setDBPrefs();
-  Services.prefs.setBoolPref(FLASHBLOCK_ENABLE_PREF, false);
-
-  yield classifierHelper.waitForInit();
-  yield classifierHelper.addUrlToDB(dbUrls);
-
-  for (let testCase of testCases) {
-    info(`RUNNING TEST: ${testCase.name} (flashblock disabled)`);
-
-    let iframeDomains = testCase.domains.slice();
-    let pageDomain = iframeDomains.shift();
-    let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
-                                                          pageDomain + URL_PATH);
-
-    yield buildDocumentStructure(tab.linkedBrowser, iframeDomains);
-
-    let pluginInfo = yield getPluginInfo(tab.linkedBrowser, iframeDomains.length);
-
-    // With flashblock disabled, all plugins should be activated.
-    ok(pluginInfo.activated, "Plugin should be activated");
-    ok(pluginInfo.hasRunningPlugin, "Plugin should be running");
-    ok(pluginInfo.listed, "Flash should be listed in navigator.plugins");
-    is(pluginInfo.flashClassification, "allowed",
-       "Page's classification should be 'allowed'");
-
-    yield BrowserTestUtils.removeTab(tab);
-  }
-});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_flashblock_off_with_always_activate.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+requestLongerTimeout(2);
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+const scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
+  this);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierTester.js",
+  this);
+
+add_task(function* checkFlashBlockLists() {
+  classifierTester.setPrefs({
+    setDBs: true,
+    flashBlockEnable: false,
+    flashSetting: classifierTester.ALWAYS_ACTIVATE_PREF_VALUE
+  });
+
+  yield classifierHelper.waitForInit();
+  yield classifierHelper.addUrlToDB(classifierTester.dbUrls);
+
+  for (let testCase of classifierTester.testCases) {
+    info(`RUNNING TEST: ${testCase.name} (Ask to Activate, Flashblock off)`);
+    let tab = yield classifierTester.buildTestCaseInNewTab(gBrowser, testCase);
+
+    let depth = testCase.domains.length - 1;
+    let pluginInfo = yield classifierTester.getPluginInfo(tab.linkedBrowser,
+                                                          depth);
+
+    classifierTester.checkPluginInfo(pluginInfo,
+                                     "unknown",
+                                     classifierTester.ALWAYS_ACTIVATE_PREF_VALUE);
+
+    yield BrowserTestUtils.removeTab(tab);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_flashblock_off_with_ask_to_activate.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+requestLongerTimeout(2);
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+const scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
+  this);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierTester.js",
+  this);
+
+add_task(function* checkFlashBlockLists() {
+  classifierTester.setPrefs({
+    setDBs: true,
+    flashBlockEnable: false,
+    flashSetting: classifierTester.ASK_TO_ACTIVATE_PREF_VALUE
+  });
+
+  yield classifierHelper.waitForInit();
+  yield classifierHelper.addUrlToDB(classifierTester.dbUrls);
+
+  for (let testCase of classifierTester.testCases) {
+    info(`RUNNING TEST: ${testCase.name} (Ask to Activate, Flashblock off)`);
+    let tab = yield classifierTester.buildTestCaseInNewTab(gBrowser, testCase);
+
+    let depth = testCase.domains.length - 1;
+    let pluginInfo = yield classifierTester.getPluginInfo(tab.linkedBrowser,
+                                                          depth);
+
+    classifierTester.checkPluginInfo(pluginInfo,
+                                     "unknown",
+                                     classifierTester.ASK_TO_ACTIVATE_PREF_VALUE);
+
+    yield BrowserTestUtils.removeTab(tab);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_flashblock_off_with_never_activate.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+requestLongerTimeout(2);
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+const scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
+  this);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierTester.js",
+  this);
+
+add_task(function* checkFlashBlockLists() {
+  classifierTester.setPrefs({
+    setDBs: true,
+    flashBlockEnable: false,
+    flashSetting: classifierTester.NEVER_ACTIVATE_PREF_VALUE
+  });
+
+  yield classifierHelper.waitForInit();
+  yield classifierHelper.addUrlToDB(classifierTester.dbUrls);
+
+  for (let testCase of classifierTester.testCases) {
+    info(`RUNNING TEST: ${testCase.name} (Never Activate, Flashblock off)`);
+    let tab = yield classifierTester.buildTestCaseInNewTab(gBrowser, testCase);
+
+    let depth = testCase.domains.length - 1;
+    let pluginInfo = yield classifierTester.getPluginInfo(tab.linkedBrowser,
+                                                          depth);
+
+    classifierTester.checkPluginInfo(pluginInfo,
+                                     "unknown",
+                                     classifierTester.NEVER_ACTIVATE_PREF_VALUE);
+
+    yield BrowserTestUtils.removeTab(tab);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_flashblock_on_with_always_activate.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+requestLongerTimeout(2);
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+const scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
+  this);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierTester.js",
+  this);
+
+add_task(function* checkFlashBlockLists() {
+  classifierTester.setPrefs({
+    setDBs: true,
+    flashBlockEnable: true,
+    flashSetting: classifierTester.ALWAYS_ACTIVATE_PREF_VALUE
+  });
+
+  yield classifierHelper.waitForInit();
+  yield classifierHelper.addUrlToDB(classifierTester.dbUrls);
+
+  for (let testCase of classifierTester.testCases) {
+    info(`RUNNING TEST: ${testCase.name} (Ask to Activate, Flashblock on)`);
+    let tab = yield classifierTester.buildTestCaseInNewTab(gBrowser, testCase);
+
+    let depth = testCase.domains.length - 1;
+    let pluginInfo = yield classifierTester.getPluginInfo(tab.linkedBrowser,
+                                                          depth);
+
+    classifierTester.checkPluginInfo(pluginInfo,
+                                     testCase.expectedFlashClassification,
+                                     classifierTester.ALWAYS_ACTIVATE_PREF_VALUE);
+
+    yield BrowserTestUtils.removeTab(tab);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_flashblock_on_with_ask_to_activate.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+requestLongerTimeout(2);
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+const scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
+  this);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierTester.js",
+  this);
+
+add_task(function* checkFlashBlockLists() {
+  classifierTester.setPrefs({
+    setDBs: true,
+    flashBlockEnable: true,
+    flashSetting: classifierTester.ASK_TO_ACTIVATE_PREF_VALUE
+  });
+
+  yield classifierHelper.waitForInit();
+  yield classifierHelper.addUrlToDB(classifierTester.dbUrls);
+
+  for (let testCase of classifierTester.testCases) {
+    info(`RUNNING TEST: ${testCase.name} (Ask to Activate, Flashblock on)`);
+    let tab = yield classifierTester.buildTestCaseInNewTab(gBrowser, testCase);
+
+    let depth = testCase.domains.length - 1;
+    let pluginInfo = yield classifierTester.getPluginInfo(tab.linkedBrowser,
+                                                          depth);
+
+    classifierTester.checkPluginInfo(pluginInfo,
+                                     testCase.expectedFlashClassification,
+                                     classifierTester.ASK_TO_ACTIVATE_PREF_VALUE);
+
+    yield BrowserTestUtils.removeTab(tab);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_flashblock_on_with_never_activate.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+requestLongerTimeout(2);
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+const scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                     getService(Ci.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierHelper.js",
+  this);
+scriptLoader.loadSubScript(
+  "chrome://mochitests/content/browser/toolkit/components/url-classifier/tests/browser/classifierTester.js",
+  this);
+
+add_task(function* checkFlashBlockLists() {
+  classifierTester.setPrefs({
+    setDBs: true,
+    flashBlockEnable: true,
+    flashSetting: classifierTester.NEVER_ACTIVATE_PREF_VALUE
+  });
+
+  yield classifierHelper.waitForInit();
+  yield classifierHelper.addUrlToDB(classifierTester.dbUrls);
+
+  for (let testCase of classifierTester.testCases) {
+    info(`RUNNING TEST: ${testCase.name} (Never Activate, Flashblock on)`);
+    let tab = yield classifierTester.buildTestCaseInNewTab(gBrowser, testCase);
+
+    let depth = testCase.domains.length - 1;
+    let pluginInfo = yield classifierTester.getPluginInfo(tab.linkedBrowser,
+                                                          depth);
+
+    classifierTester.checkPluginInfo(pluginInfo,
+                                     testCase.expectedFlashClassification,
+                                     classifierTester.NEVER_ACTIVATE_PREF_VALUE);
+
+    yield BrowserTestUtils.removeTab(tab);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/classifierTester.js
@@ -0,0 +1,337 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Cu.import("resource://gre/modules/Task.jsm");
+
+var classifierTester = {
+  URL_PATH: "/browser/toolkit/components/url-classifier/tests/browser/flash_block_frame.html",
+  OBJECT_ID: "testObject",
+  IFRAME_ID: "testFrame",
+  FLASHBLOCK_ENABLE_PREF: "plugins.flashBlock.enabled",
+  FLASH_PLUGIN_USER_SETTING_PREF: "plugin.state.flash",
+  NEVER_ACTIVATE_PREF_VALUE: 0,
+  ASK_TO_ACTIVATE_PREF_VALUE: 1,
+  ALWAYS_ACTIVATE_PREF_VALUE: 2,
+  ALLOW_CTA_PREF: "plugins.click_to_play",
+
+  dbUrls: [
+    {
+      url: "flashallow.example.com/",
+      db: "test-flashallow-simple",
+      pref: "urlclassifier.flashAllowTable"
+    },
+    {
+      url: "exception.flashallow.example.com/",
+      db: "testexcept-flashallow-simple",
+      pref: "urlclassifier.flashAllowExceptTable"
+    },
+    {
+      url: "flashblock.example.com/",
+      db: "test-flash-simple",
+      pref: "urlclassifier.flashTable"
+    },
+    {
+      url: "exception.flashblock.example.com/",
+      db: "testexcept-flash-simple",
+      pref: "urlclassifier.flashExceptTable"
+    },
+    {
+      url: "subdocument.example.com/",
+      db: "test-flashsubdoc-simple",
+      pref: "urlclassifier.flashThirdPartyTable"
+    },
+    {
+      url: "exception.subdocument.example.com/",
+      db: "testexcept-flashsubdoc-simple",
+      pref: "urlclassifier.flashThirdPartyExceptTable"
+    }
+  ],
+
+  setPrefs: function ({setDBs = true, flashBlockEnable = true, flashSetting = classifierTester.ALWAYS_ACTIVATE_PREF_VALUE} = {}) {
+    if (setDBs) {
+      for (let dbData of classifierTester.dbUrls) {
+        Services.prefs.setCharPref(dbData.pref, dbData.db);
+      }
+    }
+    Services.prefs.setBoolPref(classifierTester.FLASHBLOCK_ENABLE_PREF,
+                               flashBlockEnable);
+    Services.prefs.setIntPref(classifierTester.FLASH_PLUGIN_USER_SETTING_PREF,
+                              flashSetting);
+    Services.prefs.setBoolPref(classifierTester.ALLOW_CTA_PREF, true);
+  },
+
+  unsetPrefs: function () {
+    for (let dbData of classifierTester.dbUrls) {
+      Services.prefs.clearUserPref(dbData.pref);
+    }
+    Services.prefs.clearUserPref(classifierTester.FLASHBLOCK_ENABLE_PREF);
+    Services.prefs.clearUserPref(classifierTester.FLASH_PLUGIN_USER_SETTING_PREF);
+    Services.prefs.clearUserPref(classifierTester.ALLOW_CTA_PREF);
+  },
+
+  // The |domains| property describes the domains of the nested documents making
+  // up the page. |domains[0]| represents the domain in the URL bar. The last
+  // domain in the list is the domain of the most deeply nested iframe.
+  // Only the plugin in the most deeply nested document will be checked.
+  testCases: [
+    {
+      name: "Unknown domain",
+      domains: ["http://example.com"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Nested unknown domains",
+      domains: ["http://example.com", "http://example.org"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Allowed domain",
+      domains: ["http://flashallow.example.com"],
+      expectedFlashClassification: "allowed"
+    },
+    {
+      name: "Allowed nested domain",
+      domains: ["http://example.com", "http://flashallow.example.com"],
+      expectedFlashClassification: "allowed"
+    },
+    {
+      name: "Subdocument of allowed domain",
+      domains: ["http://flashallow.example.com", "http://example.com"],
+      expectedFlashClassification: "allowed"
+    },
+    {
+      name: "Exception to allowed domain",
+      domains: ["http://exception.flashallow.example.com"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Blocked domain",
+      domains: ["http://flashblock.example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Nested blocked domain",
+      domains: ["http://example.com", "http://flashblock.example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Subdocument of blocked subdocument",
+      domains: ["http://example.com", "http://flashblock.example.com", "http://example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Blocked subdocument nested among in allowed documents",
+      domains: ["http://flashallow.example.com", "http://flashblock.example.com", "http://flashallow.example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Exception to blocked domain",
+      domains: ["http://exception.flashblock.example.com"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Sub-document blocked domain in top-level context",
+      domains: ["http://subdocument.example.com"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Sub-document blocked domain",
+      domains: ["http://example.com", "http://subdocument.example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Sub-document blocked domain in non-Third-Party context",
+      domains: ["http://subdocument.example.com", "http://subdocument.example.com"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Sub-document blocked domain differing only by scheme",
+      domains: ["http://subdocument.example.com", "https://subdocument.example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Sub-document blocked subdocument of an allowed domain",
+      domains: ["http://flashallow.example.com", "http://subdocument.example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Subdocument of Sub-document blocked domain",
+      domains: ["http://example.com", "http://subdocument.example.com", "http://example.com"],
+      expectedFlashClassification: "denied"
+    },
+    {
+      name: "Sub-document exception in top-level context",
+      domains: ["http://exception.subdocument.example.com"],
+      expectedFlashClassification: "unknown"
+    },
+    {
+      name: "Sub-document blocked domain exception",
+      domains: ["http://example.com", "http://exception.subdocument.example.com"],
+      expectedFlashClassification: "unknown"
+    }
+  ],
+
+  // Returns null if this value should not be verified given the combination
+  // of inputs
+  expectedPluginFallbackType: function (classification, flashSetting) {
+    switch(classification) {
+      case "unknown":
+        if (flashSetting == classifierTester.ALWAYS_ACTIVATE_PREF_VALUE) {
+          return null;
+        } else if (flashSetting == classifierTester.ASK_TO_ACTIVATE_PREF_VALUE) {
+          return Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY;
+        } else if (flashSetting == classifierTester.NEVER_ACTIVATE_PREF_VALUE) {
+          return Ci.nsIObjectLoadingContent.PLUGIN_DISABLED;
+        }
+        break;
+      case "allowed":
+        if (flashSetting == classifierTester.NEVER_ACTIVATE_PREF_VALUE) {
+          return Ci.nsIObjectLoadingContent.PLUGIN_DISABLED;
+        }
+        return null;
+      case "denied":
+        return Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED;
+    }
+    throw new Error("Invalid classification or flash setting");
+  },
+
+  // Returns null if this value should not be verified given the combination
+  // of inputs
+  expectedActivated: function (classification, flashSetting) {
+    switch(classification) {
+      case "unknown":
+        return (flashSetting == classifierTester.ALWAYS_ACTIVATE_PREF_VALUE);
+      case "allowed":
+        return (flashSetting != classifierTester.NEVER_ACTIVATE_PREF_VALUE);
+      case "denied":
+        return false;
+    }
+    throw new Error("Invalid classification or flash setting");
+  },
+
+  // Returns null if this value should not be verified given the combination
+  // of inputs
+  expectedHasRunningPlugin: function (classification, flashSetting) {
+    switch(classification) {
+      case "unknown":
+        return (flashSetting == classifierTester.ALWAYS_ACTIVATE_PREF_VALUE);
+      case "allowed":
+        return (flashSetting != classifierTester.NEVER_ACTIVATE_PREF_VALUE);
+      case "denied":
+        return false;
+    }
+    throw new Error("Invalid classification or flash setting");
+  },
+
+  // Returns null if this value should not be verified given the combination
+  // of inputs
+  expectedPluginListed: function (classification, flashSetting) {
+    if (flashSetting == classifierTester.ASK_TO_ACTIVATE_PREF_VALUE &&
+        Services.prefs.getCharPref('plugins.navigator.hidden_ctp_plugin') == "Shockwave Flash") {
+      return false;
+    }
+    switch(classification) {
+      case "unknown":
+      case "allowed":
+        return (flashSetting != classifierTester.NEVER_ACTIVATE_PREF_VALUE);
+      case "denied":
+        return false;
+    }
+    throw new Error("Invalid classification or flash setting");
+  },
+
+  buildTestCaseInNewTab: function (browser, testCase) {
+    return Task.spawn(function* () {
+      let iframeDomains = testCase.domains.slice();
+      let pageDomain = iframeDomains.shift();
+      let tab = yield BrowserTestUtils.openNewForegroundTab(browser,
+                                                            pageDomain + classifierTester.URL_PATH);
+
+      let depth = 0;
+      for (let domain of iframeDomains) {
+        // Firefox does not like to load the same page in its own iframe. Put some
+        // bogus query strings in the URL to make it happy.
+        let url = domain + classifierTester.URL_PATH + "?date=" + Date.now() + "rand=" + Math.random();
+        let domainLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, true, url);
+
+        ContentTask.spawn(tab.linkedBrowser, {iframeId: classifierTester.IFRAME_ID, url: url, depth: depth},
+                          function*({iframeId, url, depth}) {
+          let doc = content.document;
+          for (let i = 0; i < depth; ++i) {
+            doc = doc.getElementById(iframeId).contentDocument;
+          }
+          doc.getElementById(iframeId).src = url;
+        });
+
+        yield domainLoaded;
+        ++depth;
+      }
+      return tab;
+    });
+  },
+
+  getPluginInfo: function (browser, depth) {
+    return ContentTask.spawn(browser,
+                             {iframeId: classifierTester.IFRAME_ID, depth: depth},
+                             function* ({iframeId, depth}) {
+      let doc = content.document;
+      let win = content.window;
+      for (let i = 0; i < depth; ++i) {
+        let frame = doc.getElementById(iframeId);
+        doc = frame.contentDocument;
+        win = frame.contentWindow;
+      }
+
+      let pluginObj = doc.getElementById("testObject");
+      if (!(pluginObj instanceof Ci.nsIObjectLoadingContent)) {
+        throw new Error("Unable to find plugin!");
+      }
+      return {
+        pluginFallbackType: pluginObj.pluginFallbackType,
+        activated: pluginObj.activated,
+        hasRunningPlugin: pluginObj.hasRunningPlugin,
+        listed: ("Shockwave Flash" in win.navigator.plugins),
+        flashClassification: doc.documentFlashClassification
+      };
+    });
+  },
+
+  checkPluginInfo: function (pluginInfo, expectedClassification, flashSetting) {
+    is(pluginInfo.flashClassification, expectedClassification,
+       "Page's classification should match expected");
+
+    let expectedPluginFallbackType =
+      classifierTester.expectedPluginFallbackType(pluginInfo.flashClassification,
+                                                  flashSetting);
+    if (expectedPluginFallbackType != null) {
+      is(pluginInfo.pluginFallbackType, expectedPluginFallbackType,
+         "Plugin should have the correct fallback type");
+    }
+
+    let expectedActivated =
+      classifierTester.expectedActivated(pluginInfo.flashClassification,
+                                         flashSetting);
+    if (expectedActivated != null) {
+      is(pluginInfo.activated, expectedActivated,
+         "Plugin should have the correct activation");
+    }
+
+    let expectedHasRunningPlugin =
+      classifierTester.expectedHasRunningPlugin(pluginInfo.flashClassification,
+                                                flashSetting);
+    if (expectedHasRunningPlugin != null) {
+      is(pluginInfo.hasRunningPlugin, expectedHasRunningPlugin,
+         "Plugin should have the correct 'plugin running' state");
+    }
+
+    let expectedPluginListed =
+      classifierTester.expectedPluginListed(pluginInfo.flashClassification,
+                                            flashSetting);
+    if (expectedPluginListed != null) {
+      is(pluginInfo.listed, expectedPluginListed,
+         "Plugin's existance in navigator.plugins should match expected");
+    }
+  }
+};
+registerCleanupFunction(classifierTester.unsetPrefs);