WIP move away from manually mocking nsIPluginTag in tests draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 24 Apr 2018 01:20:25 +0100
changeset 787114 add07e914190e31d422f334396a9cd802cff056a
parent 786869 2b89643785ecd6ef3b5824b637e4576cc56f251e
push id107649
push usergijskruitbosch@gmail.com
push dateTue, 24 Apr 2018 09:45:13 +0000
milestone61.0a1
WIP move away from manually mocking nsIPluginTag in tests MozReview-Commit-ID: 9yaIBwlU89v
dom/plugins/base/nsPluginHost.cpp
toolkit/mozapps/extensions/nsBlocklistService.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklist_plugin_outdated.js
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -256,34 +256,43 @@ class BlocklistPromiseHandler final : pu
 {
   public:
     NS_DECL_ISUPPORTS
 
     BlocklistPromiseHandler(nsPluginTag *aTag, const bool aShouldSoftblock)
       : mTag(aTag)
       , mShouldDisableWhenSoftblocked(aShouldSoftblock)
     {
+      MOZ_ASSERT(mTag, "Should always be passed a plugin tag");
       sPendingBlocklistStateRequests++;
     }
 
     void
     MaybeWriteBlocklistChanges()
     {
-      // If this is the only remaining pending request, check if we need to write
-      // state and update the child processes.
-      if (sPendingBlocklistStateRequests == 1 &&
+      // We're called immediately when the promise resolves/rejects, and (as a backup)
+      // when the handler is destroyed. To ensure we only run once, we use mTag as a
+      // sentinel, setting it to nullptr when we run.
+      if (!mTag) {
+        return;
+      }
+      mTag = nullptr;
+      sPendingBlocklistStateRequests--;
+      // If this was the only remaining pending request, check if we need to write
+      // state and if so update the child processes.
+      if (!sPendingBlocklistStateRequests &&
           sPluginBlocklistStatesChangedSinceLastWrite) {
+        sPluginBlocklistStatesChangedSinceLastWrite = false;
+
         RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
-        sPluginBlocklistStatesChangedSinceLastWrite = false;
         // Write the changed list to disk:
         host->WritePluginInfo();
 
-        // We update blocklists asynchronously by just sending a new plugin list to
-        // content.
-        // We'll need to repack our tags and send them to content again.
+        // We update blocklist info in content processes asynchronously
+        // by just sending a new plugin list to content.
         host->IncrementChromeEpoch();
         host->SendPluginsToContent();
       }
     }
 
     void
     ResolvedCallback(JSContext *aCx, JS::Handle<JS::Value> aValue) override
     {
@@ -313,18 +322,19 @@ class BlocklistPromiseHandler final : pu
     RejectedCallback(JSContext *aCx, JS::Handle<JS::Value> aValue) override
     {
       MOZ_ASSERT(false, "Shouldn't reject plugin blocklist state request");
       MaybeWriteBlocklistChanges();
     }
 
   private:
     ~BlocklistPromiseHandler() {
-      // A request just resolved. Decrement counter of pending requests.
-      sPendingBlocklistStateRequests--;
+      // If we have multiple plugins and the last pending request is GC'd
+      // and so never resolves/rejects, ensure we still write the blocklist.
+      MaybeWriteBlocklistChanges();
     }
 
     RefPtr<nsPluginTag> mTag;
     bool mShouldDisableWhenSoftblocked;
 
     // Whether we changed any of the plugins' blocklist states since
     // we last started fetching them (async). This is reset to false
     // every time we finish fetching plugin blocklist information.
@@ -2230,16 +2240,17 @@ nsresult nsPluginHost::ScanPluginsDirect
   return NS_OK;
 }
 
 void
 nsPluginHost::UpdatePluginBlocklistState(nsPluginTag* aPluginTag, bool aShouldSoftblock)
 {
   nsCOMPtr<nsIBlocklistService> blocklist =
     do_GetService("@mozilla.org/extensions/blocklist;1");
+  MOZ_ASSERT(blocklist, "Should be able to access the blocklist");
   if (!blocklist) {
     return;
   }
   // Asynchronously get the blocklist state.
   nsCOMPtr<nsISupports> result;
   blocklist->GetPluginBlocklistState(aPluginTag, EmptyString(),
                                      EmptyString(), getter_AddRefs(result));
   RefPtr<Promise> promise = do_QueryObject(result);
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -855,18 +855,19 @@ Blocklist.prototype = {
     }
     if (!this._preloadPromise) {
       this._preloadPromise = this._loadBlocklistAsyncInternal();
     }
     await this._preloadPromise;
   },
 
   async _loadBlocklistAsyncInternal() {
-    let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
     try {
+      // Get the path inside the try...catch because there's no profileDir in e.g. xpcshell tests.
+      let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
       await this._preloadBlocklistFile(profPath);
       return;
     } catch (e) {
       LOG("Blocklist::loadBlocklistAsync: Failed to load XML file " + e);
     }
 
     var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
     try {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_plugin_outdated.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_plugin_outdated.js
@@ -12,44 +12,34 @@ const nsIBLS = Ci.nsIBlocklistService;
 var gBlocklist = null;
 var gTestserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 gTestserver.registerDirectory("/data/", do_get_file("data"));
 
 
 var PLUGINS = [{
   // Tests a plugin whose state goes from not-blocked, to outdated
   name: "test_bug514327_outdated",
+  handlerURI: "chrome://testing/content/test_bug514327_outdated.html",
+  mimeEntries: [{type: "application/x-shockwave-flash"}],
   version: "5",
-  disabled: false,
-  blocklisted: false
 }, {
   // Used to trigger the blocklist dialog, which indicates the blocklist has updated
   name: "test_bug514327_1",
+  handlerURI: "chrome://testing/content/test_bug514327_1.html",
+  mimeEntries: [{type: "application/x-shockwave-flash"}],
   version: "5",
-  disabled: false,
-  blocklisted: false
 }, {
   // Used to trigger the blocklist dialog, which indicates the blocklist has updated
   name: "test_bug514327_2",
+  handlerURI: "chrome://testing/content/test_bug514327_2.html",
+  mimeEntries: [{type: "application/x-shockwave-flash"}],
   version: "5",
-  disabled: false,
-  blocklisted: false
 } ];
 
 
-// A fake plugin host for the blocklist service to use
-var PluginHost = {
-  getPluginTags(countRef) {
-    countRef.value = PLUGINS.length;
-    return PLUGINS;
-  },
-
-  QueryInterface: XPCOMUtils.generateQI(["nsIPluginHost"]),
-};
-
 var BlocklistPrompt = {
   get wrappedJSObject() { return this; },
 
   prompt(list) {
     // Should only include one item
     Assert.equal(list.length, 1);
     // And that item should be the blocked plugin, not the outdated one
     var item = list[0];
@@ -66,25 +56,34 @@ async function loadBlocklist(file) {
 
   Services.prefs.setCharPref("extensions.blocklist.url",
                              "http://example.com/data/" + file);
   Services.blocklist.QueryInterface(Ci.nsITimerCallback).notify(null);
 
   await blocklistUpdated;
 }
 
-MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
-
 let factory = XPCOMUtils.generateSingletonFactory(function() { return BlocklistPrompt; });
 Cm.registerFactory(Components.ID("{26d32654-30c7-485d-b983-b4d2568aebba}"),
                    "Blocklist Prompt",
                    "@mozilla.org/addons/blocklist-prompt;1", factory);
 
 add_task(async function setup() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+  let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+  for (let p of PLUGINS) {
+    p.niceName = p.name;
+    let plugin = pluginHost.registerFakePlugin(p);
+    plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+  }
+  registerCleanupFunction(() => {
+    for (let p of PLUGINS) {
+      pluginHost.unregisterFakePlugin(p.handlerURI);
+    }
+  });
 
   // initialize the blocklist with no entries
   copyBlocklistToProfile(do_get_file("data/test_bug514327_3_empty.xml"));
 
   await promiseStartupManager();
 
   gBlocklist = Services.blocklist;