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 379355 f975e688b2289c2a590ef87c8fa80bd9db844a0b
parent 379354 c6d898a4af88b262ff33191a6ae461fbe9b48521
child 379356 14b6ab023dec51a423a034aa8f307c56776d59c1
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, lizzard
bugs1350381
milestone53.0
Bug 1350381 - Change Flash blocking to allow the setting "Ask to Activate" to control CTA of unknown documents. r=qdot a=lizzard Previously, we operated under the understanding that with Flash blocking activated, non-whitelisted documents would be set to CTA. We are changing that such that now, documents will only be CTA'ed if Flash is set to "Ask to Activate". Flash blocking will now behave according to the following chart: User Setting Flash block Whitelisted sites Blacklisted sites Unlisted sites "Never ..." Off Deny Deny Deny "Ask ..." Off Ask Ask Ask "Always ..." Off Allow Allow Allow "Never ..." On Deny Deny Deny "Ask ..." On Allow Deny Ask "Always ..." On Allow Deny Allow This patch also completely reworks the flash blocking testing. Test data and most code remains consolidated, but will be run in multiple different tests. This avoids having to extend the timeout for Flash block testing to an extremely long length. The new Flash block testing additionally tests each of the six cases (rows) in the table above. MozReview-Commit-ID: 5aPGUEiUiCv
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
@@ -12921,22 +12921,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;
@@ -12954,16 +12953,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
@@ -3354,17 +3354,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;
   }
 
@@ -3429,20 +3429,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);