Bug 1350640 - Send blocklist state on plugin list update; r?bsmedberg draft
authorKyle Machulis <kyle@nonpolynomial.com>
Thu, 13 Jul 2017 17:11:55 -0700
changeset 610058 6642faad4dc493911576d6bc74c78d63fe56e1f1
parent 609865 e0b0865639cebc1b5afa0268a4b073fcdde0e69c
child 637756 7cc95eff84b84451fe88faf17464f961717c49d4
push id68777
push userbmo:kyle@nonpolynomial.com
push dateMon, 17 Jul 2017 21:16:23 +0000
reviewersbsmedberg
bugs1350640
milestone56.0a1
Bug 1350640 - Send blocklist state on plugin list update; r?bsmedberg Instead of synchronously checking the blocklist, package each plugin's blocklist state with it when sending the information to the content process. Whenever the blocklist is changed, just resend the whole plugin list. MozReview-Commit-ID: 1AX1EDdTRqb
dom/ipc/ContentChild.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginTags.cpp
dom/plugins/base/nsPluginTags.h
dom/plugins/ipc/PluginTypes.ipdlh
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -657,17 +657,19 @@ public:
                       bool aExistenceCheck, bool aIsFromNsIFile);
 
   typedef std::function<void(PRFileDesc*)> AnonymousTemporaryFileCallback;
   nsresult AsyncOpenAnonymousTemporaryFile(const AnonymousTemporaryFileCallback& aCallback);
 
   virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild) override;
 
   mozilla::ipc::IPCResult
-  RecvSetPluginList(const uint32_t& aPluginEpoch, nsTArray<PluginTag>&& aPluginTags, nsTArray<FakePluginTag>&& aFakePluginTags) override;
+  RecvSetPluginList(const uint32_t& aPluginEpoch,
+                    nsTArray<PluginTag>&& aPluginTags,
+                    nsTArray<FakePluginTag>&& aFakePluginTags) override;
 
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2360,16 +2360,17 @@ nsPluginHost::SetPluginsInContent(uint32
 
     SetChromeEpochForContent(aPluginEpoch);
 
     for (auto tag : aPlugins) {
 
       // Don't add the same plugin again.
       if (nsPluginTag* existing = PluginWithId(tag.id())) {
         UpdateInMemoryPluginInfo(existing);
+        existing->SetBlocklistState(tag.blocklistState());
         continue;
       }
 
       nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
                                                tag.name().get(),
                                                tag.description().get(),
                                                tag.filename().get(),
                                                "", // aFullPath
@@ -2377,17 +2378,18 @@ nsPluginHost::SetPluginsInContent(uint32
                                                nsTArray<nsCString>(tag.mimeTypes()),
                                                nsTArray<nsCString>(tag.mimeDescriptions()),
                                                nsTArray<nsCString>(tag.extensions()),
                                                tag.isJavaPlugin(),
                                                tag.isFlashPlugin(),
                                                tag.supportsAsyncRender(),
                                                tag.lastModifiedTime(),
                                                tag.isFromExtension(),
-                                               tag.sandboxLevel());
+                                               tag.sandboxLevel(),
+                                               tag.blocklistState());
       AddPluginTag(pluginTag);
     }
 
     for (const auto& tag : aFakePlugins) {
       // Don't add the same plugin again.
       for (const auto& existingTag : mFakePlugins) {
         if (existingTag->Id() == tag.id()) {
           continue;
@@ -2606,30 +2608,36 @@ nsPluginHost::SendPluginsToContent()
                                                  tag->SandboxScript()));
       continue;
     }
 
     /// FIXME-jsplugins - We need to cleanup the various plugintag classes
     /// to be more sane and avoid this dance
     nsPluginTag *tag = static_cast<nsPluginTag *>(basetag.get());
 
+    uint32_t blocklistState;
+    if (NS_WARN_IF(NS_FAILED(tag->GetBlocklistState(&blocklistState)))) {
+      return NS_ERROR_FAILURE;
+    }
+
     pluginTags.AppendElement(PluginTag(tag->mId,
                                        tag->Name(),
                                        tag->Description(),
                                        tag->MimeTypes(),
                                        tag->MimeDescriptions(),
                                        tag->Extensions(),
                                        tag->mIsJavaPlugin,
                                        tag->mIsFlashPlugin,
                                        tag->mSupportsAsyncRender,
                                        tag->FileName(),
                                        tag->Version(),
                                        tag->mLastModifiedTime,
                                        tag->IsFromExtension(),
-                                       tag->mSandboxLevel));
+                                       tag->mSandboxLevel,
+                                       blocklistState));
   }
   nsTArray<dom::ContentParent*> parents;
   dom::ContentParent::GetAll(parents);
   for (auto p : parents)
   {
     Unused << p->SendSetPluginList(newPluginEpoch, pluginTags, fakePluginTags);
   }
   return NS_OK;
@@ -3484,16 +3492,23 @@ NS_IMETHODIMP nsPluginHost::Observe(nsIS
     }
   }
   if (!strcmp("blocklist-updated", aTopic)) {
     nsPluginTag* plugin = mPlugins;
     while (plugin) {
       plugin->InvalidateBlocklistState();
       plugin = plugin->mNext;
     }
+    // We update blocklists asynchronously by just sending a new plugin list to
+    // content.
+    if (XRE_IsParentProcess()) {
+      // We'll need to repack our tags and send them to content again.
+      IncrementChromeEpoch();
+      SendPluginsToContent();
+    }
   }
 #ifdef MOZ_WIDGET_ANDROID
   if (!strcmp("application-background", aTopic)) {
     for(uint32_t i = 0; i < mInstances.Length(); i++) {
       mInstances[i]->NotifyForeground(false);
     }
   }
   if (!strcmp("application-foreground", aTopic)) {
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -293,30 +293,31 @@ nsPluginTag::nsPluginTag(uint32_t aId,
                          nsTArray<nsCString> aMimeTypes,
                          nsTArray<nsCString> aMimeDescriptions,
                          nsTArray<nsCString> aExtensions,
                          bool aIsJavaPlugin,
                          bool aIsFlashPlugin,
                          bool aSupportsAsyncRender,
                          int64_t aLastModifiedTime,
                          bool aFromExtension,
-                         int32_t aSandboxLevel)
+                         int32_t aSandboxLevel,
+                         uint16_t aBlocklistState)
   : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion, aMimeTypes,
                          aMimeDescriptions, aExtensions),
     mId(aId),
     mContentProcessRunningCount(0),
     mLibrary(nullptr),
     mIsJavaPlugin(aIsJavaPlugin),
     mIsFlashPlugin(aIsFlashPlugin),
     mSupportsAsyncRender(aSupportsAsyncRender),
     mLastModifiedTime(aLastModifiedTime),
     mSandboxLevel(aSandboxLevel),
     mNiceFileName(),
-    mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
-    mCachedBlocklistStateValid(false),
+    mCachedBlocklistState(aBlocklistState),
+    mCachedBlocklistStateValid(true),
     mIsFromExtension(aFromExtension)
 {
 }
 
 nsPluginTag::~nsPluginTag()
 {
   NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
 }
@@ -705,53 +706,57 @@ nsPluginTag::GetNiceName(nsACString & aR
 
 NS_IMETHODIMP
 nsPluginTag::GetBlocklistState(uint32_t *aResult)
 {
 #if defined(MOZ_WIDGET_ANDROID)
   *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
   return NS_OK;
 #else
-  if (mCachedBlocklistStateValid) {
+
+  // If we're in the content process, assume our cache state to always be valid,
+  // as the only way it can be updated is via a plugin list push from the
+  // parent process.
+  if (!XRE_IsParentProcess()) {
     *aResult = mCachedBlocklistState;
     return NS_OK;
   }
 
-  if (!XRE_IsParentProcess()) {
-    *aResult = nsIBlocklistService::STATE_BLOCKED;
-    dom::ContentChild* cp = dom::ContentChild::GetSingleton();
-    if (!cp->SendGetBlocklistState(mId, aResult)) {
-      return NS_OK;
-    }
-  } else {
-    nsCOMPtr<nsIBlocklistService> blocklist =
-      do_GetService("@mozilla.org/extensions/blocklist;1");
+  nsCOMPtr<nsIBlocklistService> blocklist =
+    do_GetService("@mozilla.org/extensions/blocklist;1");
 
-    if (!blocklist) {
-      *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
-      return NS_OK;
-    }
-
-    // The EmptyString()s are so we use the currently running application
-    // and toolkit versions
-    if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
-                                                     EmptyString(), aResult))) {
-      *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
-      return NS_OK;
-    }
+  if (!blocklist) {
+    *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+  }
+  // The EmptyString()s are so we use the currently running application
+  // and toolkit versions
+  else if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
+                                                   EmptyString(), aResult))) {
+    *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
   }
 
   MOZ_ASSERT(*aResult <= UINT16_MAX);
   mCachedBlocklistState = (uint16_t) *aResult;
   mCachedBlocklistStateValid = true;
   return NS_OK;
 #endif // defined(MOZ_WIDGET_ANDROID)
 }
 
 void
+nsPluginTag::SetBlocklistState(uint16_t aBlocklistState)
+{
+  // We should only ever call this on content processes. Any calls in the parent
+  // process should route through GetBlocklistState since we'll have the
+  // blocklist service there.
+  MOZ_ASSERT(!XRE_IsParentProcess());
+  mCachedBlocklistState = aBlocklistState;
+  mCachedBlocklistStateValid = true;
+}
+
+void
 nsPluginTag::InvalidateBlocklistState()
 {
   mCachedBlocklistStateValid = false;
 }
 
 NS_IMETHODIMP
 nsPluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
 {
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -130,30 +130,32 @@ public:
               nsTArray<nsCString> aMimeTypes,
               nsTArray<nsCString> aMimeDescriptions,
               nsTArray<nsCString> aExtensions,
               bool aIsJavaPlugin,
               bool aIsFlashPlugin,
               bool aSupportsAsyncRender,
               int64_t aLastModifiedTime,
               bool aFromExtension,
-              int32_t aSandboxLevel);
+              int32_t aSandboxLevel,
+              uint16_t aBlocklistState);
 
   void TryUnloadPlugin(bool inShutdown);
 
   // plugin is enabled and not blocklisted
   bool IsActive();
 
   bool IsEnabled() override;
   void SetEnabled(bool enabled);
   bool IsClicktoplay();
   bool IsBlocklisted();
 
   PluginState GetPluginState();
   void SetPluginState(PluginState state);
+  void SetBlocklistState(uint16_t aBlocklistState);
 
   bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
   const nsCString& GetNiceFileName() override;
 
   bool IsFromExtension() const;
 
   RefPtr<nsPluginTag> mNext;
   uint32_t      mId;
--- a/dom/plugins/ipc/PluginTypes.ipdlh
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -19,16 +19,17 @@ struct PluginTag
   bool isJavaPlugin;
   bool isFlashPlugin;
   bool supportsAsyncRender; // flash specific
   nsCString filename;
   nsCString version;
   int64_t lastModifiedTime;
   bool isFromExtension;
   int32_t sandboxLevel;
+  uint16_t blocklistState;
 };
 
 struct FakePluginTag
 {
   uint32_t id;
   URIParams handlerURI;
   nsCString name;
   nsCString description;