Bug 1403077 - implement the stylo blocklist mechanism. r=heycam
authorJeremy Chen <jeremychen@mozilla.com>
Wed, 27 Sep 2017 20:35:21 +0800
changeset 385122 08363e433669fe7e6eae4a5776728ed4864e4d35
parent 385121 118b67c940dc554a4adf6fb397db775861962c68
child 385123 abf7029dbcd02a59ab4f13f168bb2d89adabe89e
push id32646
push userarchaeopteryx@coole-files.de
push dateMon, 09 Oct 2017 21:47:29 +0000
treeherdermozilla-central@4afc55e7033c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1403077
milestone58.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 1403077 - implement the stylo blocklist mechanism. r=heycam 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: 8c4n6m9dYD8
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
@@ -13680,17 +13680,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
@@ -122,16 +122,17 @@
 #include "mozilla/StyleSetHandleInlines.h"
 #include "RegionBuilder.h"
 #include "SVGSVGElement.h"
 #include "DisplayItemClip.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "prenv.h"
 #include "TextDrawTarget.h"
 #include "nsDeckFrame.h"
+#include "nsIEffectiveTLDService.h" // for IsInStyloBlocklist
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 #include "GeckoProfiler.h"
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
@@ -187,16 +188,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 */ uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
 /* static */ uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
 
 static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
 
 typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
 static ContentMap* sContentMap = nullptr;
@@ -7966,16 +7969,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::AddUintVarCache(&sIdlePeriodDeadlineLimit,
                                "layout.idle_period.time_limit",
                                DEFAULT_IDLE_PERIOD_TIME_LIMIT);
   Preferences::AddUintVarCache(&sQuiescentFramesBeforeIdlePeriod,
                                "layout.idle_period.required_quiescent_frames",
                                DEFAULT_QUIESCENT_FRAMES);
 
@@ -7988,26 +8009,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
@@ -2553,16 +2553,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
@@ -3020,16 +3031,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 uint32_t sIdlePeriodDeadlineLimit;
   static uint32_t sQuiescentFramesBeforeIdlePeriod;
 
   /**
    * Helper function for LogTestDataForPaint().
    */
   static void DoLogTestDataForPaint(mozilla::layers::LayerManager* aManager,
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5791,16 +5791,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