Bug 1178963 part 3. Make nsPluginHost aware of fake plugins. r=peterv
authorJohn Schoenick <jschoenick@mozilla.com>
Wed, 20 May 2015 15:30:05 +0200
changeset 254597 4d96b3d42fd9663eacd36c1ef39c55e6ee7af452
parent 254596 a73ee6e0fc92722fbb1256b3eb43d8dc7a2267a1
child 254598 a983b06b1be393b73b6aef5cb2a2b6729145633e
push id29108
push userryanvm@gmail.com
push dateMon, 27 Jul 2015 14:12:01 +0000
treeherdermozilla-central@27ae736ef960 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1178963
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1178963 part 3. Make nsPluginHost aware of fake plugins. r=peterv
dom/plugins/base/nsIPluginHost.idl
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginHost.h
dom/plugins/base/nsPluginTags.cpp
dom/plugins/base/nsPluginTags.h
dom/webidl/FakePluginTagInit.webidl
dom/webidl/moz.build
--- a/dom/plugins/base/nsIPluginHost.idl
+++ b/dom/plugins/base/nsIPluginHost.idl
@@ -31,21 +31,21 @@ interface nsIPluginPlayPreviewInfo : nsI
 interface nsIClearSiteDataCallback : nsISupports
 {
     /**
      * callback with the result from a call to clearSiteData
      */
     void callback(in nsresult rv);
 };
 
-[scriptable, uuid(2bed7533-50e3-45dd-9e47-102e2c6f5ea8)]
+[scriptable, uuid(50677599-323f-4c7d-a450-307bdb7acbf0)]
 interface nsIPluginHost : nsISupports
 {
   /**
-   * Causes the plugins directory to be searched again for new plugin 
+   * Causes the plugins directory to be searched again for new plugin
    * libraries.
    */
   void reloadPlugins();
 
   void getPluginTags([optional] out unsigned long aPluginCount,
     [retval, array, size_is(aPluginCount)] out nsIPluginTag aResults);
 
   /*
@@ -59,16 +59,17 @@ interface nsIPluginHost : nsISupports
   const uint32_t FLAG_CLEAR_ALL = 0;
   const uint32_t FLAG_CLEAR_CACHE = 1;
 
   /*
    * For use with Get*ForType functions
    */
   const uint32_t EXCLUDE_NONE     = 0;
   const uint32_t EXCLUDE_DISABLED = 1 << 0;
+  const uint32_t EXCLUDE_FAKE     = 1 << 1;
 
   /*
    * Clear site data for a given plugin.
    *
    * @param plugin: the plugin to clear data for, such as one returned by
    *                nsIPluginHost.getPluginTags.
    * @param domain: the domain to clear data for. If this argument is null,
    *                clear data for all domains. Otherwise, it must be a domain
@@ -122,42 +123,80 @@ interface nsIPluginHost : nsISupports
 
   nsIPluginPlayPreviewInfo getPlayPreviewInfo(in AUTF8String mimeType);
 
   /**
    * Get the "permission string" for the plugin.  This is a string that can be
    * passed to the permission manager to see whether the plugin is allowed to
    * run, for example.  This will typically be based on the plugin's "nice name"
    * and its blocklist state.
+   *
+   * @mimeType The MIME type we're interested in.
+   * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
    */
   ACString getPermissionStringForType(in AUTF8String mimeType,
                                       [optional] in uint32_t excludeFlags);
 
   /**
    * Get the nsIPluginTag for this MIME type. This method works with both
    * enabled and disabled/blocklisted plugins, but an enabled plugin will
    * always be returned if available.
    *
+   * A fake plugin tag, if one exists and is available, will be returned in
+   * preference to NPAPI plugin tags unless excluded by the excludeFlags.
+   *
+   * @mimeType The MIME type we're interested in.
+   * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+   *
    * @throws NS_ERROR_NOT_AVAILABLE if no plugin is available for this MIME
    *         type.
    */
   nsIPluginTag getPluginTagForType(in AUTF8String mimeType,
                                    [optional] in uint32_t excludeFlags);
 
   /**
    * Get the nsIPluginTag enabled state for this MIME type.  See
    * nsIPluginTag.enabledState.
+   *
+   * @mimeType The MIME type we're interested in.
+   * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
    */
   unsigned long getStateForType(in AUTF8String mimeType,
                                 [optional] in uint32_t excludeFlags);
 
   /**
    * Get the blocklist state for a MIME type.  See nsIPluginTag.blocklistState.
+   *
+   * @mimeType The MIME type we're interested in.
+   * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
    */
   uint32_t getBlocklistStateForType(in AUTF8String aMimeType,
                                     [optional] in uint32_t excludeFlags);
 
   /**
+   * Create a fake plugin tag, register it, and return it.  The argument is a
+   * FakePluginTagInit dictionary.  See documentation in
+   * FakePluginTagInit.webidl for what it should look like.  Will throw
+   * NS_ERROR_UNEXPECTED if there is already a fake plugin registered with the
+   * given handler URI.
+   */
+  [implicit_jscontext]
+  nsIFakePluginTag registerFakePlugin(in jsval initDictionary);
+
+  /**
+   * Get a reference to an existing fake plugin tag for the given MIME type, if
+   * any.  Can return null.
+   */
+  nsIFakePluginTag getFakePlugin(in AUTF8String mimeType);
+
+  /**
+   * Unregister a fake plugin.  The argument can be the .handlerURI.spec of an
+   * existing nsIFakePluginTag, or just a known handler URI string that was
+   * passed in the FakePluginTagInit when registering.
+   */
+  void unregisterFakePlugin(in AUTF8String handlerURI);
+
+  /**
    * Returns true if plugins with the given mimetype will run out of process.
    */
   boolean isPluginOOP(in AUTF8String aMimeType);
 };
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -47,16 +47,17 @@
 #include "nsIScriptChannel.h"
 #include "nsIBlocklistService.h"
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/FakePluginTagInitBinding.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
@@ -114,16 +115,17 @@
 #endif
 
 #include "npapi.h"
 
 using namespace mozilla;
 using mozilla::TimeStamp;
 using mozilla::plugins::PluginTag;
 using mozilla::plugins::PluginAsyncSurrogate;
+using mozilla::dom::FakePluginTagInit;
 
 // Null out a strong ref to a linked list iteratively to avoid
 // exhausting the stack (bug 486349).
 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
   {                                                                  \
     while (list_) {                                                  \
       type_ temp = list_->mNext_;                                    \
       list_->mNext_ = nullptr;                                       \
@@ -1044,39 +1046,55 @@ nsPluginHost::TrySetUpPluginInstance(con
   return rv;
 }
 
 bool
 nsPluginHost::HavePluginForType(const nsACString & aMimeType,
                                 PluginFilter aFilter)
 {
   bool checkEnabled = aFilter & eExcludeDisabled;
-  return FindNativePluginForType(aMimeType, checkEnabled);
+  bool allowFake = !(aFilter & eExcludeFake);
+  return FindPluginForType(aMimeType, allowFake, checkEnabled);
+}
+
+nsIInternalPluginTag*
+nsPluginHost::FindPluginForType(const nsACString& aMimeType,
+                                bool aIncludeFake, bool aCheckEnabled)
+{
+  if (aIncludeFake) {
+    nsFakePluginTag* fakeTag = FindFakePluginForType(aMimeType, aCheckEnabled);
+    if (fakeTag) {
+      return fakeTag;
+    }
+  }
+
+  return FindNativePluginForType(aMimeType, aCheckEnabled);
 }
 
 NS_IMETHODIMP
 nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
                                   uint32_t aExcludeFlags,
                                   nsIPluginTag** aResult)
 {
+  bool includeFake = !(aExcludeFlags & eExcludeFake);
   bool includeDisabled = !(aExcludeFlags & eExcludeDisabled);
 
-  nsPluginTag *tag = FindNativePluginForType(aMimeType, true);
-
-  // Prefer enabled, but select disabled if none is found
-  if (includeDisabled && !tag) {
-    tag = FindNativePluginForType(aMimeType, false);
+  // First look for an enabled plugin.
+  nsRefPtr<nsIInternalPluginTag> tag = FindPluginForType(aMimeType, includeFake,
+                                                         true);
+  if (!tag && includeDisabled) {
+    tag = FindPluginForType(aMimeType, includeFake, false);
   }
 
-  if (!tag) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (tag) {
+    tag.forget(aResult);
+    return NS_OK;
   }
 
-  NS_ADDREF(*aResult = tag);
-  return NS_OK;
+  return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
 nsPluginHost::GetStateForType(const nsACString &aMimeType,
                               uint32_t aExcludeFlags,
                               uint32_t* aResult)
 {
   nsCOMPtr<nsIPluginTag> tag;
@@ -1147,62 +1165,80 @@ nsPluginHost::GetPermissionStringForType
 }
 
 bool
 nsPluginHost::HavePluginForExtension(const nsACString & aExtension,
                                      /* out */ nsACString & aMimeType,
                                      PluginFilter aFilter)
 {
   bool checkEnabled = aFilter & eExcludeDisabled;
-  return FindNativePluginForExtension(aExtension, aMimeType, checkEnabled);
+  bool allowFake = !(aFilter & eExcludeFake);
+  return FindNativePluginForExtension(aExtension, aMimeType, checkEnabled) ||
+    (allowFake &&
+     FindFakePluginForExtension(aExtension, aMimeType, checkEnabled));
 }
 
 void
 nsPluginHost::GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
                          bool aIncludeDisabled)
 {
   aPluginArray.Clear();
 
   LoadPlugins();
 
+  // Append fake plugins, then normal plugins.
+
+  uint32_t numFake = mFakePlugins.Length();
+  for (uint32_t i = 0; i < numFake; i++) {
+    aPluginArray.AppendElement(mFakePlugins[i]);
+  }
+
+  // Regular plugins
   nsPluginTag* plugin = mPlugins;
   while (plugin != nullptr) {
     if (plugin->IsEnabled() || aIncludeDisabled) {
       aPluginArray.AppendElement(plugin);
     }
     plugin = plugin->mNext;
   }
 }
 
+// FIXME-jsplugins Check users for order of fake v non-fake
 NS_IMETHODIMP
 nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
 {
   LoadPlugins();
 
   uint32_t count = 0;
+  uint32_t fakeCount = mFakePlugins.Length();
   nsRefPtr<nsPluginTag> plugin = mPlugins;
   while (plugin != nullptr) {
     count++;
     plugin = plugin->mNext;
   }
 
   *aResults = static_cast<nsIPluginTag**>
-                         (moz_xmalloc(count * sizeof(**aResults)));
+                         (moz_xmalloc((fakeCount + count) * sizeof(**aResults)));
   if (!*aResults)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  *aPluginCount = count;
+  *aPluginCount = count + fakeCount;
 
   plugin = mPlugins;
   for (uint32_t i = 0; i < count; i++) {
     (*aResults)[i] = plugin;
     NS_ADDREF((*aResults)[i]);
     plugin = plugin->mNext;
   }
 
+  for (uint32_t i = 0; i < fakeCount; i++) {
+    (*aResults)[i + count] = static_cast<nsIInternalPluginTag*>(mFakePlugins[i]);
+    NS_ADDREF((*aResults)[i + count]);
+  }
+
   return NS_OK;
 }
 
 nsPluginTag*
 nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
 {
   // We prefer the plugin with the highest version number.
   /// XXX(johns): This seems to assume the only time multiple plugins will have
@@ -1220,16 +1256,57 @@ nsPluginHost::FindPreferredPlugin(const 
     if (mozilla::Version(matches[i]->Version().get()) > preferredPlugin->Version().get()) {
       preferredPlugin = matches[i];
     }
   }
 
   return preferredPlugin;
 }
 
+nsFakePluginTag*
+nsPluginHost::FindFakePluginForExtension(const nsACString & aExtension,
+                                         /* out */ nsACString & aMimeType,
+                                         bool aCheckEnabled)
+{
+  if (aExtension.IsEmpty()) {
+    return nullptr;
+  }
+
+  int32_t numFakePlugins = mFakePlugins.Length();
+  for (int32_t i = 0; i < numFakePlugins; i++) {
+    nsFakePluginTag *plugin = mFakePlugins[i];
+    bool active;
+    if ((!aCheckEnabled ||
+         (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
+        plugin->HasExtension(aExtension, aMimeType)) {
+      return plugin;
+    }
+  }
+
+  return nullptr;
+}
+
+nsFakePluginTag*
+nsPluginHost::FindFakePluginForType(const nsACString & aMimeType,
+                                    bool aCheckEnabled)
+{
+  int32_t numFakePlugins = mFakePlugins.Length();
+  for (int32_t i = 0; i < numFakePlugins; i++) {
+    nsFakePluginTag *plugin = mFakePlugins[i];
+    bool active;
+    if ((!aCheckEnabled ||
+         (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
+        plugin->HasMimeType(aMimeType)) {
+      return plugin;
+    }
+  }
+
+  return nullptr;
+}
+
 nsPluginTag*
 nsPluginHost::FindNativePluginForType(const nsACString & aMimeType,
                                       bool aCheckEnabled)
 {
   if (aMimeType.IsEmpty()) {
     return nullptr;
   }
 
@@ -1527,16 +1604,60 @@ nsPluginHost::EnumerateSiteData(const ns
       break;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPluginHost::RegisterFakePlugin(JS::Handle<JS::Value> aInitDictionary,
+                                 JSContext* aCx,
+                                 nsIFakePluginTag **aResult)
+{
+  FakePluginTagInit initDictionary;
+  if (!initDictionary.Init(aCx, aInitDictionary)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<nsFakePluginTag> newTag;
+  nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (auto existingTag : mFakePlugins) {
+    if (newTag->HandlerURIMatches(existingTag->HandlerURI())) {
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+
+  mFakePlugins.AppendElement(newTag);
+  // FIXME-jsplugins do we need to register with the category manager here?  For
+  // shumway, for now, probably not.
+  newTag.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginHost::UnregisterFakePlugin(const nsACString& aHandlerURI)
+{
+  nsCOMPtr<nsIURI> handlerURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(handlerURI), aHandlerURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (uint32_t i = 0; i < mFakePlugins.Length(); ++i) {
+    if (mFakePlugins[i]->HandlerURIMatches(handlerURI)) {
+      mFakePlugins.RemoveElementAt(i);
+      return NS_OK;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType,
                                           bool ignoreCTP,
                                           const nsACString& redirectURL,
                                           const nsACString& whitelist)
 {
   nsAutoCString mt(mimeType);
   nsAutoCString url(redirectURL);
   if (url.Length() == 0) {
@@ -1578,16 +1699,31 @@ nsPluginHost::GetPlayPreviewInfo(const n
       NS_ADDREF(*aResult);
       return NS_OK;
     }
   }
   *aResult = nullptr;
   return NS_ERROR_NOT_AVAILABLE;
 }
 
+// FIXME-jsplugins Is this method actually needed?
+NS_IMETHODIMP
+nsPluginHost::GetFakePlugin(const nsACString & aMimeType,
+                            nsIFakePluginTag** aResult)
+{
+  nsRefPtr<nsFakePluginTag> result = FindFakePluginForType(aMimeType, false);
+  if (result) {
+    result.forget(aResult);
+    return NS_OK;
+  }
+
+  *aResult = nullptr;
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
 #define ClearDataFromSitesClosure_CID {0x9fb21761, 0x2403, 0x41ad, {0x9e, 0xfd, 0x36, 0x7e, 0xc4, 0x4f, 0xa4, 0x5e}}
 
 
 // Class to hold all the data we need need for IterateMatchesAndClear and ClearDataFromSites
 class ClearDataFromSitesClosure : public nsIClearSiteDataCallback, public nsIGetSitesWithDataCallback {
 public:
   ClearDataFromSitesClosure(nsIPluginTag* plugin, const nsACString& domain, uint64_t flags,
                             int64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback,
@@ -1644,16 +1780,17 @@ NS_IMPL_ADDREF(ClearDataFromSitesClosure
 NS_IMPL_RELEASE(ClearDataFromSitesClosure)
 
 NS_INTERFACE_MAP_BEGIN(ClearDataFromSitesClosure)
   NS_INTERFACE_MAP_ENTRY(nsIClearSiteDataCallback)
   NS_INTERFACE_MAP_ENTRY(nsIGetSitesWithDataCallback)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearSiteDataCallback)
 NS_INTERFACE_MAP_END
 
+// FIXME-jsplugins what should this do for fake plugins?
 NS_IMETHODIMP
 nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
                             uint64_t flags, int64_t maxAge, nsIClearSiteDataCallback* callbackFunc)
 {
   nsCOMPtr<nsIClearSiteDataCallback> callback(callbackFunc);
   // maxAge must be either a nonnegative integer or -1.
   NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
 
@@ -1831,36 +1968,47 @@ nsPluginHost::GetSpecialType(const nsACS
 
   return eSpecialType_None;
 }
 
 // Check whether or not a tag is a live, valid tag, and that it's loaded.
 bool
 nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
 {
+  nsCOMPtr<nsIInternalPluginTag> internalTag(do_QueryInterface(aPluginTag));
+  uint32_t fakeCount = mFakePlugins.Length();
+  for (uint32_t i = 0; i < fakeCount; i++) {
+    if (mFakePlugins[i] == internalTag) {
+      return true;
+    }
+  }
+
   nsPluginTag* tag;
   for (tag = mPlugins; tag; tag = tag->mNext) {
-    if (tag == aPluginTag) {
+    if (tag == internalTag) {
       return true;
     }
   }
   return false;
 }
 
+// FIXME-jsplugins what should happen with jsplugins here, if anything?
 nsPluginTag*
 nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
 {
   for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
     if (tag->HasSameNameAndMimes(aPluginTag)) {
         return tag;
     }
   }
   return nullptr;
 }
 
+// Don't have to worry about fake plugins here, since this is only used during
+// the plugin directory scan, which doesn't pick up fake plugins.
 nsPluginTag*
 nsPluginHost::FirstPluginWithPath(const nsCString& path)
 {
   for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
     if (tag->mFullPath.Equals(path)) {
       return tag;
     }
   }
@@ -2591,18 +2739,26 @@ nsPluginHost::FindPluginsForContent(uint
   }
 
   nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
   GetPlugins(plugins, true);
 
   for (size_t i = 0; i < plugins.Length(); i++) {
     nsCOMPtr<nsIInternalPluginTag> basetag = plugins[i];
 
-    /// XXX(johns) Bug FIXME-jsplugins - We need to cleanup the various plugintag classes
-    ///            to be more sane and avoid this dance
+    nsCOMPtr<nsIFakePluginTag> faketag = do_QueryInterface(basetag);
+    if (faketag) {
+      /// FIXME-jsplugins - We need to make content processes properly
+      /// aware of jsplugins (and add a nsIInternalPluginTag->AsNative() to
+      /// avoid this hacky static cast)
+      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());
 
     if (!nsNPAPIPlugin::RunPluginOOP(tag)) {
       // Don't expose non-OOP plugins to content processes since we have no way
       // to bridge them over.
       continue;
     }
 
@@ -2616,16 +2772,17 @@ nsPluginHost::FindPluginsForContent(uint
                                       tag->mIsFlashPlugin,
                                       tag->FileName(),
                                       tag->Version(),
                                       tag->mLastModifiedTime,
                                       tag->IsFromExtension()));
   }
 }
 
+// This function is not relevant for fake plugins.
 void
 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   ReadPluginInfo();
   WritePluginInfo();
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
@@ -2689,16 +2846,25 @@ nsPluginHost::RegisterWithCategoryManage
   if (aType == ePluginRegister) {
     catMan->AddCategoryEntry("Gecko-Content-Viewers",
                              aMimeType.get(),
                              contractId,
                              false, /* persist: broken by bug 193031 */
                              mOverrideInternalTypes,
                              nullptr);
   } else {
+    if (aType == ePluginMaybeUnregister) {
+      // Bail out if this type is still used by an enabled plugin
+      if (HavePluginForType(aMimeType)) {
+        return;
+      }
+    } else {
+      MOZ_ASSERT(aType == ePluginUnregister, "Unknown nsRegisterType");
+    }
+
     // Only delete the entry if a plugin registered for it
     nsXPIDLCString value;
     nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
                                            aMimeType.get(),
                                            getter_Copies(value));
     if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
       catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
                                   aMimeType.get(),
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -7,28 +7,28 @@
 #define nsPluginHost_h_
 
 #include "nsIPluginHost.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "prlink.h"
 #include "prclist.h"
 #include "nsIPluginTag.h"
+#include "nsPluginPlayPreviewInfo.h"
 #include "nsPluginsDir.h"
 #include "nsPluginDirServiceProvider.h"
 #include "nsAutoPtr.h"
 #include "nsWeakPtr.h"
 #include "nsIPrompt.h"
 #include "nsWeakReference.h"
 #include "MainThreadUtils.h"
 #include "nsTArray.h"
 #include "nsTObserverArray.h"
 #include "nsITimer.h"
 #include "nsPluginTags.h"
-#include "nsPluginPlayPreviewInfo.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIIDNService.h"
 #include "nsCRT.h"
 
 #ifdef XP_WIN
 #include <minwindef.h>
 #include "nsIWindowsRegKey.h"
 #endif
@@ -74,16 +74,17 @@ public:
 };
 
 class nsPluginHost final : public nsIPluginHost,
                            public nsIObserver,
                            public nsITimerCallback,
                            public nsSupportsWeakReference
 {
   friend class nsPluginTag;
+  friend class nsFakePluginTag;
   virtual ~nsPluginHost();
 
 public:
   nsPluginHost();
 
   static already_AddRefed<nsPluginHost> GetInst();
 
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
@@ -98,17 +99,18 @@ public:
 
   nsresult SetUpPluginInstance(const nsACString &aMimeType,
                                nsIURI *aURL,
                                nsPluginInstanceOwner *aOwner);
 
   // Acts like a bitfield
   enum PluginFilter {
     eExcludeNone     = nsIPluginHost::EXCLUDE_NONE,
-    eExcludeDisabled = nsIPluginHost::EXCLUDE_DISABLED
+    eExcludeDisabled = nsIPluginHost::EXCLUDE_DISABLED,
+    eExcludeFake     = nsIPluginHost::EXCLUDE_FAKE
   };
   // FIXME-jsplugins comment about fake
   bool HavePluginForType(const nsACString & aMimeType,
                          PluginFilter aFilter = eExcludeDisabled);
 
   // FIXME-jsplugins what if fake has different extensions
   bool HavePluginForExtension(const nsACString & aExtension,
                               /* out */ nsACString & aMimeType,
@@ -261,37 +263,60 @@ private:
 
   nsresult TrySetUpPluginInstance(const nsACString &aMimeType, nsIURI *aURL,
                                   nsPluginInstanceOwner *aOwner);
 
   // FIXME-jsplugins comment here about when things may be fake
   nsPluginTag*
   FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
 
-  // Return an nsPluginTag for this type, if any.  If aCheckEnabled is
-  // true, only enabled plugins will be returned.
+  // Find a plugin for the given type.  If aIncludeFake is true a fake plugin
+  // will be preferred if one exists; otherwise a fake plugin will never be
+  // returned.  If aCheckEnabled is false, disabled plugins can be returned.
+  nsIInternalPluginTag* FindPluginForType(const nsACString& aMimeType,
+                                          bool aIncludeFake, bool aCheckEnabled);
+
+  // Find specifically a fake plugin for the given type.  If aCheckEnabled is
+  // false, disabled plugins can be returned.
+  nsFakePluginTag* FindFakePluginForType(const nsACString & aMimeType,
+                                         bool aCheckEnabled);
+
+  // Find specifically a fake plugin for the given extension.  If aCheckEnabled
+  // is false, disabled plugins can be returned.  aMimeType will be filled in
+  // with the MIME type the plugin is registered for.
+  nsFakePluginTag* FindFakePluginForExtension(const nsACString & aExtension,
+                                              /* out */ nsACString & aMimeType,
+                                              bool aCheckEnabled);
+
+  // Find specifically a native (NPAPI) plugin for the given type.  If
+  // aCheckEnabled is false, disabled plugins can be returned.
   nsPluginTag* FindNativePluginForType(const nsACString & aMimeType,
                                        bool aCheckEnabled);
 
+  // Find specifically a native (NPAPI) plugin for the given extension.  If
+  // aCheckEnabled is false, disabled plugins can be returned.  aMimeType will
+  // be filled in with the MIME type the plugin is registered for.
   nsPluginTag* FindNativePluginForExtension(const nsACString & aExtension,
                                             /* out */ nsACString & aMimeType,
                                             bool aCheckEnabled);
 
   nsresult
   FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
 
   nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
 
   nsresult
   FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
 
   // FIXME revisit, no ns prefix
   // Registers or unregisters the given mime type with the category manager
-  // (performs no checks - see UpdateCategoryManager)
-  enum nsRegisterType { ePluginRegister, ePluginUnregister };
+  enum nsRegisterType { ePluginRegister,
+                        ePluginUnregister,
+                        // Checks if this type should still be registered first
+                        ePluginMaybeUnregister };
   void RegisterWithCategoryManager(const nsCString& aMimeType,
                                    nsRegisterType aType);
 
   void AddPluginTag(nsPluginTag* aPluginTag);
 
   nsresult
   ScanPluginsDirectory(nsIFile *pluginsDir,
                        bool aCreatePluginList,
@@ -344,16 +369,19 @@ private:
   // On certain platforms, we only want to load certain plugins. This function
   // centralizes loading rules.
   bool ShouldAddPlugin(nsPluginTag* aPluginTag);
 
   nsRefPtr<nsPluginTag> mPlugins;
   nsRefPtr<nsPluginTag> mCachedPlugins;
   nsRefPtr<nsInvalidPluginTag> mInvalidPlugins;
   nsTArray< nsRefPtr<nsPluginPlayPreviewInfo> > mPlayPreviewMimeTypes;
+
+  nsTArray< nsRefPtr<nsFakePluginTag> > mFakePlugins;
+
   bool mPluginsLoaded;
 
   // set by pref plugin.override_internal_types
   bool mOverrideInternalTypes;
 
   // set by pref plugin.disable
   bool mPluginsDisabled;
   // set by pref plugins.click_to_play
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -17,18 +17,20 @@
 #include "nsNPAPIPlugin.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "nsNetUtil.h"
 #include <cctype>
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/FakePluginTagInitBinding.h"
 
 using mozilla::dom::EncodingUtils;
+using mozilla::dom::FakePluginTagInit;
 using namespace mozilla;
 
 // These legacy flags are used in the plugin registry. The states are now
 // stored in prefs, but we still need to be able to import them.
 #define NS_PLUGIN_FLAG_ENABLED      0x0001    // is this plugin enabled?
 // no longer used                   0x0002    // reuse only if regenerating pluginreg.dat
 #define NS_PLUGIN_FLAG_FROMCACHE    0x0004    // this plugintag info was loaded from cache
 // no longer used                   0x0008    // reuse only if regenerating pluginreg.dat
@@ -781,16 +783,53 @@ NS_INTERFACE_TABLE_HEAD(nsFakePluginTag)
                                        nsIInternalPluginTag)
     NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIInternalPluginTag)
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsISupports,
                                        nsIInternalPluginTag)
     NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIFakePluginTag)
   NS_INTERFACE_TABLE_END
 NS_INTERFACE_TABLE_TAIL
 
+/* static */
+nsresult
+nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary,
+                        nsFakePluginTag** aPluginTag)
+{
+  NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+  nsRefPtr<nsFakePluginTag> tag = new nsFakePluginTag();
+  nsresult rv = NS_NewURI(getter_AddRefs(tag->mHandlerURI),
+                          aInitDictionary.mHandlerURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  CopyUTF16toUTF8(aInitDictionary.mNiceName, tag->mNiceName);
+  CopyUTF16toUTF8(aInitDictionary.mFullPath, tag->mFullPath);
+  CopyUTF16toUTF8(aInitDictionary.mName, tag->mName);
+  CopyUTF16toUTF8(aInitDictionary.mDescription, tag->mDescription);
+  CopyUTF16toUTF8(aInitDictionary.mFileName, tag->mFileName);
+  CopyUTF16toUTF8(aInitDictionary.mVersion, tag->mVersion);
+
+  for (const FakePluginMimeEntry& mimeEntry : aInitDictionary.mMimeEntries) {
+    CopyUTF16toUTF8(mimeEntry.mType, *tag->mMimeTypes.AppendElement());
+    CopyUTF16toUTF8(mimeEntry.mDescription,
+                    *tag->mMimeDescriptions.AppendElement());
+    CopyUTF16toUTF8(mimeEntry.mExtension, *tag->mExtensions.AppendElement());
+  }
+
+  tag.forget(aPluginTag);
+  return NS_OK;
+}
+
+bool
+nsFakePluginTag::HandlerURIMatches(nsIURI* aURI)
+{
+  bool equals = false;
+  return NS_SUCCEEDED(mHandlerURI->Equals(aURI, &equals)) && equals;
+}
+
 NS_IMETHODIMP
 nsFakePluginTag::GetHandlerURI(nsIURI **aResult)
 {
   NS_IF_ADDREF(*aResult = mHandlerURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -15,16 +15,22 @@
 #include "nsITimer.h"
 #include "nsString.h"
 
 class nsIURI;
 struct PRLibrary;
 struct nsPluginInfo;
 class nsNPAPIPlugin;
 
+namespace mozilla {
+namespace dom {
+struct FakePluginTagInit;
+} // namespace dom
+} // namespace mozilla
+
 // An interface representing plugin tags internally.
 #define NS_IINTERNALPLUGINTAG_IID \
 { 0xe8fdd227, 0x27da, 0x46ee,     \
   { 0xbe, 0xf3, 0x1a, 0xef, 0x5a, 0x8f, 0xc5, 0xb4 } }
 
 class nsIInternalPluginTag : public nsIPluginTag
 {
 public:
@@ -189,22 +195,28 @@ private:
 class nsFakePluginTag : public nsIInternalPluginTag,
                         public nsIFakePluginTag
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGINTAG
   NS_DECL_NSIFAKEPLUGINTAG
 
-  nsFakePluginTag();
+  static nsresult Create(const mozilla::dom::FakePluginTagInit& aInitDictionary,
+                         nsFakePluginTag** aPluginTag);
 
   bool IsEnabled() override;
   const nsCString& GetNiceFileName() override;
 
+  bool HandlerURIMatches(nsIURI* aURI);
+
+  nsIURI* HandlerURI() const { return mHandlerURI; }
+
 private:
+  nsFakePluginTag();
   virtual ~nsFakePluginTag();
 
   // The URI of the handler for our fake plugin.
   // FIXME-jsplugins do we need to sanity check these?
   nsCOMPtr<nsIURI>    mHandlerURI;
 
   nsCString     mFullPath;
   nsCString     mNiceName;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/FakePluginTagInit.webidl
@@ -0,0 +1,39 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+/**
+ * A fake plugin is fundamentally identified by its handlerURI.
+ *
+ * In addition to that, a fake plugin registration needs to provide at least one
+ * FakePluginMimeEntry so we'll know what types(s) the plugin is registered for.
+ * Other information is optional, though having usable niceName is highly
+ * recommended.
+ */
+dictionary FakePluginTagInit {
+  required DOMString handlerURI;
+  required sequence<FakePluginMimeEntry> mimeEntries;
+
+  // The niceName should really be provided, and be unique, if possible; it can
+  // be used as a key to persist state for this plug-in.
+  DOMString niceName = "";
+
+  // Other things can be provided but don't really matter that much.
+  DOMString fullPath = "";
+  DOMString name = "";
+  DOMString description = "";
+  DOMString fileName = "";
+  DOMString version = "";
+};
+
+/**
+ * A single MIME entry for the fake plugin.
+ */
+dictionary FakePluginMimeEntry {
+  required DOMString type;
+  DOMString description = "";
+  DOMString extension = "";
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -141,16 +141,17 @@ WEBIDL_FILES = [
     'Element.webidl',
     'EngineeringMode.webidl',
     'Event.webidl',
     'EventHandler.webidl',
     'EventListener.webidl',
     'EventSource.webidl',
     'EventTarget.webidl',
     'ExtendableEvent.webidl',
+    'FakePluginTagInit.webidl',
     'Fetch.webidl',
     'FetchEvent.webidl',
     'File.webidl',
     'FileList.webidl',
     'FileMode.webidl',
     'FileReader.webidl',
     'FileReaderSync.webidl',
     'FocusEvent.webidl',