Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Fri, 28 Sep 2018 11:53:32 +0300
changeset 438672 20d42e205b19be1d4c75640beed91dba04f6650d
parent 438652 2ba4c4ab35f5fd470d05a756adf3078a36960ddc (current diff)
parent 438671 43ace919d4d6cca870a9d614d10b52ebd658eb86 (diff)
child 438673 db301749ebcca846a11eb71a4de46e0e9404c57d
child 438678 ce24611f29dc6d64ae2d3a0c60ae5928bf87e2aa
child 438709 6285038e0fabb98fd6cd3e15022a570a07fa2ff4
push id70073
push useraciure@mozilla.com
push dateFri, 28 Sep 2018 08:56:14 +0000
treeherderautoland@db301749ebcc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
20d42e205b19 / 64.0a1 / 20180928100051 / files
nightly linux64
20d42e205b19 / 64.0a1 / 20180928100051 / files
nightly mac
20d42e205b19 / 64.0a1 / 20180928100051 / files
nightly win32
20d42e205b19 / 64.0a1 / 20180928100051 / files
nightly win64
20d42e205b19 / 64.0a1 / 20180928100051 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/components/enterprisepolicies/EnterprisePolicies.js
+++ b/browser/components/enterprisepolicies/EnterprisePolicies.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   WindowsGPOParser: "resource:///modules/policies/WindowsGPOParser.jsm",
+  macOSPoliciesParser: "resource:///modules/policies/macOSPoliciesParser.jsm",
   Policies: "resource:///modules/policies/Policies.jsm",
   JsonSchemaValidator: "resource://gre/modules/components-utils/JsonSchemaValidator.jsm",
 });
 
 // This is the file that will be searched for in the
 // ${InstallDir}/distribution folder.
 const POLICIES_FILENAME = "policies.json";
 
@@ -71,26 +72,29 @@ EnterprisePoliciesManager.prototype = {
     }
 
     this.status = Ci.nsIEnterprisePolicies.ACTIVE;
     this._parsedPolicies = {};
     this._activatePolicies(provider.policies);
   },
 
   _chooseProvider() {
+    let provider = null;
     if (AppConstants.platform == "win") {
-      let gpoProvider = new GPOPoliciesProvider();
-      if (gpoProvider.hasPolicies) {
-        return gpoProvider;
-      }
+      provider = new WindowsGPOPoliciesProvider();
+    } else if (AppConstants.platform == "macosx") {
+      provider = new macOSPoliciesProvider();
+    }
+    if (provider && provider.hasPolicies) {
+      return provider;
     }
 
-    let jsonProvider = new JSONPoliciesProvider();
-    if (jsonProvider.hasPolicies) {
-      return jsonProvider;
+    provider = new JSONPoliciesProvider();
+    if (provider.hasPolicies) {
+      return provider;
     }
 
     return null;
   },
 
   _activatePolicies(unparsedPolicies) {
     let { schema } = ChromeUtils.import("resource:///modules/policies/schema.jsm", {});
 
@@ -390,17 +394,17 @@ class JSONPoliciesProvider {
       } else {
         log.error("Error reading file");
         this._failed = true;
       }
     }
   }
 }
 
-class GPOPoliciesProvider {
+class WindowsGPOPoliciesProvider {
   constructor() {
     this._policies = null;
 
     let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
 
     // Machine policies override user policies, so we read
     // user policies first and then replace them if necessary.
     this._readData(wrk, wrk.ROOT_KEY_CURRENT_USER);
@@ -424,10 +428,46 @@ class GPOPoliciesProvider {
     if (wrk.hasChild("Mozilla\\Firefox")) {
       let isMachineRoot = (root == wrk.ROOT_KEY_LOCAL_MACHINE);
       this._policies = WindowsGPOParser.readPolicies(wrk, this._policies, isMachineRoot);
     }
     wrk.close();
   }
 }
 
+class macOSPoliciesProvider {
+  constructor() {
+    this._policies = null;
+    let prefReader = Cc["@mozilla.org/mac-preferences-reader;1"]
+                       .createInstance(Ci.nsIMacPreferencesReader);
+    if (!prefReader.policiesEnabled()) {
+      return;
+    }
+    this._policies = macOSPoliciesParser.readPolicies(prefReader);
+    this._removeUnknownPolicies();
+  }
+
+  _removeUnknownPolicies() {
+    let { schema } = ChromeUtils.import("resource:///modules/policies/schema.jsm", {});
+
+    for (let policyName of Object.keys(this._policies)) {
+      if (!schema.properties.hasOwnProperty(policyName)) {
+        log.debug(`Removing unknown policy: ${policyName}`);
+        delete this._policies[policyName];
+      }
+    }
+  }
+
+  get hasPolicies() {
+    return this._policies !== null;
+  }
+
+  get policies() {
+    return this._policies;
+  }
+
+  get failed() {
+    return this._failed;
+  }
+}
+
 var components = [EnterprisePoliciesManager];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
--- a/browser/components/enterprisepolicies/content/aboutPolicies.js
+++ b/browser/components/enterprisepolicies/content/aboutPolicies.js
@@ -199,17 +199,18 @@ function generateErrors() {
   const consoleEvents = storage.getEvents();
   const prefixes = ["Enterprise Policies",
                     "JsonSchemaValidator.jsm",
                     "Policies.jsm",
                     "GPOParser.jsm",
                     "Enterprise Policies Child",
                     "BookmarksPolicies.jsm",
                     "ProxyPolicies.jsm",
-                    "WebsiteFilter Policy"];
+                    "WebsiteFilter Policy",
+                    "macOSPoliciesParser.jsm"];
 
   let new_cont = document.getElementById("errorsContent");
   new_cont.classList.add("errors");
 
   let flag = false;
   for (let err of consoleEvents) {
     if (prefixes.includes(err.prefix)) {
       flag = true;
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/macOSPoliciesParser.jsm
@@ -0,0 +1,39 @@
+/* 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/. */
+
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PREF_LOGLEVEL = "browser.policies.loglevel";
+
+XPCOMUtils.defineLazyGetter(this, "log", () => {
+  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
+  return new ConsoleAPI({
+    prefix: "macOSPoliciesParser.jsm",
+    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
+    // messages during development. See LOG_LEVELS in Console.jsm for details.
+    maxLogLevel: "error",
+    maxLogLevelPref: PREF_LOGLEVEL,
+  });
+});
+
+var EXPORTED_SYMBOLS = ["macOSPoliciesParser"];
+
+var macOSPoliciesParser = {
+  readPolicies(reader) {
+    let nativePolicies = reader.readPreferences();
+    if (!nativePolicies) {
+      return null;
+    }
+
+    // Need an extra check here so we don't
+    // JSON.stringify if we aren't in debug mode
+    if (log.maxLogLevel == "debug") {
+      log.debug(JSON.stringify(nativePolicies, null, 2));
+    }
+
+    return nativePolicies;
+  },
+};
--- a/browser/components/enterprisepolicies/moz.build
+++ b/browser/components/enterprisepolicies/moz.build
@@ -26,11 +26,16 @@ EXTRA_JS_MODULES.policies += [
     'Policies.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES.policies += [
         'WindowsGPOParser.jsm',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    EXTRA_JS_MODULES.policies += [
+        'macOSPoliciesParser.jsm',
+]
+
 FINAL_LIBRARY = 'browsercomps'
 
 JAR_MANIFESTS += ['jar.mn']
--- a/dom/html/test/browser.ini
+++ b/dom/html/test/browser.ini
@@ -24,12 +24,16 @@ support-files =
   file_bug1108547-3.html
 [browser_content_contextmenu_userinput.js]
 [browser_DOMDocElementInserted.js]
 [browser_form_post_from_file_to_http.js]
 [browser_fullscreen-api-keys.js]
 tags = fullscreen
 [browser_fullscreen-contextmenu-esc.js]
 tags = fullscreen
+[browser_fullscreen-newtab.js]
+tags = fullscreen
+support-files = file_fullscreen-newtab.html
+skip-if = os == 'mac' # bug 1494843
 [browser_submission_flush.js]
 [browser_refresh_wyciwyg_url.js]
 support-files =
-  file_refresh_wyciwyg_url.html
\ No newline at end of file
+  file_refresh_wyciwyg_url.html
new file mode 100644
--- /dev/null
+++ b/dom/html/test/browser_fullscreen-newtab.js
@@ -0,0 +1,80 @@
+"use strict";
+
+const kPage = "http://example.org/browser/" +
+              "dom/html/test/file_fullscreen-newtab.html";
+
+function getSizeMode() {
+  return document.documentElement.getAttribute("sizemode");
+}
+
+async function runTest() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: kPage
+  }, async function (browser) {
+    await ContentTask.spawn(browser, null, function() {
+      content.document.addEventListener("fullscreenchange", () => {
+        sendAsyncMessage("Test:FullscreenChange");
+      });
+      content.document.addEventListener("fullscreenerror", () => {
+        sendAsyncMessage("Test:FullscreenError");
+      });
+    });
+    let promiseFsEvents = new Promise(resolve => {
+      let countFsChange = 0;
+      let countFsError = 0;
+      function checkAndResolve() {
+        if (countFsChange > 0 && countFsError > 0) {
+          ok(false, "Got both fullscreenchange and fullscreenerror events");
+        } else if (countFsChange > 2) {
+          ok(false, "Got too many fullscreenchange events");
+        } else if (countFsError > 1) {
+          ok(false, "Got too many fullscreenerror events");
+        } else if (countFsChange == 2 || countFsError == 1) {
+          resolve();
+        }
+      }
+      let mm = browser.messageManager;
+      mm.addMessageListener("Test:FullscreenChange", () => {
+        info("Got fullscreenchange event");
+        ++countFsChange;
+        checkAndResolve();
+      });
+      mm.addMessageListener("Test:FullscreenError", () => {
+        info("Got fullscreenerror event");
+        ++countFsError;
+        checkAndResolve();
+      });
+    });
+    let promiseNewTab =
+      BrowserTestUtils.waitForNewTab(gBrowser, "about:blank");
+    await BrowserTestUtils.synthesizeMouseAtCenter("#link", {}, browser);
+    let [newtab] = await Promise.all([promiseNewTab, promiseFsEvents]);
+    await BrowserTestUtils.removeTab(newtab);
+
+    // Ensure the browser exits fullscreen state in reasonable time.
+    await Promise.race([
+      BrowserTestUtils.waitForCondition(() => getSizeMode() == "normal"),
+      new Promise(resolve => setTimeout(resolve, 2000))
+    ]);
+
+    ok(!window.fullScreen, "The chrome window should not be in fullscreen");
+    ok(!document.fullscreen, "The chrome document should not be in fullscreen");
+  });
+}
+
+add_task(async function () {
+  await pushPrefs(
+    ["full-screen-api.unprefix.enabled", true],
+    ["full-screen-api.transition-duration.enter", "0 0"],
+    ["full-screen-api.transition-duration.leave", "0 0"]);
+  await runTest();
+});
+
+add_task(async function () {
+  await pushPrefs(
+    ["full-screen-api.unprefix.enabled", true],
+    ["full-screen-api.transition-duration.enter", "200 200"],
+    ["full-screen-api.transition-duration.leave", "200 200"]);
+  await runTest();
+});
new file mode 100644
--- /dev/null
+++ b/dom/html/test/file_fullscreen-newtab.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<a id="link" href="about:blank" target="_blank"
+   onclick="document.body.requestFullscreen()">Click here</a>
--- a/gfx/webrender/res/cs_blur.glsl
+++ b/gfx/webrender/res/cs_blur.glsl
@@ -3,17 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include shared,prim_shared
 
 varying vec3 vUv;
 flat varying vec4 vUvRect;
 flat varying vec2 vOffsetScale;
 flat varying float vSigma;
-flat varying int vBlurRadius;
+// The number of pixels on each end that we apply the blur filter over.
+flat varying int vSupport;
 
 #ifdef WR_VERTEX_SHADER
 // Applies a separable gaussian blur in one direction, as specified
 // by the dir field in the blur command.
 
 #define DIR_HORIZONTAL  0
 #define DIR_VERTICAL    1
 
@@ -45,19 +46,25 @@ void main(void) {
     RectWithSize target_rect = blur_task.common_data.task_rect;
 
 #if defined WR_FEATURE_COLOR_TARGET
     vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0).xy);
 #else
     vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
 #endif
     vUv.z = src_task.texture_layer_index;
-    vBlurRadius = int(3.0 * blur_task.blur_radius);
     vSigma = blur_task.blur_radius;
 
+    // Ensure that the support is an even number of pixels to simplify the
+    // fragment shader logic.
+    //
+    // TODO(pcwalton): Actually make use of this fact and use the texture
+    // hardware for linear filtering.
+    vSupport = int(ceil(1.5 * blur_task.blur_radius)) * 2;
+
     switch (aBlurDirection) {
         case DIR_HORIZONTAL:
             vOffsetScale = vec2(1.0 / texture_size.x, 0.0);
             break;
         case DIR_VERTICAL:
             vOffsetScale = vec2(0.0, 1.0 / texture_size.y);
             break;
         default:
@@ -96,33 +103,33 @@ void main(void) {
 //           the number of texture fetches needed for a gaussian blur.
 
 void main(void) {
     SAMPLE_TYPE original_color = SAMPLE_TEXTURE(vUv);
 
     // TODO(gw): The gauss function gets NaNs when blur radius
     //           is zero. In the future, detect this earlier
     //           and skip the blur passes completely.
-    if (vBlurRadius == 0) {
+    if (vSupport == 0) {
         oFragColor = vec4(original_color);
         return;
     }
 
     // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
     vec3 gauss_coefficient;
     gauss_coefficient.x = 1.0 / (sqrt(2.0 * 3.14159265) * vSigma);
     gauss_coefficient.y = exp(-0.5 / (vSigma * vSigma));
     gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;
 
     float gauss_coefficient_sum = 0.0;
     SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x;
     gauss_coefficient_sum += gauss_coefficient.x;
     gauss_coefficient.xy *= gauss_coefficient.yz;
 
-    for (int i=1 ; i <= vBlurRadius ; ++i) {
+    for (int i = 1; i <= vSupport; i++) {
         vec2 offset = vOffsetScale * float(i);
 
         vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw);
         avg_color += SAMPLE_TEXTURE(vec3(st0, vUv.z)) * gauss_coefficient.x;
 
         vec2 st1 = clamp(vUv.xy + offset, vUvRect.xy, vUvRect.zw);
         avg_color += SAMPLE_TEXTURE(vec3(st1, vUv.z)) * gauss_coefficient.x;
 
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -209,19 +209,21 @@ impl BorderSideHelpers for BorderSide {
             _ => return self.color,
         };
 
         // The modulate colors below are not part of the specification. They are
         // derived from the Gecko source code and experimentation, and used to
         // modulate the colors in order to generate colors for the inset/outset
         // and groove/ridge border styles.
         //
+        // NOTE(emilio): Gecko at least takes the background color into
+        // account, should we do the same? Looks a bit annoying for this.
+        //
         // NOTE(emilio): If you change this algorithm, do the same change on
-        // get_colors_for_side in cs_border_segment.glsl, and
-        // NS_GetSpecial3DColors in Gecko.
+        // get_colors_for_side in cs_border_segment.glsl.
         if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
             let scale = if lighter { 1.0 } else { 2.0 / 3.0 };
             return self.color.scale_rgb(scale)
         }
 
         let black = if lighter { 0.7 } else { 0.3 };
         ColorF::new(black, black, black, self.color.a)
     }
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -248,20 +248,24 @@ impl ClipScrollTree {
         let node_index = self.find_nearest_scrolling_ancestor(node_index);
         self.spatial_nodes[node_index.0].scroll(scroll_location)
     }
 
     pub fn update_tree(
         &mut self,
         pan: WorldPoint,
         scene_properties: &SceneProperties,
-    ) -> TransformPalette {
-        let mut transform_palette = TransformPalette::new(self.spatial_nodes.len());
+        mut transform_palette: Option<&mut TransformPalette>,
+    ) {
         if self.spatial_nodes.is_empty() {
-            return transform_palette;
+            return;
+        }
+
+        if let Some(ref mut palette) = transform_palette {
+            palette.allocate(self.spatial_nodes.len());
         }
 
         self.coord_systems.clear();
         self.coord_systems.push(CoordinateSystem::root());
 
         let root_node_index = self.root_reference_frame_index();
         let state = TransformUpdateState {
             parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
@@ -277,29 +281,29 @@ impl ClipScrollTree {
 
         while let Some((node_index, mut state)) = self.nodes_to_update.pop() {
             let node = match self.spatial_nodes.get_mut(node_index.0) {
                 Some(node) => node,
                 None => continue,
             };
 
             node.update(&mut state, &mut self.coord_systems, scene_properties);
-            node.push_gpu_data(&mut transform_palette, node_index);
+            if let Some(ref mut palette) = transform_palette {
+                node.push_gpu_data(palette, node_index);
+            }
 
             if !node.children.is_empty() {
                 node.prepare_state_for_children(&mut state);
                 self.nodes_to_update.extend(node.children
                     .iter()
                     .rev()
                     .map(|child_index| (*child_index, state.clone()))
                 );
             }
         }
-
-        transform_palette
     }
 
     pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
         for node in &mut self.spatial_nodes {
             let external_id = match node.node_type {
                 SpatialNodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
                 _ => continue,
             };
@@ -501,17 +505,17 @@ fn test_cst_simple_translation() {
 
     let child3 = add_reference_frame(
         &mut cst,
         Some(child2),
         LayoutTransform::create_translation(200.0, 200.0, 0.0),
         LayoutVector2D::zero(),
     );
 
-    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new(), None);
 
     test_pt(100.0, 100.0, &cst, child1, root, 200.0, 100.0);
     test_pt(100.0, 100.0, &cst, root, child1, 0.0, 100.0);
     test_pt(100.0, 100.0, &cst, child2, root, 200.0, 150.0);
     test_pt(100.0, 100.0, &cst, root, child2, 0.0, 50.0);
     test_pt(100.0, 100.0, &cst, child2, child1, 100.0, 150.0);
     test_pt(100.0, 100.0, &cst, child1, child2, 100.0, 50.0);
     test_pt(100.0, 100.0, &cst, child3, root, 400.0, 350.0);
@@ -546,17 +550,17 @@ fn test_cst_simple_scale() {
 
     let child3 = add_reference_frame(
         &mut cst,
         Some(child2),
         LayoutTransform::create_scale(2.0, 2.0, 1.0),
         LayoutVector2D::zero(),
     );
 
-    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new(), None);
 
     test_pt(100.0, 100.0, &cst, child1, root, 400.0, 100.0);
     test_pt(100.0, 100.0, &cst, root, child1, 25.0, 100.0);
     test_pt(100.0, 100.0, &cst, child2, root, 400.0, 200.0);
     test_pt(100.0, 100.0, &cst, root, child2, 25.0, 50.0);
     test_pt(100.0, 100.0, &cst, child3, root, 800.0, 400.0);
     test_pt(100.0, 100.0, &cst, child2, child1, 100.0, 200.0);
     test_pt(100.0, 100.0, &cst, child1, child2, 100.0, 50.0);
@@ -600,17 +604,17 @@ fn test_cst_scale_translation() {
 
     let child4 = add_reference_frame(
         &mut cst,
         Some(child3),
         LayoutTransform::create_scale(3.0, 2.0, 1.0),
         LayoutVector2D::zero(),
     );
 
-    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new(), None);
 
     test_pt(100.0, 100.0, &cst, child1, root, 200.0, 150.0);
     test_pt(100.0, 100.0, &cst, child2, root, 300.0, 450.0);
     test_pt(100.0, 100.0, &cst, root, child1, 0.0, 50.0);
     test_pt(100.0, 100.0, &cst, root, child2, 0.0, 12.5);
     test_pt(100.0, 100.0, &cst, child4, root, 1100.0, 450.0);
     test_pt(1100.0, 450.0, &cst, root, child4, 100.0, 100.0);
 
@@ -639,12 +643,12 @@ fn test_cst_translation_rotate() {
 
     let child1 = add_reference_frame(
         &mut cst,
         Some(root),
         LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::degrees(90.0)),
         LayoutVector2D::zero(),
     );
 
-    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new(), None);
 
     test_pt(100.0, 0.0, &cst, child1, root, 0.0, -100.0);
 }
--- a/gfx/webrender/src/device/gl.rs
+++ b/gfx/webrender/src/device/gl.rs
@@ -2230,17 +2230,17 @@ impl Device {
             };
             log!(level, "({}) {}", ty, msg.message);
         }
     }
 
     fn gl_describe_format(&self, format: ImageFormat) -> FormatDesc {
         match format {
             ImageFormat::R8 => FormatDesc {
-                internal: gl::RED as _,
+                internal: gl::R8 as _,
                 external: gl::RED,
                 pixel_type: gl::UNSIGNED_BYTE,
             },
             ImageFormat::R16 => FormatDesc {
                 internal: gl::R16 as _,
                 external: gl::RED,
                 pixel_type: gl::UNSIGNED_SHORT,
             },
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -343,19 +343,21 @@ impl FrameBuilder {
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters
             .total_primitives
             .set(self.prim_store.prim_count());
 
         resource_cache.begin_frame(frame_id);
         gpu_cache.begin_frame();
 
-        let mut transform_palette = clip_scroll_tree.update_tree(
+        let mut transform_palette = TransformPalette::new();
+        clip_scroll_tree.update_tree(
             pan,
             scene_properties,
+            Some(&mut transform_palette),
         );
 
         self.update_scroll_bars(clip_scroll_tree, gpu_cache);
 
         let mut render_tasks = RenderTaskTree::new(frame_id);
 
         let screen_size = self.screen_rect.size.to_i32();
         let mut special_render_passes = SpecialRenderPasses::new(&screen_size);
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -422,24 +422,29 @@ struct RelativeTransformKey {
 //           should be relative to.
 pub struct TransformPalette {
     pub transforms: Vec<TransformData>,
     metadata: Vec<TransformMetadata>,
     map: FastHashMap<RelativeTransformKey, usize>,
 }
 
 impl TransformPalette {
-    pub fn new(spatial_node_count: usize) -> Self {
+    pub fn new() -> Self {
         TransformPalette {
-            transforms: vec![TransformData::invalid(); spatial_node_count],
-            metadata: vec![TransformMetadata::invalid(); spatial_node_count],
+            transforms: Vec::new(),
+            metadata: Vec::new(),
             map: FastHashMap::default(),
         }
     }
 
+    pub fn allocate(&mut self, count: usize) {
+        self.transforms = vec![TransformData::invalid(); count];
+        self.metadata = vec![TransformMetadata::invalid(); count];
+    }
+
     pub fn set_world_transform(
         &mut self,
         index: SpatialNodeIndex,
         transform: LayoutToWorldTransform,
     ) {
         register_transform(
             &mut self.metadata,
             &mut self.transforms,
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -37,17 +37,17 @@ use resource_cache::PlainResources;
 use scene::{Scene, SceneProperties};
 use scene_builder::*;
 #[cfg(feature = "serialize")]
 use serde::{Serialize, Deserialize};
 #[cfg(feature = "debugger")]
 use serde_json;
 #[cfg(any(feature = "capture", feature = "replay"))]
 use std::path::PathBuf;
-use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
+use std::sync::atomic::{AtomicUsize, Ordering};
 use std::mem::replace;
 use std::os::raw::c_void;
 use std::sync::mpsc::{channel, Sender, Receiver};
 use std::u32;
 #[cfg(feature = "replay")]
 use tiling::Frame;
 use time::precise_time_ns;
 use util::drain_filter;
@@ -201,16 +201,19 @@ impl Document {
                 return DocumentOps {
                     // TODO: Does it make sense to track this as a scrolling even if we
                     // ended up not scrolling anything?
                     scroll: true,
                     ..DocumentOps::nop()
                 };
             }
             FrameMsg::HitTest(pipeline_id, point, flags, tx) => {
+                if !self.hit_tester_is_valid {
+                    self.rebuild_hit_tester();
+                }
 
                 let result = match self.hit_tester {
                     Some(ref hit_tester) => {
                         hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
                     }
                     None => HitTestResult { items: Vec::new() },
                 };
 
@@ -288,16 +291,34 @@ impl Document {
         self.hit_tester_is_valid = true;
 
         RenderedDocument {
             frame,
             is_new_scene,
         }
     }
 
+    fn rebuild_hit_tester(&mut self) {
+        if let Some(ref mut frame_builder) = self.frame_builder {
+            let accumulated_scale_factor = self.view.accumulated_scale_factor();
+            let pan = self.view.pan.to_f32() / accumulated_scale_factor;
+
+            self.clip_scroll_tree.update_tree(
+                pan,
+                &self.dynamic_properties,
+                None,
+            );
+
+            self.hit_tester = Some(frame_builder.create_hit_tester(
+                &self.clip_scroll_tree,
+                &self.clip_data_store,
+            ));
+        }
+    }
+
     pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
         let removed_pipelines = replace(&mut self.removed_pipelines, Vec::new());
         PipelineInfo {
             epochs: self.scene.pipeline_epochs.clone(),
             removed_pipelines,
         }
     }
 
@@ -342,30 +363,29 @@ impl Document {
 
         // Advance to the next frame.
         self.frame_id.0 += 1;
     }
 }
 
 struct DocumentOps {
     scroll: bool,
-    build_frame: bool,
 }
 
 impl DocumentOps {
     fn nop() -> Self {
         DocumentOps {
             scroll: false,
-            build_frame: false,
         }
     }
 }
 
 /// The unique id for WR resource identification.
-static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
+/// The namespace_id should start from 1.
+static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
 
 #[cfg(any(feature = "capture", feature = "replay"))]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct PlainRenderBackend {
     default_device_pixel_ratio: f32,
     frame_config: FrameBuilderConfig,
     documents: FastHashMap<DocumentId, DocumentView>,
@@ -414,19 +434,16 @@ impl RenderBackend {
         default_device_pixel_ratio: f32,
         resource_cache: ResourceCache,
         notifier: Box<RenderNotifier>,
         frame_config: FrameBuilderConfig,
         recorder: Option<Box<ApiRecordingReceiver>>,
         sampler: Option<Box<AsyncPropertySampler + Send>>,
         size_of_op: Option<VoidPtrToSizeFn>,
     ) -> RenderBackend {
-        // The namespace_id should start from 1.
-        NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
-
         RenderBackend {
             api_rx,
             payload_rx,
             result_tx,
             scene_tx,
             low_priority_scene_tx,
             scene_rx,
             payload_buffer: Vec::new(),
@@ -610,26 +627,28 @@ impl RenderBackend {
                         }
 
                         self.update_document(
                             txn.document_id,
                             replace(&mut txn.resource_updates, Vec::new()),
                             txn.clip_updates.take(),
                             replace(&mut txn.frame_ops, Vec::new()),
                             replace(&mut txn.notifications, Vec::new()),
-                            txn.build_frame,
                             txn.render_frame,
                             &mut frame_counter,
                             &mut profile_counters,
                             has_built_scene,
                         );
                     },
                     SceneBuilderResult::FlushComplete(tx) => {
                         tx.send(()).ok();
                     }
+                    SceneBuilderResult::ExternalEvent(evt) => {
+                        self.notifier.external_event(evt);
+                    }
                     SceneBuilderResult::Stopped => {
                         panic!("We haven't sent a Stop yet, how did we get a Stopped back?");
                     }
                 }
             }
 
             keep_going = match self.api_rx.recv() {
                 Ok(msg) => {
@@ -722,17 +741,17 @@ impl RenderBackend {
             }
             ApiMsg::DeleteDocument(document_id) => {
                 self.documents.remove(&document_id);
                 self.low_priority_scene_tx.send(
                     SceneBuilderRequest::DeleteDocument(document_id)
                 ).unwrap();
             }
             ApiMsg::ExternalEvent(evt) => {
-                self.notifier.external_event(evt);
+                self.low_priority_scene_tx.send(SceneBuilderRequest::ExternalEvent(evt)).unwrap();
             }
             ApiMsg::ClearNamespace(namespace_id) => {
                 self.resource_cache.clear_namespace(namespace_id);
                 self.documents.retain(|did, _doc| did.0 != namespace_id);
             }
             ApiMsg::MemoryPressure => {
                 // This is drastic. It will basically flush everything out of the cache,
                 // and the next frame will have to rebuild all of its resources.
@@ -880,17 +899,16 @@ impl RenderBackend {
             request_scene_build: None,
             blob_rasterizer: None,
             blob_requests: Vec::new(),
             resource_updates: transaction_msg.resource_updates,
             frame_ops: transaction_msg.frame_ops,
             rasterized_blobs: Vec::new(),
             notifications: transaction_msg.notifications,
             set_root_pipeline: None,
-            build_frame: transaction_msg.generate_frame,
             render_frame: transaction_msg.generate_frame,
         });
 
         self.resource_cache.pre_scene_building_update(
             &mut txn.resource_updates,
             &mut profile_counters.resources,
         );
 
@@ -916,17 +934,16 @@ impl RenderBackend {
 
         if !transaction_msg.use_scene_builder_thread && txn.can_skip_scene_builder() {
             self.update_document(
                 txn.document_id,
                 replace(&mut txn.resource_updates, Vec::new()),
                 None,
                 replace(&mut txn.frame_ops, Vec::new()),
                 replace(&mut txn.notifications, Vec::new()),
-                txn.build_frame,
                 txn.render_frame,
                 frame_counter,
                 profile_counters,
                 false
             );
 
             return;
         }
@@ -954,30 +971,29 @@ impl RenderBackend {
 
     fn update_document(
         &mut self,
         document_id: DocumentId,
         resource_updates: Vec<ResourceUpdate>,
         clip_updates: Option<ClipDataUpdateList>,
         mut frame_ops: Vec<FrameMsg>,
         mut notifications: Vec<NotificationRequest>,
-        mut build_frame: bool,
         mut render_frame: bool,
         frame_counter: &mut u32,
         profile_counters: &mut BackendProfileCounters,
         has_built_scene: bool,
     ) {
         let requested_frame = render_frame;
 
         // If we have a sampler, get more frame ops from it and add them
         // to the transaction. This is a hook to allow the WR user code to
         // fiddle with things after a potentially long scene build, but just
         // before rendering. This is useful for rendering with the latest
         // async transforms.
-        if build_frame {
+        if requested_frame || has_built_scene {
             if let Some(ref sampler) = self.sampler {
                 frame_ops.append(&mut sampler.sample());
             }
         }
 
         let doc = self.documents.get_mut(&document_id).unwrap();
 
         // If there are any additions or removals of clip modes
@@ -987,56 +1003,44 @@ impl RenderBackend {
         }
 
         // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
         // for something wrench specific and we should remove it.
         let mut scroll = false;
         for frame_msg in frame_ops {
             let _timer = profile_counters.total_time.timer();
             let op = doc.process_frame_msg(frame_msg);
-            build_frame |= op.build_frame;
             scroll |= op.scroll;
         }
 
         for update in &resource_updates {
             if let ResourceUpdate::UpdateImage(..) = update {
                 doc.frame_is_valid = false;
             }
         }
 
         self.resource_cache.post_scene_building_update(
             resource_updates,
             &mut profile_counters.resources,
         );
 
-        // After applying the new scene we need to
-        // rebuild the hit-tester, so we trigger a frame generation
-        // step.
-        //
-        // TODO: We could avoid some the cost of building the frame by only
-        // building the information required for hit-testing (See #2807).
-        build_frame |= has_built_scene;
-
         if doc.dynamic_properties.flush_pending_updates() {
             doc.frame_is_valid = false;
             doc.hit_tester_is_valid = false;
-            build_frame = true;
         }
 
         if !doc.can_render() {
             // TODO: this happens if we are building the first scene asynchronously and
             // scroll at the same time. we should keep track of the fact that we skipped
             // composition here and do it as soon as we receive the scene.
-            build_frame = false;
             render_frame = false;
         }
 
-        if doc.frame_is_valid {
-            build_frame = false;
-        }
+        // Avoid re-building the frame if the current built frame is still valid.
+        let build_frame = render_frame && !doc.frame_is_valid;
 
         let mut frame_build_time = None;
         if build_frame && doc.has_pixels() {
             profile_scope!("generate frame");
 
             *frame_counter += 1;
 
             // borrow ck hack for profile_counters
@@ -1095,16 +1099,20 @@ impl RenderBackend {
         }
 
         // Always forward the transaction to the renderer if a frame was requested,
         // otherwise gecko can get into a state where it waits (forever) for the
         // transaction to complete before sending new work.
         if requested_frame {
             self.notifier.new_frame_ready(document_id, scroll, render_frame, frame_build_time);
         }
+
+        if !doc.hit_tester_is_valid {
+            doc.rebuild_hit_tester();
+        }
     }
 
     #[cfg(not(feature = "debugger"))]
     fn get_docs_for_debugger(&self) -> String {
         String::new()
     }
 
     #[cfg(feature = "debugger")]
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -1,14 +1,14 @@
 /* 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/. */
 
 use api::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageParams, BlobImageResult};
-use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, Epoch};
+use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, ExternalEvent, Epoch};
 use api::{BuiltDisplayList, ColorF, LayoutSize, NotificationRequest, Checkpoint};
 use api::channel::MsgSender;
 #[cfg(feature = "capture")]
 use capture::CaptureConfig;
 use frame_builder::{FrameBuilderConfig, FrameBuilder};
 use clip::{ClipDataInterner, ClipDataUpdateList};
 use clip_scroll_tree::ClipScrollTree;
 use display_list_flattener::DisplayListFlattener;
@@ -34,17 +34,16 @@ pub struct Transaction {
     pub request_scene_build: Option<SceneRequest>,
     pub blob_requests: Vec<BlobImageParams>,
     pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
     pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
     pub resource_updates: Vec<ResourceUpdate>,
     pub frame_ops: Vec<FrameMsg>,
     pub notifications: Vec<NotificationRequest>,
     pub set_root_pipeline: Option<PipelineId>,
-    pub build_frame: bool,
     pub render_frame: bool,
 }
 
 impl Transaction {
     pub fn can_skip_scene_builder(&self) -> bool {
         self.request_scene_build.is_none() &&
             self.display_list_updates.is_empty() &&
             self.epoch_updates.is_empty() &&
@@ -68,17 +67,16 @@ pub struct BuiltTransaction {
     pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
     pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
     pub frame_ops: Vec<FrameMsg>,
     pub removed_pipelines: Vec<PipelineId>,
     pub notifications: Vec<NotificationRequest>,
     pub clip_updates: Option<ClipDataUpdateList>,
     pub scene_build_start_time: u64,
     pub scene_build_end_time: u64,
-    pub build_frame: bool,
     pub render_frame: bool,
 }
 
 pub struct DisplayListUpdate {
     pub pipeline_id: PipelineId,
     pub epoch: Epoch,
     pub built_display_list: BuiltDisplayList,
     pub background: Option<ColorF>,
@@ -111,32 +109,34 @@ pub struct BuiltScene {
     pub scene: Scene,
     pub frame_builder: FrameBuilder,
     pub clip_scroll_tree: ClipScrollTree,
 }
 
 // Message from render backend to scene builder.
 pub enum SceneBuilderRequest {
     Transaction(Box<Transaction>),
+    ExternalEvent(ExternalEvent),
     DeleteDocument(DocumentId),
     WakeUp,
     Flush(MsgSender<()>),
     SetFrameBuilderConfig(FrameBuilderConfig),
     SimulateLongSceneBuild(u32),
     SimulateLongLowPrioritySceneBuild(u32),
     Stop,
     #[cfg(feature = "capture")]
     SaveScene(CaptureConfig),
     #[cfg(feature = "replay")]
     LoadScenes(Vec<LoadScene>),
 }
 
 // Message from scene builder to render backend.
 pub enum SceneBuilderResult {
     Transaction(Box<BuiltTransaction>, Option<Sender<SceneSwapResult>>),
+    ExternalEvent(ExternalEvent),
     FlushComplete(MsgSender<()>),
     Stopped,
 }
 
 // Message from render backend to scene builder to indicate the
 // scene swap was completed. We need a separate channel for this
 // so that they don't get mixed with SceneBuilderRequest messages.
 pub enum SceneSwapResult {
@@ -223,16 +223,20 @@ impl SceneBuilder {
                 #[cfg(feature = "replay")]
                 Ok(SceneBuilderRequest::LoadScenes(msg)) => {
                     self.load_scenes(msg);
                 }
                 #[cfg(feature = "capture")]
                 Ok(SceneBuilderRequest::SaveScene(config)) => {
                     self.save_scene(config);
                 }
+                Ok(SceneBuilderRequest::ExternalEvent(evt)) => {
+                    self.tx.send(SceneBuilderResult::ExternalEvent(evt)).unwrap();
+                    self.api_tx.send(ApiMsg::WakeUp).unwrap();
+                }
                 Ok(SceneBuilderRequest::Stop) => {
                     self.tx.send(SceneBuilderResult::Stopped).unwrap();
                     // We don't need to send a WakeUp to api_tx because we only
                     // get the Stop when the RenderBackend loop is exiting.
                     break;
                 }
                 Ok(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)) => {
                     self.simulate_slow_ms = time_ms
@@ -302,17 +306,16 @@ impl SceneBuilder {
                 Document {
                     scene: item.scene,
                     clip_interner: item.clip_interner,
                 },
             );
 
             let txn = Box::new(BuiltTransaction {
                 document_id: item.document_id,
-                build_frame: true,
                 render_frame: item.build_frame,
                 built_scene,
                 resource_updates: Vec::new(),
                 rasterized_blobs: Vec::new(),
                 blob_rasterizer: None,
                 frame_ops: Vec::new(),
                 removed_pipelines: Vec::new(),
                 notifications: Vec::new(),
@@ -403,17 +406,16 @@ impl SceneBuilder {
         );
 
         if self.simulate_slow_ms > 0 {
             thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64));
         }
 
         Box::new(BuiltTransaction {
             document_id: txn.document_id,
-            build_frame: txn.build_frame || built_scene.is_some(),
             render_frame: txn.render_frame,
             built_scene,
             rasterized_blobs,
             resource_updates: replace(&mut txn.resource_updates, Vec::new()),
             blob_rasterizer: replace(&mut txn.blob_rasterizer, None),
             frame_ops: replace(&mut txn.frame_ops, Vec::new()),
             removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()),
             notifications: replace(&mut txn.notifications, Vec::new()),
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -374,17 +374,17 @@ pub struct BlobImageDescriptor {
     pub offset: DevicePoint,
     /// Format for the data in the backing store.
     pub format: ImageFormat,
 }
 
 /// Representation of a rasterized blob image. This is obtained by passing
 /// `BlobImageData` to the embedding via the rasterization callback.
 pub struct RasterizedBlobImage {
-    /// The bounding rectangle for this bob image.
+    /// The bounding rectangle for this blob image.
     pub rasterized_rect: DeviceUintRect,
     /// Backing store. The format is stored out of band in `BlobImageDescriptor`.
     pub data: Arc<Vec<u8>>,
 }
 
 /// Error code for when blob rasterization failed.
 #[derive(Clone, Debug)]
 pub enum BlobImageError {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-43e8d85789efb95099affe3257a9c254ef3d2f4c
+0d2e9611c07e04ac830277f16a3b46bf125b9e7c
--- a/ipc/chromium/src/base/atomicops.h
+++ b/ipc/chromium/src/base/atomicops.h
@@ -145,17 +145,17 @@ Atomic64 Release_Load(volatile const Ato
 #endif  // CPU_ARCH_64_BITS
 
 }  // namespace base::subtle
 }  // namespace base
 
 // Include our platform specific implementation.
 #if defined(OS_WIN) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_msvc.h"
-#elif defined(OS_WIN) && defined(ARCH_CPU_AARCH64_FAMILY)
+#elif defined(OS_WIN) && defined(ARCH_CPU_ARM64)
 // Works just fine, separate case in case we need to change things.
 #include "base/atomicops_internals_x86_msvc.h"
 #elif defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_macosx.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_gcc.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL)
 #include "base/atomicops_internals_arm_gcc.h"
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -1013,19 +1013,33 @@ EnqueuePromiseReactionJob(JSContext* cx,
     // instance with a non-default @@species value on the constructor) with a
     // function that returns objects that're not Promise (subclass) instances.
     // In that case, we just pretend we didn't have an object in the first
     // place.
     // If after all this we do have an object, wrap it in case we entered the
     // handler's compartment above, because we should pass objects from a
     // single compartment to the enqueuePromiseJob callback.
     RootedObject promise(cx, reaction->promise());
-    if (promise && promise->is<PromiseObject>()) {
-        if (!cx->compartment()->wrap(cx, &promise)) {
-            return false;
+    if (promise) {
+        if (promise->is<PromiseObject>()) {
+            if (!cx->compartment()->wrap(cx, &promise)) {
+                return false;
+            }
+        } else if (IsWrapper(promise)) {
+            // `promise` can be already-wrapped promise object at this point.
+            JSObject* unwrappedPromise = UncheckedUnwrap(promise);
+            if (unwrappedPromise->is<PromiseObject>()) {
+                if (!cx->compartment()->wrap(cx, &promise)) {
+                    return false;
+                }
+            } else {
+                promise = nullptr;
+            }
+        } else {
+            promise = nullptr;
         }
     }
 
     // Using objectFromIncumbentGlobal, we can derive the incumbent global by
     // unwrapping and then getting the global. This is very convoluted, but
     // much better than having to store the original global as a private value
     // because we couldn't wrap it to store it as a normal JS value.
     Rooted<GlobalObject*> global(cx);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -643,16 +643,20 @@ JSRuntime::enqueuePromiseJob(JSContext* 
                              Handle<GlobalObject*> incumbentGlobal)
 {
     MOZ_ASSERT(cx->enqueuePromiseJobCallback,
                "Must set a callback using JS::SetEnqueuePromiseJobCallback before using Promises");
 
     void* data = cx->enqueuePromiseJobCallbackData;
     RootedObject allocationSite(cx);
     if (promise) {
+#ifdef DEBUG
+        AssertSameCompartment(job, promise);
+#endif
+
         RootedObject unwrappedPromise(cx, promise);
         // While the job object is guaranteed to be unwrapped, the promise
         // might be wrapped. See the comments in EnqueuePromiseReactionJob in
         // builtin/Promise.cpp for details.
         if (IsWrapper(promise)) {
             unwrappedPromise = UncheckedUnwrap(promise);
         }
         if (unwrappedPromise->is<PromiseObject>()) {
--- a/layout/reftests/svg/filters/css-filter-chains/reftest.list
+++ b/layout/reftests/svg/filters/css-filter-chains/reftest.list
@@ -1,7 +1,7 @@
 # These tests verify that CSS filter chains behave properly.
 # e.g. filter: blur(3px) grayscale(0.5) invert(0.2);
 
 # Some platforms render this complex filter chain a little differently, and that's ok.
-fuzzy(0-5,0-13638) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&layersGPUAccelerated,0-35,0-13638) fuzzy-if(webrender,5-6,17922-18643) == long-chain.html long-chain-ref.html # Win10: Bug 1258241
+fuzzy(0-5,0-13638) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&layersGPUAccelerated,0-35,0-13638) fuzzy-if(webrender,5-6,17922-18853) == long-chain.html long-chain-ref.html # Win10: Bug 1258241
 == moz-element.html moz-element-ref.html
-fuzzy-if(webrender,15-15,7958-8262) == same-filter.html same-filter-ref.html
+fuzzy-if(webrender,15-15,7678-8262) == same-filter.html same-filter-ref.html
--- a/layout/reftests/svg/filters/css-filters/reftest.list
+++ b/layout/reftests/svg/filters/css-filters/reftest.list
@@ -1,37 +1,37 @@
 # These tests verify that CSS filters behave properly.
 # e.g. filter: blur(3px)
 
-fuzzy-if(webrender,9-9,4780-4784) == blur.html blur-ref.html
+fuzzy-if(webrender,9-9,4780-5120) == blur.html blur-ref.html
 == blur.svg blur-ref.svg
 == blur-calc.html blur-calc-ref.html
 == blur-calc-negative.html blur-calc-negative-ref.html
 fuzzy-if(cocoaWidget&&webrender,1-1,1-1) skip-if(d2d) == blur-cap-large-radius-on-software.html blur-cap-large-radius-on-software-ref.html
-fuzzy-if(webrender,9-9,4780-4784) == blur-em-radius.html blur-em-radius-ref.html
+fuzzy-if(webrender,9-9,4780-5120) == blur-em-radius.html blur-em-radius-ref.html
 == blur-invalid-radius.html blur-invalid-radius-ref.html
-fuzzy-if(webrender,9-9,4780-4784) == blur-rem-radius.html blur-rem-radius-ref.html
+fuzzy-if(webrender,9-9,4780-5120) == blur-rem-radius.html blur-rem-radius-ref.html
 == blur-zero-radius.html blur-zero-radius-ref.html
-fuzzy-if(webrender,5-7,19040-21308) == blur-zoomed-page.html blur-zoomed-page-ref.html
+fuzzy-if(webrender,5-7,19040-22652) == blur-zoomed-page.html blur-zoomed-page-ref.html
 == brightness.html brightness-ref.html
 == brightness-darken.html brightness-darken-ref.html
 == brightness-extreme.html brightness-extreme-ref.html
 == brightness-one.html brightness-one-ref.html
 == brightness-percent.html brightness-percent-ref.html
 == brightness-zero.html brightness-zero-ref.html
 == containing-block-1.html containing-block-1-ref.html
 == contrast.html contrast-ref.html
 == contrast-extreme.html contrast-extreme-ref.html
 == contrast-one.html contrast-one-ref.html
 == contrast-percent.html contrast-percent-ref.html
 == contrast-reduce.html contrast-reduce-ref.html
 == contrast-zero.html contrast-zero-ref.html
-fuzzy-if(webrender,9-9,2625-2628) == drop-shadow.html drop-shadow-ref.html
-fuzzy-if(webrender,9-9,2625-2628) fails-if(webrender&&winWidget) == drop-shadow-default-color.html drop-shadow-default-color-ref.html
-fuzzy-if(webrender,9-9,2625-2628) fails-if(webrender&&winWidget) == drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
+fuzzy-if(webrender,9-9,2625-3002) == drop-shadow.html drop-shadow-ref.html
+fuzzy-if(webrender,9-9,2625-3002) fails-if(webrender&&winWidget) == drop-shadow-default-color.html drop-shadow-default-color-ref.html
+fuzzy-if(webrender,9-9,2625-3002) fails-if(webrender&&winWidget) == drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
 == filter-on-huge-bbox.html pass.svg
 == filter-on-outer-svg.html pass.svg
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale.html grayscale-ref.html
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale-one.html grayscale-one-ref.html
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale-over-one.html grayscale-over-one-ref.html
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale-percent.html grayscale-percent-ref.html
 fuzzy-if(webrender,0-1,0-10000) == grayscale-zero.html grayscale-zero-ref.html
 == hue-rotate.html hue-rotate-ref.html
--- a/testing/profiles/perf/user.js
+++ b/testing/profiles/perf/user.js
@@ -61,17 +61,17 @@ user_pref("extensions.systemAddon.update
 user_pref("extensions.update.background.url", "http://127.0.0.1/extensions-dummy/updateBackgroundURL");
 user_pref("extensions.update.notifyUser", false);
 user_pref("extensions.update.url", "http://127.0.0.1/extensions-dummy/updateURL");
 user_pref("extensions.webservice.discoverURL", "http://127.0.0.1/extensions-dummy/discoveryURL");
 user_pref("identity.fxaccounts.auth.uri", "https://127.0.0.1/fxa-dummy/");
 user_pref("identity.fxaccounts.migrateToDevEdition", false);
 // Make tests run consistently on DevEdition (which has a lightweight theme
 // selected by default).
-user_pref("lightweightThemes.selectedThemeID", "");
+user_pref("lightweightThemes.selectedThemeID", "default-theme@mozilla.org");
 user_pref("media.capturestream_hints.enabled", true);
 user_pref("media.gmp-manager.url", "http://127.0.0.1/gmpmanager-dummy/update.xml");
 // Don't block old libavcodec libraries when testing, because our test systems
 // cannot easily be upgraded.
 user_pref("media.libavcodec.allow-obsolete", true);
 user_pref("media.navigator.enabled", true);
 user_pref("media.navigator.permission.disabled", true);
 user_pref("media.peerconnection.enabled", true);
--- a/testing/profiles/unittest/user.js
+++ b/testing/profiles/unittest/user.js
@@ -184,17 +184,17 @@ user_pref("layout.css.prefixes.webkit", 
 // Make sure CSS error reporting is enabled for tests
 user_pref("layout.css.report_errors", true);
 // Enable CSS shape-outside for testing
 user_pref("layout.css.shape-outside.enabled", true);
 // Disable spammy layout warnings because they pollute test logs
 user_pref("layout.spammy_warnings.enabled", false);
 // Make tests run consistently on DevEdition (which has a lightweight theme
 // selected by default).
-user_pref("lightweightThemes.selectedThemeID", "");
+user_pref("lightweightThemes.selectedThemeID", "default-theme@mozilla.org");
 // Disable all recommended Marionette preferences for Gecko tests.
 // The prefs recommended by Marionette are typically geared towards
 // consumer automation; not vendor testing.
 user_pref("marionette.prefs.recommended", false);
 user_pref("media.av1.enabled", true);
 user_pref("media.cache_size", 1000);
 user_pref("media.dormant-on-pause-timeout-ms", 0); // Enter dormant immediately without waiting for timeout.
 // Set the number of shmems the PChromiumCDM protocol pre-allocates to 0,
--- a/toolkit/components/extensions/test/browser/browser_ext_management_themes.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_management_themes.js
@@ -120,16 +120,17 @@ add_task(async function test_management_
     headerURL: "http://mochi.test:8888/data/header.png",
     previewURL: "http://mochi.test:8888/data/preview.png",
     iconURL: "http://mochi.test:8888/data/icon.png",
     textcolor: Math.random().toString(),
     accentcolor: Math.random().toString(),
   };
   is(await extension.awaitMessage("onInstalled"), "Bling", "LWT installed");
   is(await extension.awaitMessage("onEnabled"), "Bling", "LWT enabled");
+  is(await extension.awaitMessage("onDisabled"), "Default", "default disabled");
 
   await theme.startup();
   is(await extension.awaitMessage("onInstalled"), "Simple theme test", "webextension theme installed");
   is(await extension.awaitMessage("onDisabled"), "Bling", "LWT disabled");
   // no enabled event when installed.
 
   extension.sendMessage("test");
   is(await extension.awaitMessage("onEnabled"), "Default", "default enabled");
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1308,16 +1308,22 @@ nsBaseWidget::CreateCompositorSession(in
                                       int aHeight,
                                       CompositorOptions* aOptionsOut)
 {
   MOZ_ASSERT(aOptionsOut);
 
   do {
     CreateCompositorVsyncDispatcher();
 
+    gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
+    // Make sure GPU process is ready for use.
+    // If it failed to connect to GPU process, GPU process usage is disabled in EnsureGPUReady().
+    // It could update gfxVars and gfxConfigs.
+    gpu->EnsureGPUReady();
+
     // If widget type does not supports acceleration, we use ClientLayerManager
     // even when gfxVars::UseWebRender() is true. WebRender could coexist only
     // with BasicCompositor.
     bool enableWR = gfx::gfxVars::UseWebRender() && WidgetTypeSupportsAcceleration()
       && AllowWebRenderForThisWindow();
     bool enableAPZ = UseAPZ();
     CompositorOptions options(enableAPZ, enableWR);
 
@@ -1333,17 +1339,16 @@ nsBaseWidget::CreateCompositorSession(in
     RefPtr<LayerManager> lm;
     if (options.UseWebRender()) {
       lm = new WebRenderLayerManager(this);
     } else {
       lm = new ClientLayerManager(this);
     }
 
     bool retry = false;
-    gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
     mCompositorSession = gpu->CreateTopLevelCompositor(
       this,
       lm,
       GetDefaultScale(),
       options,
       UseExternalCompositingSurface(),
       gfx::IntSize(aWidth, aHeight),
       &retry);
--- a/xpcom/base/moz.build
+++ b/xpcom/base/moz.build
@@ -25,25 +25,28 @@ XPIDL_SOURCES += [
     'nsIUUIDGenerator.idl',
     'nsIVersionComparator.idl',
     'nsIWeakReference.idl',
     'nsrootidl.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     XPIDL_SOURCES += [
+        'nsIMacPreferencesReader.idl',
         'nsIMacUtils.idl',
     ]
     EXPORTS.mozilla += [
         'MacHelpers.h',
         'MacStringHelpers.h',
+        'nsMacPreferencesReader.h',
     ]
     UNIFIED_SOURCES += [
         'MacHelpers.mm',
         'MacStringHelpers.mm',
+        'nsMacPreferencesReader.mm',
     ]
 
 XPIDL_MODULE = 'xpcom_base'
 
 EXPORTS += [
     '!ErrorList.h',
     '!ErrorNamesInternal.h',
     'CodeAddressService.h',
@@ -208,16 +211,17 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../build',
     '/dom/base',
+    '/mfbt',
     '/xpcom/ds',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     CXXFLAGS += CONFIG['TK_CFLAGS']
 
 if CONFIG['ENABLE_CLANG_PLUGIN'] and CONFIG['CC_TYPE'] == 'clang-cl':
     AllowCompilerWarnings()  # workaround for bug 1090497
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsIMacPreferencesReader.idl
@@ -0,0 +1,34 @@
+/* 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 "nsISupportsPrimitives.idl"
+
+%{C++
+#define ENTERPRISE_POLICIES_ENABLED_KEY "EnterprisePoliciesEnabled"
+%}
+
+/**
+ * This interface is designed to provide scriptable access to the macOS
+ * preferences system.
+ *
+ * This interface is highly macOS specific.
+ */
+[scriptable, uuid(06da64da-647f-4286-ac20-50ab4190cfe3)]
+interface nsIMacPreferencesReader : nsISupports
+{
+  /**
+   * This method checks whether macOS policies are enabled.
+   *
+   * @return true if macOS policies are enabled, false otherwise.
+   */
+  bool policiesEnabled();
+
+  /**
+   * This method reads and returns the macOS preferences.
+   *
+   * @return A JSON object containing all macOS preferences.
+   */
+  [implicit_jscontext]
+  jsval readPreferences();
+};
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsMacPreferencesReader.h
@@ -0,0 +1,32 @@
+/* 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/. */
+
+#ifndef MacPreferencesReader_h__
+#define MacPreferencesReader_h__
+
+//-----------------------------------------------------------------------------
+
+#include "nsIMacPreferencesReader.h"
+
+#define  NS_MACPREFERENCESREADER_CID \
+{ 0x95790842, 0x75a0, 0x430d, \
+  { 0x98, 0xbf, 0xf5, 0xce, 0x37, 0x88, 0xea, 0x6d } }
+#define NS_MACPREFERENCESREADER_CONTRACTID \
+  "@mozilla.org/mac-preferences-reader;1"
+
+//-----------------------------------------------------------------------------
+
+class nsMacPreferencesReader : public nsIMacPreferencesReader
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMACPREFERENCESREADER
+
+  nsMacPreferencesReader() {};
+
+protected:
+  virtual ~nsMacPreferencesReader() = default;
+};
+
+#endif  // MacPreferencesReader_h__
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsMacPreferencesReader.mm
@@ -0,0 +1,97 @@
+/* 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 "nsMacPreferencesReader.h"
+
+#include "js/JSON.h"
+#include "JSONWriter.h"
+#include "nsISupportsPrimitives.h"
+
+NS_IMPL_ISUPPORTS(nsMacPreferencesReader, nsIMacPreferencesReader)
+
+using namespace mozilla;
+
+struct StringWriteFunc : public JSONWriteFunc
+{
+  nsCString& mCString;
+  explicit StringWriteFunc(nsCString& aCString) : mCString(aCString)
+  {
+  }
+  void Write(const char* aStr) override { mCString.Append(aStr); }
+};
+
+static void
+EvaluateDict(JSONWriter* aWriter, NSDictionary<NSString*, id>* aDict);
+
+static void
+EvaluateArray(JSONWriter* aWriter, NSArray* aArray)
+{
+  for (id elem in aArray) {
+    if ([elem isKindOfClass:[NSString class]]) {
+      aWriter->StringElement([elem UTF8String]);
+    } else if ([elem isKindOfClass:[NSNumber class]]) {
+      aWriter->IntElement([elem longLongValue]);
+    } else if ([elem isKindOfClass:[NSArray class]]) {
+      aWriter->StartArrayElement();
+      EvaluateArray(aWriter, elem);
+      aWriter->EndArray();
+    } else if ([elem isKindOfClass:[NSDictionary class]]) {
+      aWriter->StartObjectElement();
+      EvaluateDict(aWriter, elem);
+      aWriter->EndObject();
+    }
+  }
+}
+
+static void
+EvaluateDict(JSONWriter* aWriter, NSDictionary<NSString*, id>* aDict)
+{
+  for (NSString* key in aDict) {
+    id value = aDict[key];
+    if ([value isKindOfClass:[NSString class]]) {
+      aWriter->StringProperty([key UTF8String], [value UTF8String]);
+    } else if ([value isKindOfClass:[NSNumber class]]) {
+      aWriter->IntProperty([key UTF8String], [value longLongValue]);
+    } else if ([value isKindOfClass:[NSArray class]]) {
+      aWriter->StartArrayProperty([key UTF8String]);
+      EvaluateArray(aWriter, value);
+      aWriter->EndArray();
+    } else if ([value isKindOfClass:[NSDictionary class]]) {
+      aWriter->StartObjectProperty([key UTF8String]);
+      EvaluateDict(aWriter, value);
+      aWriter->EndObject();
+    }
+  }
+}
+
+NS_IMETHODIMP
+nsMacPreferencesReader::PoliciesEnabled(bool* aPoliciesEnabled)
+{
+  NSString* policiesEnabledStr =
+    [NSString stringWithUTF8String:ENTERPRISE_POLICIES_ENABLED_KEY];
+  *aPoliciesEnabled = [[NSUserDefaults standardUserDefaults]
+                         boolForKey:policiesEnabledStr] == YES;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacPreferencesReader::ReadPreferences(JSContext* aCx,
+                                        JS::MutableHandle<JS::Value> aResult)
+{
+  nsAutoCString jsonStr;
+  JSONWriter w(MakeUnique<StringWriteFunc>(jsonStr));
+  w.Start();
+  EvaluateDict(&w, [[NSUserDefaults standardUserDefaults]
+                     dictionaryRepresentation]);
+  w.End();
+
+  NS_ConvertUTF8toUTF16 jsString(nsDependentCString(jsonStr.get()));
+  auto json = static_cast<const char16_t*>(jsString.get());
+
+  JS::RootedValue val(aCx);
+  MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, json, jsonStr.Length(), &val));
+
+  aResult.set(val);
+  return NS_OK;
+}
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -88,16 +88,17 @@ extern nsresult nsStringInputStreamConst
 #include "SpecialSystemDirectory.h"
 
 #if defined(XP_WIN)
 #include "nsWindowsRegKey.h"
 #endif
 
 #ifdef MOZ_WIDGET_COCOA
 #include "nsMacUtilsImpl.h"
+#include "nsMacPreferencesReader.h"
 #endif
 
 #include "nsSystemInfo.h"
 #include "nsMemoryReporterManager.h"
 #include "nsMemoryInfoDumper.h"
 #include "nsSecurityConsoleMessage.h"
 #include "nsMessageLoop.h"
 #include "nss.h"
@@ -208,16 +209,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsScripta
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsVariantCC)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHashPropertyBagCC)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUUIDGenerator, Init)
 
 #ifdef MOZ_WIDGET_COCOA
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacUtilsImpl)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacPreferencesReader)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemInfo, Init)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemoryReporterManager, Init)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMemoryInfoDumper)
 
@@ -246,16 +248,20 @@ char16_t* gGREBinPath = nullptr;
 static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID);
 static NS_DEFINE_CID(kINIParserFactoryCID, NS_INIPARSERFACTORY_CID);
 
 NS_DEFINE_NAMED_CID(NS_CHROMEREGISTRY_CID);
 NS_DEFINE_NAMED_CID(NS_CHROMEPROTOCOLHANDLER_CID);
 
 NS_DEFINE_NAMED_CID(NS_SECURITY_CONSOLE_MESSAGE_CID);
 
+#ifdef MOZ_WIDGET_COCOA
+NS_DEFINE_NAMED_CID(NS_MACPREFERENCESREADER_CID);
+#endif
+
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsChromeRegistry,
                                          nsChromeRegistry::GetSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChromeProtocolHandler)
 
 static already_AddRefed<nsIFactory>
 CreateINIParserFactory(const mozilla::Module& aModule,
                        const mozilla::Module::CIDEntry& aEntry)
 {
@@ -273,29 +279,35 @@ CreateINIParserFactory(const mozilla::Mo
 #define COMPONENT_M(NAME, Ctor, Selector) { &kNS_##NAME##_CID, false, nullptr, Ctor, Selector },
 const mozilla::Module::CIDEntry kXPCOMCIDEntries[] = {
   { &kComponentManagerCID, true, nullptr, nsComponentManagerImpl::Create, Module::ALLOW_IN_GPU_PROCESS },
   { &kINIParserFactoryCID, false, CreateINIParserFactory },
 #include "XPCOMModule.inc"
   { &kNS_CHROMEREGISTRY_CID, false, nullptr, nsChromeRegistryConstructor },
   { &kNS_CHROMEPROTOCOLHANDLER_CID, false, nullptr, nsChromeProtocolHandlerConstructor },
   { &kNS_SECURITY_CONSOLE_MESSAGE_CID, false, nullptr, nsSecurityConsoleMessageConstructor },
+#ifdef MOZ_WIDGET_COCOA
+  { &kNS_MACPREFERENCESREADER_CID, false, nullptr, nsMacPreferencesReaderConstructor },
+#endif
   { nullptr }
 };
 #undef COMPONENT
 #undef COMPONENT_M
 
 #define COMPONENT(NAME, Ctor) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID },
 #define COMPONENT_M(NAME, Ctor, Selector) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID, Selector },
 const mozilla::Module::ContractIDEntry kXPCOMContracts[] = {
 #include "XPCOMModule.inc"
   { NS_CHROMEREGISTRY_CONTRACTID, &kNS_CHROMEREGISTRY_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "chrome", &kNS_CHROMEPROTOCOLHANDLER_CID },
   { NS_INIPARSERFACTORY_CONTRACTID, &kINIParserFactoryCID },
   { NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &kNS_SECURITY_CONSOLE_MESSAGE_CID },
+#ifdef MOZ_WIDGET_COCOA
+  { NS_MACPREFERENCESREADER_CONTRACTID, &kNS_MACPREFERENCESREADER_CID },
+#endif
   { nullptr }
 };
 #undef COMPONENT
 #undef COMPONENT_M
 
 const mozilla::Module kXPCOMModule = {
   mozilla::Module::kVersion, kXPCOMCIDEntries, kXPCOMContracts,
   nullptr,