Bug 1178963 part 2. Implement nsFakePluginTag to represent a non-NPAPI "plugin" that is actually implemented in JavaScript. r=peterv
authorJohn Schoenick <jschoenick@mozilla.com>
Wed, 20 May 2015 15:30:05 +0200
changeset 254596 a73ee6e0fc92722fbb1256b3eb43d8dc7a2267a1
parent 254595 ae321edfafccfe0afa4794b9efe0501815c8f669
child 254597 4d96b3d42fd9663eacd36c1ef39c55e6ee7af452
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 2. Implement nsFakePluginTag to represent a non-NPAPI "plugin" that is actually implemented in JavaScript. r=peterv
dom/plugins/base/nsIPluginTag.idl
dom/plugins/base/nsPluginTags.cpp
dom/plugins/base/nsPluginTags.h
--- a/dom/plugins/base/nsIPluginTag.idl
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -1,16 +1,18 @@
 /* -*- 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/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(32563b21-3c5e-4864-baaa-4e49b90b64f2)]
+interface nsIURI;
+
+[scriptable, uuid(5daa99d5-265a-4397-b429-c943803e2619)]
 interface nsIPluginTag : nsISupports
 {
   // enabledState is stored as one of the following as an integer in prefs,
   // so if new states are added, they must not renumber the existing states.
   const unsigned long STATE_DISABLED = 0;
   const unsigned long STATE_CLICKTOPLAY = 1;
   const unsigned long STATE_ENABLED = 2;
 
@@ -21,16 +23,18 @@ interface nsIPluginTag : nsISupports
   readonly attribute AUTF8String name;
 
   // The 'nice' name of this plugin, e.g. 'flash' 'java'
   readonly attribute AUTF8String niceName;
 
   /**
    * true only if this plugin is "hardblocked" and cannot be enabled.
    */
+  // FIXME-jsplugins QI to fakePluginTag possible
+  // FIXME-jsplugins implement missing + tests (whatever that means)
   readonly attribute boolean blocklisted;
 
   /**
    * true if the state is non-default and locked, false otherwise.
    */
   readonly attribute boolean isEnabledStateLocked;
 
   // If this plugin is capable of being used (not disabled, blocklisted, etc)
@@ -50,8 +54,22 @@ interface nsIPluginTag : nsISupports
                     [retval, array, size_is(aCount)] out wstring aResults);
   void getMimeDescriptions([optional] out unsigned long aCount,
                            [retval, array, size_is(aCount)]
                            out wstring aResults);
   void getExtensions([optional] out unsigned long aCount,
                      [retval, array, size_is(aCount)]
                      out wstring aResults);
 };
+
+/**
+ * An interface representing a "fake" plugin: one implemented in JavaScript, not
+ * as a NPAPI plug-in.  See nsIPluginHost.registerFakePlugin and the
+ * documentation for the FakePluginTagInit dictionary.
+ */
+[scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
+interface nsIFakePluginTag : nsIPluginTag
+{
+  // The URI that should be loaded into the tag (as a frame) to handle the
+  // plugin. Note that the original data/src value for the plugin is not loaded
+  // and will need to be requested by the handler via XHR or similar if desired.
+  readonly attribute nsIURI handlerURI;
+};
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -13,16 +13,17 @@
 #include "nsIBlocklistService.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIPlatformCharset.h"
 #include "nsPluginLogging.h"
 #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"
 
 using mozilla::dom::EncodingUtils;
 using namespace mozilla;
 
 // These legacy flags are used in the plugin registry. The states are now
@@ -93,24 +94,34 @@ MakeNiceFileName(const nsCString & aFile
     niceName.Truncate(niceNameLength);
   }
 
   ToLowerCase(niceName);
   return niceName;
 }
 
 static nsCString
-MakePrefNameForPlugin(const char* const subname, nsPluginTag* aTag)
+MakePrefNameForPlugin(const char* const subname, nsIInternalPluginTag* aTag)
 {
   nsCString pref;
+  nsAutoCString pluginName(aTag->GetNiceFileName());
+
+  if (pluginName.IsEmpty()) {
+    // Use filename if nice name fails
+    pluginName = aTag->FileName();
+    if (pluginName.IsEmpty()) {
+      MOZ_ASSERT_UNREACHABLE("Plugin with no filename or nice name in list");
+      pluginName.AssignLiteral("unknown-plugin-name");
+    }
+  }
 
   pref.AssignLiteral("plugin.");
   pref.Append(subname);
   pref.Append('.');
-  pref.Append(aTag->GetNiceFileName());
+  pref.Append(pluginName);
 
   return pref;
 }
 
 static nsresult
 CStringArrayToXPCArray(nsTArray<nsCString> & aArray,
                        uint32_t* aCount,
                        char16_t*** aResults)
@@ -129,22 +140,43 @@ CStringArrayToXPCArray(nsTArray<nsCStrin
   for (uint32_t i = 0; i < count; i++) {
     (*aResults)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(aArray[i]));
   }
 
   return NS_OK;
 }
 
 static nsCString
-GetStatePrefNameForPlugin(nsPluginTag* aTag)
+GetStatePrefNameForPlugin(nsIInternalPluginTag* aTag)
 {
   return MakePrefNameForPlugin("state", aTag);
 }
 
+static nsresult
+IsEnabledStateLockedForPlugin(nsIInternalPluginTag* aTag,
+                              bool* aIsEnabledStateLocked)
+{
+  *aIsEnabledStateLocked = false;
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+  if (NS_WARN_IF(!prefs)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  unused << prefs->PrefIsLocked(GetStatePrefNameForPlugin(aTag).get(),
+                                aIsEnabledStateLocked);
+
+  return NS_OK;
+}
+
 /* nsIInternalPluginTag */
+nsIInternalPluginTag::nsIInternalPluginTag()
+{
+}
+
 nsIInternalPluginTag::nsIInternalPluginTag(const char* aName,
                                            const char* aDescription,
                                            const char* aFileName,
                                            const char* aVersion)
   : mName(aName)
   , mDescription(aDescription)
   , mFileName(aFileName)
   , mVersion(aVersion)
@@ -167,16 +199,30 @@ nsIInternalPluginTag::nsIInternalPluginT
   , mExtensions(aExtensions)
 {
 }
 
 nsIInternalPluginTag::~nsIInternalPluginTag()
 {
 }
 
+bool
+nsIInternalPluginTag::HasExtension(const nsACString& aExtension,
+                                   nsACString& aMatchingType) const
+{
+  return SearchExtensions(mExtensions, mMimeTypes, aExtension, aMatchingType);
+}
+
+bool
+nsIInternalPluginTag::HasMimeType(const nsACString& aMimeType) const
+{
+  return mMimeTypes.Contains(aMimeType,
+                             nsCaseInsensitiveCStringArrayComparator());
+}
+
 /* nsPluginTag */
 
 uint32_t nsPluginTag::sNextId;
 
 nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
                          int64_t aLastModifiedTime,
                          bool fromExtension)
   : nsIInternalPluginTag(aPluginInfo->fName, aPluginInfo->fDescription,
@@ -490,27 +536,17 @@ nsPluginTag::GetBlocklisted(bool* aBlock
 {
   *aBlocklisted = IsBlocklisted();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked)
 {
-  *aIsEnabledStateLocked = false;
-  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
-
-  if (NS_WARN_IF(!prefs)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  unused << prefs->PrefIsLocked(GetStatePrefNameForPlugin(this).get(),
-                                aIsEnabledStateLocked);
-
-  return NS_OK;
+  return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked);
 }
 
 bool
 nsPluginTag::IsClicktoplay()
 {
   const PluginState state = GetPluginState();
   return (state == ePluginState_Clicktoplay);
 }
@@ -624,17 +660,19 @@ void nsPluginTag::TryUnloadPlugin(bool i
     return;
   }
   if (inShutdown || mPlugin->GetLibrary()->IsOOP()) {
     mPlugin->Shutdown();
     mPlugin = nullptr;
   }
 }
 
-nsCString nsPluginTag::GetNiceFileName() {
+const nsCString&
+nsPluginTag::GetNiceFileName()
+{
   if (!mNiceFileName.IsEmpty()) {
     return mNiceFileName;
   }
 
   if (mIsFlashPlugin) {
     mNiceFileName.AssignLiteral("flash");
     return mNiceFileName;
   }
@@ -700,30 +738,16 @@ nsPluginTag::GetBlocklistState(uint32_t 
 
   MOZ_ASSERT(*aResult <= UINT16_MAX);
   mCachedBlocklistState = (uint16_t) *aResult;
   mCachedBlocklistStateValid = true;
   return NS_OK;
 #endif // defined(MOZ_WIDGET_ANDROID)
 }
 
-bool
-nsPluginTag::HasMimeType(const nsACString & aMimeType) const
-{
-  return mMimeTypes.Contains(aMimeType,
-                             nsCaseInsensitiveCStringArrayComparator());
-}
-
-bool
-nsPluginTag::HasExtension(const nsACString & aExtension,
-                          nsACString & aMatchingType) const
-{
-  return SearchExtensions(mExtensions, mMimeTypes, aExtension, aMatchingType);
-}
-
 void
 nsPluginTag::InvalidateBlocklistState()
 {
   mCachedBlocklistStateValid = false;
 }
 
 NS_IMETHODIMP
 nsPluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
@@ -732,8 +756,188 @@ nsPluginTag::GetLastModifiedTime(PRTime*
   *aLastModifiedTime = mLastModifiedTime;
   return NS_OK;
 }
 
 bool nsPluginTag::IsFromExtension() const
 {
   return mIsFromExtension;
 }
+
+/* nsFakePluginTag */
+
+nsFakePluginTag::nsFakePluginTag()
+  : mState(nsPluginTag::ePluginState_Disabled)
+{
+}
+
+nsFakePluginTag::~nsFakePluginTag()
+{
+}
+
+NS_IMPL_ADDREF(nsFakePluginTag)
+NS_IMPL_RELEASE(nsFakePluginTag)
+NS_INTERFACE_TABLE_HEAD(nsFakePluginTag)
+  NS_INTERFACE_TABLE_BEGIN
+    NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsIPluginTag,
+                                       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
+
+NS_IMETHODIMP
+nsFakePluginTag::GetHandlerURI(nsIURI **aResult)
+{
+  NS_IF_ADDREF(*aResult = mHandlerURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetDescription(/* utf-8 */ nsACString& aResult)
+{
+  aResult = mDescription;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetFilename(/* utf-8 */ nsACString& aResult)
+{
+  aResult = mFileName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetFullpath(/* utf-8 */ nsACString& aResult)
+{
+  aResult = mFullPath;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetVersion(/* utf-8 */ nsACString& aResult)
+{
+  aResult = mVersion;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetName(/* utf-8 */ nsACString& aResult)
+{
+  aResult = mName;
+  return NS_OK;
+}
+
+const nsCString&
+nsFakePluginTag::GetNiceFileName()
+{
+  // We don't try to mimic the special-cased flash/java names if the fake plugin
+  // claims one of their MIME types, but do allow directly setting niceName if
+  // emulating those is desired.
+  if (mNiceName.IsEmpty() && !mFileName.IsEmpty()) {
+    mNiceName = MakeNiceFileName(mFileName);
+  }
+
+  return mNiceName;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetNiceName(/* utf-8 */ nsACString& aResult)
+{
+  aResult = GetNiceFileName();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetBlocklistState(uint32_t* aResult)
+{
+  // Fake tags don't currently support blocklisting
+  *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetBlocklisted(bool* aBlocklisted)
+{
+  // Fake tags can't be blocklisted
+  *aBlocklisted = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked)
+{
+  return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked);
+}
+
+bool
+nsFakePluginTag::IsEnabled()
+{
+  return mState == nsPluginTag::ePluginState_Enabled ||
+         mState == nsPluginTag::ePluginState_Clicktoplay;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetDisabled(bool* aDisabled)
+{
+  *aDisabled = !IsEnabled();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetClicktoplay(bool* aClicktoplay)
+{
+  *aClicktoplay = (mState == nsPluginTag::ePluginState_Clicktoplay);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetEnabledState(uint32_t* aEnabledState)
+{
+  *aEnabledState = (uint32_t)mState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::SetEnabledState(uint32_t aEnabledState)
+{
+  // There are static asserts above enforcing that this enum matches
+  mState = (nsPluginTag::PluginState)aEnabledState;
+  // FIXME-jsplugins update
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetMimeTypes(uint32_t* aCount, char16_t*** aResults)
+{
+  return CStringArrayToXPCArray(mMimeTypes, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetMimeDescriptions(uint32_t* aCount, char16_t*** aResults)
+{
+  return CStringArrayToXPCArray(mMimeDescriptions, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetExtensions(uint32_t* aCount, char16_t*** aResults)
+{
+  return CStringArrayToXPCArray(mExtensions, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetActive(bool *aResult)
+{
+  // Fake plugins can't be blocklisted, so this is just !Disabled
+  *aResult = IsEnabled();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
+{
+  // FIXME-jsplugins What should this return, if anything?
+  MOZ_ASSERT(aLastModifiedTime);
+  *aLastModifiedTime = 0;
+  return NS_OK;
+}
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -10,55 +10,68 @@
 #include "nscore.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIPluginTag.h"
 #include "nsITimer.h"
 #include "nsString.h"
 
+class nsIURI;
 struct PRLibrary;
 struct nsPluginInfo;
 class nsNPAPIPlugin;
 
 // 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:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IINTERNALPLUGINTAG_IID)
 
+  nsIInternalPluginTag();
   nsIInternalPluginTag(const char* aName, const char* aDescription,
                        const char* aFileName, const char* aVersion);
   nsIInternalPluginTag(const char* aName, const char* aDescription,
                        const char* aFileName, const char* aVersion,
                        const nsTArray<nsCString>& aMimeTypes,
                        const nsTArray<nsCString>& aMimeDescriptions,
                        const nsTArray<nsCString>& aExtensions);
 
   virtual bool IsEnabled() = 0;
+  virtual const nsCString& GetNiceFileName() = 0;
 
   const nsCString& Name() const { return mName; }
   const nsCString& Description() const { return mDescription; }
 
   const nsTArray<nsCString>& MimeTypes() const { return mMimeTypes; }
 
   const nsTArray<nsCString>& MimeDescriptions() const {
     return mMimeDescriptions;
   }
 
   const nsTArray<nsCString>& Extensions() const { return mExtensions; }
 
   const nsCString& FileName() const { return mFileName; }
 
   const nsCString& Version() const { return mVersion; }
 
+  // Returns true if this plugin claims it supports this MIME type.  The
+  // comparison is done ASCII-case-insensitively.
+  bool HasMimeType(const nsACString & aMimeType) const;
+
+  // Returns true if this plugin claims it supports the given extension.  In
+  // that case, aMatchingType is set to the MIME type the plugin claims
+  // corresponds to this extension.  The match on aExtension is done
+  // ASCII-case-insensitively.
+  bool HasExtension(const nsACString & aExtension,
+                    /* out */ nsACString & aMatchingType) const;
 protected:
   ~nsIInternalPluginTag();
 
   nsCString     mName; // UTF-8
   nsCString     mDescription; // UTF-8
   nsCString     mFileName; // UTF-8
   nsCString     mVersion;  // UTF-8
   nsTArray<nsCString> mMimeTypes; // UTF-8
@@ -124,17 +137,17 @@ public:
 
   PluginState GetPluginState();
   void SetPluginState(PluginState state);
 
   // import legacy flags from plugin registry into the preferences
   void ImportFlagsToPrefs(uint32_t flag);
 
   bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
-  nsCString GetNiceFileName();
+  const nsCString& GetNiceFileName() override;
 
   bool IsFromExtension() const;
 
   nsRefPtr<nsPluginTag> mNext;
   uint32_t      mId;
 
   // Number of PluginModuleParents living in all content processes.
   size_t        mContentProcessRunningCount;
@@ -147,25 +160,16 @@ public:
   bool          mIsJavaPlugin;
   bool          mIsFlashPlugin;
   nsCString     mFullPath; // UTF-8
   int64_t       mLastModifiedTime;
   nsCOMPtr<nsITimer> mUnloadTimer;
 
   void          InvalidateBlocklistState();
 
-  // Returns true if this plugin claims it supports this MIME type.  The
-  // comparison is done ASCII-case-insensitively.
-  bool          HasMimeType(const nsACString & aMimeType) const;
-  // Returns true if this plugin claims it supports the given extension.  In hat
-  // case, aMatchingType is set to the MIME type the plugin claims corresponds
-  // to this extension.  Again, the extension is done ASCII-case-insensitively.
-  bool          HasExtension(const nsACString & aExtension,
-                             /* out */ nsACString & aMatchingType) const;
-
 private:
   virtual ~nsPluginTag();
 
   nsCString     mNiceFileName; // UTF-8
   uint16_t      mCachedBlocklistState;
   bool          mCachedBlocklistStateValid;
   bool          mIsFromExtension;
 
@@ -174,9 +178,38 @@ private:
                 const char* const* aExtensions,
                 uint32_t aVariantCount);
   nsresult EnsureMembersAreUTF8();
   void FixupVersion();
 
   static uint32_t sNextId;
 };
 
+// A class representing "fake" plugin tags; that is plugin tags not
+// corresponding to actual NPAPI plugins.  In practice these are all
+// JS-implemented plugins; maybe we want a better name for this class?
+class nsFakePluginTag : public nsIInternalPluginTag,
+                        public nsIFakePluginTag
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPLUGINTAG
+  NS_DECL_NSIFAKEPLUGINTAG
+
+  nsFakePluginTag();
+
+  bool IsEnabled() override;
+  const nsCString& GetNiceFileName() override;
+
+private:
+  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;
+
+  nsPluginTag::PluginState mState;
+};
+
 #endif // nsPluginTags_h_