Bug 1338287 - Make nsIDocument::DocumentFlashClassification accessible from privileged JS draft
authorKirk Steuber <ksteuber@mozilla.com>
Mon, 13 Feb 2017 13:39:40 -0800
changeset 483154 4bf5da6054d181d5e449838363b5622e7e873ae5
parent 481376 eea9995ed14c07675c972400e8ce36b3608c01b1
child 545573 210461824d9c1aa73e7331f9d681e1e95ffafd6d
push id45237
push userksteuber@mozilla.com
push dateMon, 13 Feb 2017 21:41:05 +0000
bugs1338287
milestone54.0a1
Bug 1338287 - Make nsIDocument::DocumentFlashClassification accessible from privileged JS MozReview-Commit-ID: 1II2puZMub5
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/base/nsObjectLoadingContent.cpp
dom/webidl/Document.webidl
toolkit/components/url-classifier/tests/browser/browser_flash_block_lists.js
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13038,17 +13038,17 @@ ArrayContainsTable(const nsTArray<nsCStr
 
 /**
  * Retrieves the classification of the Flash plugins in the document based on
  * the classification lists.
  *
  * For more information, see
  * toolkit/components/url-classifier/flash-block-lists.rst
  */
-nsIDocument::FlashClassification
+FlashClassification
 nsDocument::PrincipalFlashClassification(bool aIsTopLevel)
 {
   nsresult rv;
 
   // If flash blocking is disabled, it is equivalent to all sites being
   // whitelisted.
   if (!Preferences::GetBool("plugins.flashBlock.enabled")) {
     return FlashClassification::Allowed;
@@ -13127,17 +13127,17 @@ nsDocument::PrincipalFlashClassification
   if (!aIsTopLevel && ArrayContainsTable(results, subDocDenyTables) &&
       !ArrayContainsTable(results, subDocDenyExceptionsTables)) {
     return FlashClassification::Denied;
   }
 
   return FlashClassification::Unknown;
 }
 
-nsIDocument::FlashClassification
+FlashClassification
 nsDocument::ComputeFlashClassification()
 {
   nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
   if (!current) {
     return FlashClassification::Denied;
   }
   nsCOMPtr<nsIDocShellTreeItem> parent;
   DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
@@ -13176,17 +13176,17 @@ nsDocument::ComputeFlashClassification()
 /**
  * Retrieves the classification of plugins in this document. This is dependent
  * on the classification of this document and all parent documents.
  * This function is infallible - It must return some classification that
  * callers can act on.
  *
  * This function will NOT return FlashClassification::Unclassified
  */
-nsIDocument::FlashClassification
+FlashClassification
 nsDocument::DocumentFlashClassification()
 {
   if (mFlashClassification == FlashClassification::Unclassified) {
     FlashClassification result = ComputeFlashClassification();
     mFlashClassification = result;
     MOZ_ASSERT(result != FlashClassification::Unclassified,
       "nsDocument::GetPluginClassification should never return Unclassified");
   }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1300,17 +1300,17 @@ protected:
                               bool aPersisted);
 
   virtual nsPIDOMWindowOuter* GetWindowInternal() const override;
   virtual nsIScriptGlobalObject* GetScriptHandlingObjectInternal() const override;
   virtual bool InternalAllowXULXBL() override;
 
   void UpdateScreenOrientation();
 
-  virtual FlashClassification DocumentFlashClassification() override;
+  virtual mozilla::dom::FlashClassification DocumentFlashClassification() override;
 
 #define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_)                        \
   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, nsIDocumentObserver, \
                                            func_, params_);
 
 #ifdef DEBUG
   void VerifyRootContentState();
 #endif
@@ -1324,21 +1324,21 @@ protected:
 
   // Apply the fullscreen state to the document, and trigger related
   // events. It returns false if the fullscreen element ready check
   // fails and nothing gets changed.
   bool ApplyFullscreen(const FullscreenRequest& aRequest);
 
   // Retrieves the classification of the Flash plugins in the document based on
   // the classification lists.
-  FlashClassification PrincipalFlashClassification(bool aIsTopLevel);
+  mozilla::dom::FlashClassification PrincipalFlashClassification(bool aIsTopLevel);
 
   // Attempts to determine the Flash classification of this page based on the
   // the classification lists and the classification of parent documents.
-  FlashClassification ComputeFlashClassification();
+  mozilla::dom::FlashClassification ComputeFlashClassification();
 
   nsTArray<nsIObserver*> mCharSetObservers;
 
   PLDHashTable *mSubDocuments;
 
   // Array of owning references to all children
   nsAttrAndChildArray mChildren;
 
@@ -1374,17 +1374,17 @@ protected:
   // full-screen element onto this stack, and when we cancel full-screen we
   // pop one off this stack, restoring the previous full-screen state
   nsTArray<nsWeakPtr> mFullScreenStack;
 
   // The root of the doc tree in which this document is in. This is only
   // non-null when this document is in fullscreen mode.
   nsWeakPtr mFullscreenRoot;
 
-  FlashClassification mFlashClassification;
+  mozilla::dom::FlashClassification mFlashClassification;
 private:
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
   /**
    * Check if the passed custom element name, aOptions.mIs, is a registered
    * custom element type or not, then return the custom element name for future
    * usage.
    *
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2890,24 +2890,17 @@ public:
   // determine whether some code is being called from a tracking script.
   void NoteScriptTrackingStatus(const nsACString& aURL, bool isTracking);
   bool IsScriptTracking(const nsACString& aURL) const;
 
   bool PrerenderHref(nsIURI* aHref);
 
   // For more information on Flash classification, see
   // toolkit/components/url-classifier/flash-block-lists.rst
-  enum class FlashClassification {
-    Unclassified,   // Denotes a classification that has not yet been computed.
-                    // Allows for lazy classification.
-    Unknown,        // Site is not on the whitelist or blacklist
-    Allowed,        // Site is on the Flash whitelist
-    Denied          // Site is on the Flash blacklist
-  };
-  virtual FlashClassification DocumentFlashClassification() = 0;
+  virtual mozilla::dom::FlashClassification DocumentFlashClassification() = 0;
 
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -3334,21 +3334,21 @@ nsObjectLoadingContent::ShouldPlay(Fallb
     return false;
   }
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetTop();
   NS_ENSURE_TRUE(topWindow, false);
   nsCOMPtr<nsIDocument> topDoc = topWindow->GetDoc();
   NS_ENSURE_TRUE(topDoc, false);
 
   // Check the flash blocking status for this page (this applies to Flash only)
-  nsIDocument::FlashClassification documentClassification = nsIDocument::FlashClassification::Allowed;
+  FlashClassification documentClassification = FlashClassification::Allowed;
   if (IsFlashMIME(mContentType)) {
     documentClassification = ownerDoc->DocumentFlashClassification();
   }
-  if (documentClassification == nsIDocument::FlashClassification::Denied) {
+  if (documentClassification == FlashClassification::Denied) {
     aReason = eFallbackSuppressed;
     return false;
   }
 
   // Check the permission manager for permission based on the principal of
   // the toplevel content.
   nsCOMPtr<nsIPermissionManager> permissionManager = services::GetPermissionManager();
   NS_ENSURE_TRUE(permissionManager, false);
@@ -3411,17 +3411,17 @@ nsObjectLoadingContent::ShouldPlay(Fallb
 
   if (PreferFallback(enabledState == nsIPluginTag::STATE_CLICKTOPLAY)) {
     aReason = eFallbackAlternate;
     return false;
   }
 
   switch (enabledState) {
   case nsIPluginTag::STATE_ENABLED:
-    return documentClassification == nsIDocument::FlashClassification::Allowed;
+    return documentClassification == FlashClassification::Allowed;
   case nsIPluginTag::STATE_CLICKTOPLAY:
     return false;
   }
   MOZ_CRASH("Unexpected enabledState");
 }
 
 bool
 nsObjectLoadingContent::FavorFallbackMode(bool aIsPluginClickToPlay) {
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -431,16 +431,30 @@ partial interface Document {
 // Extension to give chrome and XBL JS the ability to determine whether
 // the document is sandboxed without permission to run scripts
 // and whether inline scripts are blocked by the document's CSP.
 partial interface Document {
   [Func="IsChromeOrXBL"] readonly attribute boolean hasScriptsBlockedBySandbox;
   [Func="IsChromeOrXBL"] readonly attribute boolean inlineScriptAllowedByCSP;
 };
 
+// For more information on Flash classification, see
+// toolkit/components/url-classifier/flash-block-lists.rst
+enum FlashClassification {
+  "unclassified",   // Denotes a classification that has not yet been computed.
+                    // Allows for lazy classification.
+  "unknown",        // Site is not on the whitelist or blacklist
+  "allowed",        // Site is on the Flash whitelist
+  "denied"          // Site is on the Flash blacklist
+};
+partial interface Document {
+  [ChromeOnly]
+  readonly attribute FlashClassification documentFlashClassification;
+};
+
 Document implements XPathEvaluator;
 Document implements GlobalEventHandlers;
 Document implements DocumentAndElementEventHandlers;
 Document implements TouchEventHandlers;
 Document implements ParentNode;
 Document implements OnErrorEventHandlerForNodes;
 Document implements GeometryUtils;
 Document implements FontFaceSource;
--- a/toolkit/components/url-classifier/tests/browser/browser_flash_block_lists.js
+++ b/toolkit/components/url-classifier/tests/browser/browser_flash_block_lists.js
@@ -71,142 +71,159 @@ registerCleanupFunction(unsetDBPrefs);
 // Only the plugin in the most deeply nested document will be checked.
 var testCases = [
   {
     name: "Unknown domain",
     domains: ["http://example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   },
   {
     name: "Nested unknown domains",
     domains: ["http://example.com", "http://example.org"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   },
   {
     name: "Allowed domain",
     domains: ["http://flashallow.example.com"],
     expectedActivated: true,
     expectedHasRunningPlugin: true,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "allowed"
   },
   {
     name: "Allowed nested domain",
     domains: ["http://example.com", "http://flashallow.example.com"],
     expectedActivated: true,
     expectedHasRunningPlugin: true,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "allowed"
   },
   {
     name: "Subdocument of allowed domain",
     domains: ["http://flashallow.example.com", "http://example.com"],
     expectedActivated: true,
     expectedHasRunningPlugin: true,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "allowed"
   },
   {
     name: "Exception to allowed domain",
     domains: ["http://exception.flashallow.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   },
   {
     name: "Blocked domain",
     domains: ["http://flashblock.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Nested blocked domain",
     domains: ["http://example.com", "http://flashblock.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Subdocument of blocked subdocument",
     domains: ["http://example.com", "http://flashblock.example.com", "http://example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Blocked subdocument nested among in allowed documents",
     domains: ["http://flashallow.example.com", "http://flashblock.example.com", "http://flashallow.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Exception to blocked domain",
     domains: ["http://exception.flashblock.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   },
   {
     name: "Sub-document blocked domain in top-level context",
     domains: ["http://subdocument.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   },
   {
     name: "Sub-document blocked domain",
     domains: ["http://example.com", "http://subdocument.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Sub-document blocked subdocument of an allowed domain",
     domains: ["http://flashallow.example.com", "http://subdocument.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Subdocument of Sub-document blocked domain",
     domains: ["http://example.com", "http://subdocument.example.com", "http://example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: false
+    pluginListed: false,
+    expectedFlashClassification: "denied"
   },
   {
     name: "Sub-document exception in top-level context",
     domains: ["http://exception.subdocument.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   },
   {
     name: "Sub-document blocked domain exception",
     domains: ["http://example.com", "http://exception.subdocument.example.com"],
     expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
     expectedActivated: false,
     expectedHasRunningPlugin: false,
-    pluginListed: true
+    pluginListed: true,
+    expectedFlashClassification: "unknown"
   }
 ];
 
 function buildDocumentStructure(browser, iframeDomains) {
   return Task.spawn(function* () {
     let depth = 0;
     for (let domain of iframeDomains) {
       // Firefox does not like to load the same page in its own iframe. Put some
@@ -244,17 +261,18 @@ function getPluginInfo(browser, depth) {
     let pluginObj = doc.getElementById("testObject");
     if (!(pluginObj instanceof Ci.nsIObjectLoadingContent)) {
       throw new Error("Unable to find plugin!");
     }
     return {
       pluginFallbackType: pluginObj.pluginFallbackType,
       activated: pluginObj.activated,
       hasRunningPlugin: pluginObj.hasRunningPlugin,
-      listed: ("Shockwave Flash" in win.navigator.plugins)
+      listed: ("Shockwave Flash" in win.navigator.plugins),
+      flashClassification: doc.documentFlashClassification
     };
   });
 }
 
 add_task(function* checkFlashBlockLists() {
   setDBPrefs();
 
   yield classifierHelper.waitForInit();
@@ -283,16 +301,20 @@ add_task(function* checkFlashBlockLists(
     if ("expectedHasRunningPlugin" in testCase) {
       is(pluginInfo.hasRunningPlugin, testCase.expectedHasRunningPlugin,
         "Plugin should have the correct 'plugin running' state");
     }
     if ("pluginListed" in testCase) {
       is(pluginInfo.listed, testCase.pluginListed,
         "Plugin's existance in navigator.plugins should match expected")
     }
+    if ("expectedFlashClassification" in testCase) {
+      is(pluginInfo.flashClassification, testCase.expectedFlashClassification,
+        "Page's classification should match expected");
+    }
 
     yield BrowserTestUtils.removeTab(tab);
   }
 });
 
 add_task(function* checkFlashBlockDisabled() {
   setDBPrefs();
   Services.prefs.setBoolPref(FLASHBLOCK_ENABLE_PREF, false);
@@ -311,12 +333,14 @@ add_task(function* checkFlashBlockDisabl
     yield buildDocumentStructure(tab.linkedBrowser, iframeDomains);
 
     let pluginInfo = yield getPluginInfo(tab.linkedBrowser, iframeDomains.length);
 
     // With flashblock disabled, all plugins should be activated.
     ok(pluginInfo.activated, "Plugin should be activated");
     ok(pluginInfo.hasRunningPlugin, "Plugin should be running");
     ok(pluginInfo.listed, "Flash should be listed in navigator.plugins");
+    is(pluginInfo.flashClassification, "allowed",
+       "Page's classification should be 'allowed'");
 
     yield BrowserTestUtils.removeTab(tab);
   }
 });