Bug 1403077 - Part 1: Implement the stylo blocklist mechanism. r=heycam, a=ritu
authorJeremy Chen <jeremychen@mozilla.com>
Wed, 11 Oct 2017 20:57:00 -0400
changeset 432431 c2c10508ec8d
parent 432430 10279b6ebdf8
child 432432 1059f19c8136
push id7953
push userryanvm@gmail.com
push dateThu, 12 Oct 2017 14:05:58 +0000
treeherdermozilla-beta@f4ba99b41183 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam, ritu
bugs1403077
milestone57.0
Bug 1403077 - Part 1: Implement the stylo blocklist mechanism. r=heycam, a=ritu In this patch, we read the stylo blocklist into nsLayoutUtils's global static variable during nsLayoutUtils::Initialize(). So, we can decide if we should fallback to use Gecko backend while updating style backend for a document. We add "layout.css.stylo-blocklist.blocked_domains" and "layout.css.stylo-blocklist.enabled" to ContentPrefs.cpp because they are read very early (during nsLayoutUtils::Initialize). MozReview-Commit-ID: IiTVf6qD9uO
dom/base/nsDocument.cpp
dom/ipc/ContentPrefs.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
modules/libpref/init/all.js
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13611,17 +13611,18 @@ nsIDocument::UpdateStyleBackendType()
   if (nsLayoutUtils::StyloEnabled()) {
     // Disable stylo only for system principal. Other principals aren't
     // able to use XUL by default, and the back door to enable XUL is
     // mostly just for testing, which means they don't matter, and we
     // shouldn't respect them at the same time.
     // Note that, since tests can have XUL support, we still need to
     // explicitly exclude XUL documents here.
     if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()) &&
-        !IsXULDocument() && !ShouldUseGeckoBackend(mDocumentURI)) {
+        !IsXULDocument() && !ShouldUseGeckoBackend(mDocumentURI) &&
+        !nsLayoutUtils::IsInStyloBlocklist(NodePrincipal())) {
       mStyleBackendType = StyleBackendType::Servo;
     }
   }
 #endif
 }
 
 /**
  * Retrieves the classification of the Flash plugins in the document based on
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -116,16 +116,20 @@ const char* mozilla::dom::ContentPrefs::
   "javascript.options.throw_on_asmjs_validation_failure",
   "javascript.options.throw_on_debuggee_would_run",
   "javascript.options.wasm",
   "javascript.options.wasm_baselinejit",
   "javascript.options.wasm_ionjit",
   "javascript.options.werror",
   "javascript.use_us_english_locale",
   "jsloader.shareGlobal",
+#ifdef MOZ_STYLO
+  "layout.css.stylo-blocklist.blocked_domains",
+  "layout.css.stylo-blocklist.enabled",
+#endif
   "layout.idle_period.required_quiescent_frames",
   "layout.idle_period.time_limit",
   "layout.interruptible-reflow.enabled",
   "mathml.disabled",
   "media.apple.forcevda",
   "media.clearkey.persistent-license.enabled",
   "media.cubeb.backend",
   "media.cubeb.sandbox",
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -119,16 +119,17 @@
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "RegionBuilder.h"
 #include "SVGSVGElement.h"
 #include "DisplayItemClip.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "prenv.h"
+#include "nsIEffectiveTLDService.h" // for IsInStyloBlocklist
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 #include "GeckoProfiler.h"
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
@@ -184,16 +185,18 @@ typedef nsStyleTransformMatrix::Transfor
 /* static */ uint32_t nsLayoutUtils::sZoomMaxPercent;
 /* static */ uint32_t nsLayoutUtils::sZoomMinPercent;
 /* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled;
 /* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled;
 /* static */ bool nsLayoutUtils::sSVGTransformBoxEnabled;
 /* static */ bool nsLayoutUtils::sTextCombineUprightDigitsEnabled;
 #ifdef MOZ_STYLO
 /* static */ bool nsLayoutUtils::sStyloEnabled;
+/* static */ bool nsLayoutUtils::sStyloBlocklistEnabled;
+/* static */ nsTArray<nsCString>* nsLayoutUtils::sStyloBlocklist = nullptr;
 #endif
 /* static */ bool nsLayoutUtils::sStyleAttrWithXMLBaseDisabled;
 /* static */ uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
 /* static */ uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
 
 static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
 
 typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
@@ -7837,16 +7840,34 @@ nsLayoutUtils::Initialize()
   if (PR_GetEnv("STYLO_FORCE_ENABLED")) {
     sStyloEnabled = true;
   } else if (PR_GetEnv("STYLO_FORCE_DISABLED")) {
     sStyloEnabled = false;
   } else {
     Preferences::AddBoolVarCache(&sStyloEnabled,
                                  "layout.css.servo.enabled");
   }
+  // We should only create the blocklist ONCE, and ignore any blocklist
+  // reloads happen. Because otherwise we could have a top level page that
+  // uses Stylo (if its load happens before the blocklist reload) and a
+  // child iframe that uses Gecko (if its load happens after the blocklist
+  // reload). If some page contains both backends, and they try to move
+  // element across backend boundary, it could crash (see bug 1404020).
+  sStyloBlocklistEnabled =
+    Preferences::GetBool("layout.css.stylo-blocklist.enabled");
+  if (sStyloBlocklistEnabled && !sStyloBlocklist) {
+    nsAutoCString blocklist;
+    Preferences::GetCString("layout.css.stylo-blocklist.blocked_domains", blocklist);
+    if (!blocklist.IsEmpty()) {
+      sStyloBlocklist = new nsTArray<nsCString>;
+      for (const nsACString& domainString : blocklist.Split(',')) {
+        sStyloBlocklist->AppendElement(domainString);
+      }
+    }
+  }
 #endif
   Preferences::AddBoolVarCache(&sStyleAttrWithXMLBaseDisabled,
                                "layout.css.style-attr-with-xml-base.disabled");
   Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,
                                "layout.idle_period.time_limit",
                                DEFAULT_IDLE_PERIOD_TIME_LIMIT);
   Preferences::AddUintVarCache(&sQuiescentFramesBeforeIdlePeriod,
                                "layout.idle_period.required_quiescent_frames",
@@ -7861,26 +7882,65 @@ nsLayoutUtils::Initialize()
 /* static */
 void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = nullptr;
   }
-
+#ifdef MOZ_STYLO
+  if (sStyloBlocklist) {
+    sStyloBlocklist->Clear();
+    delete sStyloBlocklist;
+    sStyloBlocklist = nullptr;
+  }
+#endif
   for (auto& callback : kPrefCallbacks) {
     Preferences::UnregisterCallback(callback.func, callback.name);
   }
   nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
 
   // so the cached initial quotes array doesn't appear to be a leak
   nsStyleList::Shutdown();
 }
 
+#ifdef MOZ_STYLO
+/* static */
+bool
+nsLayoutUtils::IsInStyloBlocklist(nsIPrincipal* aPrincipal)
+{
+  if (!sStyloBlocklist) {
+    return false;
+  }
+
+  // Note that a non-codebase principal (eg the system principal) will return
+  // a null URI.
+  nsCOMPtr<nsIURI> codebaseURI;
+  aPrincipal->GetURI(getter_AddRefs(codebaseURI));
+  if (!codebaseURI) {
+    return false;
+  }
+
+  nsCOMPtr<nsIEffectiveTLDService> tldService =
+    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(tldService, false);
+
+  // Check if a document's eTLD+1 domain belongs to one of the stylo blocklist.
+  nsAutoCString baseDomain;
+  NS_SUCCEEDED(tldService->GetBaseDomain(codebaseURI, 0, baseDomain));
+  for (const nsCString& domains : *sStyloBlocklist) {
+    if (baseDomain.Equals(domains)) {
+      return true;
+    }
+  }
+  return false;
+}
+#endif
+
 /* static */
 void
 nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
                                     imgIRequest* aRequest,
                                     bool* aRequestRegistered)
 {
   if (!aPresContext) {
     return;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2524,16 +2524,27 @@ public:
    */
   static bool InvalidationDebuggingIsEnabled() {
     return sInvalidationDebuggingIsEnabled || getenv("MOZ_DUMP_INVALIDATION") != 0;
   }
 
   static void Initialize();
   static void Shutdown();
 
+#ifdef MOZ_STYLO
+  /**
+   * Principal-based blocklist for stylo.
+   * Check if aPrincipal is blocked by stylo's blocklist and should fallback to
+   * use Gecko's style backend. Note that using a document's principal rather
+   * than the document URI will let us piggy-back off the existing principal
+   * relationships and symmetries.
+   */
+  static bool IsInStyloBlocklist(nsIPrincipal* aPrincipal);
+#endif
+
   /**
    * Register an imgIRequest object with a refresh driver.
    *
    * @param aPresContext The nsPresContext whose refresh driver we want to
    *        register with.
    * @param aRequest A pointer to the imgIRequest object which the client wants
    *        to register with the refresh driver.
    * @param aRequestRegistered A pointer to a boolean value which indicates
@@ -2986,16 +2997,18 @@ private:
   static uint32_t sZoomMaxPercent;
   static uint32_t sZoomMinPercent;
   static bool sInvalidationDebuggingIsEnabled;
   static bool sInterruptibleReflowEnabled;
   static bool sSVGTransformBoxEnabled;
   static bool sTextCombineUprightDigitsEnabled;
 #ifdef MOZ_STYLO
   static bool sStyloEnabled;
+  static bool sStyloBlocklistEnabled;
+  static nsTArray<nsCString>* sStyloBlocklist;
 #endif
   static bool sStyleAttrWithXMLBaseDisabled;
   static uint32_t sIdlePeriodDeadlineLimit;
   static uint32_t sQuiescentFramesBeforeIdlePeriod;
 
   /**
    * Helper function for LogTestDataForPaint().
    */
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5778,16 +5778,18 @@ pref("dom.webkitBlink.dirPicker.enabled"
 pref("dom.webkitBlink.filesystem.enabled", true);
 #endif
 
 pref("media.block-autoplay-until-in-foreground", true);
 
 // Is Stylo CSS support built and enabled?
 // Only define this pref if Stylo support is actually built in.
 #ifdef MOZ_STYLO
+pref("layout.css.stylo-blocklist.enabled", true);
+pref("layout.css.stylo-blocklist.blocked_domains", "");
 #ifdef MOZ_STYLO_ENABLE
 pref("layout.css.servo.enabled", true);
 #else
 pref("layout.css.servo.enabled", false);
 #endif
 #endif
 
 // HSTS Priming