Bug 1282484 - Add a mechanism to control plugin fallback content. r=qDot draft
authorFelipe Gomes <felipc@gmail.com>
Wed, 09 Nov 2016 17:24:03 -0200
changeset 436805 cc62186c8ee3b96853e0dbfc8d5b50fb70fd69c7
parent 431796 8c9eed5227f8681cf08d2ee8fb3bfd5d743e4096
child 436806 5d2e1e5b2457bbdf2f9b6c73517120aa849bc93d
push id35191
push userfelipc@gmail.com
push dateWed, 09 Nov 2016 19:25:33 +0000
reviewersqDot
bugs1282484
milestone52.0a1
Bug 1282484 - Add a mechanism to control plugin fallback content. r=qDot MozReview-Commit-ID: Aabu5bvgvhb
browser/app/profile/firefox.js
dom/base/nsObjectLoadingContent.cpp
dom/base/nsObjectLoadingContent.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -635,16 +635,24 @@ pref("findbar.modalHighlight", true);
 #endif
 
 // Tracks when accessibility is loaded into the previous session.
 pref("accessibility.loadedInLastSession", false);
 
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
+// This pref can take 3 possible string values:
+// "always"     - always use favor fallback mode
+// "follow-ctp" - activate if ctp is active for the given
+//                plugin object (could be due to a plugin-wide
+//                setting or a site-specific setting)
+// "never"      - never use favor fallback mode
+pref("plugins.favorfallback.mode", "follow-ctp");
+
 pref("plugin.default.state", 1);
 
 // Plugins bundled in XPIs are enabled by default.
 pref("plugin.defaultXpi.state", 2);
 
 // Flash is enabled by default, and Java is click-to-activate by default on
 // all channels.
 pref("plugin.state.flash", 2);
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -104,16 +104,17 @@
 #include "mozilla/dom/HTMLObjectElement.h"
 #endif
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 static const char *kPrefJavaMIME = "plugin.java.mime";
 static const char *kPrefYoutubeRewrite = "plugins.rewrite_youtube_embeds";
 static const char *kPrefBlockURIs = "browser.safebrowsing.blockedURIs.enabled";
+static const char *kPrefFavorFallbackMode = "plugins.favorfallback.mode";
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::net;
 
 static LogModule*
 GetObjectLog()
 {
@@ -3499,45 +3500,108 @@ nsObjectLoadingContent::ShouldPlay(Fallb
       uint64_t nowms = PR_Now() / 1000;
       permissionManager->UpdateExpireTime(
         topDoc->NodePrincipal(), permissionString.Data(), false,
         nowms + sSessionTimeoutMinutes * 60 * 1000,
         nowms / 1000 + uint64_t(sPersistentTimeoutDays) * 24 * 60 * 60 * 1000);
     }
     switch (permission) {
     case nsIPermissionManager::ALLOW_ACTION:
+      if (PreferFallback(false /* isPluginClickToPlay */)) {
+        aReason = eFallbackAlternate;
+        return false;
+      }
+
       return true;
     case nsIPermissionManager::DENY_ACTION:
       aReason = eFallbackDisabled;
       return false;
     case nsIPermissionManager::PROMPT_ACTION:
+      if (PreferFallback(true /* isPluginClickToPlay */)) {
+        // False is already returned in this case, but
+        // it's important to correctly set aReason too.
+        aReason = eFallbackAlternate;
+      }
+
       return false;
     case nsIPermissionManager::UNKNOWN_ACTION:
       break;
     default:
       MOZ_ASSERT(false);
       return false;
     }
   }
 
   // No site-specific permissions. Vulnerable plugins are automatically CtP
   if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
       blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
     return false;
   }
 
+  if (PreferFallback(enabledState == nsIPluginTag::STATE_CLICKTOPLAY)) {
+    aReason = eFallbackAlternate;
+    return false;
+  }
+
   switch (enabledState) {
   case nsIPluginTag::STATE_ENABLED:
     return true;
   case nsIPluginTag::STATE_CLICKTOPLAY:
     return false;
   }
   MOZ_CRASH("Unexpected enabledState");
 }
 
+bool
+nsObjectLoadingContent::FavorFallbackMode(bool aIsPluginClickToPlay) {
+  if (!IsFlashMIME(mContentType)) {
+    return false;
+  }
+
+  nsCString prefString;
+  if (NS_SUCCEEDED(Preferences::GetCString(kPrefFavorFallbackMode, &prefString))) {
+    if (aIsPluginClickToPlay &&
+        prefString.EqualsLiteral("follow-ctp")) {
+      return true;
+    }
+
+    if (prefString.EqualsLiteral("always")) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool
+nsObjectLoadingContent::HasGoodFallback() {
+  nsCOMPtr<nsIContent> thisContent =
+  do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "must be a content");
+
+  if (!thisContent->IsHTMLElement() || mContentType.IsEmpty()) {
+    // Don't let custom fallback handlers run outside HTML, tags without a
+    // determined type should always just be alternate content
+    return false;
+  }
+
+  if (!thisContent->IsHTMLElement(nsGkAtoms::object)) {
+    return false;
+  }
+
+  // xxx to be filled
+
+  return false;
+}
+
+bool
+nsObjectLoadingContent::PreferFallback(bool aIsPluginClickToPlay) {
+  return FavorFallbackMode(aIsPluginClickToPlay) && HasGoodFallback();
+}
+
 nsIDocument*
 nsObjectLoadingContent::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   if (!thisContent->IsInComposedDoc()) {
     return nullptr;
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -456,16 +456,42 @@ class nsObjectLoadingContent : public ns
     /**
      * If this object is allowed to play plugin content, or if it would display
      * click-to-play instead.
      * NOTE that this does not actually check if the object is a loadable plugin
      * NOTE This ignores the current activated state. The caller should check this if appropriate.
      */
     bool ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentType);
 
+    /**
+     * This method tells if the fallback content should be attempted to be used
+     * over the original object content.
+     * It will look at prefs and this plugin's CTP state to make a decision.
+     *
+     * NOTE that this doesn't say whether the fallback _will_ be used, only whether
+     * we should look into it to possibly use it. The final answer will be
+     * given by the PreferFallback method.
+     *
+     * @param aIsPluginClickToPlay Whether this object instance is CTP.
+     */
+    bool FavorFallbackMode(bool aIsPluginClickToPlay);
+
+    /**
+     * Whether the page has provided good fallback content to this object.
+     */
+    bool HasGoodFallback();
+
+    /**
+     * This method tells the final answer on whether this object's fallback
+     * content should be used instead of the original plugin content.
+     *
+     * @param aIsPluginClickToPlay Whether this object instance is CTP.
+     */
+    bool PreferFallback(bool aIsPluginClickToPlay);
+
     /*
      * Helper to check if mBaseURI can be used by java as a codebase
      */
     bool CheckJavaCodebase();
 
     /**
      * Helper to check if our current URI passes policy
      *