Merge mozilla-central to inbound
authorarthur.iakab <aiakab@mozilla.com>
Wed, 26 Sep 2018 15:57:57 +0300
changeset 438335 7e8d28888963098d54ab622d5dde31fe8d797bf1
parent 438334 74f5bf70f7dc9d26d0017bd1ab535b1edaf5619e (current diff)
parent 438288 32fb340597628c8eb3cea9b507a073a63fb6b81e (diff)
child 438336 aebcd9c08c51b24eec808c1190a05f253341cb33
push id34717
push usershindli@mozilla.com
push dateWed, 26 Sep 2018 21:52:33 +0000
treeherdermozilla-central@7ac88abc3c57 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone64.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
Merge mozilla-central to inbound
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1493,16 +1493,21 @@ pref("toolkit.telemetry.hybridContent.en
 pref("browser.ping-centre.telemetry", true);
 pref("browser.ping-centre.log", false);
 pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre");
 pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre");
 
 // Enable GMP support in the addon manager.
 pref("media.gmp-provider.enabled", true);
 
+// Enable blocking access to storage from tracking resources by default on Nightly
+#ifdef NIGHTLY_BUILD
+pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
+#endif
+
 pref("browser.contentblocking.allowlist.storage.enabled", true);
 
 pref("browser.contentblocking.global-toggle.enabled", true);
 
 // Define a set of default features for the Content Blocking UI
 pref("browser.contentblocking.fastblock.ui.enabled", true);
 pref("browser.contentblocking.fastblock.control-center.ui.enabled", true);
 pref("browser.contentblocking.trackingprotection.ui.enabled", true);
--- a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
+++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
@@ -31,16 +31,25 @@ Object.defineProperty(details, "blockLis
   get() {
     let trackingTable = Services.prefs.getCharPref("urlclassifier.trackingTable");
     // If content-track-digest256 is in the tracking table,
     // the user has enabled the strict list.
     return trackingTable.includes("content") ? "strict" : "basic";
   },
 });
 
+Object.defineProperty(details, "hasTouchScreen", {
+  enumerable: true,
+  get() {
+    // return a boolean to indicate there's a touch screen detected or not
+    let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+    return gfxInfo.getInfo().ApzTouchInput == 1;
+  },
+});
+
 if (AppConstants.platform == "linux") {
   XPCOMUtils.defineLazyPreferenceGetter(details, "layers.acceleration.force-enabled", "layers.acceleration.force-enabled", false);
 }
 
 let WebCompatReporter = {
   get endpoint() {
     return Services.urlFormatter.formatURLPref(
       "extensions.webcompat-reporter.newIssueEndpoint");
--- a/build/moz.configure/node.configure
+++ b/build/moz.configure/node.configure
@@ -1,102 +1,60 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 option('--disable-nodejs',
        help='Require Node.js to build')
+option(env='NODEJS', nargs=1, help='Path to nodejs')
 
 
-@depends(host)
-@imports('os')
-@imports(_from='os', _import='environ')
-def node_toolchain_search_path(host):
-    # XXX partly copied from tooltool.py; should be hoisted somewhere central
-
-    # Also add in the location to which `mach bootstrap` or
-    # `mach artifact toolchain` installs clang.
-    mozbuild_state_dir = environ.get('MOZBUILD_STATE_PATH', os.path.expanduser(
-                                     os.path.join('~', '.mozbuild')))
-
-    if host.kernel == "WINNT":
-        mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node')
-    else:
-        mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node', 'bin')
-
-    # We still fallback to the PATH, since on OSes that don't have toolchain
-    # artifacts available to download, Node may be coming from $PATH.
-    path = [environ.get('PATH')]
-    updated_path = [mozbuild_node_path] + path
-
-    return updated_path
+@depends('--enable-nodejs', 'NODEJS')
+@checking('for nodejs',
+          callback=lambda x: '%s (%s)' % (x.path, x.str_version) if x else 'no')
+@imports(_from='mozbuild.nodeutil', _import='find_node_executable')
+@imports(_from='mozbuild.nodeutil', _import='NODE_MIN_VERSION')
+def nodejs(require, env_node):
+    node_exe = env_node[0] if env_node else None
 
-# "nodejs" is first in the tuple on the assumption that it's only likely to
-# exist on systems (probably linux distros) where there is a program in the path
-# called "node" that does something else.
-nodejs = check_prog('NODEJS', ('nodejs', 'node',),
-                    allow_missing=True, paths=node_toolchain_search_path,
-                    paths_have_priority=True)
-
-
-@depends_if(nodejs)
-@checking('for node.js version')
-@imports('os')
-@imports('re')
-@imports('subprocess')
-def nodejs_version(node):
-    env = dict(os.environ)
-    env[b'NODE_DISABLE_COLORS'] = b'1'
-
-    out = check_cmd_output(node, '--version', env=env)
-
-    match = re.search(r'v(.+)$', out)
-
-    if not match:
-        return None
-
-    return Version(match.group(1))
-
-
-@depends('--enable-nodejs', nodejs, nodejs_version)
-def nodejs_suitability(require, node, version):
-    MIN_NODE_VERSION = Version('8.11')
+    nodejs, version = find_node_executable(node_exe)
 
     MAYBE_FILE_A_BUG = '''
 
-If you believe this is a bug, <https://mzl.la/2vLbXAv> is a good way
-to file.  Executing `mach bootstrap --no-system-changes` should
-install a compatible version in ~/.mozbuild on most platforms.
-More details: <https://bit.ly/2BbyD1E>
-'''
+    Executing `mach bootstrap --no-system-changes` should
+    install a compatible version in ~/.mozbuild on most platforms.
+    If you believe this is a bug, <https://mzl.la/2vLbXAv> is a good way
+    to file.  More details: <https://bit.ly/2BbyD1E>
+    '''
 
-    if not node:
-        msg = ('could not find Node.js executable; ensure `node` or `nodejs` '
-               'is in PATH or set NODEJS in environment to point to an '
-               'executable.%s' % (MAYBE_FILE_A_BUG)
+    if not nodejs:
+        msg = ('could not find Node.js executable later than %s; ensure '
+               '`node` or `nodejs` is in PATH or set NODEJS in environment '
+               'to point to an executable.%s' % (NODE_MIN_VERSION, MAYBE_FILE_A_BUG)
                )
 
         if require:
             raise FatalCheckError(msg)
         else:
             log.warning(msg)
             log.warning('(This will become an error in the near future.)')
             return
 
     if not version:
-        msg = 'could not resolve Node.js version.%s' % (MAYBE_FILE_A_BUG)
-        if require:
-            raise FatalCheckError(msg)
-        else:
-            log.warning(msg)
-            log.warning('(This will become an error in the near future.)')
-            return
-
-    if version < MIN_NODE_VERSION:
-        msg = 'NODEJS must point to node %s or newer; %s found. %s' % (
-            MIN_NODE_VERSION, version, MAYBE_FILE_A_BUG)
+        msg = 'NODEJS must point to node %s or newer; found node location: %s. %s' % (
+            NODE_MIN_VERSION, nodejs, MAYBE_FILE_A_BUG)
 
         if require:
             raise FatalCheckError(msg)
         else:
             log.warning(msg)
+            return
+
+    return namespace(
+        path=nodejs,
+        version=version,
+        str_version='.'.join(str(v) for v in version),
+    )
+
+
+set_config('NODEJS', depends_if(nodejs)(lambda p: p.path))
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -26,17 +26,17 @@ endif
 
 USE_AUTOTARGETS_MK = 1
 include $(MOZILLA_DIR)/config/makefiles/makeutils.mk
 
 ifdef REBUILD_CHECK
 REPORT_BUILD = $(info $(shell $(PYTHON) $(MOZILLA_DIR)/config/rebuild_check.py $@ $^))
 REPORT_BUILD_VERBOSE = $(REPORT_BUILD)
 else
-REPORT_BUILD = $(info $(notdir $@))
+REPORT_BUILD = $(info $(relativesrcdir)/$(notdir $@))
 
 ifdef BUILD_VERBOSE_LOG
 REPORT_BUILD_VERBOSE = $(REPORT_BUILD)
 else
 REPORT_BUILD_VERBOSE = $(call BUILDSTATUS,BUILD_VERBOSE $(relativesrcdir))
 endif
 
 endif
--- a/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/devtools/server/tests/mochitest/inspector-helpers.js
@@ -4,18 +4,16 @@
    runNextTest */
 "use strict";
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const {DebuggerClient} = require("devtools/shared/client/debugger-client");
 const {DebuggerServer} = require("devtools/server/main");
 
 const Services = require("Services");
-// promise is still used in tests using this helper
-const defer = require("devtools/shared/defer");
 const {DocumentWalker: _documentWalker} = require("devtools/server/actors/inspector/document-walker");
 
 // Always log packets when running tests.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(function() {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
@@ -82,25 +80,25 @@ function attachURL(url, callback) {
       });
     }
   });
 
   return cleanup;
 }
 
 function promiseOnce(target, event) {
-  const deferred = defer();
-  target.on(event, (...args) => {
-    if (args.length === 1) {
-      deferred.resolve(args[0]);
-    } else {
-      deferred.resolve(args);
-    }
+  return new Promise(resolve => {
+    target.on(event, (...args) => {
+      if (args.length === 1) {
+        resolve(args[0]);
+      } else {
+        resolve(args);
+      }
+    });
   });
-  return deferred.promise;
 }
 
 function sortOwnershipChildren(children) {
   return children.sort((a, b) => a.name.localeCompare(b.name));
 }
 
 function serverOwnershipSubtree(walker, node) {
   const actor = walker._refMap.get(node);
@@ -166,47 +164,46 @@ function assertOwnershipTrees(walker) {
   is(JSON.stringify(clientTree, null, " "), JSON.stringify(serverTree, null, " "),
      "Server and client ownership trees should match.");
 
   return ownershipTreeSize(clientTree.root);
 }
 
 // Verify that an actorID is inaccessible both from the client library and the server.
 function checkMissing(client, actorID) {
-  let deferred = defer();
-  const front = client.getActor(actorID);
-  ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID);
+  return new Promise(resolve => {
+    const front = client.getActor(actorID);
+    ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID);
 
-  deferred = defer();
-  client.request({
-    to: actorID,
-    type: "request",
-  }, response => {
-    is(response.error, "noSuchActor", "node list actor should no longer be contactable.");
-    deferred.resolve(undefined);
+    client.request({
+      to: actorID,
+      type: "request",
+    }, response => {
+      is(response.error, "noSuchActor",
+        "node list actor should no longer be contactable.");
+      resolve(undefined);
+    });
   });
-  return deferred.promise;
 }
 
 // Verify that an actorID is accessible both from the client library and the server.
 function checkAvailable(client, actorID) {
-  let deferred = defer();
-  const front = client.getActor(actorID);
-  ok(front, "Front should be accessible from the client for actorID: " + actorID);
+  return new Promise(resolve => {
+    const front = client.getActor(actorID);
+    ok(front, "Front should be accessible from the client for actorID: " + actorID);
 
-  deferred = defer();
-  client.request({
-    to: actorID,
-    type: "garbageAvailableTest",
-  }, response => {
-    is(response.error, "unrecognizedPacketType",
-       "node list actor should be contactable.");
-    deferred.resolve(undefined);
+    client.request({
+      to: actorID,
+      type: "garbageAvailableTest",
+    }, response => {
+      is(response.error, "unrecognizedPacketType",
+        "node list actor should be contactable.");
+      resolve(undefined);
+    });
   });
-  return deferred.promise;
 }
 
 function promiseDone(currentPromise) {
   currentPromise.catch(err => {
     ok(false, "Promise failed: " + err);
     if (err.stack) {
       dump(err.stack);
     }
@@ -270,30 +267,30 @@ function assertFrameLoad(mutations) {
 // that mutation out of the list
 function assertChildList(mutations) {
   return assertAndStrip(mutations, "Should have had a frame load change.", isChildList);
 }
 
 // Load mutations aren't predictable, so keep accumulating mutations until
 // the one we're looking for shows up.
 function waitForMutation(walker, test, mutations = []) {
-  const deferred = defer();
-  for (const change of mutations) {
-    if (test(change)) {
-      deferred.resolve(mutations);
+  return new Promise(resolve => {
+    for (const change of mutations) {
+      if (test(change)) {
+        resolve(mutations);
+      }
     }
-  }
 
-  walker.once("mutations", newMutations => {
-    waitForMutation(walker, test, mutations.concat(newMutations)).then(finalMutations => {
-      deferred.resolve(finalMutations);
+    walker.once("mutations", newMutations => {
+      waitForMutation(walker, test, mutations.concat(newMutations))
+        .then(finalMutations => {
+          resolve(finalMutations);
+        });
     });
   });
-
-  return deferred.promise;
 }
 
 var _tests = [];
 function addTest(test) {
   _tests.push(test);
 }
 
 function addAsyncTest(generator) {
--- a/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
+++ b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
@@ -19,21 +19,22 @@ window.onload = function() {
 };
 
 let gWalker = null;
 let gDoc = null;
 
 addAsyncTest(async function() {
   const url = document.getElementById("inspectorContent").href;
 
-  const def = defer();
-  attachURL(url, function(err, client, tab, doc) {
-    def.resolve({client, tab, doc});
+  const attachURLPromise = new Promise(resolve => {
+    attachURL(url, function(err, client, tab, doc) {
+      resolve({client, tab, doc});
+    });
   });
-  const {client, tab, doc} = await def.promise;
+  const {client, tab, doc} = await attachURLPromise;
   gDoc = doc;
 
   const {InspectorFront} = require("devtools/shared/fronts/inspector");
   const inspector = InspectorFront(client, tab);
   gWalker = await inspector.getWalker();
 
   runNextTest();
 });
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -8209,16 +8209,53 @@ exports.CSS_PROPERTIES = {
       "inherit",
       "initial",
       "mandatory",
       "none",
       "proximity",
       "unset"
     ]
   },
+  "scrollbar-color": {
+    "isInherited": true,
+    "subproperties": [
+      "scrollbar-color"
+    ],
+    "supports": [
+      2
+    ],
+    "values": [
+      "COLOR",
+      "auto",
+      "currentColor",
+      "hsl",
+      "hsla",
+      "inherit",
+      "initial",
+      "rgb",
+      "rgba",
+      "transparent",
+      "unset"
+    ]
+  },
+  "scrollbar-width": {
+    "isInherited": false,
+    "subproperties": [
+      "scrollbar-width"
+    ],
+    "supports": [],
+    "values": [
+      "auto",
+      "inherit",
+      "initial",
+      "none",
+      "thin",
+      "unset"
+    ]
+  },
   "shape-image-threshold": {
     "isInherited": false,
     "subproperties": [
       "shape-image-threshold"
     ],
     "supports": [],
     "values": [
       "inherit",
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -179,17 +179,17 @@ WebGLContext::IsExtensionSupported(WebGL
         // EXT_
         case WebGLExtensionID::EXT_blend_minmax:
             return WebGLExtensionBlendMinMax::IsSupported(this);
         case WebGLExtensionID::EXT_color_buffer_half_float:
             return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
         case WebGLExtensionID::EXT_frag_depth:
             return WebGLExtensionFragDepth::IsSupported(this);
         case WebGLExtensionID::EXT_shader_texture_lod:
-            return gl->IsSupported(gl::GLFeature::shader_texture_lod);
+            return WebGLExtensionShaderTextureLod::IsSupported(this);
         case WebGLExtensionID::EXT_sRGB:
             return WebGLExtensionSRGB::IsSupported(this);
 
         // OES_
         case WebGLExtensionID::OES_element_index_uint:
             return gl->IsSupported(gl::GLFeature::element_index_uint);
         case WebGLExtensionID::OES_standard_derivatives:
             return gl->IsSupported(gl::GLFeature::standard_derivatives);
--- a/dom/canvas/WebGLExtensionShaderTextureLod.cpp
+++ b/dom/canvas/WebGLExtensionShaderTextureLod.cpp
@@ -9,17 +9,30 @@
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionShaderTextureLod::WebGLExtensionShaderTextureLod(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
+    MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 }
 
 WebGLExtensionShaderTextureLod::~WebGLExtensionShaderTextureLod()
 {
 }
 
+bool
+WebGLExtensionShaderTextureLod::IsSupported(const WebGLContext* webgl)
+{
+    gl::GLContext* gl = webgl->GL();
+    if (gl->IsGLES() && gl->Version() >= 300) {
+        // ANGLE's shader translator doesn't yet translate
+        // WebGL1+EXT_shader_texture_lod to ES3. (Bug 1491221)
+        return false;
+    }
+    return gl->IsSupported(gl::GLFeature::shader_texture_lod);
+}
+
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionShaderTextureLod, EXT_shader_texture_lod)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -246,16 +246,18 @@ public:
 
 class WebGLExtensionShaderTextureLod
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionShaderTextureLod(WebGLContext*);
     virtual ~WebGLExtensionShaderTextureLod();
 
+    static bool IsSupported(const WebGLContext* context);
+
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 class WebGLExtensionTextureFilterAnisotropic
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionTextureFilterAnisotropic(WebGLContext*);
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -117,17 +117,16 @@ Key::ToLocaleBasedKey(Key& aTarget, cons
   }
 
   if (IsFloat() || IsDate() || IsBinary()) {
     aTarget.mBuffer = mBuffer;
     return NS_OK;
   }
 
   aTarget.mBuffer.Truncate();
-  aTarget.mBuffer.SetCapacity(mBuffer.Length());
 
   auto* it = reinterpret_cast<const unsigned char*>(mBuffer.BeginReading());
   auto* end = reinterpret_cast<const unsigned char*>(mBuffer.EndReading());
 
   // First we do a pass and see if there are any strings in this key. We only
   // want to copy/decode when necessary.
   bool canShareBuffers = true;
   while (it < end) {
@@ -145,16 +144,18 @@ Key::ToLocaleBasedKey(Key& aTarget, cons
   }
 
   if (canShareBuffers) {
     MOZ_ASSERT(it == end);
     aTarget.mBuffer = mBuffer;
     return NS_OK;
   }
 
+  aTarget.mBuffer.SetCapacity(mBuffer.Length());
+
   // A string was found, so we need to copy the data we've read so far
   auto* start = reinterpret_cast<const unsigned char*>(mBuffer.BeginReading());
   if (it > start) {
     char* buffer;
     if (!aTarget.mBuffer.GetMutableData(&buffer, it-start)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -22,31 +22,16 @@
 #include "nsIDocShellTreeItem.h"
 #include "nsPIDOMWindow.h"
 
 mozilla::LazyLogModule gAutoplayPermissionLog("Autoplay");
 
 #define AUTOPLAY_LOG(msg, ...)                                             \
   MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
 
-static const char*
-AllowAutoplayToStr(const uint32_t state)
-{
-  switch (state) {
-    case nsIAutoplay::ALLOWED:
-      return "allowed";
-    case nsIAutoplay::BLOCKED:
-      return "blocked";
-    case nsIAutoplay::PROMPT:
-      return "prompt";
-    default:
-      return "unknown";
-  }
-}
-
 namespace mozilla {
 namespace dom {
 
 static nsIDocument*
 ApproverDocOf(const nsIDocument& aDocument)
 {
   nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell();
   if (!ds) {
@@ -198,17 +183,17 @@ AutoplayPolicy::IsAllowedToPlay(const HT
   if (IsMediaElementAllowedToPlay(aElement)) {
     return true;
   }
 
   const bool result = IsMediaElementAllowedToPlay(aElement) ||
     autoplayDefault == nsIAutoplay::ALLOWED;
 
   AUTOPLAY_LOG("IsAllowedToPlay, mediaElement=%p, isAllowToPlay=%s",
-                &aElement, AllowAutoplayToStr(result));
+                &aElement, result ? "allowed" : "blocked");
 
   return result;
 }
 
 /* static */ bool
 AutoplayPolicy::IsAllowedToPlay(const AudioContext& aContext)
 {
   if (!Preferences::GetBool("media.autoplay.block-webaudio", false)) {
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -250,17 +250,17 @@ ConstructPlanarYCbCrData(const VideoInfo
   data.mCbCrStride = Cb.mStride;
   data.mCbSkip = Cb.mSkip;
   data.mCrSkip = Cr.mSkip;
   data.mPicX = aPicture.x;
   data.mPicY = aPicture.y;
   data.mPicSize = aPicture.Size();
   data.mStereoMode = aInfo.mStereoMode;
   data.mYUVColorSpace = aBuffer.mYUVColorSpace;
-  data.mBitDepth = aBuffer.mBitDepth;
+  data.mColorDepth = aBuffer.mColorDepth;
   return data;
 }
 
 /* static */ bool
 VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
                                const VideoInfo& aInfo,
                                const YCbCrBuffer &aBuffer,
                                const IntRect& aPicture,
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -451,16 +451,17 @@ class PlanarYCbCrImage;
 class VideoInfo;
 
 // Holds a decoded video frame, in YCbCr format. These are queued in the reader.
 class VideoData : public MediaData
 {
 public:
   typedef gfx::IntRect IntRect;
   typedef gfx::IntSize IntSize;
+  typedef gfx::ColorDepth ColorDepth;
   typedef layers::ImageContainer ImageContainer;
   typedef layers::Image Image;
   typedef layers::PlanarYCbCrImage PlanarYCbCrImage;
 
   static const Type sType = VIDEO_DATA;
   static const char* sTypeName;
 
   // YCbCr data obtained from decoding the video. The index's are:
@@ -476,17 +477,17 @@ public:
       uint32_t mHeight;
       uint32_t mStride;
       uint32_t mOffset;
       uint32_t mSkip;
     };
 
     Plane mPlanes[3];
     YUVColorSpace mYUVColorSpace = YUVColorSpace::BT601;
-    uint32_t mBitDepth = 8;
+    ColorDepth mColorDepth = ColorDepth::COLOR_8;
   };
 
   class Listener
   {
   public:
     virtual void OnSentToCompositor() = 0;
     virtual ~Listener() { }
   };
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -13,16 +13,17 @@
 #include "nsTArray.h"
 #include "AudioConfig.h"
 #include "ImageTypes.h"
 #include "MediaData.h"
 #include "TrackID.h" // for TrackID
 #include "TimeUnits.h"
 #include "mozilla/gfx/Point.h" // for gfx::IntSize
 #include "mozilla/gfx/Rect.h"  // for gfx::IntRect
+#include "mozilla/gfx/Types.h"  // for gfx::ColorDepth
 
 namespace mozilla {
 
 class AudioInfo;
 class VideoInfo;
 class TextInfo;
 
 class MetadataTag
@@ -222,17 +223,17 @@ public:
   VideoInfo(const VideoInfo& aOther)
     : TrackInfo(aOther)
     , mDisplay(aOther.mDisplay)
     , mStereoMode(aOther.mStereoMode)
     , mImage(aOther.mImage)
     , mCodecSpecificConfig(aOther.mCodecSpecificConfig)
     , mExtraData(aOther.mExtraData)
     , mRotation(aOther.mRotation)
-    , mBitDepth(aOther.mBitDepth)
+    , mColorDepth(aOther.mColorDepth)
     , mImageRect(aOther.mImageRect)
     , mAlphaPresent(aOther.mAlphaPresent)
   {
   }
 
   bool IsValid() const override
   {
     return mDisplay.width > 0 && mDisplay.height > 0;
@@ -331,17 +332,17 @@ public:
   RefPtr<MediaByteBuffer> mCodecSpecificConfig;
   RefPtr<MediaByteBuffer> mExtraData;
 
   // Describing how many degrees video frames should be rotated in clock-wise to
   // get correct view.
   Rotation mRotation;
 
   // Should be 8, 10 or 12. Default value is 8.
-  uint8_t mBitDepth = 8;
+  gfx::ColorDepth mColorDepth = gfx::ColorDepth::COLOR_8;
 
 private:
   // mImage may be cropped; currently only used with the WebM container.
   // A negative width or height indicate that no cropping is to occur.
   gfx::IntRect mImageRect;
 
   // Indicates whether or not frames may contain alpha information.
   bool mAlphaPresent = false;
--- a/dom/media/mp4/MP4Decoder.cpp
+++ b/dom/media/mp4/MP4Decoder.cpp
@@ -6,16 +6,17 @@
 
 #include "MP4Decoder.h"
 #include "H264.h"
 #include "MP4Demuxer.h"
 #include "MediaContainerType.h"
 #include "PDMFactory.h"
 #include "VideoUtils.h"
 #include "mozilla/StaticPrefs.h"
+#include "mozilla/gfx/Tools.h"
 #include "nsMimeTypes.h"
 
 namespace mozilla {
 
 static bool
 IsWhitelistedH264Codec(const nsAString& aCodec)
 {
   uint8_t profile = 0, constraint = 0, level = 0;
@@ -107,17 +108,18 @@ MP4Decoder::GetTracksInfo(const MediaCon
     if (IsVP9CodecString(codec)) {
       auto trackInfo =
         CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
           NS_LITERAL_CSTRING("video/vp9"), aType);
       uint8_t profile = 0;
       uint8_t level = 0;
       uint8_t bitDepth = 0;
       if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) {
-        trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth;
+        trackInfo->GetAsVideoInfo()->mColorDepth =
+          gfx::ColorDepthForBitDepth(bitDepth);
       }
       tracks.AppendElement(std::move(trackInfo));
       continue;
     }
 #ifdef MOZ_AV1
     if (IsAV1CodecString(codec)) {
       tracks.AppendElement(
         CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -189,34 +189,34 @@ public:
   virtual bool
   Supports(const TrackInfo& aTrackInfo,
            DecoderDoctorDiagnostics* aDiagnostics) const
   {
     if (!SupportsMimeType(aTrackInfo.mMimeType, aDiagnostics)) {
       return false;
     }
     const auto videoInfo = aTrackInfo.GetAsVideoInfo();
-    return !videoInfo || SupportsBitDepth(videoInfo->mBitDepth, aDiagnostics);
+    return !videoInfo || SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics);
   }
 
 protected:
   PlatformDecoderModule() { }
   virtual ~PlatformDecoderModule() { }
 
   friend class H264Converter;
   friend class PDMFactory;
   friend class dom::RemoteDecoderModule;
   friend class EMEDecoderModule;
 
-  // Indicates if the PlatformDecoderModule supports decoding of aBitDepth.
-  // Should override this method when the platform can support bitDepth != 8.
-  virtual bool SupportsBitDepth(const uint8_t aBitDepth,
-                                DecoderDoctorDiagnostics* aDiagnostics) const
+  // Indicates if the PlatformDecoderModule supports decoding of aColorDepth.
+  // Should override this method when the platform can support color depth != 8.
+  virtual bool SupportsColorDepth(gfx::ColorDepth aColorDepth,
+                                  DecoderDoctorDiagnostics* aDiagnostics) const
   {
-    return aBitDepth == 8;
+    return aColorDepth == gfx::ColorDepth::COLOR_8;
   }
 
   // Creates a Video decoder. The layers backend is passed in so that
   // decoders can determine whether hardware accelerated decoding can be used.
   // Asynchronous decoding of video should be done in runnables dispatched
   // to aVideoTaskQueue. If the task queue isn't needed, the decoder should
   // not hold a reference to it.
   // On Windows the task queue's threads in have MSCOM initialized with
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -73,28 +73,28 @@ public:
     if (audioCodec == AV_CODEC_ID_NONE && videoCodec == AV_CODEC_ID_NONE) {
       return false;
     }
     AVCodecID codec = audioCodec != AV_CODEC_ID_NONE ? audioCodec : videoCodec;
     return !!FFmpegDataDecoder<V>::FindAVCodec(mLib, codec);
   }
 
 protected:
-  bool SupportsBitDepth(const uint8_t aBitDepth,
-                        DecoderDoctorDiagnostics* aDiagnostics) const override
+  bool SupportsColorDepth(gfx::ColorDepth aColorDepth,
+                          DecoderDoctorDiagnostics* aDiagnostics) const override
   {
     // We don't support bitDepth > 8 when compositor backend is D3D11.
     // But we don't have KnowsCompositor or any object
     // that we can ask for the layersbackend type.
     // We should remove this restriction until
     // we solve the D3D11 compositor backend issue.
 #if defined(XP_LINUX) || defined(XP_MACOSX)
     return true;
 #endif
-    return aBitDepth == 8;
+    return aColorDepth == gfx::ColorDepth::COLOR_8;
   }
 
 private:
   FFmpegLibWrapper* mLib;
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -293,31 +293,31 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
 #if LIBAVCODEC_VERSION_MAJOR >= 57
       ||
       mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE
 #endif
       ) {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = mFrame->width;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height;
     if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P10LE) {
-      b.mBitDepth = 10;
+      b.mColorDepth = gfx::ColorDepth::COLOR_10;
     }
 #if LIBAVCODEC_VERSION_MAJOR >= 57
     else if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE) {
-      b.mBitDepth = 12;
+      b.mColorDepth = gfx::ColorDepth::COLOR_12;
     }
 #endif
   } else if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV422P) {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height;
   } else {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
     if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV420P10LE) {
-      b.mBitDepth = 10;
+      b.mColorDepth = gfx::ColorDepth::COLOR_10;
     }
   }
   if (mLib->av_frame_get_colorspace) {
     switch (mLib->av_frame_get_colorspace(mFrame)) {
       case AVCOL_SPC_BT709:
         b.mYUVColorSpace = YUVColorSpace::BT709;
         break;
       case AVCOL_SPC_SMPTE170M:
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -194,17 +194,17 @@ WMFDecoderModule::SupportsMimeType(
   return Supports(*trackInfo, aDiagnostics);
 }
 
 bool
 WMFDecoderModule::Supports(const TrackInfo& aTrackInfo,
                            DecoderDoctorDiagnostics* aDiagnostics) const
 {
   const auto videoInfo = aTrackInfo.GetAsVideoInfo();
-  if (videoInfo && !SupportsBitDepth(videoInfo->mBitDepth, aDiagnostics)) {
+  if (videoInfo && !SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics)) {
     return false;
   }
 
   if ((aTrackInfo.mMimeType.EqualsLiteral("audio/mp4a-latm") ||
        aTrackInfo.mMimeType.EqualsLiteral("audio/mp4")) &&
        WMFDecoderModule::HasAAC()) {
     const auto audioInfo = aTrackInfo.GetAsAudioInfo();
     if (audioInfo && audioInfo->mRate > 0) {
--- a/dom/media/webm/WebMDecoder.cpp
+++ b/dom/media/webm/WebMDecoder.cpp
@@ -53,17 +53,18 @@ WebMDecoder::GetTracksInfo(const MediaCo
         trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
           NS_LITERAL_CSTRING("video/vp8"), aType);
       }
       if (trackInfo) {
         uint8_t profile = 0;
         uint8_t level = 0;
         uint8_t bitDepth = 0;
         if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) {
-          trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth;
+          trackInfo->GetAsVideoInfo()->mColorDepth =
+            gfx::ColorDepthForBitDepth(bitDepth);
         }
         tracks.AppendElement(std::move(trackInfo));
         continue;
       }
     }
 #ifdef MOZ_AV1
     if (StaticPrefs::MediaAv1Enabled() && IsAV1CodecString(codec)) {
       tracks.AppendElement(
@@ -97,17 +98,17 @@ WebMDecoder::IsSupportedType(const Media
   }
 
   if (tracks.IsEmpty()) {
     // WebM guarantees that the only codecs it contained are vp8, vp9, opus or vorbis.
     return true;
   }
 
   // Verify that we have a PDM that supports the whitelisted types, include
-  // bitdepth
+  // color depth
   RefPtr<PDMFactory> platform = new PDMFactory();
   for (const auto& track : tracks) {
     if (!track || !platform->Supports(*track, nullptr /* diagnostic */)) {
       return false;
     }
   }
 
   return true;
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -1574,17 +1574,17 @@ PresentationPresentingInfo::ResolvedCall
   // Use Element to support both HTMLIFrameElement and nsXULElement.
   Element* frame = nullptr;
   nsresult rv = UNWRAP_OBJECT(Element, &obj, frame);
   if (NS_WARN_IF(!frame)) {
     ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
-  nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface((nsIFrameLoaderOwner*) frame);
+  nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(frame);
   if (NS_WARN_IF(!owner)) {
     ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   RefPtr<nsFrameLoader> frameLoader = owner->GetFrameLoader();
   if (NS_WARN_IF(!frameLoader)) {
     ReplyError(NS_ERROR_DOM_OPERATION_ERR);
--- a/dom/vr/test/mochitest/mochitest.ini
+++ b/dom/vr/test/mochitest/mochitest.ini
@@ -3,22 +3,30 @@
 support-files =
   VRSimulationDriver.js
   requestPresent.js
   runVRTest.js
   WebVRHelpers.js
 
 [test_vrController_displayId.html]
 # Enable Linux after Bug 1310655 # TIMED_OUT for Android.
-skip-if = (os != "win" && release_or_beta) || (os == "android")
+# skip-if = (os != "win" && release_or_beta) || (os == "android")
+# Re-enable this once Bug 1466702 has landed
+skip-if = true
 [test_vrDisplay_canvas2d.html]
-skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
+# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
+# Re-enable this once Bug 1466702 has landed
+skip-if = true
 [test_vrDisplay_exitPresent.html]
-skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
+# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
+# Re-enable this once Bug 1466702 has landed
+skip-if = true
 [test_vrDisplay_getFrameData.html]
 # Enable Linux after Bug 1310655, enable Android after Bug 1348246
-skip-if = (os != "win" && release_or_beta) || (os == "android")
+# skip-if = (os != "win" && release_or_beta) || (os == "android")
+# Re-enable this once Bug 1466702 has landed
+skip-if = true
 [test_vrDisplay_onvrdisplayconnect.html]
 skip-if = true
 [test_vrDisplay_onvrdisplaydeactivate_crosscontent.html]
 skip-if = true
 [test_vrDisplay_requestPresent.html]
 skip-if = true
--- a/dom/webidl/CaretStateChangedEvent.webidl
+++ b/dom/webidl/CaretStateChangedEvent.webidl
@@ -24,16 +24,17 @@ dictionary CaretStateChangedEventInit : 
   boolean selectionEditable = false;
   DOMString selectedTextContent = "";
 };
 
 [Constructor(DOMString type, optional CaretStateChangedEventInit eventInit),
  ChromeOnly]
 interface CaretStateChangedEvent : Event {
   readonly attribute boolean collapsed;
+  /* The bounding client rect is relative to the visual viewport. */
   readonly attribute DOMRectReadOnly? boundingClientRect;
   readonly attribute CaretChangedReason reason;
   readonly attribute boolean caretVisible;
   readonly attribute boolean caretVisuallyVisible;
   readonly attribute boolean selectionVisible;
   readonly attribute boolean selectionEditable;
   readonly attribute DOMString selectedTextContent;
 };
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -48,17 +48,17 @@ typedef OfflineResourceList ApplicationC
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
   [Throws, CrossOriginCallable] void close();
   [Throws, CrossOriginReadable] readonly attribute boolean closed;
   [Throws] void stop();
   [Throws, CrossOriginCallable] void focus();
   [Throws, CrossOriginCallable] void blur();
-  [Replaceable] readonly attribute any event;
+  [Replaceable, Pref="dom.window.event.enabled"] readonly attribute any event;
 
   // other browsing contexts
   [Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;
   [Replaceable, CrossOriginReadable] readonly attribute unsigned long length;
   //[Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy top;
   [Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy? top;
   [Throws, CrossOriginReadable] attribute any opener;
   //[Throws] readonly attribute WindowProxy parent;
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -105,51 +105,89 @@ BytesPerPixel(SurfaceFormat aFormat)
   case SurfaceFormat::Depth:
     return sizeof(uint16_t);
   default:
     return 4;
   }
 }
 
 static inline SurfaceFormat
-SurfaceFormatForAlphaBitDepth(uint32_t aBitDepth)
+SurfaceFormatForColorDepth(ColorDepth aColorDepth)
 {
-  if (aBitDepth == 8) {
-    return SurfaceFormat::A8;
-  } else if (aBitDepth == 10 ||
-             aBitDepth == 12) {
-    return SurfaceFormat::A16;
+  SurfaceFormat format = SurfaceFormat::A8;
+  switch (aColorDepth) {
+    case ColorDepth::COLOR_8:
+      break;
+    case ColorDepth::COLOR_10:
+    case ColorDepth::COLOR_12:
+      format = SurfaceFormat::A16;
+      break;
+    case ColorDepth::UNKNOWN:
+      MOZ_ASSERT_UNREACHABLE("invalid color depth value");
   }
-  MOZ_ASSERT_UNREACHABLE("Unsupported alpha bit depth");
-  return SurfaceFormat::UNKNOWN;
+  return format;
+}
+
+static inline uint32_t
+BitDepthForColorDepth(ColorDepth aColorDepth)
+{
+  uint32_t depth = 8;
+  switch (aColorDepth) {
+    case ColorDepth::COLOR_8:
+      break;
+    case ColorDepth::COLOR_10:
+      depth = 10;
+      break;
+    case ColorDepth::COLOR_12:
+      depth = 12;
+      break;
+    case ColorDepth::UNKNOWN:
+      MOZ_ASSERT_UNREACHABLE("invalid color depth value");
+  }
+  return depth;
 }
 
 static inline ColorDepth
-ColorDepthForAlphaBitDepth(uint32_t aBitDepth)
+ColorDepthForBitDepth(uint8_t aBitDepth)
 {
+  ColorDepth depth = ColorDepth::COLOR_8;
   switch (aBitDepth) {
-  case 8: return ColorDepth::COLOR_8;
-  case 10: return ColorDepth::COLOR_10;
-  case 12: return ColorDepth::COLOR_12;
-  default:
-    MOZ_ASSERT_UNREACHABLE("Unsupported alpha bit depth");
-    return ColorDepth::COLOR_8;
+    case 8:
+      break;
+    case 10:
+      depth = ColorDepth::COLOR_10;
+      break;
+    case 12:
+      depth = ColorDepth::COLOR_12;
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("invalid color depth value");
   }
+  return depth;
 }
 
 // 10 and 12 bits color depth image are using 16 bits integers for storage
-// As such we need to rescale the value from 8,10 or 12 to 16.
+// As such we need to rescale the value from 10 or 12 bits to 16.
 static inline uint32_t
-RescalingFactorForAlphaBitDepth(uint32_t aBitDepth)
+RescalingFactorForColorDepth(ColorDepth aColorDepth)
 {
-  MOZ_ASSERT(aBitDepth == 8 || aBitDepth == 10 || aBitDepth == 12);
-  uint32_t pixelBits =
-    8 * BytesPerPixel(SurfaceFormatForAlphaBitDepth(aBitDepth));
-  uint32_t paddingBits = pixelBits - aBitDepth;
-  return pow(2, paddingBits);
+  uint32_t factor = 1;
+  switch (aColorDepth) {
+    case ColorDepth::COLOR_8:
+      break;
+    case ColorDepth::COLOR_10:
+      factor = 64;
+      break;
+    case ColorDepth::COLOR_12:
+      factor = 16;
+      break;
+    case ColorDepth::UNKNOWN:
+      MOZ_ASSERT_UNREACHABLE("invalid color depth value");
+  }
+  return factor;
 }
 
 static inline bool
 IsOpaqueFormat(SurfaceFormat aFormat) {
   switch (aFormat) {
     case SurfaceFormat::B8G8R8X8:
     case SurfaceFormat::R8G8B8X8:
     case SurfaceFormat::X8R8G8B8:
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -101,17 +101,17 @@ inline bool IsOpaque(SurfaceFormat aForm
     return false;
   }
 }
 
 enum class ColorDepth : uint8_t {
   COLOR_8,
   COLOR_10,
   COLOR_12,
-  MAX
+  UNKNOWN
 };
 
 enum class FilterType : int8_t {
   BLEND = 0,
   TRANSFORM,
   MORPHOLOGY,
   COLOR_MATRIX,
   FLOOD,
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -155,24 +155,23 @@ GPUProcessHost::InitAfterConnect(bool aS
 void
 GPUProcessHost::Shutdown()
 {
   MOZ_ASSERT(!mShutdownRequested);
 
   mListener = nullptr;
 
   if (mGPUChild) {
-    mGPUChild->SendShutdownVR();
-
     // OnChannelClosed uses this to check if the shutdown was expected or
     // unexpected.
     mShutdownRequested = true;
 
     // The channel might already be closed if we got here unexpectedly.
     if (!mChannelClosed) {
+      mGPUChild->SendShutdownVR();
       mGPUChild->Close();
     }
 
 #ifndef NS_FREE_PERMANENT_DATA
     // No need to communicate shutdown, the GPU process doesn't need to
     // communicate anything back.
     KillHard("NormalShutdown");
 #endif
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -700,16 +700,24 @@ template <>
 struct ParamTraits<mozilla::gfx::SurfaceFormat>
   : public ContiguousEnumSerializer<
              mozilla::gfx::SurfaceFormat,
              mozilla::gfx::SurfaceFormat::B8G8R8A8,
              mozilla::gfx::SurfaceFormat::UNKNOWN>
 {};
 
 template <>
+struct ParamTraits<mozilla::gfx::ColorDepth>
+  : public ContiguousEnumSerializer<
+             mozilla::gfx::ColorDepth,
+             mozilla::gfx::ColorDepth::COLOR_8,
+             mozilla::gfx::ColorDepth::UNKNOWN>
+{};
+
+template <>
 struct ParamTraits<mozilla::StereoMode>
   : public ContiguousEnumSerializer<
              mozilla::StereoMode,
              mozilla::StereoMode::MONO,
              mozilla::StereoMode::MAX>
 {};
 
 template <>
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -1,22 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "BufferTexture.h"
-#include "mozilla/layers/ImageDataSerializer.h"
-#include "mozilla/layers/ISurfaceAllocator.h"
-#include "mozilla/layers/CompositableForwarder.h"
+#include "libyuv.h"
+#include "mozilla/Move.h"
+#include "mozilla/fallible.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
-#include "mozilla/gfx/2D.h"
-#include "mozilla/fallible.h"
-#include "libyuv.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ImageDataSerializer.h"
 
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"
 #endif
 
 namespace mozilla {
 namespace layers {
 
@@ -163,18 +164,18 @@ BufferTextureData::CreateInternal(Layers
 
 BufferTextureData*
 BufferTextureData::CreateForYCbCr(KnowsCompositor* aAllocator,
                                   gfx::IntSize aYSize,
                                   uint32_t aYStride,
                                   gfx::IntSize aCbCrSize,
                                   uint32_t aCbCrStride,
                                   StereoMode aStereoMode,
+                                  gfx::ColorDepth aColorDepth,
                                   YUVColorSpace aYUVColorSpace,
-                                  uint32_t aBitDepth,
                                   TextureFlags aTextureFlags)
 {
   uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(
     aYSize, aYStride, aCbCrSize, aCbCrStride);
   if (bufSize == 0) {
     return nullptr;
   }
 
@@ -195,18 +196,18 @@ BufferTextureData::CreateForYCbCr(KnowsC
                                      aAllocator->GetCompositorBackendType(),
                                      supportsTextureDirectMapping)
       : true;
 
   YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aYStride,
                                                aCbCrSize, aCbCrStride,
                                                yOffset, cbOffset, crOffset,
                                                aStereoMode,
+                                               aColorDepth,
                                                aYUVColorSpace,
-                                               aBitDepth,
                                                hasIntermediateBuffer);
 
   return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder()
                                    : nullptr,
                         descriptor,
                         gfx::BackendType::NONE,
                         bufSize,
                         aTextureFlags);
@@ -249,20 +250,20 @@ BufferTextureData::GetCbCrSize() const
 }
 
 Maybe<YUVColorSpace>
 BufferTextureData::GetYUVColorSpace() const
 {
   return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor);
 }
 
-Maybe<uint32_t>
-BufferTextureData::GetBitDepth() const
+Maybe<gfx::ColorDepth>
+BufferTextureData::GetColorDepth() const
 {
-  return ImageDataSerializer::BitDepthFromBufferDescriptor(mDescriptor);
+  return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor);
 }
 
 Maybe<StereoMode>
 BufferTextureData::GetStereoMode() const
 {
   return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor);
 }
 
@@ -349,17 +350,18 @@ BufferTextureData::BorrowMappedYCbCrData
   const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
 
   uint8_t* data = GetBuffer();
   auto ySize = desc.ySize();
   auto cbCrSize = desc.cbCrSize();
 
   aMap.stereoMode = desc.stereoMode();
   aMap.metadata = nullptr;
-  uint32_t bytesPerPixel = desc.bitDepth() > 8 ? 2 : 1;
+  uint32_t bytesPerPixel =
+    BytesPerPixel(SurfaceFormatForColorDepth(desc.colorDepth()));
 
   aMap.y.data = data + desc.yOffset();
   aMap.y.size = ySize;
   aMap.y.stride = desc.yStride();
   aMap.y.skip = 0;
   aMap.y.bytesPerPixel = bytesPerPixel;
 
   aMap.cb.data = data + desc.cbOffset();
@@ -429,21 +431,21 @@ BufferTextureData::UpdateFromSurface(gfx
 
   srcSurf->Unmap();
   surface->Unmap();
 
   return true;
 }
 
 void
-BufferTextureData::SetDesciptor(const BufferDescriptor& aDescriptor)
+BufferTextureData::SetDescriptor(BufferDescriptor&& aDescriptor)
 {
   MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor);
   MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize());
-  mDescriptor = aDescriptor;
+  mDescriptor = std::move(aDescriptor);
 }
 
 bool
 MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
 {
   MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
   if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
     return false;
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -31,18 +31,18 @@ public:
                                    LayersIPCChannel* aAllocator);
 
   static BufferTextureData* CreateForYCbCr(KnowsCompositor* aAllocator,
                                            gfx::IntSize aYSize,
                                            uint32_t aYStride,
                                            gfx::IntSize aCbCrSize,
                                            uint32_t aCbCrStride,
                                            StereoMode aStereoMode,
+                                           gfx::ColorDepth aColorDepth,
                                            YUVColorSpace aYUVColorSpace,
-                                           uint32_t aBitDepth,
                                            TextureFlags aTextureFlags);
 
   virtual bool Lock(OpenMode aMode) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual void FillInfo(TextureData::Info& aInfo) const override;
 
@@ -53,23 +53,23 @@ public:
   virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
 
   // use TextureClient's default implementation
   virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
 
   virtual BufferTextureData* AsBufferTextureData() override { return this; }
 
   // Don't use this.
-  void SetDesciptor(const BufferDescriptor& aDesc);
+  void SetDescriptor(BufferDescriptor&& aDesc);
 
   Maybe<gfx::IntSize> GetCbCrSize() const;
 
   Maybe<YUVColorSpace> GetYUVColorSpace() const;
 
-  Maybe<uint32_t> GetBitDepth() const;
+  Maybe<gfx::ColorDepth> GetColorDepth() const;
 
   Maybe<StereoMode> GetStereoMode() const;
 
 protected:
   gfx::IntSize GetSize() const;
 
   gfx::SurfaceFormat GetFormat() const;
 
--- a/gfx/layers/D3D11YCbCrImage.cpp
+++ b/gfx/layers/D3D11YCbCrImage.cpp
@@ -30,17 +30,17 @@ bool
 D3D11YCbCrImage::SetData(KnowsCompositor* aAllocator,
                          ImageContainer* aContainer,
                          const PlanarYCbCrData& aData)
 {
   mPictureRect = IntRect(
     aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height);
   mYSize = aData.mYSize;
   mCbCrSize = aData.mCbCrSize;
-  mBitDepth = aData.mBitDepth;
+  mColorDepth = aData.mColorDepth;
   mColorSpace = aData.mYUVColorSpace;
 
   D3D11YCbCrRecycleAllocator* allocator =
     aContainer->GetD3D11YCbCrRecycleAllocator(aAllocator);
   if (!allocator) {
     return false;
   }
 
@@ -245,17 +245,17 @@ D3D11YCbCrImage::GetAsSourceSurface()
   }
 
   MOZ_ASSERT(mapCb.RowPitch == mapCr.RowPitch);
 
   data.mPicX = mPictureRect.X();
   data.mPicY = mPictureRect.Y();
   data.mPicSize = mPictureRect.Size();
   data.mStereoMode = StereoMode::MONO;
-  data.mBitDepth = mBitDepth;
+  data.mColorDepth = mColorDepth;
   data.mYUVColorSpace = mColorSpace;
   data.mYSkip = data.mCbSkip = data.mCrSkip = 0;
   data.mYSize = mYSize;
   data.mCbCrSize = mCbCrSize;
   data.mYChannel = static_cast<uint8_t*>(mapY.pData);
   data.mYStride = mapY.RowPitch;
   data.mCbChannel = static_cast<uint8_t*>(mapCb.pData);
   data.mCrChannel = static_cast<uint8_t*>(mapCr.pData);
@@ -350,17 +350,17 @@ DXGIYCbCrTextureAllocationHelper::IsComp
 {
   MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
 
   DXGIYCbCrTextureData* dxgiData = aTextureClient->GetInternalData()->AsDXGIYCbCrTextureData();
   if (!dxgiData ||
       aTextureClient->GetSize() != mData.mYSize ||
       dxgiData->GetYSize() != mData.mYSize ||
       dxgiData->GetCbCrSize() != mData.mCbCrSize ||
-      dxgiData->GetBitDepth() != mData.mBitDepth ||
+      dxgiData->GetColorDepth() != mData.mColorDepth ||
       dxgiData->GetYUVColorSpace() != mData.mYUVColorSpace) {
     return false;
   }
 
   ID3D11Texture2D* textureY = dxgiData->GetD3D11Texture(0);
   ID3D11Texture2D* textureCb = dxgiData->GetD3D11Texture(1);
   ID3D11Texture2D* textureCr = dxgiData->GetD3D11Texture(2);
 
@@ -384,22 +384,23 @@ DXGIYCbCrTextureAllocationHelper::IsComp
   }
 
   return true;
 }
 
 already_AddRefed<TextureClient>
 DXGIYCbCrTextureAllocationHelper::Allocate(KnowsCompositor* aAllocator)
 {
-  CD3D11_TEXTURE2D_DESC newDesc(
-    mData.mBitDepth == 8 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_R16_UNORM,
-    mData.mYSize.width,
-    mData.mYSize.height,
-    1,
-    1);
+  CD3D11_TEXTURE2D_DESC newDesc(mData.mColorDepth == gfx::ColorDepth::COLOR_8
+                                  ? DXGI_FORMAT_R8_UNORM
+                                  : DXGI_FORMAT_R16_UNORM,
+                                mData.mYSize.width,
+                                mData.mYSize.height,
+                                1,
+                                1);
   // WebRender requests keyed mutex
   if (mDevice == gfx::DeviceManagerDx::Get()->GetCompositorDevice() &&
       !gfxVars::UseWebRender()) {
     newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
   } else {
     newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
   }
 
@@ -439,17 +440,17 @@ DXGIYCbCrTextureAllocationHelper::Alloca
   return TextureClient::CreateWithData(
     DXGIYCbCrTextureData::Create(
       textureY,
       textureCb,
       textureCr,
       mData.mYSize,
       mData.mYSize,
       mData.mCbCrSize,
-      mData.mBitDepth,
+      mData.mColorDepth,
       mData.mYUVColorSpace),
     mTextureFlags,
     forwarder);
 }
 
 already_AddRefed<TextureClient>
 D3D11YCbCrRecycleAllocator::Allocate(SurfaceFormat aFormat,
                                      IntSize aSize,
--- a/gfx/layers/D3D11YCbCrImage.h
+++ b/gfx/layers/D3D11YCbCrImage.h
@@ -79,17 +79,17 @@ public:
   gfx::IntRect GetPictureRect() const override { return mPictureRect; }
 
 private:
   const DXGIYCbCrTextureData* GetData() const;
 
   gfx::IntSize mYSize;
   gfx::IntSize mCbCrSize;
   gfx::IntRect mPictureRect;
-  uint32_t mBitDepth;
+  gfx::ColorDepth mColorDepth;
   YUVColorSpace mColorSpace;
   RefPtr<TextureClient> mTextureClient;
 };
 
 } // namepace layers
 } // namespace mozilla
 
 #endif // GFX_D3D11_YCBCR_IMAGE_H
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -157,26 +157,30 @@ struct EffectRGB : public TexturedEffect
     : TexturedEffect(EffectTypes::RGB, aTexture, aPremultiplied, aSamplingFilter)
   {}
 
   virtual const char* Name() override { return "EffectRGB"; }
 };
 
 struct EffectYCbCr : public TexturedEffect
 {
-  EffectYCbCr(TextureSource *aSource, YUVColorSpace aYUVColorSpace, uint32_t aBitDepth, gfx::SamplingFilter aSamplingFilter)
+  EffectYCbCr(TextureSource* aSource,
+              YUVColorSpace aYUVColorSpace,
+              gfx::ColorDepth aColorDepth,
+              gfx::SamplingFilter aSamplingFilter)
     : TexturedEffect(EffectTypes::YCBCR, aSource, false, aSamplingFilter)
     , mYUVColorSpace(aYUVColorSpace)
-    , mBitDepth(aBitDepth)
-  {}
+    , mColorDepth(aColorDepth)
+  {
+  }
 
   virtual const char* Name() override { return "EffectYCbCr"; }
 
   YUVColorSpace mYUVColorSpace;
-  uint32_t mBitDepth;
+  gfx::ColorDepth mColorDepth;
 };
 
 struct EffectNV12 : public TexturedEffect
 {
   EffectNV12(TextureSource *aSource, gfx::SamplingFilter aSamplingFilter)
     : TexturedEffect(EffectTypes::NV12, aSource, false, aSamplingFilter)
   {}
 
@@ -268,18 +272,20 @@ CreateTexturedEffect(TextureHost* aHost,
                      bool isAlphaPremultiplied)
 {
   MOZ_ASSERT(aHost);
   MOZ_ASSERT(aSource);
 
   RefPtr<TexturedEffect> result;
   if (aHost->GetReadFormat() == gfx::SurfaceFormat::YUV) {
     MOZ_ASSERT(aHost->GetYUVColorSpace() != YUVColorSpace::UNKNOWN);
-    result = new EffectYCbCr(
-      aSource, aHost->GetYUVColorSpace(), aHost->GetBitDepth(), aSamplingFilter);
+    result = new EffectYCbCr(aSource,
+                             aHost->GetYUVColorSpace(),
+                             aHost->GetColorDepth(),
+                             aSamplingFilter);
   } else {
     result = CreateTexturedEffect(aHost->GetReadFormat(),
                                   aSource,
                                   aSamplingFilter,
                                   isAlphaPremultiplied);
   }
   return result.forget();
 }
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -11,16 +11,17 @@
 #include <sys/types.h>                  // for int32_t
 #include "gfxTypes.h"
 #include "ImageTypes.h"                 // for ImageFormat, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Mutex.h"              // for Mutex
 #include "mozilla/RecursiveMutex.h"     // for RecursiveMutex, etc
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/gfx/Point.h"          // For IntSize
+#include "mozilla/gfx/Types.h"          // For ColorDepth
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend, etc
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr, nsAutoArrayPtr, etc
 #include "nsAutoRef.h"                  // for nsCountedRef
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Image::Release, etc
@@ -760,32 +761,32 @@ struct PlanarYCbCrData
   int32_t mCbSkip;
   int32_t mCrSkip;
   // Picture region
   uint32_t mPicX;
   uint32_t mPicY;
   gfx::IntSize mPicSize;
   StereoMode mStereoMode;
   YUVColorSpace mYUVColorSpace;
-  uint32_t mBitDepth;
+  gfx::ColorDepth mColorDepth;
 
   gfx::IntRect GetPictureRect() const
   {
     return gfx::IntRect(mPicX, mPicY,
                      mPicSize.width,
                      mPicSize.height);
   }
 
   PlanarYCbCrData()
     : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0)
     , mCbChannel(nullptr), mCrChannel(nullptr)
     , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0)
     , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO)
     , mYUVColorSpace(YUVColorSpace::BT601)
-    , mBitDepth(8)
+    , mColorDepth(gfx::ColorDepth::COLOR_8)
   {}
 };
 
 /****** Image subtypes for the different formats ******/
 
 /**
  * We assume that the image data is in the REC 470M color space (see
  * Theora specification, section 4.3.1).
--- a/gfx/layers/ImageDataSerializer.cpp
+++ b/gfx/layers/ImageDataSerializer.cpp
@@ -172,25 +172,25 @@ Maybe<YUVColorSpace> YUVColorSpaceFromBu
       return Nothing();
     case BufferDescriptor::TYCbCrDescriptor:
       return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
     default:
       MOZ_CRASH("GFX:  YUVColorSpaceFromBufferDescriptor");
   }
 }
 
-Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+Maybe<gfx::ColorDepth> ColorDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor)
 {
   switch (aDescriptor.type()) {
     case BufferDescriptor::TRGBDescriptor:
       return Nothing();
     case BufferDescriptor::TYCbCrDescriptor:
-      return Some(aDescriptor.get_YCbCrDescriptor().bitDepth());
+      return Some(aDescriptor.get_YCbCrDescriptor().colorDepth());
     default:
-      MOZ_CRASH("GFX:  BitDepthFromBufferDescriptor");
+      MOZ_CRASH("GFX:  ColorDepthFromBufferDescriptor");
   }
 }
 
 Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
 {
   switch (aDescriptor.type()) {
     case BufferDescriptor::TRGBDescriptor:
       return Nothing();
@@ -249,17 +249,17 @@ DataSourceSurfaceFromYCbCrDescriptor(uin
   ycbcrData.mYStride      = aDescriptor.yStride();
   ycbcrData.mYSize        = ySize;
   ycbcrData.mCbChannel    = GetCbChannel(aBuffer, aDescriptor);
   ycbcrData.mCrChannel    = GetCrChannel(aBuffer, aDescriptor);
   ycbcrData.mCbCrStride   = aDescriptor.cbCrStride();
   ycbcrData.mCbCrSize     = aDescriptor.cbCrSize();
   ycbcrData.mPicSize      = ySize;
   ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
-  ycbcrData.mBitDepth     = aDescriptor.bitDepth();
+  ycbcrData.mColorDepth   = aDescriptor.colorDepth();
 
   gfx::ConvertYCbCrToRGB(ycbcrData,
                          gfx::SurfaceFormat::B8G8R8X8,
                          ySize,
                          map.mData,
                          map.mStride);
 
   result->Unmap();
@@ -281,16 +281,16 @@ ConvertAndScaleFromYCbCrDescriptor(uint8
   ycbcrData.mYStride      = aDescriptor.yStride();;
   ycbcrData.mYSize        = aDescriptor.ySize();
   ycbcrData.mCbChannel    = GetCbChannel(aBuffer, aDescriptor);
   ycbcrData.mCrChannel    = GetCrChannel(aBuffer, aDescriptor);
   ycbcrData.mCbCrStride   = aDescriptor.cbCrStride();
   ycbcrData.mCbCrSize     = aDescriptor.cbCrSize();
   ycbcrData.mPicSize      = aDescriptor.ySize();
   ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
-  ycbcrData.mBitDepth     = aDescriptor.bitDepth();
+  ycbcrData.mColorDepth   = aDescriptor.colorDepth();
 
   gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride);
 }
 
 } // namespace ImageDataSerializer
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ImageDataSerializer.h
+++ b/gfx/layers/ImageDataSerializer.h
@@ -60,17 +60,17 @@ void ComputeYCbCrOffsets(int32_t yStride
 gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
-Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+Maybe<gfx::ColorDepth> ColorDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
 uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
 uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
--- a/gfx/layers/TextureSourceProvider.cpp
+++ b/gfx/layers/TextureSourceProvider.cpp
@@ -41,32 +41,25 @@ TextureSourceProvider::ReadUnlockTexture
   for (auto it = texturesIdsToUnlockByPid.ConstIter(); !it.Done(); it.Next()) {
     TextureSync::SetTexturesUnlocked(it.Key(), *it.UserData());
   }
 #else
   for (auto& texture : mUnlockAfterComposition) {
     texture->ReadUnlock();
   }
 #endif
-  mReferenceUntilAfterComposition.Clear();
   mUnlockAfterComposition.Clear();
 }
 
 void
 TextureSourceProvider::UnlockAfterComposition(TextureHost* aTexture)
 {
   mUnlockAfterComposition.AppendElement(aTexture);
 }
 
-void
-TextureSourceProvider::ReferenceUntilAfterComposition(DataTextureSource* aTextureSource)
-{
-  mReferenceUntilAfterComposition.AppendElement(aTextureSource);
-}
-
 bool
 TextureSourceProvider::NotifyNotUsedAfterComposition(TextureHost* aTextureHost)
 {
   mNotifyNotUsedAfterComposition.AppendElement(aTextureHost);
 
   // If Compositor holds many TextureHosts without compositing,
   // the TextureHosts should be flushed to reduce memory consumption.
   const int thresholdCount = 5;
--- a/gfx/layers/TextureSourceProvider.h
+++ b/gfx/layers/TextureSourceProvider.h
@@ -59,23 +59,16 @@ public:
   /// Most compositor backends operate asynchronously under the hood. This
   /// means that when a layer stops using a texture it is often desirable to
   /// wait for the end of the next composition before releasing the texture's
   /// ReadLock.
   /// This function provides a convenient way to do this delayed unlocking, if
   /// the texture itself requires it.
   virtual void UnlockAfterComposition(TextureHost* aTexture);
 
-  /// This is used for client storage support on OSX. On Nvidia hardware, it
-  /// seems that if we glDeleteTextures on a texture too early, even if we've
-  /// waited on the texture with glFinishObjectAPPLE, we'll see visual defects.
-  /// This just holds a reference to the texture until ReadUnlockTextures,
-  /// but we don't need to unlock it since we have already done so.
-  void ReferenceUntilAfterComposition(DataTextureSource* aTextureSource);
-
   /// Most compositor backends operate asynchronously under the hood. This
   /// means that when a layer stops using a texture it is often desirable to
   /// wait for the end of the next composition before NotifyNotUsed() call.
   /// This function provides a convenient way to do this delayed NotifyNotUsed()
   /// call, if the texture itself requires it.
   /// See bug 1260611 and bug 1252835
   ///
   /// Returns true if notified, false otherwise.
@@ -133,19 +126,16 @@ protected:
   void ReadUnlockTextures();
 
   virtual ~TextureSourceProvider();
 
 private:
   // An array of locks that will need to be unlocked after the next composition.
   nsTArray<RefPtr<TextureHost>> mUnlockAfterComposition;
 
-  // See ReferenceUntilAfterComposition.
-  nsTArray<RefPtr<DataTextureSource>> mReferenceUntilAfterComposition;
-
   // An array of TextureHosts that will need to call NotifyNotUsed() after the next composition.
   nsTArray<RefPtr<TextureHost>> mNotifyNotUsedAfterComposition;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_gfx_layers_TextureSourceProvider_h
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -100,18 +100,18 @@ ImageClient::CreateTextureClientForImage
     const PlanarYCbCrData* data = ycbcr->GetData();
     if (!data) {
       return nullptr;
     }
     texture = TextureClient::CreateForYCbCr(aForwarder,
                                             data->mYSize, data->mYStride,
                                             data->mCbCrSize, data->mCbCrStride,
                                             data->mStereoMode,
+                                            data->mColorDepth,
                                             data->mYUVColorSpace,
-                                            data->mBitDepth,
                                             TextureFlags::DEFAULT);
     if (!texture) {
       return nullptr;
     }
 
     TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
     if (!autoLock.Succeeded()) {
       return nullptr;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1300,34 +1300,37 @@ TextureClient::CreateForRawBufferAccess(
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator,
                               gfx::IntSize aYSize,
                               uint32_t aYStride,
                               gfx::IntSize aCbCrSize,
                               uint32_t aCbCrStride,
                               StereoMode aStereoMode,
+                              gfx::ColorDepth aColorDepth,
                               YUVColorSpace aYUVColorSpace,
-                              uint32_t aBitDepth,
                               TextureFlags aTextureFlags)
 {
   if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
     return nullptr;
   }
 
-  TextureData* data =
-    BufferTextureData::CreateForYCbCr(aAllocator,
-                                      aYSize, aYStride,
-                                      aCbCrSize, aCbCrStride,
-                                      aStereoMode, aYUVColorSpace,
-                                      aBitDepth, aTextureFlags);
+  TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator,
+                                                        aYSize,
+                                                        aYStride,
+                                                        aCbCrSize,
+                                                        aCbCrStride,
+                                                        aStereoMode,
+                                                        aColorDepth,
+                                                        aYUVColorSpace,
+                                                        aTextureFlags);
   if (!data) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags,
                                       aAllocator->GetTextureForwarder());
 }
 
@@ -1791,33 +1794,34 @@ UpdateYCbCrTextureClient(TextureClient* 
   MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
 
   MappedYCbCrTextureData mapped;
   if (!aTexture->BorrowMappedYCbCrData(mapped)) {
     NS_WARNING("Failed to extract YCbCr info!");
     return false;
   }
 
+  uint32_t bytesPerPixel =
+    BytesPerPixel(SurfaceFormatForColorDepth(aData.mColorDepth));
   MappedYCbCrTextureData srcData;
   srcData.y.data = aData.mYChannel;
   srcData.y.size = aData.mYSize;
   srcData.y.stride = aData.mYStride;
   srcData.y.skip = aData.mYSkip;
-  MOZ_ASSERT(aData.mBitDepth == 8 || (aData.mBitDepth > 8 && aData.mBitDepth <= 16));
-  srcData.y.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1;
+  srcData.y.bytesPerPixel = bytesPerPixel;
   srcData.cb.data = aData.mCbChannel;
   srcData.cb.size = aData.mCbCrSize;
   srcData.cb.stride = aData.mCbCrStride;
   srcData.cb.skip = aData.mCbSkip;
-  srcData.cb.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1;
+  srcData.cb.bytesPerPixel = bytesPerPixel;
   srcData.cr.data = aData.mCrChannel;
   srcData.cr.size = aData.mCbCrSize;
   srcData.cr.stride = aData.mCbCrStride;
   srcData.cr.skip = aData.mCrSkip;
-  srcData.cr.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1;
+  srcData.cr.bytesPerPixel = bytesPerPixel;
   srcData.metadata = nullptr;
 
   if (!srcData.CopyInto(mapped)) {
     NS_WARNING("Failed to copy image data!");
     return false;
   }
 
   if (TextureRequiresLocking(aTexture->GetFlags())) {
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -360,18 +360,18 @@ public:
   // Creates and allocates a TextureClient supporting the YCbCr format.
   static already_AddRefed<TextureClient>
   CreateForYCbCr(KnowsCompositor* aAllocator,
                  gfx::IntSize aYSize,
                  uint32_t aYStride,
                  gfx::IntSize aCbCrSize,
                  uint32_t aCbCrStride,
                  StereoMode aStereoMode,
+                 gfx::ColorDepth aColorDepth,
                  YUVColorSpace aYUVColorSpace,
-                 uint32_t aBitDepth,
                  TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient (can be accessed through raw
   // pointers).
   static already_AddRefed<TextureClient>
   CreateForRawBufferAccess(KnowsCompositor* aAllocator,
                            gfx::SurfaceFormat aFormat,
                            gfx::IntSize aSize,
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -104,34 +104,34 @@ YCbCrTextureClientAllocationHelper::IsCo
 
   BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
   if (!bufferData ||
       aTextureClient->GetSize() != mData.mYSize ||
       bufferData->GetCbCrSize().isNothing() ||
       bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
       bufferData->GetYUVColorSpace().isNothing() ||
       bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
-      bufferData->GetBitDepth().isNothing() ||
-      bufferData->GetBitDepth().ref() != mData.mBitDepth ||
+      bufferData->GetColorDepth().isNothing() ||
+      bufferData->GetColorDepth().ref() != mData.mColorDepth ||
       bufferData->GetStereoMode().isNothing() ||
       bufferData->GetStereoMode().ref() != mData.mStereoMode) {
     return false;
   }
   return true;
 }
 
 already_AddRefed<TextureClient>
 YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
 {
   return TextureClient::CreateForYCbCr(aAllocator,
                                        mData.mYSize, mData.mYStride,
                                        mData.mCbCrSize, mData.mCbCrStride,
                                        mData.mStereoMode,
+                                       mData.mColorDepth,
                                        mData.mYUVColorSpace,
-                                       mData.mBitDepth,
                                        mTextureFlags);
 }
 
 TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
   : mSurfaceAllocator(aAllocator)
   , mMaxPooledSize(kMaxPooledSized)
   , mLock("TextureClientRecycleAllocatorImp.mLock")
   , mIsDestroyed(false)
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -922,20 +922,16 @@ BufferTextureHost::MaybeNotifyUnlocked()
 
 void
 BufferTextureHost::UnbindTextureSource()
 {
   if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
     mFirstSource->Unbind();
   }
 
-  if (mFirstSource && mFirstSource->IsDirectMap() && mProvider) {
-    mProvider->ReferenceUntilAfterComposition(mFirstSource);
-  }
-
   // This texture is not used by any layer anymore.
   // If the texture doesn't have an intermediate buffer, it means we are
   // compositing synchronously on the CPU, so we don't need to wait until
   // the end of the next composition to ReadUnlock (which other textures do
   // by default).
   // If the texture has an intermediate buffer we don't care either because
   // texture uploads are also performed synchronously for BufferTextureHost.
   ReadUnlock();
@@ -963,24 +959,24 @@ BufferTextureHost::GetYUVColorSpace() co
 {
   if (mFormat == gfx::SurfaceFormat::YUV) {
     const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
     return desc.yUVColorSpace();
   }
   return YUVColorSpace::UNKNOWN;
 }
 
-uint32_t
-BufferTextureHost::GetBitDepth() const
+gfx::ColorDepth
+BufferTextureHost::GetColorDepth() const
 {
   if (mFormat == gfx::SurfaceFormat::YUV) {
     const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
-    return desc.bitDepth();
+    return desc.colorDepth();
   }
-  return 8;
+  return gfx::ColorDepth::COLOR_8;
 }
 
 bool
 BufferTextureHost::UploadIfNeeded()
 {
   return MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
 }
 
@@ -1083,27 +1079,27 @@ BufferTextureHost::Upload(nsIntRegion *a
       srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
       srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource();
     }
 
     RefPtr<gfx::DataSourceSurface> tempY =
       gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
                                                     desc.yStride(),
                                                     desc.ySize(),
-                                                    SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
+                                                    SurfaceFormatForColorDepth(desc.colorDepth()));
     RefPtr<gfx::DataSourceSurface> tempCb =
       gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
                                                     desc.cbCrStride(),
                                                     desc.cbCrSize(),
-                                                    SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
+                                                    SurfaceFormatForColorDepth(desc.colorDepth()));
     RefPtr<gfx::DataSourceSurface> tempCr =
       gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
                                                     desc.cbCrStride(),
                                                     desc.cbCrSize(),
-                                                    SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
+                                                    SurfaceFormatForColorDepth(desc.colorDepth()));
     // We don't support partial updates for Y U V textures
     NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
     if (!tempY ||
         !tempCb ||
         !tempCr ||
         !srcY->Update(tempY) ||
         !srcU->Update(tempCb) ||
         !srcV->Update(tempCr)) {
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -446,19 +446,19 @@ public:
    * Return the format used for reading the texture.
    * Apple's YCBCR_422 is R8G8B8X8.
    */
   virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); }
 
   virtual YUVColorSpace GetYUVColorSpace() const { return YUVColorSpace::UNKNOWN; }
 
   /**
-   * Return the bit depth of the image. Used with YUV textures.
+   * Return the color depth of the image. Used with YUV textures.
    */
-  virtual uint32_t GetBitDepth() const { return 8; }
+  virtual gfx::ColorDepth GetColorDepth() const { return gfx::ColorDepth::COLOR_8; }
 
   /**
    * Called during the transaction. The TextureSource may or may not be composited.
    *
    * Note that this is called outside of lock/unlock.
    */
   virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
 
@@ -752,17 +752,17 @@ public:
    *
    * If the shared format is YCbCr and the compositor does not support it,
    * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
    */
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual YUVColorSpace GetYUVColorSpace() const override;
 
-  virtual uint32_t GetBitDepth() const override;
+  virtual gfx::ColorDepth GetColorDepth() const override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
 
   virtual BufferTextureHost* AsBufferTextureHost() override { return this; }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -893,17 +893,17 @@ CompositorD3D11::DrawGeometry(const Geom
         return;
       }
 
       const float* yuvToRgb = gfxUtils::YuvToRgbMatrix4x3RowMajor(ycbcrEffect->mYUVColorSpace);
       memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb, sizeof(mPSConstants.yuvColorMatrix));
 
       // Adjust range according to the bit depth.
       mPSConstants.vCoefficient[0] =
-        RescalingFactorForAlphaBitDepth(ycbcrEffect->mBitDepth);
+        RescalingFactorForColorDepth(ycbcrEffect->mColorDepth);
 
       TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11();
       TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
       TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
 
       ID3D11ShaderResourceView* srViews[3] = { sourceY->GetShaderResourceView(),
                                                sourceCb->GetShaderResourceView(),
                                                sourceCr->GetShaderResourceView() };
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -621,17 +621,17 @@ DXGIYCbCrTextureData::Create(IDirect3DTe
                              IDirect3DTexture9* aTextureCb,
                              IDirect3DTexture9* aTextureCr,
                              HANDLE aHandleY,
                              HANDLE aHandleCb,
                              HANDLE aHandleCr,
                              const gfx::IntSize& aSize,
                              const gfx::IntSize& aSizeY,
                              const gfx::IntSize& aSizeCbCr,
-                             uint32_t aBitDepth,
+                             gfx::ColorDepth aColorDepth,
                              YUVColorSpace aYUVColorSpace)
 {
   if (!aHandleY || !aHandleCb || !aHandleCr ||
       !aTextureY || !aTextureCb || !aTextureCr) {
     return nullptr;
   }
 
   DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData();
@@ -639,30 +639,30 @@ DXGIYCbCrTextureData::Create(IDirect3DTe
   texture->mHandles[1] = aHandleCb;
   texture->mHandles[2] = aHandleCr;
   texture->mD3D9Textures[0] = aTextureY;
   texture->mD3D9Textures[1] = aTextureCb;
   texture->mD3D9Textures[2] = aTextureCr;
   texture->mSize = aSize;
   texture->mSizeY = aSizeY;
   texture->mSizeCbCr = aSizeCbCr;
-  texture->mBitDepth = aBitDepth;
+  texture->mColorDepth = aColorDepth;
   texture->mYUVColorSpace = aYUVColorSpace;
 
   return texture;
 }
 
 DXGIYCbCrTextureData*
 DXGIYCbCrTextureData::Create(ID3D11Texture2D* aTextureY,
                              ID3D11Texture2D* aTextureCb,
                              ID3D11Texture2D* aTextureCr,
                              const gfx::IntSize& aSize,
                              const gfx::IntSize& aSizeY,
                              const gfx::IntSize& aSizeCbCr,
-                             uint32_t aBitDepth,
+                             gfx::ColorDepth aColorDepth,
                              YUVColorSpace aYUVColorSpace)
 {
   if (!aTextureY || !aTextureCb || !aTextureCr) {
     return nullptr;
   }
 
   aTextureY->SetPrivateDataInterface(sD3D11TextureUsage,
     new TextureMemoryMeasurer(aSize.width * aSize.height));
@@ -701,17 +701,17 @@ DXGIYCbCrTextureData::Create(ID3D11Textu
   texture->mHandles[1] = handleCb;
   texture->mHandles[2] = handleCr;
   texture->mD3D11Textures[0] = aTextureY;
   texture->mD3D11Textures[1] = aTextureCb;
   texture->mD3D11Textures[2] = aTextureCr;
   texture->mSize = aSize;
   texture->mSizeY = aSizeY;
   texture->mSizeCbCr = aSizeCbCr;
-  texture->mBitDepth = aBitDepth;
+  texture->mColorDepth = aColorDepth;
   texture->mYUVColorSpace = aYUVColorSpace;
 
   return texture;
 }
 
 void
 DXGIYCbCrTextureData::FillInfo(TextureData::Info& aInfo) const
 {
@@ -720,20 +720,24 @@ DXGIYCbCrTextureData::FillInfo(TextureDa
   aInfo.supportsMoz2D = false;
   aInfo.hasIntermediateBuffer = false;
   aInfo.hasSynchronization = false;
 }
 
 void
 DXGIYCbCrTextureData::SerializeSpecific(SurfaceDescriptorDXGIYCbCr* const aOutDesc)
 {
-  *aOutDesc = SurfaceDescriptorDXGIYCbCr(
-    (WindowsHandle)mHandles[0], (WindowsHandle)mHandles[1], (WindowsHandle)mHandles[2],
-    mSize, mSizeY, mSizeCbCr, mBitDepth, mYUVColorSpace
-  );
+  *aOutDesc = SurfaceDescriptorDXGIYCbCr((WindowsHandle)mHandles[0],
+                                         (WindowsHandle)mHandles[1],
+                                         (WindowsHandle)mHandles[2],
+                                         mSize,
+                                         mSizeY,
+                                         mSizeCbCr,
+                                         mColorDepth,
+                                         mYUVColorSpace);
 }
 
 bool
 DXGIYCbCrTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
 {
   SurfaceDescriptorDXGIYCbCr desc;
   SerializeSpecific(&desc);
 
@@ -1171,17 +1175,17 @@ DXGITextureHostD3D11::PushDisplayItems(w
 }
 
 DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
   const SurfaceDescriptorDXGIYCbCr& aDescriptor)
   : TextureHost(aFlags)
   , mSize(aDescriptor.size())
   , mSizeCbCr(aDescriptor.sizeCbCr())
   , mIsLocked(false)
-  , mBitDepth(aDescriptor.bitDepth())
+  , mColorDepth(aDescriptor.colorDepth())
   , mYUVColorSpace(aDescriptor.yUVColorSpace())
 {
   mHandles[0] = aDescriptor.handleY();
   mHandles[1] = aDescriptor.handleCb();
   mHandles[2] = aDescriptor.handleCr();
 }
 
 bool
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -140,27 +140,27 @@ public:
          IDirect3DTexture9* aTextureCb,
          IDirect3DTexture9* aTextureCr,
          HANDLE aHandleY,
          HANDLE aHandleCb,
          HANDLE aHandleCr,
          const gfx::IntSize& aSize,
          const gfx::IntSize& aSizeY,
          const gfx::IntSize& aSizeCbCr,
-         uint32_t aBitDepth,
+         gfx::ColorDepth aColorDepth,
          YUVColorSpace aYUVColorSpace);
 
   static DXGIYCbCrTextureData*
   Create(ID3D11Texture2D* aTextureCb,
          ID3D11Texture2D* aTextureY,
          ID3D11Texture2D* aTextureCr,
          const gfx::IntSize& aSize,
          const gfx::IntSize& aSizeY,
          const gfx::IntSize& aSizeCbCr,
-         uint32_t aBitDepth,
+         gfx::ColorDepth aColorDepth,
          YUVColorSpace aYUVColorSpace);
 
   virtual bool Lock(OpenMode) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual void FillInfo(TextureData::Info& aInfo) const override;
 
@@ -188,36 +188,36 @@ public:
     return mSizeY;
   }
 
   gfx::IntSize GetCbCrSize() const
   {
     return mSizeCbCr;
   }
 
-  uint32_t GetBitDepth() const
+  gfx::ColorDepth GetColorDepth() const
   {
-    return mBitDepth;
+    return mColorDepth;
   }
 
   YUVColorSpace GetYUVColorSpace() const
   {
     return mYUVColorSpace;
   }
 
   ID3D11Texture2D* GetD3D11Texture(size_t index) { return mD3D11Textures[index]; }
 
 protected:
    RefPtr<ID3D11Texture2D> mD3D11Textures[3];
    RefPtr<IDirect3DTexture9> mD3D9Textures[3];
    HANDLE mHandles[3];
    gfx::IntSize mSize;
    gfx::IntSize mSizeY;
    gfx::IntSize mSizeCbCr;
-   uint32_t mBitDepth;
+   gfx::ColorDepth mColorDepth;
    YUVColorSpace mYUVColorSpace;
 };
 
 /**
  * TextureSource that provides with the necessary APIs to be composited by a
  * CompositorD3D11.
  */
 class TextureSourceD3D11
@@ -410,19 +410,19 @@ public:
   virtual bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) override;
 
   virtual void DeallocateDeviceData() override{}
 
   virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
 
   virtual gfx::SurfaceFormat GetFormat() const override{ return gfx::SurfaceFormat::YUV; }
 
-  virtual YUVColorSpace GetYUVColorSpace() const override { return mYUVColorSpace; }
+  virtual gfx::ColorDepth GetColorDepth() const override { return mColorDepth; }
 
-  virtual uint32_t GetBitDepth() const override { return mBitDepth; }
+  virtual YUVColorSpace GetYUVColorSpace() const override { return mYUVColorSpace; }
 
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
@@ -457,17 +457,17 @@ protected:
 
   RefPtr<ID3D11Texture2D> mTextures[3];
   RefPtr<DataTextureSourceD3D11> mTextureSources[3];
 
   gfx::IntSize mSize;
   gfx::IntSize mSizeCbCr;
   WindowsHandle mHandles[3];
   bool mIsLocked;
-  uint32_t mBitDepth;
+  gfx::ColorDepth mColorDepth;
   YUVColorSpace mYUVColorSpace;
 };
 
 class CompositingRenderTargetD3D11 : public CompositingRenderTarget,
                                      public TextureSourceD3D11
 {
 public:
   CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -4,16 +4,17 @@
 
 using struct gfxPoint from "gfxPoint.h";
 using nsIntRegion from "nsRegion.h";
 using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h";
 using mozilla::StereoMode from "ImageTypes.h";
 using mozilla::YUVColorSpace from "ImageTypes.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using gfxImageFormat from "gfxTypes.h";
 
 namespace mozilla {
 namespace layers {
@@ -47,17 +48,17 @@ struct SurfaceDescriptorD3D10 {
 
 struct SurfaceDescriptorDXGIYCbCr {
   WindowsHandle handleY;
   WindowsHandle handleCb;
   WindowsHandle handleCr;
   IntSize size;
   IntSize sizeY;
   IntSize sizeCbCr;
-  uint32_t bitDepth;
+  ColorDepth colorDepth;
   YUVColorSpace yUVColorSpace;
 };
 
 struct SurfaceDescriptorMacIOSurface {
   uint32_t surfaceId;
   double scaleFactor;
   bool isOpaque;
 };
@@ -107,18 +108,18 @@ struct YCbCrDescriptor {
   IntSize ySize;
   uint32_t yStride;
   IntSize cbCrSize;
   uint32_t cbCrStride;
   uint32_t yOffset;
   uint32_t cbOffset;
   uint32_t crOffset;
   StereoMode stereoMode;
+  ColorDepth colorDepth;
   YUVColorSpace yUVColorSpace;
-  uint32_t bitDepth;
   bool hasIntermediateBuffer;
 };
 
 union BufferDescriptor {
   RGBDescriptor;
   YCbCrDescriptor;
 };
 
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -119,27 +119,27 @@ SharedPlanarYCbCrImage::AdoptData(const 
     std::max(aData.mYSize.width,
              std::max(aData.mYSize.height,
                       std::max(aData.mCbCrSize.width, aData.mCbCrSize.height))) <= fwd->GetMaxTextureSize();
   bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(
     gfx::SurfaceFormat::YUV, fwd->GetCompositorBackendType(),
     supportsTextureDirectMapping);
 
   static_cast<BufferTextureData*>(mTextureClient->GetInternalData())
-    ->SetDesciptor(YCbCrDescriptor(aData.mYSize,
-                                   aData.mYStride,
-                                   aData.mCbCrSize,
-                                   aData.mCbCrStride,
-                                   yOffset,
-                                   cbOffset,
-                                   crOffset,
-                                   aData.mStereoMode,
-                                   aData.mYUVColorSpace,
-                                   aData.mBitDepth,
-                                   hasIntermediateBuffer));
+    ->SetDescriptor(YCbCrDescriptor(aData.mYSize,
+                                    aData.mYStride,
+                                    aData.mCbCrSize,
+                                    aData.mCbCrStride,
+                                    yOffset,
+                                    cbOffset,
+                                    crOffset,
+                                    aData.mStereoMode,
+                                    aData.mColorDepth,
+                                    aData.mYUVColorSpace,
+                                    hasIntermediateBuffer));
 
   return true;
 }
 
 bool
 SharedPlanarYCbCrImage::IsValid() const
 {
   return mTextureClient && mTextureClient->IsValid();
@@ -186,17 +186,17 @@ SharedPlanarYCbCrImage::Allocate(PlanarY
   mData.mCrChannel = aData.mCrChannel;
   mData.mYSize = aData.mYSize;
   mData.mCbCrSize = aData.mCbCrSize;
   mData.mPicX = aData.mPicX;
   mData.mPicY = aData.mPicY;
   mData.mPicSize = aData.mPicSize;
   mData.mStereoMode = aData.mStereoMode;
   mData.mYUVColorSpace = aData.mYUVColorSpace;
-  mData.mBitDepth = aData.mBitDepth;
+  mData.mColorDepth = aData.mColorDepth;
   // those members are not always equal to aData's, due to potentially different
   // packing.
   mData.mYSkip = 0;
   mData.mCbSkip = 0;
   mData.mCrSkip = 0;
   mData.mYStride = aData.mYStride;
   mData.mCbCrStride = aData.mCbCrStride;
 
--- a/gfx/layers/mlgpu/MLGDevice.cpp
+++ b/gfx/layers/mlgpu/MLGDevice.cpp
@@ -290,33 +290,32 @@ MLGDevice::GetBufferForColorSpace(YUVCol
     return nullptr;
   }
 
   mColorSpaceBuffers[aColorSpace] = resource;
   return resource;
 }
 
 RefPtr<MLGBuffer>
-MLGDevice::GetBufferForBitDepthCoefficient(uint8_t aBitDepth)
+MLGDevice::GetBufferForColorDepthCoefficient(ColorDepth aColorDepth)
 {
-  ColorDepth depth = ColorDepthForAlphaBitDepth(aBitDepth);
-  if (mColorDepthBuffers[depth]) {
-    return mColorDepthBuffers[depth];
+  if (mColorDepthBuffers[aColorDepth]) {
+    return mColorDepthBuffers[aColorDepth];
   }
 
-  YCbCrBitDepthConstants buffer;
-  buffer.coefficient = gfx::RescalingFactorForAlphaBitDepth(aBitDepth);
+  YCbCrColorDepthConstants buffer;
+  buffer.coefficient = gfx::RescalingFactorForColorDepth(aColorDepth);
 
   RefPtr<MLGBuffer> resource = CreateBuffer(
     MLGBufferType::Constant, sizeof(buffer), MLGUsage::Immutable, &buffer);
   if (!resource) {
     return nullptr;
   }
 
-  mColorDepthBuffers[depth] = resource;
+  mColorDepthBuffers[aColorDepth] = resource;
   return resource;
 }
 
 bool
 MLGDevice::Synchronize()
 {
   return true;
 }
--- a/gfx/layers/mlgpu/MLGDevice.h
+++ b/gfx/layers/mlgpu/MLGDevice.h
@@ -357,17 +357,18 @@ public:
   void SetPSTexture(uint32_t aSlot, TextureSource* aSource);
   void SetSamplerMode(uint32_t aIndex, gfx::SamplingFilter aFilter);
 
   // This creates or returns a previously created constant buffer, containing
   // a YCbCrShaderConstants instance.
   RefPtr<MLGBuffer> GetBufferForColorSpace(YUVColorSpace aColorSpace);
   // This creates or returns a previously created constant buffer, containing
   // a YCbCrBitDepthConstants instance.
-  RefPtr<MLGBuffer> GetBufferForBitDepthCoefficient(uint8_t aBitDepth);
+  RefPtr<MLGBuffer> GetBufferForColorDepthCoefficient(
+    gfx::ColorDepth aColorDepth);
 
   // A shared buffer that can be used to build VertexBufferSections.
   SharedVertexBuffer* GetSharedVertexBuffer() {
     return mSharedVertexBuffer.get();
   }
   // A shared buffer that can be used to build ConstantBufferSections. Intended
   // to be used with vertex shaders.
   SharedConstantBuffer* GetSharedVSBuffer() {
@@ -489,17 +490,17 @@ private:
   UniquePtr<BufferCache> mConstantBufferCache;
 
   nsCString mFailureId;
   nsCString mFailureMessage;
   bool mInitialized;
 
   typedef EnumeratedArray<YUVColorSpace, YUVColorSpace::UNKNOWN, RefPtr<MLGBuffer>> ColorSpaceArray;
   ColorSpaceArray mColorSpaceBuffers;
-  typedef EnumeratedArray<gfx::ColorDepth, gfx::ColorDepth::MAX, RefPtr<MLGBuffer>> ColorDepthArray;
+  typedef EnumeratedArray<gfx::ColorDepth, gfx::ColorDepth::UNKNOWN, RefPtr<MLGBuffer>> ColorDepthArray;
   ColorDepthArray mColorDepthBuffers;
 
 protected:
   bool mIsValid;
   bool mCanUseClearView;
   bool mCanUseConstantBufferOffsetBinding;
   size_t mMaxConstantBufferBindSize;
 
--- a/gfx/layers/mlgpu/RenderPassMLGPU.cpp
+++ b/gfx/layers/mlgpu/RenderPassMLGPU.cpp
@@ -814,17 +814,17 @@ VideoRenderPass::SetupPipeline()
   MOZ_ASSERT(colorSpace != YUVColorSpace::UNKNOWN);
 
   RefPtr<MLGBuffer> ps1 = mDevice->GetBufferForColorSpace(colorSpace);
   if (!ps1) {
     return;
   }
 
   RefPtr<MLGBuffer> ps2 =
-    mDevice->GetBufferForBitDepthCoefficient(mHost->GetBitDepth());
+    mDevice->GetBufferForColorDepthCoefficient(mHost->GetColorDepth());
   if (!ps2) {
     return;
   }
 
   if (mGeometry == GeometryMode::UnitQuad) {
     mDevice->SetVertexShader(VertexShaderID::TexturedQuad);
   } else {
     mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
--- a/gfx/layers/mlgpu/ShaderDefinitionsMLGPU.h
+++ b/gfx/layers/mlgpu/ShaderDefinitionsMLGPU.h
@@ -87,17 +87,17 @@ struct MaskInformation
   uint32_t hasMask;
   uint32_t padding[2];
 };
 
 struct YCbCrShaderConstants {
   float yuvColorMatrix[3][4];
 };
 
-struct YCbCrBitDepthConstants {
+struct YCbCrColorDepthConstants {
   float coefficient;
   uint32_t padding[3];
 };
 
 struct BlendVertexShaderConstants {
   float backdropTransform[4][4];
 };
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -983,17 +983,17 @@ CompositorOGL::GetShaderConfigFor(Effect
   case EffectTypes::SOLID_COLOR:
     config.SetRenderColor(true);
     break;
   case EffectTypes::YCBCR:
   {
     config.SetYCbCr(true);
     EffectYCbCr* effectYCbCr = static_cast<EffectYCbCr*>(aEffect);
     config.SetColorMultiplier(
-      RescalingFactorForAlphaBitDepth(effectYCbCr->mBitDepth));
+      RescalingFactorForColorDepth(effectYCbCr->mColorDepth));
     config.SetTextureTarget(
       effectYCbCr->mTexture->AsSourceOGL()->GetTextureTarget());
     break;
   }
   case EffectTypes::NV12:
     config.SetNV12(true);
     config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
     break;
@@ -1481,16 +1481,17 @@ CompositorOGL::DrawGeometry(const Geomet
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
       }
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
       }
 
       BindAndDrawGeometryWithTextureRect(program, aGeometry,
                                          texturedEffect->mTextureCoords, source);
+      source->AsSourceOGL()->MaybeFenceTexture();
     }
     break;
   case EffectTypes::YCBCR: {
       EffectYCbCr* effectYCbCr =
         static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
       TextureSource* sourceYCbCr = effectYCbCr->mTexture;
       const int Y = 0, Cb = 1, Cr = 2;
       TextureSourceOGL* sourceY =  sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
@@ -1521,16 +1522,19 @@ CompositorOGL::DrawGeometry(const Geomet
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
       }
       didSetBlendMode = SetBlendMode(gl(), blendMode);
       BindAndDrawGeometryWithTextureRect(program,
                                          aGeometry,
                                          effectYCbCr->mTextureCoords,
                                          sourceYCbCr->GetSubSource(Y));
+      sourceY->MaybeFenceTexture();
+      sourceCb->MaybeFenceTexture();
+      sourceCr->MaybeFenceTexture();
     }
     break;
   case EffectTypes::NV12: {
       EffectNV12* effectNV12 =
         static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
       TextureSource* sourceNV12 = effectNV12->mTexture;
       const int Y = 0, CbCr = 1;
       TextureSourceOGL* sourceY =  sourceNV12->GetSubSource(Y)->AsSourceOGL();
@@ -1558,16 +1562,18 @@ CompositorOGL::DrawGeometry(const Geomet
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
       }
       didSetBlendMode = SetBlendMode(gl(), blendMode);
       BindAndDrawGeometryWithTextureRect(program,
                                          aGeometry,
                                          effectNV12->mTextureCoords,
                                          sourceNV12->GetSubSource(Y));
+      sourceY->MaybeFenceTexture();
+      sourceCbCr->MaybeFenceTexture();
     }
     break;
   case EffectTypes::RENDER_TARGET: {
       EffectRenderTarget* effectRenderTarget =
         static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
       RefPtr<CompositingRenderTargetOGL> surface
         = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get());
 
@@ -1640,16 +1646,19 @@ CompositorOGL::DrawGeometry(const Geomet
       program->SetTexturePass2(true);
       BindAndDrawGeometryWithTextureRect(program,
                                          aGeometry,
                                          effectComponentAlpha->mTextureCoords,
                                          effectComponentAlpha->mOnBlack);
 
       mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                      LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+
+      sourceOnBlack->MaybeFenceTexture();
+      sourceOnWhite->MaybeFenceTexture();
     }
     break;
   default:
     MOZ_ASSERT(false, "Unhandled effect type");
     break;
   }
 
   if (didSetBlendMode) {
@@ -1932,33 +1941,33 @@ CompositorOGL::CreateDataTextureSourceAr
   uint8_t* buf = bufferTexture->GetBuffer();
   const BufferDescriptor& buffDesc = bufferTexture->GetBufferDescriptor();
   const YCbCrDescriptor& desc = buffDesc.get_YCbCrDescriptor();
 
   RefPtr<gfx::DataSourceSurface> tempY =
     gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
                                                   desc.yStride(),
                                                   desc.ySize(),
-                                                  SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
+                                                  SurfaceFormatForColorDepth(desc.colorDepth()));
   if (!tempY) {
     return nullptr;
   }
   RefPtr<gfx::DataSourceSurface> tempCb =
     gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
                                                   desc.cbCrStride(),
                                                   desc.cbCrSize(),
-                                                  SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
+                                                  SurfaceFormatForColorDepth(desc.colorDepth()));
   if (!tempCb) {
     return nullptr;
   }
   RefPtr<gfx::DataSourceSurface> tempCr =
     gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
                                                   desc.cbCrStride(),
                                                   desc.cbCrSize(),
-                                                  SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
+                                                  SurfaceFormatForColorDepth(desc.colorDepth()));
   if (!tempCr) {
     return nullptr;
   }
 
   RefPtr<DirectMapTextureSource> srcY = new DirectMapTextureSource(this, tempY);
   RefPtr<DirectMapTextureSource> srcU = new DirectMapTextureSource(this, tempCb);
   RefPtr<DirectMapTextureSource> srcV = new DirectMapTextureSource(this, tempCr);
 
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -314,53 +314,81 @@ GLTextureSource::IsValid() const
 
 DirectMapTextureSource::DirectMapTextureSource(TextureSourceProvider* aProvider,
                                                gfx::DataSourceSurface* aSurface)
   : GLTextureSource(aProvider,
                     0,
                     LOCAL_GL_TEXTURE_RECTANGLE_ARB,
                     aSurface->GetSize(),
                     aSurface->GetFormat())
+  , mSync(0)
 {
   MOZ_ASSERT(aSurface);
 
   UpdateInternal(aSurface, nullptr, nullptr, true);
 }
 
+DirectMapTextureSource::~DirectMapTextureSource()
+{
+  if (!mSync || !gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) {
+    return;
+  }
+
+  gl()->fDeleteSync(mSync);
+  mSync = 0;
+}
+
 bool
 DirectMapTextureSource::Update(gfx::DataSourceSurface* aSurface,
                                nsIntRegion* aDestRegion,
                                gfx::IntPoint* aSrcOffset)
 {
   if (!aSurface) {
     return false;
   }
 
   return UpdateInternal(aSurface, aDestRegion, aSrcOffset, false);
 }
 
+void
+DirectMapTextureSource::MaybeFenceTexture()
+{
+  if (!gl() ||
+      !gl()->MakeCurrent() ||
+      gl()->IsDestroyed()) {
+    return;
+  }
+
+  if (mSync) {
+    gl()->fDeleteSync(mSync);
+  }
+  mSync = gl()->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+
 bool
 DirectMapTextureSource::Sync(bool aBlocking)
 {
-  if (!gl() || !gl()->MakeCurrent()) {
+  if (!gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) {
     // We use this function to decide whether we can unlock the texture
     // and clean it up. If we return false here and for whatever reason
     // the context is absent or invalid, the compositor will keep a
     // reference to this texture forever.
     return true;
   }
 
-  if (!gl()->IsDestroyed()) {
-    if (aBlocking) {
-      gl()->fFinishObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle);
-    } else {
-      return gl()->fTestObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle);
-    }
+  if (!mSync) {
+    return false;
   }
-  return true;
+
+  GLenum waitResult = gl()->fClientWaitSync(mSync,
+                                            LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT,
+                                            aBlocking ? LOCAL_GL_TIMEOUT_IGNORED : 0);
+  return waitResult == LOCAL_GL_ALREADY_SIGNALED ||
+         waitResult == LOCAL_GL_CONDITION_SATISFIED; 
 }
 
 bool
 DirectMapTextureSource::UpdateInternal(gfx::DataSourceSurface* aSurface,
                                        nsIntRegion* aDestRegion,
                                        gfx::IntPoint* aSrcOffset,
                                        bool aInit)
 {
@@ -404,16 +432,21 @@ DirectMapTextureSource::UpdateInternal(g
                                        mTextureHandle,
                                        aSurface->GetSize(),
                                        nullptr,
                                        aInit,
                                        srcPoint,
                                        LOCAL_GL_TEXTURE0,
                                        LOCAL_GL_TEXTURE_RECTANGLE_ARB);
 
+  if (mSync) {
+    gl()->fDeleteSync(mSync);
+    mSync = 0;
+  }
+
   gl()->fPixelStorei(LOCAL_GL_UNPACK_CLIENT_STORAGE_APPLE, LOCAL_GL_FALSE);
   return true;
 }
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 // SurfaceTextureHost
 
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -87,16 +87,24 @@ public:
     , mHasCachedSamplingFilter(false)
   {}
 
   virtual bool IsValid() const = 0;
 
   virtual void BindTexture(GLenum aTextureUnit,
                            gfx::SamplingFilter aSamplingFilter) = 0;
 
+  // To be overridden in textures that need this. This method will be called
+  // when the compositor has used the texture to draw. This allows us to set
+  // a fence with glFenceSync which we can wait on later to ensure the GPU
+  // is done with the draw calls using that texture. We would like to be able
+  // to simply use glFinishObjectAPPLE, but this returns earlier than
+  // expected with nvidia drivers.
+  virtual void MaybeFenceTexture() {}
+
   virtual gfx::IntSize GetSize() const = 0;
 
   virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_2D; }
 
   virtual gfx::SurfaceFormat GetFormat() const = 0;
 
   virtual GLenum GetWrapMode() const = 0;
 
@@ -293,33 +301,38 @@ protected:
 // should be alive until the ~ClientStorageTextureSource(). And if we try to
 // update the surface we mapped before, we need to call Sync() to make sure
 // the surface is not used by compositor.
 class DirectMapTextureSource : public GLTextureSource
 {
 public:
   DirectMapTextureSource(TextureSourceProvider* aProvider,
                          gfx::DataSourceSurface* aSurface);
+  ~DirectMapTextureSource();
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   virtual bool IsDirectMap() override { return true; }
 
   // If aBlocking is false, check if this texture is no longer being used
   // by the GPU - if aBlocking is true, this will block until the GPU is
   // done with it.
   virtual bool Sync(bool aBlocking) override;
 
+  virtual void MaybeFenceTexture() override;
+
 private:
   bool UpdateInternal(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion,
                       gfx::IntPoint* aSrcOffset,
                       bool aInit);
+
+  GLsync mSync;
 };
 
 class GLTextureHost : public TextureHost
 {
 public:
   GLTextureHost(TextureFlags aFlags,
                 GLuint aTextureHandle,
                 GLenum aTarget,
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -376,17 +376,17 @@ AsyncImagePipelineManager::ApplyAsyncIma
 
   aPipeline->mIsChanged = false;
 
   wr::LayoutSize contentSize { aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height() };
   wr::DisplayListBuilder builder(aPipelineId, contentSize);
 
   float opacity = 1.0f;
   Maybe<wr::WrClipId> referenceFrameId = builder.PushStackingContext(
-    wr::ToLayoutRect(aPipeline->mScBounds),
+    wr::ToRoundedLayoutRect(aPipeline->mScBounds),
     nullptr,
     nullptr,
     &opacity,
     aPipeline->mScTransform.IsIdentity() ? nullptr : &aPipeline->mScTransform,
     wr::TransformStyle::Flat,
     nullptr,
     aPipeline->mMixBlendMode,
     nsTArray<wr::WrFilterOp>(),
@@ -399,25 +399,25 @@ AsyncImagePipelineManager::ApplyAsyncIma
     if (aPipeline->mScaleToSize.isSome()) {
       rect = LayoutDeviceRect(0, 0, aPipeline->mScaleToSize.value().width, aPipeline->mScaleToSize.value().height);
     }
 
     if (aPipeline->mUseExternalImage) {
       MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
       Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
       aPipeline->mCurrentTexture->PushDisplayItems(builder,
-                                                  wr::ToLayoutRect(rect),
-                                                  wr::ToLayoutRect(rect),
+                                                  wr::ToRoundedLayoutRect(rect),
+                                                  wr::ToRoundedLayoutRect(rect),
                                                   aPipeline->mFilter,
                                                   range_keys);
       HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture->AsWebRenderTextureHost());
     } else {
       MOZ_ASSERT(keys.Length() == 1);
-      builder.PushImage(wr::ToLayoutRect(rect),
-                        wr::ToLayoutRect(rect),
+      builder.PushImage(wr::ToRoundedLayoutRect(rect),
+                        wr::ToRoundedLayoutRect(rect),
                         true,
                         aPipeline->mFilter,
                         keys[0]);
     }
   }
 
   builder.PopStackingContext(referenceFrameId.isSome());
 
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -669,35 +669,35 @@ struct DIGroup
     if (empty) {
       ClearImageKey(aWrManager, true);
       return;
     }
 
     PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
 
     // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);?
-    bool isOpaque = false;
+    wr::OpacityType opacity = wr::OpacityType::HasAlphaChannel;
 
     TakeExternalSurfaces(recorder, mExternalSurfaces, aWrManager, aResources);
     bool hasItems = recorder->Finish();
     GP("%d Finish\n", hasItems);
     Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
     if (!mKey) {
       if (!hasItems) // we don't want to send a new image that doesn't have any items in it
         return;
       wr::ImageKey key = aWrManager->WrBridge()->GetNextImageKey();
       GP("No previous key making new one %d\n", key.mHandle);
-      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), isOpaque);
+      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t));
       if (!aResources.AddBlobImage(key, descriptor, bytes)) {
         return;
       }
       mKey = Some(key);
     } else {
-      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), isOpaque);
+      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       auto bottomRight = mInvalidRect.BottomRight();
       GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, dtSize.width, dtSize.height);
       MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width && bottomRight.y <= dtSize.height);
       GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
       if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
         return;
       }
     }
@@ -1886,17 +1886,21 @@ WebRenderCommandBuilder::GenerateFallbac
     nsAutoPtr<nsDisplayItemGeometry> newGeometry;
     newGeometry = aItem->AllocateGeometry(aDisplayListBuilder);
     fallbackData->SetGeometry(std::move(newGeometry));
 
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
                                                       gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
     if (useBlobImage) {
       bool snapped;
-      bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);
+      wr::OpacityType opacity =
+        aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped)
+            .Contains(paintBounds)
+          ? wr::OpacityType::Opaque
+          : wr::OpacityType::HasAlphaChannel;
       std::vector<RefPtr<ScaledFont>> fonts;
 
       RefPtr<WebRenderDrawEventRecorder> recorder =
         MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts) {
           size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto& scaled : aScaledFonts) {
             BlobFont font = {
@@ -1917,17 +1921,17 @@ WebRenderCommandBuilder::GenerateFallbac
                                                  fallbackData->mBasicLayerManager, scale, highlight);
       recorder->FlushItem(IntRect(0, 0, paintSize.width, paintSize.height));
       TakeExternalSurfaces(recorder, fallbackData->mExternalSurfaces, mManager, aResources);
       recorder->Finish();
 
       if (isInvalidated) {
         Range<uint8_t> bytes((uint8_t *)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
         wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
-        wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque);
+        wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), opacity);
         if (!aResources.AddBlobImage(key, descriptor, bytes)) {
           return nullptr;
         }
         fallbackData->SetKey(key);
         fallbackData->SetFonts(fonts);
       } else {
         // If there is no invalidation region and we don't have a image key,
         // it means we don't need to push image for the item.
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -44,27 +44,27 @@ struct ParamTraits<mozilla::wr::ImageDes
 
   static void
   Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.format);
     WriteParam(aMsg, aParam.width);
     WriteParam(aMsg, aParam.height);
     WriteParam(aMsg, aParam.stride);
-    WriteParam(aMsg, aParam.is_opaque);
+    WriteParam(aMsg, aParam.opacity);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->format)
         && ReadParam(aMsg, aIter, &aResult->width)
         && ReadParam(aMsg, aIter, &aResult->height)
         && ReadParam(aMsg, aIter, &aResult->stride)
-        && ReadParam(aMsg, aIter, &aResult->is_opaque);
+        && ReadParam(aMsg, aIter, &aResult->opacity);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::IdNamespace>
   : public PlainOldDataSerializer<mozilla::wr::IdNamespace>
 {
 };
@@ -178,11 +178,17 @@ struct ParamTraits<mozilla::wr::WebRende
 };
 
 template<>
 struct ParamTraits<mozilla::wr::MemoryReport>
   : public PlainOldDataSerializer<mozilla::wr::MemoryReport>
 {
 };
 
+template<>
+struct ParamTraits<mozilla::wr::OpacityType>
+  : public PlainOldDataSerializer<mozilla::wr::OpacityType>
+{
+};
+
 } // namespace IPC
 
 #endif // GFX_WEBRENDERMESSAGEUTILS_H
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -261,17 +261,17 @@ TEST(Layers, TextureYCbCrSerialization) 
   clientData.mCrChannel = crSurface->Data();
   clientData.mYSize = ySurface->GetSize();
   clientData.mPicSize = ySurface->GetSize();
   clientData.mCbCrSize = cbSurface->GetSize();
   clientData.mYStride = ySurface->Stride();
   clientData.mCbCrStride = cbSurface->Stride();
   clientData.mStereoMode = StereoMode::MONO;
   clientData.mYUVColorSpace = YUVColorSpace::BT601;
-  clientData.mBitDepth = 8;
+  clientData.mColorDepth = ColorDepth::COLOR_8;
   clientData.mYSkip = 0;
   clientData.mCbSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mPicX = 0;
   clientData.mPicX = 0;
 
   uint32_t namespaceId = 1;
@@ -290,16 +290,23 @@ TEST(Layers, TextureYCbCrSerialization) 
     retry--;
   }
 
   // Skip this testing if IPDL connection is not ready
   if (!retry && !imageBridge->IPCOpen()) {
     return;
   }
 
-  RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(imageBridge, clientData.mYSize, clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride,
-                                                               StereoMode::MONO, YUVColorSpace::BT601,
-                                                               8, TextureFlags::DEALLOCATE_CLIENT);
+  RefPtr<TextureClient> client =
+    TextureClient::CreateForYCbCr(imageBridge,
+                                  clientData.mYSize,
+                                  clientData.mYStride,
+                                  clientData.mCbCrSize,
+                                  clientData.mCbCrStride,
+                                  StereoMode::MONO,
+                                  gfx::ColorDepth::COLOR_8,
+                                  YUVColorSpace::BT601,
+                                  TextureFlags::DEALLOCATE_CLIENT);
 
   TestTextureClientYCbCr(client, clientData);
 
   // XXX - Test more texture client types.
 }
--- a/gfx/tests/gtest/TextureHelper.h
+++ b/gfx/tests/gtest/TextureHelper.h
@@ -55,26 +55,26 @@ CreateYCbCrTextureClientWithBackend(Laye
   clientData.mStereoMode = StereoMode::MONO;
   clientData.mYSkip = 0;
   clientData.mCbSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mPicX = 0;
   clientData.mPicX = 0;
 
-  // Create YCbCrTexture for basice backend.
+  // Create YCbCrTexture for basic backend.
   if (aLayersBackend == LayersBackend::LAYERS_BASIC) {
     return TextureClient::CreateForYCbCr(nullptr,
                                          clientData.mYSize,
                                          clientData.mYStride,
                                          clientData.mCbCrSize,
                                          clientData.mCbCrStride,
                                          StereoMode::MONO,
+                                         gfx::ColorDepth::COLOR_8,
                                          YUVColorSpace::BT601,
-                                         8,
                                          TextureFlags::DEALLOCATE_CLIENT);
   }
 
 #ifdef XP_WIN
   RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetImageDevice();
 
   if (device && aLayersBackend == LayersBackend::LAYERS_D3D11) {
     DXGIYCbCrTextureAllocationHelper helper(clientData, TextureFlags::DEFAULT, device);
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -203,40 +203,57 @@ VRDisplayHost::RemoveLayer(VRLayerParent
   vm->RefreshVRDisplays();
 }
 
 void
 VRDisplayHost::StartFrame()
 {
   AUTO_PROFILER_TRACING("VR", "GetSensorState");
 
+  TimeStamp now = TimeStamp::Now();
 #if defined(MOZ_WIDGET_ANDROID)
+  const TimeStamp lastFrameStart = mDisplayInfo.mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames];
   const bool isPresenting = mLastUpdateDisplayInfo.GetPresentingGroups() != 0;
-  double duration = mLastFrameStart.IsNull() ? 0.0 : (TimeStamp::Now() - mLastFrameStart).ToMilliseconds();
+  double duration = lastFrameStart.IsNull() ? 0.0 : (now - lastFrameStart).ToMilliseconds();
   /**
    * Do not start more VR frames until the last submitted frame is already processed.
    */
   if (isPresenting && mLastStartedFrame > 0 && mDisplayInfo.mDisplayState.mLastSubmittedFrameId < mLastStartedFrame && duration < (double)ANDROID_MAX_FRAME_DURATION) {
     return;
   }
 #endif // !defined(MOZ_WIDGET_ANDROID)
 
-  mLastFrameStart = TimeStamp::Now();
   ++mDisplayInfo.mFrameId;
-  mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames] = GetSensorState();
+  size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames;
+  mDisplayInfo.mLastSensorState[bufferIndex] = GetSensorState();
+  mDisplayInfo.mLastFrameStart[bufferIndex] = now;
   mFrameStarted = true;
 #if defined(MOZ_WIDGET_ANDROID)
   mLastStartedFrame = mDisplayInfo.mFrameId;
 #endif // !defined(MOZ_WIDGET_ANDROID)  
 }
 
 void
 VRDisplayHost::NotifyVSync()
 {
   /**
+   * If this display isn't presenting, refresh the sensors and trigger
+   * VRDisplay.requestAnimationFrame at the normal 2d display refresh rate.
+   */
+  if (mDisplayInfo.mPresentingGroups == 0) {
+    VRManager *vm = VRManager::Get();
+    MOZ_ASSERT(vm);
+    vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
+  }
+}
+
+void
+VRDisplayHost::CheckWatchDog()
+{
+  /**
    * We will trigger a new frame immediately after a successful frame texture
    * submission.  If content fails to call VRDisplay.submitFrame after
    * dom.vr.display.rafMaxDuration milliseconds has elapsed since the last
    * VRDisplay.requestAnimationFrame, we act as a "watchdog" and kick-off
    * a new VRDisplay.requestAnimationFrame to avoid a render loop stall and
    * to give content a chance to recover.
    *
    * If the lower level VR platform API's are rejecting submitted frames,
@@ -255,41 +272,54 @@ VRDisplayHost::NotifyVSync()
    * The slowest expected refresh rate for a VR display currently is an
    * Oculus CV1 when ASW (Asynchronous Space Warp) is enabled, at 45hz.
    * A dom.vr.display.rafMaxDuration value of 50 milliseconds results in a 20hz
    * rate, which avoids inadvertent triggering of the watchdog during
    * Oculus ASW even if every second frame is dropped.
    */
   bool bShouldStartFrame = false;
 
-  if (mDisplayInfo.mPresentingGroups == 0) {
-    // If this display isn't presenting, refresh the sensors and trigger
-    // VRDisplay.requestAnimationFrame at the normal 2d display refresh rate.
+  // If content fails to call VRDisplay.submitFrame, we must eventually
+  // time-out and trigger a new frame.
+  TimeStamp lastFrameStart = mDisplayInfo.mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames];
+  if (lastFrameStart.IsNull()) {
     bShouldStartFrame = true;
   } else {
-    // If content fails to call VRDisplay.submitFrame, we must eventually
-    // time-out and trigger a new frame.
-    if (mLastFrameStart.IsNull()) {
+    TimeDuration duration = TimeStamp::Now() - lastFrameStart;
+    if (duration.ToMilliseconds() > gfxPrefs::VRDisplayRafMaxDuration()) {
       bShouldStartFrame = true;
-    } else {
-      TimeDuration duration = TimeStamp::Now() - mLastFrameStart;
-      if (duration.ToMilliseconds() > gfxPrefs::VRDisplayRafMaxDuration()) {
-        bShouldStartFrame = true;
-      }
     }
   }
 
   if (bShouldStartFrame) {
     VRManager *vm = VRManager::Get();
     MOZ_ASSERT(vm);
     vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
   }
 }
 
 void
+VRDisplayHost::Run1msTasks(double aDeltaTime)
+{
+ // To override in children
+}
+
+void
+VRDisplayHost::Run10msTasks()
+{
+  CheckWatchDog();
+}
+
+void
+VRDisplayHost::Run100msTasks()
+{
+  // to override in children
+}
+
+void
 VRDisplayHost::SubmitFrameInternal(const layers::SurfaceDescriptor &aTexture,
                                    uint64_t aFrameId,
                                    const gfx::Rect& aLeftEyeRect,
                                    const gfx::Rect& aRightEyeRect)
 {
 #if !defined(MOZ_WIDGET_ANDROID)
   MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
 #endif // !defined(MOZ_WIDGET_ANDROID)
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -40,16 +40,19 @@ public:
   void RemoveLayer(VRLayerParent* aLayer);
 
   virtual void ZeroSensor() = 0;
   virtual void StartPresentation() = 0;
   virtual void StopPresentation() = 0;
   virtual void StartVRNavigation();
   virtual void StopVRNavigation(const TimeDuration& aTimeout);
   void NotifyVSync();
+  virtual void Run1msTasks(double aDeltaTime);
+  virtual void Run10msTasks();
+  virtual void Run100msTasks();
 
   void StartFrame();
   void SubmitFrame(VRLayerParent* aLayer,
                    const layers::SurfaceDescriptor& aTexture,
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect);
 
@@ -92,19 +95,19 @@ protected:
   virtual VRHMDSensorState GetSensorState() = 0;
 
   RefPtr<VRThread> mSubmitThread;
 private:
   void SubmitFrameInternal(const layers::SurfaceDescriptor& aTexture,
                            uint64_t aFrameId,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect);
+  void CheckWatchDog();
 
   VRDisplayInfo mLastUpdateDisplayInfo;
-  TimeStamp mLastFrameStart;
   bool mFrameStarted;
 #if defined(MOZ_WIDGET_ANDROID)
 protected:
   uint64_t mLastSubmittedFrameId;
   uint64_t mLastStartedFrame;
 #endif // defined(MOZ_WIDGET_ANDROID)
 
 #if defined(XP_WIN)
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -40,34 +40,51 @@ using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace gfx {
 
 static StaticRefPtr<VRManager> sVRManagerSingleton;
 
+ /**
+  * When VR content is active, we run the tasks at 1ms
+  * intervals, enabling multiple events to be processed
+  * per frame, such as haptic feedback pulses.
+  */
+const uint32_t kVRActiveTaskInterval = 1; // milliseconds
+
+ /**
+  * When VR content is inactive, we run the tasks at 100ms
+  * intervals, enabling VR display enumeration and
+  * presentation startup to be relatively responsive
+  * while not consuming unnecessary resources.
+  */
+const uint32_t kVRIdleTaskInterval = 100; // milliseconds
+
 /*static*/ void
 VRManager::ManagerInit()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // TODO: We should make VRManager::ManagerInit
   // be called when entering VR content pages.
   if (sVRManagerSingleton == nullptr) {
     sVRManagerSingleton = new VRManager();
     ClearOnShutdown(&sVRManagerSingleton);
   }
 }
 
 VRManager::VRManager()
   : mInitialized(false)
+  , mAccumulator100ms(0.0f)
   , mVRDisplaysRequested(false)
   , mVRControllersRequested(false)
   , mVRServiceStarted(false)
+  , mTaskInterval(0)
 {
   MOZ_COUNT_CTOR(VRManager);
   MOZ_ASSERT(sVRManagerSingleton == nullptr);
 
   RefPtr<VRSystemManager> mgr;
 
   /**
    * We must add the VRDisplayManager's to mManagers in a careful order to
@@ -146,16 +163,17 @@ VRManager::~VRManager()
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mInitialized);
   MOZ_COUNT_DTOR(VRManager);
 }
 
 void
 VRManager::Destroy()
 {
+  StopTasks();
   mVRDisplays.Clear();
   mVRControllers.Clear();
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Destroy();
   }
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   if (mVRService) {
     mVRService->Stop();
@@ -190,16 +208,17 @@ VRManager::Shutdown()
   }
 #endif
   mVRServiceStarted = false;
 }
 
 void
 VRManager::Init()
 {
+  StartTasks();
   mInitialized = true;
 }
 
 /* static */VRManager*
 VRManager::Get()
 {
   MOZ_ASSERT(sVRManagerSingleton != nullptr);
 
@@ -247,33 +266,212 @@ VRManager::UpdateRequestedDevices()
  * This must be called even when no WebVR site is active.
  * If we don't have a 2d display attached to the system, we can call this
  * at the VR display's native refresh rate.
  **/
 void
 VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp)
 {
   MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
-  UpdateRequestedDevices();
 
   for (const auto& manager : mManagers) {
     manager->NotifyVSync();
   }
+}
+
+void
+VRManager::StartTasks()
+{
+  MOZ_ASSERT(VRListenerThread());
+  if (!mTaskTimer) {
+    mTaskInterval = GetOptimalTaskInterval();
+    mTaskTimer = NS_NewTimer();
+    mTaskTimer->SetTarget(VRListenerThreadHolder::Loop()->SerialEventTarget());
+    mTaskTimer->InitWithNamedFuncCallback(
+      TaskTimerCallback,
+      this,
+      mTaskInterval,
+      nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
+      "VRManager::TaskTimerCallback");
+  }
+}
+
+void
+VRManager::StopTasks()
+{
+  if (mTaskTimer) {
+    MOZ_ASSERT(VRListenerThread());
+    mTaskTimer->Cancel();
+    mTaskTimer = nullptr;
+  }
+}
+
+/*static*/ void
+VRManager::StopVRListenerThreadTasks()
+{
+  if (sVRManagerSingleton) {
+    sVRManagerSingleton->StopTasks();
+  }
+}
+
+/*static*/ void
+VRManager::TaskTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  /**
+   * It is safe to use the pointer passed in aClosure to reference the
+   * VRManager object as the timer is canceled in VRManager::Destroy.
+   * VRManager::Destroy set mInitialized to false, which is asserted
+   * in the VRManager destructor, guaranteeing that this functions
+   * runs if and only if the VRManager object is valid.
+   */
+  VRManager* self = static_cast<VRManager*>(aClosure);
+  self->RunTasks();
+}
+
+void
+VRManager::RunTasks()
+{
+  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+
+  // Will be called once every 1ms when a VR presentation
+  // is active or once per vsync when a VR presentation is
+  // not active.
+
+  TimeStamp now = TimeStamp::Now();
+  double lastTickMs = mAccumulator100ms;
+  double deltaTime = 0.0f;
+  if (!mLastTickTime.IsNull()) {
+    deltaTime = (now - mLastTickTime).ToMilliseconds();
+  }
+  mAccumulator100ms += deltaTime;
+  mLastTickTime = now;
+
+  if (deltaTime > 0.0f && floor(mAccumulator100ms) != floor(lastTickMs)) {
+    // Even if more than 1 ms has passed, we will only
+    // execute Run1msTasks() once.
+    Run1msTasks(deltaTime);
+  }
+
+  if (floor(mAccumulator100ms * 0.1f) != floor(lastTickMs * 0.1f)) {
+    // Even if more than 10 ms has passed, we will only
+    // execute Run10msTasks() once.
+     Run10msTasks();
+  }
+
+  if (mAccumulator100ms >= 100.0f) {
+    // Even if more than 100 ms has passed, we will only
+    // execute Run100msTasks() once.
+    Run100msTasks();
+    mAccumulator100ms = fmod(mAccumulator100ms, 100.0f);
+  }
+
+  uint32_t optimalTaskInterval = GetOptimalTaskInterval();
+  if (mTaskTimer && optimalTaskInterval != mTaskInterval) {
+    mTaskTimer->SetDelay(optimalTaskInterval);
+    mTaskInterval = optimalTaskInterval;
+  }
+}
+
+uint32_t
+VRManager::GetOptimalTaskInterval()
+{
+  /**
+   * When either VR content is detected or VR hardware
+   * has already been activated, we schedule tasks more
+   * frequently.
+   */
+  bool wantGranularTasks = mVRDisplaysRequested ||
+                           mVRControllersRequested ||
+                           mVRDisplays.Count() ||
+                           mVRControllers.Count();
+  if (wantGranularTasks) {
+    return kVRActiveTaskInterval;
+  }
+
+  return kVRIdleTaskInterval;
+}
+
+/**
+ * Run1msTasks() is guaranteed not to be
+ * called more than once within 1ms.
+ * When VR is not active, this will be
+ * called once per VSync if it wasn't
+ * called within the last 1ms.
+ */
+void
+VRManager::Run1msTasks(double aDeltaTime)
+{
+  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+
+  for (const auto& manager : mManagers) {
+    manager->Run1msTasks(aDeltaTime);
+  }
+
+  for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRDisplayHost* display = iter.UserData();
+    display->Run1msTasks(aDeltaTime);
+  }
+}
+
+/**
+ * Run10msTasks() is guaranteed not to be
+ * called more than once within 10ms.
+ * When VR is not active, this will be
+ * called once per VSync if it wasn't
+ * called within the last 10ms.
+ */
+void
+VRManager::Run10msTasks()
+{
+  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+
+  UpdateRequestedDevices();
+
+  for (const auto& manager : mManagers) {
+    manager->Run10msTasks();
+  }
+
+  for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRDisplayHost* display = iter.UserData();
+    display->Run10msTasks();
+  }
+}
+
+/**
+ * Run100msTasks() is guaranteed not to be
+ * called more than once within 100ms.
+ * When VR is not active, this will be
+ * called once per VSync if it wasn't
+ * called within the last 100ms.
+ */
+void
+VRManager::Run100msTasks()
+{
+  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
 
   // We must continually refresh the VR display enumeration to check
   // for events that we must fire such as Window.onvrdisplayconnect
   // Note that enumeration itself may activate display hardware, such
   // as Oculus, so we only do this when we know we are displaying content
   // that is looking for VR displays.
   RefreshVRDisplays();
 
   // Update state and enumeration of VR controllers
   RefreshVRControllers();
 
   CheckForInactiveTimeout();
+
+  for (const auto& manager : mManagers) {
+    manager->Run100msTasks();
+  }
+
+  for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRDisplayHost* display = iter.UserData();
+    display->Run100msTasks();
+  }
 }
 
 void
 VRManager::CheckForInactiveTimeout()
 {
   // Shut down the VR devices when not in use
   if (mVRDisplaysRequested || mVRControllersRequested) {
     // We are using a VR device, keep it alive
@@ -306,17 +504,17 @@ VRManager::NotifyVRVsync(const uint32_t&
     }
   }
 
   RefPtr<VRDisplayHost> display = GetDisplay(aDisplayID);
   if (display) {
     display->StartFrame();
   }
 
-  RefreshVRDisplays();
+  DispatchVRDisplayInfoUpdate();
 }
 
 void
 VRManager::EnumerateVRDisplays()
 {
   /**
    * Throttle the rate of enumeration to the interval set in
    * VRDisplayEnumerateInterval
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -9,16 +9,17 @@
 
 #include "nsRefPtrHashtable.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "gfxVR.h"
 
+class nsITimer;
 namespace mozilla {
 namespace layers {
 class TextureHost;
 }
 namespace gfx {
 
 class VRLayerParent;
 class VRManagerParent;
@@ -57,26 +58,35 @@ public:
 
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      double aIntensity, double aDuration, const VRManagerPromise& aPromise);
   void StopVibrateHaptic(uint32_t aControllerIdx);
   void NotifyVibrateHapticCompleted(const VRManagerPromise& aPromise);
   void DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult);
   void StartVRNavigation(const uint32_t& aDisplayID);
   void StopVRNavigation(const uint32_t& aDisplayID, const TimeDuration& aTimeout);
+  static void StopVRListenerThreadTasks();
 
 protected:
   VRManager();
   ~VRManager();
 
 private:
 
   void Init();
   void Destroy();
   void Shutdown();
+  void StartTasks();
+  void StopTasks();
+  static void TaskTimerCallback(nsITimer* aTimer, void* aClosure);
+  void RunTasks();
+  void Run1msTasks(double aDeltaTime);
+  void Run10msTasks();
+  void Run100msTasks();
+  uint32_t GetOptimalTaskInterval();
 
   void DispatchVRDisplayInfoUpdate();
   void UpdateRequestedDevices();
   void EnumerateVRDisplays();
   void CheckForInactiveTimeout();
 
   typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet;
   VRManagerParentSet mVRManagerParents;
@@ -90,22 +100,26 @@ private:
   typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRControllerHost> VRControllerHostHashMap;
   VRControllerHostHashMap mVRControllers;
 
   Atomic<bool> mInitialized;
 
   TimeStamp mLastControllerEnumerationTime;
   TimeStamp mLastDisplayEnumerationTime;
   TimeStamp mLastActiveTime;
+  TimeStamp mLastTickTime;
+  double mAccumulator100ms;
   RefPtr<VRSystemManagerPuppet> mPuppetManager;
   RefPtr<VRSystemManagerExternal> mExternalManager;
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   RefPtr<VRService> mVRService;
 #endif
   bool mVRDisplaysRequested;
   bool mVRControllersRequested;
   bool mVRServiceStarted;
+  uint32_t mTaskInterval;
+  RefPtr<nsITimer> mTaskTimer;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // GFX_VR_MANAGER_H
--- a/gfx/vr/VRThread.cpp
+++ b/gfx/vr/VRThread.cpp
@@ -1,23 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "VRThread.h"
 #include "nsThreadUtils.h"
+#include "VRManager.h"
 
 namespace mozilla {
 
 namespace gfx {
 
 static StaticRefPtr<VRListenerThreadHolder> sVRListenerThreadHolder;
-static bool sFinishedVRListenerShutDown = false;
+static bool sFinishedVRListenerShutDown = true;
 static const uint32_t kDefaultThreadLifeTime = 60; // in 60 seconds.
 static const uint32_t kDelayPostTaskTime = 20000; // in 20000 ms.
 
 VRListenerThreadHolder* GetVRListenerThreadHolder()
 {
   return sVRListenerThreadHolder;
 }
 
@@ -88,26 +89,26 @@ VRListenerThreadHolder::CreateThread()
   return vrThread;
 }
 
 void
 VRListenerThreadHolder::Start()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
   MOZ_ASSERT(!sVRListenerThreadHolder, "The VR listener thread has already been started!");
-
+  sFinishedVRListenerShutDown = false;
   sVRListenerThreadHolder = new VRListenerThreadHolder();
 }
 
 void
 VRListenerThreadHolder::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
   MOZ_ASSERT(sVRListenerThreadHolder, "The VR listener thread has already been shut down!");
-
+  VRManager::StopVRListenerThreadTasks();
   sVRListenerThreadHolder = nullptr;
 
   SpinEventLoopUntil([&]() { return sFinishedVRListenerShutDown; });
 }
 
 /* static */ bool
 VRListenerThreadHolder::IsInVRListenerThread()
 {
--- a/gfx/vr/external_api/moz_external_vr.h
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -24,17 +24,17 @@ namespace mozilla {
 #ifdef MOZILLA_INTERNAL_API
 namespace dom {
   enum class GamepadHand : uint8_t;
   enum class GamepadCapabilityFlags : uint16_t;
 }
 #endif //  MOZILLA_INTERNAL_API
 namespace gfx {
 
-static const int32_t kVRExternalVersion = 2;
+static const int32_t kVRExternalVersion = 3;
 
 // We assign VR presentations to groups with a bitmask.
 // Currently, we will only display either content or chrome.
 // Later, we will have more groups to support VR home spaces and
 // multitasking environments.
 // These values are not exposed to regular content and only affect
 // chrome-only API's.  They may be changed at any time.
 static const uint32_t kVRGroupNone = 0;
@@ -43,16 +43,17 @@ static const uint32_t kVRGroupChrome = 1
 static const uint32_t kVRGroupAll = 0xffffffff;
 
 static const int kVRDisplayNameMaxLen = 256;
 static const int kVRControllerNameMaxLen = 256;
 static const int kVRControllerMaxCount = 16;
 static const int kVRControllerMaxButtons = 64;
 static const int kVRControllerMaxAxis = 16;
 static const int kVRLayerMaxCount = 8;
+static const int kVRHapticsMaxCount = 32;
 
 #if defined(__ANDROID__)
 typedef uint64_t VRLayerTextureHandle;
 #else
 typedef void* VRLayerTextureHandle;
 #endif
 
 struct Point3D_POD
@@ -349,24 +350,44 @@ struct VRLayerState
 {
   VRLayerType type;
   union {
     VRLayer_2D_Content layer_2d_content;
     VRLayer_Stereo_Immersive layer_stereo_immersive;
   };
 };
 
+struct VRHapticState
+{
+  // Reference frame for timing.
+  // When 0, this does not represent an active haptic pulse.
+  uint64_t inputFrameID;
+  // Index within VRSystemState.controllerState identifying the controller
+  // to emit the haptic pulse
+  uint32_t controllerIndex;
+  // 0-based index indicating which haptic actuator within the controller
+  uint32_t hapticIndex;
+  // Start time of the haptic feedback pulse, relative to the start of
+  // inputFrameID, in seconds
+  float pulseStart;
+  // Duration of the haptic feedback pulse, in seconds
+  float pulseDuration;
+  // Intensity of the haptic feedback pulse, from 0.0f to 1.0f
+  float pulseIntensity;
+};
+
 struct VRBrowserState
 {
 #if defined(__ANDROID__)
   bool shutdown;
 #endif // defined(__ANDROID__)
   bool presentationActive;
   bool navigationTransitionActive;
   VRLayerState layerState[kVRLayerMaxCount];
+  VRHapticState hapticState[kVRHapticsMaxCount];
 };
 
 struct VRSystemState
 {
   bool enumerationCompleted;
   VRDisplayState displayState;
   VRHMDSensorState sensorState;
   VRControllerState controllerState[kVRControllerMaxCount];
--- a/gfx/vr/gfxVR.cpp
+++ b/gfx/vr/gfxVR.cpp
@@ -53,16 +53,34 @@ VRSystemManager::NotifyVSync()
 
   // Ensure that the controller state is updated at least
   // on every 2d display VSync when not in a VR presentation.
   if (!GetIsPresenting()) {
     HandleInput();
   }
 }
 
+void
+VRSystemManager::Run1msTasks(double aDeltaTime)
+{
+  // To be overridden by children
+}
+
+void
+VRSystemManager::Run10msTasks()
+{
+  // To be overridden by children
+}
+
+void
+VRSystemManager::Run100msTasks()
+{
+  // To be overridden by children
+}
+
 /**
  * VRSystemManager::GetHMDs must not be called unless
  * VRSystemManager::ShouldInhibitEnumeration is called
  * on all VRSystemManager instances and they all return
  * false.
  *
  * This is used to ensure that VR devices that can be
  * enumerated by multiple API's are only enumerated by
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -58,16 +58,17 @@ struct VRDisplayInfo
   VRDeviceType mType;
   uint32_t mPresentingGroups;
   uint32_t mGroupMask;
   uint64_t mFrameId;
   VRDisplayState mDisplayState;
   VRControllerState mControllerState[kVRControllerMaxCount];
 
   VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
+  TimeStamp mLastFrameStart[kVRMaxLatencyFrames];
   const VRHMDSensorState& GetSensorState() const
   {
     return mLastSensorState[mFrameId % kVRMaxLatencyFrames];
   }
 
   VRDeviceType GetType() const { return mType; }
   uint32_t GetDisplayID() const { return mDisplayID; }
   const char* GetDisplayName() const { return mDisplayState.mDisplayName; }
@@ -182,16 +183,19 @@ protected:
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRSystemManager)
 
   virtual void Destroy() = 0;
   virtual void Shutdown() = 0;
   virtual void Enumerate() = 0;
   virtual void NotifyVSync();
+  virtual void Run1msTasks(double aDeltaTime);
+  virtual void Run10msTasks();
+  virtual void Run100msTasks();
   virtual bool ShouldInhibitEnumeration();
   virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) = 0;
   virtual bool GetIsPresenting() = 0;
   virtual void HandleInput() = 0;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0;
   virtual void ScanForControllers() = 0;
   virtual void RemoveControllers() = 0;
   virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -49,16 +49,17 @@ static const char* kShmemName = "/moz.ge
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState)
   : VRDisplayHost(VRDeviceType::External)
+  , mHapticPulseRemaining{}
   , mBrowserState{}
   , mLastSensorState{}
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
   mDisplayInfo.mDisplayState = aDisplayState;
 
   // default to an identity quaternion
   mLastSensorState.pose.orientation[3] = 1.0f;
@@ -68,35 +69,52 @@ VRDisplayExternal::~VRDisplayExternal()
 {
   Destroy();
   MOZ_COUNT_DTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
 }
 
 void
 VRDisplayExternal::Destroy()
 {
+  StopAllHaptics();
   StopPresentation();
 }
 
 void
 VRDisplayExternal::ZeroSensor()
 {
 }
 
 void
-VRDisplayExternal::Refresh()
+VRDisplayExternal::Run1msTasks(double aDeltaTime)
+{
+  VRDisplayHost::Run1msTasks(aDeltaTime);
+  UpdateHaptics(aDeltaTime);
+}
+
+void
+VRDisplayExternal::Run10msTasks()
 {
+  VRDisplayHost::Run10msTasks();
+  ExpireNavigationTransition();
+  PullState();
+  PushState();
 
+  // 1ms tasks will always be run before
+  // the 10ms tasks, so no need to include
+  // them here as well.
+}
+
+void
+VRDisplayExternal::ExpireNavigationTransition()
+{
   if (!mVRNavigationTransitionEnd.IsNull() &&
       TimeStamp::Now() > mVRNavigationTransitionEnd) {
     mBrowserState.navigationTransitionActive = false;
   }
-
-  PullState();
-  PushState();
 }
 
 VRHMDSensorState
 VRDisplayExternal::GetSensorState()
 {
   return mLastSensorState;
 }
 
@@ -109,57 +127,20 @@ VRDisplayExternal::StartPresentation()
   mTelemetry.Clear();
   mTelemetry.mPresentationStart = TimeStamp::Now();
 
   // Indicate that we are ready to start immersive mode
   mBrowserState.presentationActive = true;
   mBrowserState.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
   PushState();
 
-#if defined(MOZ_WIDGET_ANDROID)
-  mLastSubmittedFrameId = 0;
-  mLastStartedFrame = 0;
-  /**
-   * Android compositor is paused when presentation starts. That causes VRManager::NotifyVsync() not to be called.
-   * We post a VRTask to call VRManager::NotifyVsync() while the compositor is paused on Android.
-   * VRManager::NotifyVsync() should be called constinuosly while the compositor is paused because Gecko WebVR Architecture
-   * relies on that to act as a "watchdog" in order to avoid render loop stalls and recover from SubmitFrame call timeouts.
-   */
-  PostVRTask();
-#endif
-  // TODO - Implement telemetry:
-
+  mDisplayInfo.mDisplayState.mLastSubmittedFrameId = 0;
   // mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
 }
 
-#if defined(MOZ_WIDGET_ANDROID)
-void
-VRDisplayExternal::PostVRTask() {
-  MessageLoop * vrLoop = VRListenerThreadHolder::Loop();
-  if (!vrLoop || !mBrowserState.presentationActive) {
-    return;
-  }
-  RefPtr<Runnable> task = NewRunnableMethod(
-    "VRDisplayExternal::RunVRTask",
-    this,
-    &VRDisplayExternal::RunVRTask);
-  VRListenerThreadHolder::Loop()->PostDelayedTask(task.forget(), 50);
-}
-
-void
-VRDisplayExternal::RunVRTask() {
-  if (mBrowserState.presentationActive) {
-    VRManager *vm = VRManager::Get();
-    vm->NotifyVsync(TimeStamp::Now());
-    PostVRTask();
-  }
-}
-
-#endif // defined(MOZ_WIDGET_ANDROID)
-
 void
 VRDisplayExternal::StopPresentation()
 {
   if (!mBrowserState.presentationActive) {
     return;
   }
 
   // Indicate that we have stopped immersive mode
@@ -301,16 +282,135 @@ VRDisplayExternal::SubmitFrame(const lay
 #endif
   }
 #endif // defined(MOZ_WIDGET_ANDROID)
 
   return mDisplayInfo.mDisplayState.mLastSubmittedFrameSuccessful;
 }
 
 void
+VRDisplayExternal::VibrateHaptic(uint32_t aControllerIdx,
+                                 uint32_t aHapticIndex,
+                                 double aIntensity,
+                                 double aDuration,
+                                 const VRManagerPromise& aPromise)
+{
+  TimeStamp now = TimeStamp::Now();
+  size_t bestSlotIndex = 0;
+  // Default to an empty slot, or the slot holding the oldest haptic pulse
+  for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+    const VRHapticState& state = mBrowserState.hapticState[i];
+    if (state.inputFrameID == 0) {
+      // Unused slot, use it
+      bestSlotIndex = i;
+      break;
+    }
+    if (mHapticPulseRemaining[i] < mHapticPulseRemaining[bestSlotIndex]) {
+      // If no empty slots are available, fall back to overriding
+      // the pulse which is ending soonest.
+      bestSlotIndex = i;
+    }
+  }
+  // Override the last pulse on the same actuator if present.
+  for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+    const VRHapticState& state = mBrowserState.hapticState[i];
+    if (state.inputFrameID == 0) {
+      // This is an empty slot -- no match
+      continue;
+    }
+    if (state.controllerIndex == aControllerIdx &&
+        state.hapticIndex == aHapticIndex) {
+      // Found pulse on same actuator -- let's override it.
+      bestSlotIndex = i;
+    }
+  }
+  ClearHapticSlot(bestSlotIndex);
+
+  // Populate the selected slot with new haptic state
+  size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames;
+  VRHapticState& bestSlot = mBrowserState.hapticState[bestSlotIndex];
+  bestSlot.inputFrameID =
+    mDisplayInfo.mLastSensorState[bufferIndex].inputFrameID;
+  bestSlot.controllerIndex = aControllerIdx;
+  bestSlot.hapticIndex = aHapticIndex;
+  bestSlot.pulseStart =
+    (now - mDisplayInfo.mLastFrameStart[bufferIndex]).ToSeconds();
+  bestSlot.pulseDuration = aDuration;
+  bestSlot.pulseIntensity = aIntensity;
+   // Convert from seconds to ms
+  mHapticPulseRemaining[bestSlotIndex] = aDuration * 1000.0f;
+  MOZ_ASSERT(bestSlotIndex <= mHapticPromises.Length());
+  if (bestSlotIndex == mHapticPromises.Length()) {
+    mHapticPromises.AppendElement(
+      UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise)));
+  } else {
+    mHapticPromises[bestSlotIndex] =
+      UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise));
+  }
+  PushState();
+}
+
+void
+VRDisplayExternal::ClearHapticSlot(size_t aSlot)
+{
+  MOZ_ASSERT(aSlot < mozilla::ArrayLength(mBrowserState.hapticState));
+  memset(&mBrowserState.hapticState[aSlot], 0, sizeof(VRHapticState));
+  mHapticPulseRemaining[aSlot] = 0.0f;
+  if (aSlot < mHapticPromises.Length() && mHapticPromises[aSlot]) {
+    VRManager* vm = VRManager::Get();
+    vm->NotifyVibrateHapticCompleted(*mHapticPromises[aSlot]);
+    mHapticPromises[aSlot] = nullptr;
+  }
+}
+
+void
+VRDisplayExternal::UpdateHaptics(double aDeltaTime)
+{
+  bool bNeedPush = false;
+  // Check for any haptic pulses that have ended and clear them
+  for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+    const VRHapticState& state = mBrowserState.hapticState[i];
+    if (state.inputFrameID == 0) {
+      // Nothing in this slot
+      continue;
+    }
+    mHapticPulseRemaining[i] -= aDeltaTime;
+    if (mHapticPulseRemaining[i] <= 0.0f) {
+      // The pulse has finished
+      ClearHapticSlot(i);
+      bNeedPush = true;
+    }
+  }
+  if (bNeedPush) {
+    PushState();
+  }
+}
+
+void
+VRDisplayExternal::StopVibrateHaptic(uint32_t aControllerIdx)
+{
+  for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+    VRHapticState& state = mBrowserState.hapticState[i];
+    if (state.controllerIndex == aControllerIdx) {
+      memset(&state, 0, sizeof(VRHapticState));
+    }
+  }
+  PushState();
+}
+
+void
+VRDisplayExternal::StopAllHaptics()
+{
+  for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) {
+    ClearHapticSlot(i);
+  }
+  PushState();
+}
+
+void
 VRDisplayExternal::PushState(bool aNotifyCond)
 {
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
   manager->PushState(&mBrowserState, aNotifyCond);
 }
 
 #if defined(MOZ_WIDGET_ANDROID)
@@ -538,25 +638,24 @@ VRSystemManagerExternal::Shutdown()
   }
   CloseShmem();
 #if defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
 #endif
 }
 
 void
-VRSystemManagerExternal::NotifyVSync()
+VRSystemManagerExternal::Run100msTasks()
 {
-  VRSystemManager::NotifyVSync();
+  VRSystemManager::Run100msTasks();
+  // 1ms and 10ms tasks will always be run before
+  // the 100ms tasks, so no need to run them
+  // redundantly here.
 
   CheckForShutdown();
-
-  if (mDisplay) {
-    mDisplay->Refresh();
-  }
 }
 
 void
 VRSystemManagerExternal::Enumerate()
 {
   if (mDisplay == nullptr) {
     OpenShmem();
     if (mExternalShmem) {
@@ -618,64 +717,83 @@ VRSystemManagerExternal::GetIsPresenting
     return displayInfo.GetPresentingGroups() != 0;
   }
 
   return false;
 }
 
 void
 VRSystemManagerExternal::VibrateHaptic(uint32_t aControllerIdx,
-                                      uint32_t aHapticIndex,
-                                      double aIntensity,
-                                      double aDuration,
-                                      const VRManagerPromise& aPromise)
+                                       uint32_t aHapticIndex,
+                                       double aIntensity,
+                                       double aDuration,
+                                       const VRManagerPromise& aPromise)
 {
-  // TODO - Implement this
+  if (mDisplay) {
+    // VRDisplayClient::FireGamepadEvents() assigns a controller ID with ranges
+    // based on displayID.  We must translate this to the indexes understood by
+    // VRDisplayExternal.
+    uint32_t controllerBaseIndex =
+      kVRControllerMaxCount * mDisplay->GetDisplayInfo().mDisplayID;
+    uint32_t controllerIndex = aControllerIdx - controllerBaseIndex;
+    double aDurationSeconds = aDuration * 0.001f;
+    mDisplay->VibrateHaptic(
+      controllerIndex, aHapticIndex, aIntensity, aDurationSeconds, aPromise);
+  }
 }
 
 void
 VRSystemManagerExternal::StopVibrateHaptic(uint32_t aControllerIdx)
 {
-  // TODO - Implement this
+  if (mDisplay) {
+    // VRDisplayClient::FireGamepadEvents() assigns a controller ID with ranges
+    // based on displayID.  We must translate this to the indexes understood by
+    // VRDisplayExternal.
+    uint32_t controllerBaseIndex =
+      kVRControllerMaxCount * mDisplay->GetDisplayInfo().mDisplayID;
+    uint32_t controllerIndex = aControllerIdx - controllerBaseIndex;
+    mDisplay->StopVibrateHaptic(controllerIndex);
+  }
 }
 
 void
-VRSystemManagerExternal::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
+VRSystemManagerExternal::GetControllers(
+  nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
 {
-  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
+  // Controller updates are handled in VRDisplayClient for
+  // VRSystemManagerExternal
   aControllerResult.Clear();
 }
 
 void
 VRSystemManagerExternal::ScanForControllers()
 {
-  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
-  if (mDisplay) {
-    mDisplay->Refresh();
-  }
+  // Controller updates are handled in VRDisplayClient for
+  // VRSystemManagerExternal
   return;
 }
 
 void
 VRSystemManagerExternal::HandleInput()
 {
-  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
-  if (mDisplay) {
-    mDisplay->Refresh();
-  }
+  // Controller updates are handled in VRDisplayClient for
+  // VRSystemManagerExternal
   return;
 }
 
 void
 VRSystemManagerExternal::RemoveControllers()
 {
-  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
+  if (mDisplay) {
+    mDisplay->StopAllHaptics();
+  }
+  // Controller updates are handled in VRDisplayClient for
+  // VRSystemManagerExternal
 }
 
-
 #if defined(MOZ_WIDGET_ANDROID)
 bool
 VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
                                    VRHMDSensorState* aSensorState /* = nullptr */,
                                    VRControllerState* aControllerState /* = nullptr */,
                                    const std::function<bool()>& aWaitCondition /* = nullptr */)
 {
   MOZ_ASSERT(mExternalShmem);
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -9,27 +9,26 @@
 
 #include "nsTArray.h"
 #include "nsIScreen.h"
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/EnumeratedArray.h"
+#include "mozilla/UniquePtr.h"
 
 #include "gfxVR.h"
 #include "VRDisplayHost.h"
 
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
 namespace gfx {
-class VRThread;
-
 namespace impl {
 
 class VRDisplayExternal : public VRDisplayHost
 {
 public:
   void ZeroSensor() override;
 
 protected:
@@ -43,49 +42,61 @@ protected:
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect) override;
 
 public:
   explicit VRDisplayExternal(const VRDisplayState& aDisplayState);
   void Refresh();
   const VRControllerState& GetLastControllerState(uint32_t aStateIndex) const;
+  void VibrateHaptic(uint32_t aControllerIdx,
+                     uint32_t aHapticIndex,
+                     double aIntensity,
+                     double aDuration,
+                     const VRManagerPromise& aPromise);
+  void StopVibrateHaptic(uint32_t aControllerIdx);
+  void StopAllHaptics();
+  void Run1msTasks(double aDeltaTime) override;
+  void Run10msTasks() override;
 protected:
   virtual ~VRDisplayExternal();
   void Destroy();
 
 private:
   bool PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
                             VRLayerTextureType* aTextureType,
                             VRLayerTextureHandle* aTextureHandle);
   void PushState(bool aNotifyCond = false);
 #if defined(MOZ_WIDGET_ANDROID)
   bool PullState(const std::function<bool()>& aWaitCondition = nullptr);
-  void PostVRTask();
-  void RunVRTask();
 #else
   bool PullState();
 #endif
-
+  void ClearHapticSlot(size_t aSlot);
+  void ExpireNavigationTransition();
+  void UpdateHaptics(double aDeltaTime);
+  nsTArray<UniquePtr<VRManagerPromise>> mHapticPromises;
+  // Duration of haptic pulse time remaining (milliseconds)
+  double mHapticPulseRemaining[kVRHapticsMaxCount];
   VRTelemetry mTelemetry;
   TimeStamp mVRNavigationTransitionEnd;
   VRBrowserState mBrowserState;
   VRHMDSensorState mLastSensorState;
 };
 
 } // namespace impl
 
 class VRSystemManagerExternal : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerExternal> Create(VRExternalShmem* aAPIShmem = nullptr);
 
   virtual void Destroy() override;
   virtual void Shutdown() override;
-  virtual void NotifyVSync() override;
+  virtual void Run100msTasks() override;
   virtual void Enumerate() override;
   virtual bool ShouldInhibitEnumeration() override;
   virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
   virtual bool GetIsPresenting() override;
   virtual void HandleInput() override;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
                               aControllerResult) override;
   virtual void ScanForControllers() override;
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -1316,23 +1316,23 @@ VRControllerOculus::VRControllerOculus(d
   , mHandTrigger(0.0f)
   , mVibrateThread(nullptr)
   , mIsVibrateStopped(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost);
 
   VRControllerState& state = mControllerInfo.mControllerState;
 
-  const char* touchID = "";
+  char* touchID = (char*)"";
   switch (aHand) {
     case dom::GamepadHand::Left:
-      touchID = "Oculus Touch (Left)";
+      touchID = (char*)"Oculus Touch (Left)";
       break;
     case dom::GamepadHand::Right:
-      touchID = "Oculus Touch (Right)";
+      touchID = (char*)"Oculus Touch (Right)";
       break;
     default:
       MOZ_ASSERT(false);
       break;
   }
 
   strncpy(state.controllerName, touchID, kVRControllerNameMaxLen);
 
@@ -1803,16 +1803,23 @@ VRSystemManagerOculus::HandleButtonPress
     return;
   }
 
   if (pressedDiff & aButtonMask ||
       touchedDiff & aButtonMask) {
     // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
     // touched event, otherwise it is an old event and needs to notify
     // the button has been released.
+    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("Oculus handleButton(aButton = %d, length = %d, controller: %s.)",
+                              aButton,
+                              controller->GetControllerInfo().GetNumButtons(),
+                              controller->GetControllerInfo().GetControllerName());
+    }
     NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
                    aButtonMask & aButtonTouched,
                    (aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
   }
 }
 
 void
 VRSystemManagerOculus::HandleIndexTriggerPress(uint32_t aControllerIdx,
@@ -1829,16 +1836,23 @@ VRSystemManagerOculus::HandleIndexTrigge
   // We prefer to let developers to set their own threshold for the adjustment.
   // Therefore, we don't check ButtonPressed and ButtonTouched with TouchMask here.
   // we just check the button value is larger than the threshold value or not.
   const float threshold = gfxPrefs::VRControllerTriggerThreshold();
 
   // Avoid sending duplicated events in IPC channels.
   if (oldValue != aValue ||
       touchedDiff & aTouchMask) {
+    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("Oculus handleIndexTrigger(aButton = %d, length = %d, controller: %s.)",
+                              aButton,
+                              controller->GetControllerInfo().GetNumButtons(),
+                              controller->GetControllerInfo().GetControllerName());
+    }
     NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
                    aButtonTouched & aTouchMask, aValue);
     controller->SetIndexTrigger(aValue);
   }
 }
 
 void
 VRSystemManagerOculus::HandleHandTriggerPress(uint32_t aControllerIdx,
@@ -1850,31 +1864,45 @@ VRSystemManagerOculus::HandleHandTrigger
   const float oldValue = controller->GetHandTrigger();
   // We prefer to let developers to set their own threshold for the adjustment.
   // Therefore, we don't check ButtonPressed and ButtonTouched with TouchMask here.
   // we just check the button value is larger than the threshold value or not.
   const float threshold = gfxPrefs::VRControllerTriggerThreshold();
 
   // Avoid sending duplicated events in IPC channels.
   if (oldValue != aValue) {
+    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("Oculus handleHandTrigger(aButton = %d, length = %d, controller: %s.)",
+                              aButton,
+                              controller->GetControllerInfo().GetNumButtons(),
+                              controller->GetControllerInfo().GetControllerName());
+    }
     NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
                    aValue > threshold, aValue);
     controller->SetHandTrigger(aValue);
   }
 }
 
 void
 VRSystemManagerOculus::HandleTouchEvent(uint32_t aControllerIdx, uint32_t aButton,
                                         uint64_t aTouchMask, uint64_t aButtonTouched)
 {
   RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]);
   MOZ_ASSERT(controller);
   const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
 
   if (touchedDiff & aTouchMask) {
+    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("Oculus HandleTouchEvent(aButton = %d, length = %d, controller: %s.)",
+                               aButton,
+                               controller->GetControllerInfo().GetNumButtons(),
+                               controller->GetControllerInfo().GetControllerName());
+    }
     NewButtonEvent(aControllerIdx, aButton, false, aTouchMask & aButtonTouched, 0.0f);
   }
 }
 
 void
 VRSystemManagerOculus::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
                                       float aValue)
 {
@@ -1882,16 +1910,23 @@ VRSystemManagerOculus::HandleAxisMove(ui
   MOZ_ASSERT(controller);
   float value = aValue;
 
   if (abs(aValue) < 0.0000009f) {
     value = 0.0f; // Clear noise signal
   }
 
   if (controller->GetAxisMove(aAxis) != value) {
+    if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("Oculus HandleAxisMove(aAxis = %d, length = %d, controller: %s.)",
+                               aAxis,
+                               controller->GetControllerInfo().GetNumAxes(),
+                               controller->GetControllerInfo().GetControllerName());
+    }
     NewAxisMove(aControllerIdx, aAxis, value);
     controller->SetAxisMove(aAxis, value);
   }
 }
 
 void
 VRSystemManagerOculus::HandlePoseTracking(uint32_t aControllerIdx,
                                           const GamepadPoseState& aPose,
@@ -1990,17 +2025,21 @@ VRSystemManagerOculus::ScanForController
       switch (activeControllerArray[i]) {
         case ovrControllerType::ovrControllerType_LTouch:
           hand = GamepadHand::Left;
           break;
         case ovrControllerType::ovrControllerType_RTouch:
           hand = GamepadHand::Right;
           break;
         default:
+<<<<<<< working copy
+          continue;
+=======
           break;
+>>>>>>> merge rev
       }
       RefPtr<VRControllerOculus> oculusController = new VRControllerOculus(hand,
                                                       mDisplay->GetDisplayInfo().GetDisplayID());
       mOculusController.AppendElement(oculusController);
 
       // Not already present, add it.
       AddGamepad(oculusController->GetControllerInfo());
 
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -792,107 +792,146 @@ VRSystemManagerOpenVR::HandleInput()
           case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
             if (mIsWindowsMR) {
               // Adjust the input mapping for Windows MR which has
               // different order.
               axisIdx = (axisIdx == 0) ? 2 : 0;
               buttonIdx = (buttonIdx == 0) ? 4 : 0;
             }
 
-            HandleAxisMove(i, axisIdx,
-                           state.rAxis[j].x);
+            if (!HandleAxisMove(i, axisIdx, state.rAxis[j].x)) {
+              RemoveControllers();
+              return;
+            }
             ++axisIdx;
-            HandleAxisMove(i, axisIdx,
-                           state.rAxis[j].y * yAxisInvert);
+
+            if (!HandleAxisMove(i, axisIdx, state.rAxis[j].y * yAxisInvert)) {
+              RemoveControllers();
+              return;
+            }
             ++axisIdx;
-            HandleButtonPress(i, buttonIdx,
-                              ::vr::ButtonMaskFromId(
-                                 static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)),
-                                 state.ulButtonPressed, state.ulButtonTouched);
+
+            if (!HandleButtonPress(i, buttonIdx,
+                                   ::vr::ButtonMaskFromId(
+                                   static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)),
+                                   state.ulButtonPressed, state.ulButtonTouched)) {
+              RemoveControllers();
+              return;
+            }
             ++buttonIdx;
 
             if (mIsWindowsMR) {
               axisIdx = (axisIdx == 4) ? 2 : 4;
               buttonIdx = (buttonIdx == 5) ? 1 : 2;
             }
             break;
           case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
             if (j <= 2) {
-              HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x);
+              if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) {
+                RemoveControllers();
+                return;
+              }
               ++buttonIdx;
               ++triggerIdx;
             } else {
               // For SteamVR Knuckles.
-              HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x);
+              if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) {
+                RemoveControllers();
+                return;
+              }
               ++buttonIdx;
               ++triggerIdx;
-              HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y);
+              if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y)) {
+                RemoveControllers();
+                return;
+              }
               ++buttonIdx;
               ++triggerIdx;
             }
             break;
         }
       }
       MOZ_ASSERT(axisIdx ==
                  controller->GetControllerInfo().GetNumAxes());
 
       const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
                                          trackedIndex, ::vr::Prop_SupportedButtons_Uint64);
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_A)) {
-        HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_A),
-                          state.ulButtonPressed, state.ulButtonTouched);
+        if (!HandleButtonPress(i, buttonIdx,
+                               BTN_MASK_FROM_ID(k_EButton_A),
+                               state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_Grip)) {
-        HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_Grip),
-                          state.ulButtonPressed, state.ulButtonTouched);
+        if (!HandleButtonPress(i, buttonIdx,
+                              BTN_MASK_FROM_ID(k_EButton_Grip),
+                              state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
-        HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu),
-                          state.ulButtonPressed, state.ulButtonTouched);
+        if (!HandleButtonPress(i, buttonIdx,
+                               BTN_MASK_FROM_ID(k_EButton_ApplicationMenu),
+                               state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       if (mIsWindowsMR) {
         // button 4 in Windows MR has already been assigned
         // to k_eControllerAxis_TrackPad.
         ++buttonIdx;
       }
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
-        HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_DPad_Left),
-                          state.ulButtonPressed, state.ulButtonTouched);
+        if (!HandleButtonPress(i, buttonIdx,
+                               BTN_MASK_FROM_ID(k_EButton_DPad_Left),
+                               state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
-        HandleButtonPress(i, buttonIdx,
+        if (!HandleButtonPress(i, buttonIdx,
                           BTN_MASK_FROM_ID(k_EButton_DPad_Up),
-                          state.ulButtonPressed, state.ulButtonTouched);
+                          state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
-        HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_DPad_Right),
-                          state.ulButtonPressed, state.ulButtonTouched);
+        if (!HandleButtonPress(i, buttonIdx,
+                               BTN_MASK_FROM_ID(k_EButton_DPad_Right),
+                               state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       if (supportedButtons &
           BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
-        HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_DPad_Down),
-                          state.ulButtonPressed, state.ulButtonTouched);
+        if (!HandleButtonPress(i, buttonIdx,
+                               BTN_MASK_FROM_ID(k_EButton_DPad_Down),
+                               state.ulButtonPressed, state.ulButtonTouched)) {
+          RemoveControllers();
+          return;
+        }
         ++buttonIdx;
       }
       MOZ_ASSERT(buttonIdx ==
                  controller->GetControllerInfo().GetNumButtons());
       controller->SetButtonPressed(state.ulButtonPressed);
       controller->SetButtonTouched(state.ulButtonTouched);
 
       // Start to process pose
@@ -936,77 +975,104 @@ VRSystemManagerOpenVR::HandleInput()
         poseState.linearVelocity[2] = pose.vVelocity.v[2];
         poseState.isPositionValid = true;
       }
       HandlePoseTracking(i, poseState, controller);
     }
   }
 }
 
-void
+bool
 VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
                                          uint32_t aButton,
                                          uint64_t aButtonMask,
                                          uint64_t aButtonPressed,
                                          uint64_t aButtonTouched)
 {
   RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
   MOZ_ASSERT(controller);
   const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
   const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
 
   if (!pressedDiff && !touchedDiff) {
-    return;
+    return true;
   }
 
   if (pressedDiff & aButtonMask ||
       touchedDiff & aButtonMask) {
     // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
     // touched event, otherwise it is an old event and needs to notify
     // the button has been released.
+    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleButton(aButton = %d, length = %d, controller: %s.)",
+                              aButton,
+                              controller->GetControllerInfo().GetNumButtons(),
+                              controller->GetControllerInfo().GetControllerName());
+      return false;
+    }
     NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
                    aButtonMask & aButtonTouched,
                    (aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
   }
+  return true;
 }
 
-void
+bool
 VRSystemManagerOpenVR::HandleTriggerPress(uint32_t aControllerIdx,
                                           uint32_t aButton,
                                           uint32_t aTrigger,
                                           float aValue)
 {
   RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
   MOZ_ASSERT(controller);
   const float oldValue = controller->GetTrigger(aTrigger);
   // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
   // We prefer to let developers to set their own threshold for the adjustment.
   // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
   // we just check the button value is larger than the threshold value or not.
   const float threshold = gfxPrefs::VRControllerTriggerThreshold();
 
   // Avoid sending duplicated events in IPC channels.
   if (oldValue != aValue) {
+    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleTrigger(aButton = %d, length = %d, controller: %s.)",
+                              aButton,
+                              controller->GetControllerInfo().GetNumButtons(),
+                              controller->GetControllerInfo().GetControllerName());
+      return false;
+    }
     NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
                    aValue > threshold, aValue);
     controller->SetTrigger(aTrigger, aValue);
   }
+  return true;
 }
 
-void
+bool
 VRSystemManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
                                       float aValue)
 {
   RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
   MOZ_ASSERT(controller);
 
   if (controller->GetAxisMove(aAxis) != aValue) {
+    if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) {
+      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
+      MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleAxis(aAxis = %d, length = %d, controller: %s.)",
+                              aAxis,
+                              controller->GetControllerInfo().GetNumAxes(),
+                              controller->GetControllerInfo().GetControllerName());
+      return false;
+    }
     NewAxisMove(aControllerIdx, aAxis, aValue);
     controller->SetAxisMove(aAxis, aValue);
   }
+  return true;
 }
 
 void
 VRSystemManagerOpenVR::HandlePoseTracking(uint32_t aControllerIdx,
                                           const GamepadPoseState& aPose,
                                           VRControllerHost* aController)
 {
   MOZ_ASSERT(aController);
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -141,26 +141,26 @@ public:
                              double aDuration,
                              const VRManagerPromise& aPromise) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
 
 protected:
   VRSystemManagerOpenVR();
 
 private:
-  void HandleButtonPress(uint32_t aControllerIdx,
+  bool HandleButtonPress(uint32_t aControllerIdx,
                          uint32_t aButton,
                          uint64_t aButtonMask,
                          uint64_t aButtonPressed,
                          uint64_t aButtonTouched);
-  void HandleTriggerPress(uint32_t aControllerIdx,
+  bool HandleTriggerPress(uint32_t aControllerIdx,
                           uint32_t aButton,
                           uint32_t aTrigger,
                           float aValue);
-  void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
+  bool HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
                       float aValue);
   void HandlePoseTracking(uint32_t aControllerIdx,
                           const dom::GamepadPoseState& aPose,
                           VRControllerHost* aController);
   dom::GamepadHand GetGamepadHandFromControllerRole(
                           ::vr::ETrackedControllerRole aRole);
   void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
                              ::vr::TrackedDeviceIndex_t aDeviceIndex,
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -707,16 +707,31 @@ VRSystemManagerPuppet::Destroy()
 
 void
 VRSystemManagerPuppet::Shutdown()
 {
   mPuppetHMDs.Clear();
 }
 
 void
+VRSystemManagerPuppet::Run10msTasks()
+{
+  VRSystemManager::Run10msTasks();
+
+  /**
+   * When running headless mochitests on some of our automated test
+   * infrastructure, 2d display vsyncs are not always generated.
+   * To workaround, we produce a vsync manually.
+   */
+  VRManager *vm = VRManager::Get();
+  MOZ_ASSERT(vm);
+  vm->NotifyVsync(TimeStamp::Now());
+}
+
+void
 VRSystemManagerPuppet::NotifyVSync()
 {
   VRSystemManager::NotifyVSync();
 
   for (const auto& display: mPuppetHMDs) {
     display->Refresh();
   }
 }
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -134,16 +134,17 @@ public:
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
                              double aIntensity,
                              double aDuration,
                              const VRManagerPromise& aPromise) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
   virtual void NotifyVSync() override;
+  virtual void Run10msTasks() override;
 
 protected:
   VRSystemManagerPuppet();
 
 private:
   void HandleButtonPress(uint32_t aControllerIdx,
                          uint32_t aButton,
                          uint64_t aButtonMask,
--- a/gfx/vr/ipc/VRGPUChild.cpp
+++ b/gfx/vr/ipc/VRGPUChild.cpp
@@ -38,42 +38,13 @@ VRGPUChild::Get()
   MOZ_ASSERT(IsCreated(), "VRGPUChild haven't initialized yet.");
   return sVRGPUChildSingleton;
 }
 
 /*static*/ void
 VRGPUChild::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (sVRGPUChildSingleton) {
-    sVRGPUChildSingleton->Destroy();
-    sVRGPUChildSingleton = nullptr;
-  }
-}
-
-class DeferredDeleteVRGPUChild : public Runnable
-{
-public:
-  explicit DeferredDeleteVRGPUChild(RefPtr<VRGPUChild> aChild)
-    : Runnable("gfx::DeferredDeleteVRGPUChild")
-    , mChild(std::move(aChild))
-  {
-  }
-
-  NS_IMETHODIMP Run() override {
-    mChild->Close();
-    return NS_OK;
-  }
-
-private:
-  RefPtr<VRGPUChild> mChild;
-};
-
-void
-VRGPUChild::Destroy()
-{
-  // Keep ourselves alive until everything has been shut down
-  RefPtr<VRGPUChild> selfRef = this;
-  NS_DispatchToMainThread(new DeferredDeleteVRGPUChild(this));
+  sVRGPUChildSingleton = nullptr;
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/ipc/VRGPUChild.h
+++ b/gfx/vr/ipc/VRGPUChild.h
@@ -21,18 +21,16 @@ public:
   static bool InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint);
   static bool IsCreated();
   static void ShutDown();
 
 protected:
   explicit VRGPUChild() {}
   ~VRGPUChild() {}
 
-  void Destroy();
-
 private:
   DISALLOW_COPY_AND_ASSIGN(VRGPUChild);
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // GFX_VR_GPU_CHILD_H
\ No newline at end of file
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -128,16 +128,17 @@ VRManagerParent::CreateSameProcess()
 
 bool
 VRManagerParent::CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   MessageLoop* loop = VRListenerThreadHolder::Loop();
 
   RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid(), false);
   vmp->mVRListenerThreadHolder = VRListenerThreadHolder::GetSingleton();
+  vmp->mSelfRef = vmp;
   loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
     "gfx::VRManagerParent::Bind",
     vmp,
     &VRManagerParent::Bind,
     std::move(aEndpoint)));
   return true;
 }
 
@@ -221,17 +222,16 @@ VRManagerParent::RecvSetHaveEventListene
   mHaveEventListener = aHaveEventListener;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvControllerListenerAdded()
 {
   // Force update the available controllers for GamepadManager,
-  // remove the existing controllers and sync them by NotifyVsync().
   VRManager* vm = VRManager::Get();
   vm->RemoveControllers();
   mHaveControllerListener = true;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvControllerListenerRemoved()
@@ -271,35 +271,57 @@ mozilla::ipc::IPCResult
 VRManagerParent::RecvCreateVRServiceTestController(const nsCString& aID, const uint32_t& aPromiseID)
 {
   uint32_t controllerIdx = 1; // ID's are 1 based
   nsTArray<VRControllerInfo> controllerInfoArray;
   impl::VRControllerPuppet* controllerPuppet = nullptr;
   VRManager* vm = VRManager::Get();
 
   /**
-   * When running headless mochitests on some of our automated test
-   * infrastructure, 2d display vsyncs are not always generated.
-   * In this case, the test controllers can't be created immediately
-   * after the VR display was created as the state of the VR displays
-   * are updated during vsync.
-   * To workaround, we produce a vsync manually.
+   * The controller is created asynchronously in the VRListener thread.
+   * We will wait up to kMaxControllerCreationTime milliseconds before
+   * assuming that the controller will never be created.
    */
-  vm->NotifyVsync(TimeStamp::Now());
+  const int kMaxControllerCreationTime = 1000;
+  /**
+   * min(100ms, kVRIdleTaskInterval) * 10 as a very
+   * pessimistic estimation of the maximum duration possible.
+   * It's possible that the IPC message queues could be so busy
+   * that this time is elapsed while still succeeding to create
+   * the controllers; however, in this case the browser would be
+   * locking up for more than a second at a time and something else
+   * has gone horribly wrong.
+   */
+   const int kTestInterval = 10;
+  /**
+   * We will keep checking every kTestInterval milliseconds until
+   * we see the controllers or kMaxControllerCreationTime milliseconds
+   * have elapsed and we will give up.
+   */
 
-  // Get VRControllerPuppet from VRManager
-  vm->GetVRControllerInfo(controllerInfoArray);
-  for (auto& controllerInfo : controllerInfoArray) {
-    if (controllerInfo.GetType() == VRDeviceType::Puppet) {
-      if (controllerIdx == mControllerTestID) {
-        controllerPuppet = static_cast<impl::VRControllerPuppet*>(
-                           vm->GetController(controllerInfo.GetControllerID()).get());
-        break;
+  int testDuration = 0;
+  while (!controllerPuppet && testDuration < kMaxControllerCreationTime) {
+    testDuration += kTestInterval;
+#ifdef XP_WIN
+    Sleep(kTestInterval);
+#else
+    sleep(kTestInterval);
+#endif
+
+    // Get VRControllerPuppet from VRManager
+    vm->GetVRControllerInfo(controllerInfoArray);
+    for (auto& controllerInfo : controllerInfoArray) {
+      if (controllerInfo.GetType() == VRDeviceType::Puppet) {
+        if (controllerIdx == mControllerTestID) {
+          controllerPuppet = static_cast<impl::VRControllerPuppet*>(
+                             vm->GetController(controllerInfo.GetControllerID()).get());
+          break;
+        }
+        ++controllerIdx;
       }
-      ++controllerIdx;
     }
   }
 
   // We might not have a controllerPuppet if the test did
   // not create a VR display first.
   if (!controllerPuppet) {
     // We send a device ID of "0" to indicate failure
     if (SendReplyCreateVRServiceTestController(aID, aPromiseID, 0)) {
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -106,16 +106,20 @@ class VRManagerPromise final
 
 public:
   explicit VRManagerPromise(RefPtr<VRManagerParent> aParent, uint32_t aPromiseID)
   : mParent(aParent), mPromiseID(aPromiseID)
   {}
   ~VRManagerPromise() {
     mParent = nullptr;
   }
+  bool operator==(const VRManagerPromise& aOther) const
+  {
+    return mParent == aOther.mParent && mPromiseID == aOther.mPromiseID;
+  }
 
 private:
   RefPtr<VRManagerParent> mParent;
   uint32_t mPromiseID;
 };
 
 } // namespace mozilla
 } // namespace gfx
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -110,16 +110,19 @@ struct ParamTraits<mozilla::gfx::VRDispl
     WriteParam(aMsg, aParam.mDisplayID);
     WriteParam(aMsg, aParam.mPresentingGroups);
     WriteParam(aMsg, aParam.mGroupMask);
     WriteParam(aMsg, aParam.mFrameId);
     WriteParam(aMsg, aParam.mDisplayState);
     for (size_t i = 0; i < mozilla::ArrayLength(aParam.mLastSensorState); i++) {
       WriteParam(aMsg, aParam.mLastSensorState[i]);
     }
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.mLastFrameStart); i++) {
+      WriteParam(aMsg, aParam.mLastFrameStart[i]);
+    }
     for (size_t i = 0; i < mozilla::ArrayLength(aParam.mControllerState); i++) {
       WriteParam(aMsg, aParam.mControllerState[i]);
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->mType)) ||
@@ -130,16 +133,21 @@ struct ParamTraits<mozilla::gfx::VRDispl
         !ReadParam(aMsg, aIter, &(aResult->mDisplayState))) {
       return false;
     }
     for (size_t i = 0; i < mozilla::ArrayLength(aResult->mLastSensorState); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mLastSensorState[i]))) {
         return false;
       }
     }
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->mLastFrameStart); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mLastFrameStart[i]))) {
+        return false;
+      }
+    }
     for (size_t i = 0; i < mozilla::ArrayLength(aResult->mControllerState); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mControllerState[i]))) {
         return false;
       }
     }
     return true;
   }
 };
--- a/gfx/vr/service/OpenVRSession.cpp
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -3,25 +3,31 @@
 
 #if defined(XP_WIN)
 #include <d3d11.h>
 #include "mozilla/gfx/DeviceManagerDx.h"
 #endif // defined(XP_WIN)
 
 #include "mozilla/dom/GamepadEventTypes.h"
 #include "mozilla/dom/GamepadBinding.h"
+#include "VRThread.h"
 
 #if !defined(M_PI)
 #define M_PI 3.14159265358979323846264338327950288
 #endif
 
 #define BTN_MASK_FROM_ID(_id) \
   ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
 
-static const uint32_t kNumOpenVRHaptcs = 1;
+// Haptic feedback is updated every 5ms, as this is
+// the minimum period between new haptic pulse requests.
+// Effectively, this results in a pulse width modulation
+// with an interval of 5ms.  Through experimentation, the
+// maximum duty cycle was found to be about 3.9ms
+const uint32_t kVRHapticUpdateInterval = 5;
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gfx {
 
 namespace {
 
@@ -93,19 +99,22 @@ UpdateTrigger(VRControllerState& aState,
 
 }; // anonymous namespace
 
 OpenVRSession::OpenVRSession()
   : VRSession()
   , mVRSystem(nullptr)
   , mVRChaperone(nullptr)
   , mVRCompositor(nullptr)
-  , mControllerDeviceIndex{0}
+  , mControllerDeviceIndex{}
+  , mHapticPulseRemaining{}
+  , mHapticPulseIntensity{}
   , mShouldQuit(false)
   , mIsWindowsMR(false)
+  , mControllerHapticStateMutex("OpenVRSession::mControllerHapticStateMutex")
 {
 }
 
 OpenVRSession::~OpenVRSession()
 {
   Shutdown();
 }
 
@@ -157,16 +166,19 @@ OpenVRSession::Initialize(mozilla::gfx::
   // Configure coordinate system
   mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
 
   if (!InitState(aSystemState)) {
     Shutdown();
     return false;
   }
 
+  StartHapticThread();
+  StartHapticTimer();
+  
   // Succeeded
   return true;
 }
 
 #if defined(XP_WIN)
 bool
 OpenVRSession::CreateD3DObjects()
 {
@@ -179,16 +191,18 @@ OpenVRSession::CreateD3DObjects()
   }
   return true;
 }
 #endif
 
 void
 OpenVRSession::Shutdown()
 {
+  StopHapticTimer();
+  StopHapticThread();
   if (mVRSystem || mVRCompositor || mVRSystem) {
     ::vr::VR_Shutdown();
     mVRCompositor = nullptr;
     mVRChaperone = nullptr;
     mVRSystem = nullptr;
   }
 }
 
@@ -375,16 +389,18 @@ OpenVRSession::UpdateHeadsetPose(VRSyste
   }
 }
 
 void
 OpenVRSession::EnumerateControllers(VRSystemState& aState)
 {
   MOZ_ASSERT(mVRSystem);
 
+  MutexAutoLock lock(mControllerHapticStateMutex);
+
   bool controllerPresent[kVRControllerMaxCount] = { false };
 
   // Basically, we would have HMDs in the tracked devices,
   // but we are just interested in the controllers.
   for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
        trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
 
     if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
@@ -487,17 +503,17 @@ OpenVRSession::EnumerateControllers(VRSy
       }
 
       nsCString deviceId;
       GetControllerDeviceId(deviceType, trackedDevice, deviceId);
 
       strncpy(controllerState.controllerName, deviceId.BeginReading(), kVRControllerNameMaxLen);
       controllerState.numButtons = numButtons;
       controllerState.numAxes = numAxes;
-      controllerState.numHaptics = kNumOpenVRHaptcs;
+      controllerState.numHaptics = kNumOpenVRHaptics;
 
       // If the Windows MR controller doesn't has the amount
       // of buttons or axes as our expectation, switching off
       // the workaround for Windows MR.
       if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
         mIsWindowsMR = false;
         NS_WARNING("OpenVR - Switching off Windows MR mode.");
       }
@@ -755,17 +771,16 @@ OpenVRSession::GetControllerDeviceId(::v
 void
 OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState)
 {
   UpdateHeadsetPose(aSystemState);
   UpdateEyeParameters(aSystemState);
   EnumerateControllers(aSystemState);
   UpdateControllerButtons(aSystemState);
   UpdateControllerPoses(aSystemState);
-  aSystemState.sensorState.inputFrameID++;
 }
 
 bool
 OpenVRSession::ShouldQuit() const
 {
   return mShouldQuit;
 }
 
@@ -925,10 +940,158 @@ OpenVRSession::StopPresentation()
 }
 
 bool
 OpenVRSession::StartPresentation()
 {
   return true;
 }
 
+void
+OpenVRSession::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+                             float aIntensity, float aDuration)
+{
+  MutexAutoLock lock(mControllerHapticStateMutex);
+  if (aHapticIndex >= kNumOpenVRHaptics ||
+      aControllerIdx >= kVRControllerMaxCount) {
+    return;
+  }
+
+  ::vr::TrackedDeviceIndex_t deviceIndex = mControllerDeviceIndex[aControllerIdx];
+  if (deviceIndex == 0) {
+    return;
+  }
+
+  mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
+  mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
+
+  /**
+   *  TODO - The haptic feedback pulses will have latency of one frame and we
+   *         are simulating intensity with pulse-width modulation.
+   *         We should use of the OpenVR Input API to correct this
+   *         and replace the TriggerHapticPulse calls which have been
+   *         deprecated.
+   */
+}
+
+void
+OpenVRSession::StartHapticThread()
+{
+  if (!mHapticThread) {
+    mHapticThread = new VRThread(NS_LITERAL_CSTRING("VR_OpenVR_Haptics"));
+  }
+  mHapticThread->Start();
+}
+
+void
+OpenVRSession::StopHapticThread()
+{
+  if (mHapticThread) {
+    mHapticThread->Shutdown();
+    mHapticThread = nullptr;
+  }
+}
+
+void
+OpenVRSession::StartHapticTimer()
+{
+  if (!mHapticTimer && mHapticThread) {
+    mLastHapticUpdate = TimeStamp();
+    mHapticTimer = NS_NewTimer();
+    mHapticTimer->SetTarget(mHapticThread->GetThread()->EventTarget());
+    mHapticTimer->InitWithNamedFuncCallback(
+      HapticTimerCallback,
+      this,
+      kVRHapticUpdateInterval,
+      nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
+      "OpenVRSession::HapticTimerCallback");
+  }
+}
+
+void
+OpenVRSession::StopHapticTimer()
+{
+  if (mHapticTimer) {
+    mHapticTimer->Cancel();
+    mHapticTimer = nullptr;
+  }
+}
+
+/*static*/ void
+OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  /**
+   * It is safe to use the pointer passed in aClosure to reference the
+   * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
+   * which is called by the OpenVRSession destructor, guaranteeing
+   * that this function runs if and only if the VRManager object is valid.
+   */
+  OpenVRSession* self = static_cast<OpenVRSession*>(aClosure);
+  self->UpdateHaptics();
+}
+
+void
+OpenVRSession::UpdateHaptics()
+{
+  MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread());
+  MOZ_ASSERT(mVRSystem);
+
+  MutexAutoLock lock(mControllerHapticStateMutex);
+
+  TimeStamp now = TimeStamp::Now();
+  if (mLastHapticUpdate.IsNull()) {
+    mLastHapticUpdate = now;
+    return;
+  }
+  float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
+  mLastHapticUpdate = now;
+
+  for (int iController = 0; iController < kVRControllerMaxCount; iController++) {
+    for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
+      ::vr::TrackedDeviceIndex_t deviceIndex = mControllerDeviceIndex[iController];
+      if (deviceIndex == 0) {
+        continue;
+      }
+      float intensity = mHapticPulseIntensity[iController][iHaptic];
+      float duration = mHapticPulseRemaining[iController][iHaptic];
+      if (duration <= 0.0f || intensity <= 0.0f) {
+        continue;
+      }
+      // We expect OpenVR to vibrate for 5 ms, but we found it only response the
+      // commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
+      // to a loop of 3.9 ms for make users feel that is a continuous events.
+      const float microSec = (duration < 0.0039f ? duration : 0.0039f) * 1000000.0f * intensity;
+      mVRSystem->TriggerHapticPulse(deviceIndex, iHaptic, (uint32_t)microSec);
+
+      duration -= deltaTime;
+      if (duration < 0.0f) {
+        duration = 0.0f;
+      }
+      mHapticPulseRemaining[iController][iHaptic] = duration;
+    }
+  }
+}
+
+void
+OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx)
+{
+  MutexAutoLock lock(mControllerHapticStateMutex);
+  if (aControllerIdx >= kVRControllerMaxCount) {
+    return;
+  }
+  for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
+    mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
+  }
+}
+
+void
+OpenVRSession::StopAllHaptics()
+{
+  MutexAutoLock lock(mControllerHapticStateMutex);
+  for (auto& controller : mHapticPulseRemaining) {
+    for (auto& haptic : controller) {
+      haptic = 0.0f;
+    }
+  }
+}
+
 } // namespace mozilla
 } // namespace gfx
--- a/gfx/vr/service/OpenVRSession.h
+++ b/gfx/vr/service/OpenVRSession.h
@@ -6,50 +6,62 @@
 
 #ifndef GFX_VR_SERVICE_OPENVRSESSION_H
 #define GFX_VR_SERVICE_OPENVRSESSION_H
 
 #include "VRSession.h"
 
 #include "openvr.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/TimeStamp.h"
 #include "moz_external_vr.h"
 
 #if defined(XP_WIN)
 #include <d3d11_1.h>
 #elif defined(XP_MACOSX)
 class MacIOSurface;
 #endif
+class nsITimer;
 
 namespace mozilla {
 namespace gfx {
+class VRThread;
+
+static const int kNumOpenVRHaptics = 1;
 
 class OpenVRSession : public VRSession
 {
 public:
   OpenVRSession();
   virtual ~OpenVRSession();
 
   bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
   bool ShouldQuit() const override;
   bool StartPresentation() override;
   void StopPresentation() override;
   bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override;
+  void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+                    float aIntensity, float aDuration) override;
+  void StopVibrateHaptic(uint32_t aControllerIdx) override;
+  void StopAllHaptics() override;
 
 private:
   // OpenVR State
   ::vr::IVRSystem* mVRSystem = nullptr;
   ::vr::IVRChaperone* mVRChaperone = nullptr;
   ::vr::IVRCompositor* mVRCompositor = nullptr;
   ::vr::TrackedDeviceIndex_t mControllerDeviceIndex[kVRControllerMaxCount];
+  float mHapticPulseRemaining[kVRControllerMaxCount][kNumOpenVRHaptics];
+  float mHapticPulseIntensity[kVRControllerMaxCount][kNumOpenVRHaptics];
   bool mShouldQuit;
   bool mIsWindowsMR;
+  TimeStamp mLastHapticUpdate;
 
   bool InitState(mozilla::gfx::VRSystemState& aSystemState);
   void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
   void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
   void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
   void EnumerateControllers(VRSystemState& aState);
   void UpdateControllerPoses(VRSystemState& aState);
   void UpdateControllerButtons(VRSystemState& aState);
@@ -59,14 +71,23 @@ private:
                    const VRLayerEyeRect& aLeftEyeRect,
                    const VRLayerEyeRect& aRightEyeRect);
 #if defined(XP_WIN)
   bool CreateD3DObjects();
 #endif
   void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
                              ::vr::TrackedDeviceIndex_t aDeviceIndex,
                              nsCString& aId);
+  void UpdateHaptics();
+  void StartHapticThread();
+  void StopHapticThread();
+  void StartHapticTimer();
+  void StopHapticTimer();
+  static void HapticTimerCallback(nsITimer* aTimer, void* aClosure);
+  RefPtr<nsITimer> mHapticTimer;
+  RefPtr<VRThread> mHapticThread;
+  mozilla::Mutex mControllerHapticStateMutex;
 };
 
 } // namespace mozilla
 } // namespace gfx
 
 #endif // GFX_VR_SERVICE_OPENVRSESSION_H
--- a/gfx/vr/service/VRService.cpp
+++ b/gfx/vr/service/VRService.cpp
@@ -3,16 +3,17 @@
 /* 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 "VRService.h"
 #include "OpenVRSession.h"
 #include "gfxPrefs.h"
 #include "base/thread.h"                // for Thread
+#include <cstring>                      // for memcmp
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace std;
 
 namespace {
 
 int64_t
@@ -52,20 +53,23 @@ VRService::Create()
 
   RefPtr<VRService> service = new VRService();
   return service.forget();
 }
 
 VRService::VRService()
  : mSystemState{}
  , mBrowserState{}
+ , mBrowserGeneration(0)
  , mServiceThread(nullptr)
  , mShutdownRequested(false)
  , mAPIShmem(nullptr)
  , mTargetShmemFile(0)
+ , mLastHapticState{}
+ , mFrameStartTime{}
 {
   // When we have the VR process, we map the memory
   // of mAPIShmem from GPU process.
   // If we don't have the VR process, we will instantiate
   // mAPIShmem in VRService.
   if (!gfxPrefs::VRProcessEnabled()) {
     mAPIShmem = new VRExternalShmem();
     memset(mAPIShmem, 0, sizeof(VRExternalShmem));
@@ -285,67 +289,116 @@ VRService::ServiceWaitForImmersive()
 
 void
 VRService::ServiceImmersiveMode()
 {
   MOZ_ASSERT(IsInServiceThread());
   MOZ_ASSERT(mSession);
 
   mSession->ProcessEvents(mSystemState);
+  UpdateHaptics();
   PushState(mSystemState);
   PullState(mBrowserState);
 
   if (mSession->ShouldQuit() || mShutdownRequested) {
     // Shut down
     MessageLoop::current()->PostTask(NewRunnableMethod(
       "gfx::VRService::ServiceShutdown",
       this, &VRService::ServiceShutdown
     ));
     return;
   } else if (!IsImmersiveContentActive(mBrowserState)) {
     // Exit immersive mode
+    mSession->StopAllHaptics();
     mSession->StopPresentation();
     MessageLoop::current()->PostTask(NewRunnableMethod(
       "gfx::VRService::ServiceWaitForImmersive",
       this, &VRService::ServiceWaitForImmersive
     ));
     return;
   }
 
   uint64_t newFrameId = FrameIDFromBrowserState(mBrowserState);
   if (newFrameId != mSystemState.displayState.mLastSubmittedFrameId) {
     // A new immersive frame has been received.
     // Submit the textures to the VR system compositor.
     bool success = false;
     for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
       const VRLayerState& layer = mBrowserState.layerState[iLayer];
       if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+        // SubmitFrame may block in order to control the timing for
+        // the next frame start
         success = mSession->SubmitFrame(layer.layer_stereo_immersive);
         break;
       }
     }
 
     // Changing mLastSubmittedFrameId triggers a new frame to start
     // rendering.  Changes to mLastSubmittedFrameId and the values
     // used for rendering, such as headset pose, must be pushed
     // atomically to the browser.
     mSystemState.displayState.mLastSubmittedFrameId = newFrameId;
     mSystemState.displayState.mLastSubmittedFrameSuccessful = success;
+
+    // StartFrame may block to control the timing for the next frame start
     mSession->StartFrame(mSystemState);
+    mSystemState.sensorState.inputFrameID++;
+    size_t historyIndex = mSystemState.sensorState.inputFrameID % ArrayLength(mFrameStartTime);
+    mFrameStartTime[historyIndex] = TimeStamp::Now();
     PushState(mSystemState);
   }
 
   // Continue immersive mode
   MessageLoop::current()->PostTask(NewRunnableMethod(
     "gfx::VRService::ServiceImmersiveMode",
     this, &VRService::ServiceImmersiveMode
   ));
 }
 
 void
+VRService::UpdateHaptics()
+{
+  MOZ_ASSERT(IsInServiceThread());
+  MOZ_ASSERT(mSession);
+  
+  for (size_t i = 0; i < ArrayLength(mBrowserState.hapticState); i++) {
+    VRHapticState& state = mBrowserState.hapticState[i];
+    VRHapticState& lastState = mLastHapticState[i];
+    // Note that VRHapticState is asserted to be a POD type, thus memcmp is safe
+    if (memcmp(&state, &lastState, sizeof(VRHapticState)) == 0) {
+      // No change since the last update
+      continue;
+    }
+    if (state.inputFrameID == 0) {
+      // The haptic feedback was stopped
+      mSession->StopVibrateHaptic(state.controllerIndex);
+    } else {
+      TimeStamp now;
+      if (now.IsNull()) {
+        // TimeStamp::Now() is expensive, so we
+        // must call it only when needed and save the
+        // output for further loop iterations.
+        now = TimeStamp::Now();
+      }
+      // This is a new haptic pulse, or we are overriding a prior one
+      size_t historyIndex = state.inputFrameID % ArrayLength(mFrameStartTime);
+      float startOffset = (float)(now - mFrameStartTime[historyIndex]).ToSeconds();
+
+      // state.pulseStart is guaranteed never to be in the future
+      mSession->VibrateHaptic(state.controllerIndex,
+                              state.hapticIndex,
+                              state.pulseIntensity,
+                              state.pulseDuration + state.pulseStart - startOffset);
+    }
+    // Record the state for comparison in the next run
+    memcpy(&lastState, &state, sizeof(VRHapticState));
+  }
+}
+
+void
 VRService::PushState(const mozilla::gfx::VRSystemState& aState)
 {
   if (!mAPIShmem) {
     return;
   }
   // Copying the VR service state to the shmem is atomic, infallable,
   // and non-blocking on x86/x64 architectures.  Arm requires a mutex
   // that is locked for the duration of the memcpy to and from shmem on
@@ -380,19 +433,22 @@ VRService::PullState(mozilla::gfx::VRBro
 
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
       memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
       pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
     }
 #else
   VRExternalShmem tmp;
-  memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem));
-  if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) {
-    memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
+  if (mAPIShmem->browserGenerationA != mBrowserGeneration) {
+    memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem));
+    if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) {
+      memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
+      mBrowserGeneration = tmp.browserGenerationA;
+    }
   }
 #endif
 }
 
 VRExternalShmem*
 VRService::GetAPIShmem()
 {
   return mAPIShmem;
--- a/gfx/vr/service/VRService.h
+++ b/gfx/vr/service/VRService.h
@@ -14,16 +14,18 @@
 namespace base {
 class Thread;
 } // namespace base
 namespace mozilla {
 namespace gfx {
 
 class VRSession;
 
+static const int kVRFrameTimingHistoryDepth = 100;
+
 class VRService
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRService)
   static already_AddRefed<VRService> Create();
 
   void Start();
   void Stop();
@@ -46,25 +48,29 @@ private:
    * by the browser.
    */
   VRSystemState mSystemState;
   /**
    * VRBrowserState contains the most recent state of the browser.
    * mBrowserState is memcpy'ed from the Shmem atomically
    */
   VRBrowserState mBrowserState;
+  int64_t mBrowserGeneration;
 
   UniquePtr<VRSession> mSession;
   base::Thread* mServiceThread;
   bool mShutdownRequested;
 
   VRExternalShmem* MOZ_OWNING_REF mAPIShmem;
   base::ProcessHandle mTargetShmemFile;
+  VRHapticState mLastHapticState[kVRHapticsMaxCount];
+  TimeStamp mFrameStartTime[kVRFrameTimingHistoryDepth];
 
   bool IsInServiceThread();
+  void UpdateHaptics();
 
   /**
    * The VR Service thread is a state machine that always has one
    * task queued depending on the state.
    *
    * VR Service thread state task functions:
    */
   void ServiceInitialize();
--- a/gfx/vr/service/VRSession.h
+++ b/gfx/vr/service/VRSession.h
@@ -29,16 +29,20 @@ public:
   virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual void Shutdown() = 0;
   virtual void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual void StartFrame(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual bool ShouldQuit() const = 0;
   virtual bool StartPresentation() = 0;
   virtual void StopPresentation() = 0;
   virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) = 0;
+  virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+                             float aIntensity, float aDuration) = 0;
+  virtual void StopVibrateHaptic(uint32_t aControllerIdx) = 0;
+  virtual void StopAllHaptics() = 0;
 
 #if defined(XP_WIN)
 protected:
   bool CreateD3DContext(RefPtr<ID3D11Device> aDevice);
   RefPtr<ID3D11Device1> mDevice;
   RefPtr<ID3D11DeviceContext1> mContext;
   ID3D11Device1* GetD3DDevice();
   ID3D11DeviceContext1* GetD3DDeviceContext();
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -96,44 +96,49 @@ ImageFormatToSurfaceFormat(ImageFormat a
 struct ImageDescriptor: public wr::WrImageDescriptor {
   // We need a default constructor for ipdl serialization.
   ImageDescriptor()
   {
     format = (ImageFormat)0;
     width = 0;
     height = 0;
     stride = 0;
-    is_opaque = false;
+    opacity = OpacityType::HasAlphaChannel;
   }
 
   ImageDescriptor(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat)
   {
     format = wr::SurfaceFormatToImageFormat(aFormat).value();
     width = aSize.width;
     height = aSize.height;
     stride = 0;
-    is_opaque = gfx::IsOpaqueFormat(aFormat);
+    opacity = gfx::IsOpaqueFormat(aFormat) ? OpacityType::Opaque
+                                           : OpacityType::HasAlphaChannel;
   }
 
   ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride, gfx::SurfaceFormat aFormat)
   {
     format = wr::SurfaceFormatToImageFormat(aFormat).value();
     width = aSize.width;
     height = aSize.height;
     stride = aByteStride;
-    is_opaque = gfx::IsOpaqueFormat(aFormat);
+    opacity = gfx::IsOpaqueFormat(aFormat) ? OpacityType::Opaque
+                                           : OpacityType::HasAlphaChannel;
   }
 
-  ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride, gfx::SurfaceFormat aFormat, bool opaque)
+  ImageDescriptor(const gfx::IntSize& aSize,
+                  uint32_t aByteStride,
+                  gfx::SurfaceFormat aFormat,
+                  OpacityType aOpacity)
   {
     format = wr::SurfaceFormatToImageFormat(aFormat).value();
     width = aSize.width;
     height = aSize.height;
     stride = aByteStride;
-    is_opaque = opaque;
+    opacity = aOpacity;
   }
 
 };
 
 // Whenever possible, use wr::WindowId instead of manipulating uint64_t.
 inline uint64_t AsUint64(const WindowId& aId) {
   return static_cast<uint64_t>(aId.mHandle);
 }
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -33,16 +33,24 @@ use core_graphics::font::CGFont;
 pub enum WrExternalImageBufferType {
     TextureHandle = 0,
     TextureRectHandle = 1,
     TextureArrayHandle = 2,
     TextureExternalHandle = 3,
     ExternalBuffer = 4,
 }
 
+/// Used to indicate if an image is opaque, or has an alpha channel.
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum OpacityType {
+    Opaque = 0,
+    HasAlphaChannel = 1,
+}
+
 impl WrExternalImageBufferType {
     fn to_wr(self) -> ExternalImageType {
         match self {
             WrExternalImageBufferType::TextureHandle =>
                 ExternalImageType::TextureHandle(TextureTarget::Default),
             WrExternalImageBufferType::TextureRectHandle =>
                 ExternalImageType::TextureHandle(TextureTarget::Rect),
             WrExternalImageBufferType::TextureArrayHandle =>
@@ -278,30 +286,30 @@ impl From<ImageMask> for WrImageMask {
 
 #[repr(C)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct WrImageDescriptor {
     pub format: ImageFormat,
     pub width: u32,
     pub height: u32,
     pub stride: u32,
-    pub is_opaque: bool,
+    pub opacity: OpacityType,
 }
 
 impl<'a> Into<ImageDescriptor> for &'a WrImageDescriptor {
     fn into(self) -> ImageDescriptor {
         ImageDescriptor {
             size: DeviceUintSize::new(self.width, self.height),
             stride: if self.stride != 0 {
                 Some(self.stride)
             } else {
                 None
             },
             format: self.format,
-            is_opaque: self.is_opaque,
+            is_opaque: self.opacity == OpacityType::Opaque,
             offset: 0,
             allow_mipmaps: false,
         }
     }
 }
 
 /// cbindgen:field-names=[mHandle]
 #[repr(C)]
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -160,16 +160,24 @@ enum class MixBlendMode : uint32_t {
   Hue = 12,
   Saturation = 13,
   Color = 14,
   Luminosity = 15,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
+// Used to indicate if an image is opaque, or has an alpha channel.
+enum class OpacityType : uint8_t {
+  Opaque = 0,
+  HasAlphaChannel = 1,
+
+  Sentinel /* this must be last for serialization purposes. */
+};
+
 enum class RepeatMode : uint32_t {
   Stretch,
   Repeat,
   Round,
   Space,
 
   Sentinel /* this must be last for serialization purposes. */
 };
@@ -1006,24 +1014,24 @@ struct WrExternalImageHandler {
   }
 };
 
 struct WrImageDescriptor {
   ImageFormat format;
   uint32_t width;
   uint32_t height;
   uint32_t stride;
-  bool is_opaque;
+  OpacityType opacity;
 
   bool operator==(const WrImageDescriptor& aOther) const {
     return format == aOther.format &&
            width == aOther.width &&
            height == aOther.height &&
            stride == aOther.stride &&
-           is_opaque == aOther.is_opaque;
+           opacity == aOther.opacity;
   }
 };
 
 using NormalizedRect = TypedRect<float, NormalizedCoordinates>;
 
 struct WrTransformProperty {
   uint64_t id;
   LayoutTransform transform;
--- a/gfx/ycbcr/YCbCrUtils.cpp
+++ b/gfx/ycbcr/YCbCrUtils.cpp
@@ -101,70 +101,72 @@ ConvertYCbCrToRGB(const layers::PlanarYC
              (aData.mCbCrSize.height == aData.mYSize.height ||
               aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1));
 
   // Used if converting to 8 bits YUV.
   UniquePtr<uint8_t[]> yChannel;
   UniquePtr<uint8_t[]> cbChannel;
   UniquePtr<uint8_t[]> crChannel;
   layers::PlanarYCbCrData dstData;
-  const layers::PlanarYCbCrData& srcData = aData.mBitDepth == 8 ? aData : dstData;
+  const layers::PlanarYCbCrData& srcData =
+    aData.mColorDepth == ColorDepth::COLOR_8 ? aData : dstData;
 
-  if (aData.mBitDepth != 8) {
-    MOZ_ASSERT(aData.mBitDepth > 8 && aData.mBitDepth <= 16);
+  if (aData.mColorDepth != ColorDepth::COLOR_8) {
     // Convert to 8 bits data first.
     dstData.mPicSize = aData.mPicSize;
     dstData.mPicX = aData.mPicX;
     dstData.mPicY = aData.mPicY;
     dstData.mYSize = aData.mYSize;
     // We align the destination stride to 32 bytes, so that libyuv can use
     // SSE optimised code.
     dstData.mYStride = (aData.mYSize.width + 31) & ~31;
     dstData.mCbCrSize = aData.mCbCrSize;
     dstData.mCbCrStride = (aData.mCbCrSize.width + 31) & ~31;
     dstData.mYUVColorSpace = aData.mYUVColorSpace;
-    dstData.mBitDepth = 8;
+    dstData.mColorDepth = ColorDepth::COLOR_8;
 
     size_t ySize = GetAlignedStride<1>(dstData.mYStride, aData.mYSize.height);
     size_t cbcrSize =
       GetAlignedStride<1>(dstData.mCbCrStride, aData.mCbCrSize.height);
     if (ySize == 0 || cbcrSize == 0) {
       return;
     }
     yChannel = MakeUnique<uint8_t[]>(ySize);
     cbChannel = MakeUnique<uint8_t[]>(cbcrSize);
     crChannel = MakeUnique<uint8_t[]>(cbcrSize);
 
     dstData.mYChannel = yChannel.get();
     dstData.mCbChannel = cbChannel.get();
     dstData.mCrChannel = crChannel.get();
 
+    int bitDepth = BitDepthForColorDepth(aData.mColorDepth);
+
     ConvertYCbCr16to8Line(dstData.mYChannel,
                           dstData.mYStride,
                           reinterpret_cast<uint16_t*>(aData.mYChannel),
                           aData.mYStride / 2,
                           aData.mYSize.width,
                           aData.mYSize.height,
-                          aData.mBitDepth);
+                          bitDepth);
 
     ConvertYCbCr16to8Line(dstData.mCbChannel,
                           dstData.mCbCrStride,
                           reinterpret_cast<uint16_t*>(aData.mCbChannel),
                           aData.mCbCrStride / 2,
                           aData.mCbCrSize.width,
                           aData.mCbCrSize.height,
-                          aData.mBitDepth);
+                          bitDepth);
 
     ConvertYCbCr16to8Line(dstData.mCrChannel,
                           dstData.mCbCrStride,
                           reinterpret_cast<uint16_t*>(aData.mCrChannel),
                           aData.mCbCrStride / 2,
                           aData.mCbCrSize.width,
                           aData.mCbCrSize.height,
-                          aData.mBitDepth);
+                          bitDepth);
   }
 
   YUVType yuvtype =
     TypeFromSize(srcData.mYSize.width,
                  srcData.mYSize.height,
                  srcData.mCbCrSize.width,
                  srcData.mCbCrSize.height);
 
--- a/intl/l10n/docs/fluent_tutorial.rst
+++ b/intl/l10n/docs/fluent_tutorial.rst
@@ -651,16 +651,17 @@ select the strategy to be used:
    This strategy replaces all Latin characters with their accented equivalents,
    and duplicates some vowels to create roughly 30% longer strings.
 
 
  - :js:`bidi` - ɥsıʅƃuƎ ıpıԐ
 
    This strategy replaces all Latin characters with their 180 degree rotated versions
    and enforces right to left text flow using Unicode UAX#9 `Explicit Directional Embeddings`__.
+   In this mode, the UI directionality will also be set to right-to-left.
 
 __ https://www.unicode.org/reports/tr9/#Explicit_Directional_Embeddings
 
 Inner Structure of Fluent
 =========================
 
 The inner structure of Fluent in Gecko is out of scope of this tutorial, but
 since the class and file names may show up during debugging or profiling,
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -411,23 +411,37 @@ LocaleService::FilterMatches(const nsTAr
       HANDLE_STRATEGY;
     }
   }
 }
 
 bool
 LocaleService::IsAppLocaleRTL()
 {
-  nsAutoCString locale;
-  GetAppLocaleAsBCP47(locale);
-
+  // First, let's check if there's a manual override
+  // preference for directionality set.
   int pref = Preferences::GetInt("intl.uidirection", -1);
   if (pref >= 0) {
     return (pref > 0);
   }
+
+  // If not, check if there is a pseudo locale `bidi`
+  // set.
+  nsAutoCString locale;
+  if (NS_SUCCEEDED(Preferences::GetCString("intl.l10n.pseudo", locale))) {
+    if (locale.EqualsLiteral("bidi")) {
+      return true;
+    }
+    if (locale.EqualsLiteral("accented")) {
+      return false;
+    }
+  }
+
+  GetAppLocaleAsBCP47(locale);
+
   return uloc_isRightToLeft(locale.get());
 }
 
 NS_IMETHODIMP
 LocaleService::Observe(nsISupports *aSubject, const char *aTopic,
                       const char16_t *aData)
 {
   MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
--- a/intl/locale/tests/unit/test_localeService.js
+++ b/intl/locale/tests/unit/test_localeService.js
@@ -137,16 +137,41 @@ add_test(function test_requestedLocales(
 });
 
 add_test(function test_isAppLocaleRTL() {
   Assert.ok(typeof localeService.isAppLocaleRTL === 'boolean');
 
   run_next_test();
 });
 
+add_test(function test_isAppLocaleRTL_pseudo() {
+  let avLocales = localeService.availableLocales;
+  let reqLocales = localeService.requestedLocales;
+
+  localeService.availableLocales = ["en-US"];
+  localeService.requestedLocales = ["en-US"];
+  Services.prefs.setIntPref("intl.uidirection", -1);
+  Services.prefs.setCharPref("intl.l10n.pseudo", "");
+
+  Assert.ok(localeService.isAppLocaleRTL === false);
+
+  Services.prefs.setCharPref("intl.l10n.pseudo", "bidi");
+  Assert.ok(localeService.isAppLocaleRTL === true);
+
+  Services.prefs.setCharPref("intl.l10n.pseudo", "accented");
+  Assert.ok(localeService.isAppLocaleRTL === false);
+
+  // Clean up
+  localeService.availableLocales = avLocales;
+  localeService.requestedLocales = reqLocales;
+  Services.prefs.clearUserPref("intl.l10n.pseudo");
+
+  run_next_test();
+});
+
 add_test(function test_packagedLocales() {
   const locales = localeService.packagedLocales;
   Assert.ok(locales.length !== 0, "Packaged locales are empty");
   run_next_test();
 });
 
 add_test(function test_availableLocales() {
   const avLocales = localeService.availableLocales;
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -1383,23 +1383,30 @@ AccessibleCaretManager::DispatchCaretSta
     commonAncestorFrame = commonAncestorNode->AsContent()->GetPrimaryFrame();
   }
 
   if (commonAncestorFrame && rootFrame) {
     nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame, rect);
     nsRect clampedRect = nsLayoutUtils::ClampRectToScrollFrames(commonAncestorFrame,
                                                                 rect);
     nsLayoutUtils::TransformRect(commonAncestorFrame, rootFrame, clampedRect);
-    domRect->SetLayoutRect(clampedRect);
+    rect = clampedRect;
     init.mSelectionVisible = !clampedRect.IsEmpty();
   } else {
-    domRect->SetLayoutRect(rect);
     init.mSelectionVisible = true;
   }
 
+  // The rect computed above is relative to rootFrame, which is the (layout)
+  // viewport frame. However, the consumers of this event expect the bounds
+  // of the selection relative to the screen (visual viewport origin), so
+  // translate between the two.
+  rect -= mPresShell->GetVisualViewportOffsetRelativeToLayoutViewport();
+
+  domRect->SetLayoutRect(rect);
+
   // Send isEditable info w/ event detail. This info can help determine
   // whether to show cut command on selection dialog or not.
   init.mSelectionEditable = commonAncestorFrame &&
     GetEditingHostForFrame(commonAncestorFrame);
 
   init.mBoundingClientRect = domRect;
   init.mReason = aReason;
   init.mCollapsed = sel->IsCollapsed();
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -3427,27 +3427,33 @@ ComputeWhereToScroll(int16_t aWhereToScr
   *aRangeMax = std::max(resultCoord, aRectMin);
   return resultCoord;
 }
 
 /**
  * This function takes a scrollable frame, a rect in the coordinate system
  * of the scrolled frame, and a desired percentage-based scroll
  * position and attempts to scroll the rect to that position in the
- * scrollport.
+ * visual viewport.
  *
  * This needs to work even if aRect has a width or height of zero.
+ *
+ * Note that, since we are performing a layout scroll, it's possible that
+ * this fnction will sometimes be unsuccessful; the content will move as
+ * fast as it can on the screen using layout viewport scrolling, and then
+ * stop there, even if it could get closer to the desired position by
+ * moving the visual viewport within the layout viewport.
  */
 static void ScrollToShowRect(nsIScrollableFrame*      aFrameAsScrollable,
                              const nsRect&            aRect,
                              nsIPresShell::ScrollAxis aVertical,
                              nsIPresShell::ScrollAxis aHorizontal,
                              uint32_t                 aFlags)
 {
-  nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition();
+  nsPoint scrollPt = aFrameAsScrollable->GetVisualViewportOffset();
   nsRect visibleRect(scrollPt,
                      aFrameAsScrollable->GetVisualViewportSize());
 
   nsSize lineSize;
   // Don't call GetLineScrollAmount unless we actually need it. Not only
   // does this save time, but it's not safe to call GetLineScrollAmount
   // during reflow (because it depends on font size inflation and doesn't
   // use the in-reflow-safe font-size inflation path). If we did call it,
@@ -10601,16 +10607,26 @@ nsIPresShell::SetVisualViewportSize(nsco
 
     if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) {
       rootScrollFrame->MarkScrollbarsDirtyForReflow();
     }
     MarkFixedFramesForReflow(nsIPresShell::eResize);
   }
 }
 
+nsPoint
+nsIPresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const
+{
+   nsPoint result;
+   if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
+     result = GetVisualViewportOffset() - sf->GetScrollPosition();
+   }
+   return result;
+}
+
 void
 nsIPresShell::RecomputeFontSizeInflationEnabled()
 {
   mFontSizeInflationEnabled = DetermineFontSizeInflationState();
 
   float fontScale = nsLayoutUtils::SystemFontScale();
   if (fontScale == 0.0f) {
     return;
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1672,16 +1672,18 @@ public:
   void SetVisualViewportOffset(const nsPoint& aScrollOffset) {
     mVisualViewportOffset = aScrollOffset;
   }
 
   nsPoint GetVisualViewportOffset() const {
     return mVisualViewportOffset;
   }
 
+  nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const;
+
   virtual void WindowSizeMoveDone() = 0;
   virtual void SysColorChanged() = 0;
   virtual void ThemeChanged() = 0;
   virtual void BackingScaleFactorChanged() = 0;
 
   /**
    * Documents belonging to an invisible DocShell must not be painted ever.
    */
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2936,16 +2936,28 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     !isScrollOriginDowngrade;
   if (allowScrollOriginChange) {
     mLastScrollOrigin = aOrigin;
     mAllowScrollOriginDowngrade = false;
   }
   mLastSmoothScrollOrigin = nullptr;
   mScrollGeneration = ++sScrollGenerationCounter;
 
+  // If the new scroll offset is going to clobber APZ's scroll offset, for
+  // the RCD-RSF this will have the effect of resetting the visual viewport
+  // offset to be the same as the new scroll (= layout viewport) offset.
+  // The APZ callback transform, which reflects the difference between these
+  // offsets, will subsequently be cleared. However, it we wait for APZ to
+  // clear it, the main thread could end up using the old value and get
+  // incorrect results, so just clear it now.
+  if (mIsRoot && nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin)) {
+    content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(),
+                         nsINode::DeleteProperty<CSSPoint>);
+  }
+
   ScrollVisual();
 
   bool schedulePaint = true;
   if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
       !nsLayoutUtils::ShouldDisableApzForElement(content) &&
       gfxPrefs::APZPaintSkipping()) {
     // If APZ is enabled with paint-skipping, there are certain conditions in
     // which we can skip paints:
@@ -4112,16 +4124,26 @@ ScrollFrameHelper::GetVisualViewportSize
 {
   nsIPresShell* presShell = mOuter->PresShell();
   if (mIsRoot && presShell->IsVisualViewportSizeSet()) {
     return presShell->GetVisualViewportSize();
   }
   return mScrollPort.Size();
 }
 
+nsPoint
+ScrollFrameHelper::GetVisualViewportOffset() const
+{
+  nsIPresShell* presShell = mOuter->PresShell();
+  if (mIsRoot && presShell->IsVisualViewportSizeSet()) {
+    return presShell->GetVisualViewportOffset();
+  }
+  return GetScrollPosition();
+}
+
 static void
 AdjustForWholeDelta(int32_t aDelta, nscoord* aCoord)
 {
   if (aDelta < 0) {
     *aCoord = nscoord_MIN;
   } else if (aDelta > 0) {
     *aCoord = nscoord_MAX;
   }
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -212,16 +212,17 @@ public:
       mScrollPort.XMost() - mScrolledFrame->GetRect().XMost();
     pt.y = mScrollPort.y - mScrolledFrame->GetPosition().y;
     return pt;
   }
   nsRect GetScrollRange() const;
   // Get the scroll range assuming the viewport has size (aWidth, aHeight).
   nsRect GetScrollRange(nscoord aWidth, nscoord aHeight) const;
   nsSize GetVisualViewportSize() const;
+  nsPoint GetVisualViewportOffset() const;
   void ScrollSnap(nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD);
   void ScrollSnap(const nsPoint &aDestination,
                   nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD);
 
 protected:
   nsRect GetScrollRangeForClamping() const;
 
 public:
@@ -852,16 +853,19 @@ public:
     return mHelper.GetLogicalScrollPosition();
   }
   virtual nsRect GetScrollRange() const override {
     return mHelper.GetScrollRange();
   }
   virtual nsSize GetVisualViewportSize() const override {
     return mHelper.GetVisualViewportSize();
   }
+  virtual nsPoint GetVisualViewportOffset() const override {
+    return mHelper.GetVisualViewportOffset();
+  }
   virtual nsSize GetLineScrollAmount() const override {
     return mHelper.GetLineScrollAmount();
   }
   virtual nsSize GetPageScrollAmount() const override {
     return mHelper.GetPageScrollAmount();
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
@@ -1304,16 +1308,19 @@ public:
     return mHelper.GetLogicalScrollPosition();
   }
   virtual nsRect GetScrollRange() const override {
     return mHelper.GetScrollRange();
   }
   virtual nsSize GetVisualViewportSize() const override {
     return mHelper.GetVisualViewportSize();
   }
+  virtual nsPoint GetVisualViewportOffset() const override {
+    return mHelper.GetVisualViewportOffset();
+  }
   virtual nsSize GetLineScrollAmount() const override {
     return mHelper.GetLineScrollAmount();
   }
   virtual nsSize GetPageScrollAmount() const override {
     return mHelper.GetPageScrollAmount();
   }
   /**
    * @note This method might destroy the frame, pres shell and other objects.
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -148,16 +148,25 @@ public:
    */
   virtual nsRect GetScrollRange() const = 0;
   /**
    * Get the size of the view port to use when clamping the scroll
    * position.
    */
   virtual nsSize GetVisualViewportSize() const = 0;
   /**
+   * Returns the offset of the visual viewport relative to
+   * the origin of the scrolled content. Note that only the RCD-RSF
+   * has a distinct visual viewport; for other scroll frames, the
+   * visual viewport always coincides with the layout viewport, and
+   * consequently the offset this function returns is equal to
+   * GetScrollPosition().
+   */
+  virtual nsPoint GetVisualViewportOffset() const = 0;
+  /**
    * Return how much we would try to scroll by in each direction if
    * asked to scroll by one "line" vertically and horizontally.
    */
   virtual nsSize GetLineScrollAmount() const = 0;
   /**
    * Return how much we would try to scroll by in each direction if
    * asked to scroll by one "page" vertically and horizontally.
    */
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -253,16 +253,23 @@ pref("dom.script_loader.bytecode_cache.e
 // Other values might lead to experimental strategies. For more details, have a
 // look at: ScriptLoader::ShouldCacheBytecode function.
 pref("dom.script_loader.bytecode_cache.strategy", 0);
 
 #ifdef JS_BUILD_BINAST
 pref("dom.script_loader.binast_encoding.enabled", false);
 #endif
 
+// Whether window.event is enabled
+#ifdef NIGHTLY_BUILD
+pref("dom.window.event.enabled", true);
+#else
+pref("dom.window.event.enabled", false);
+#endif
+
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
 pref("ui.use_native_colors", true);
 pref("ui.click_hold_context_menus", false);
 
 // Pop up context menu on mouseup instead of mousedown, if that's the OS default.
@@ -2943,20 +2950,20 @@ pref("layout.css.initial-letter.enabled"
 
 // Is support for mix-blend-mode enabled?
 pref("layout.css.mix-blend-mode.enabled", true);
 
 // Is support for isolation enabled?
 pref("layout.css.isolation.enabled", true);
 
 // Is support for CSS Scrollbar color properties enabled?
-pref("layout.css.scrollbar-colors.enabled", false);
+pref("layout.css.scrollbar-colors.enabled", true);
 
 // Is support for scrollbar-width property enabled?
-pref("layout.css.scrollbar-width.enabled", false);
+pref("layout.css.scrollbar-width.enabled", true);
 
 // Set the threshold distance in CSS pixels below which scrolling will snap to
 // an edge, when scroll snapping is set to "proximity".
 pref("layout.css.scroll-snap.proximity-threshold", 200);
 
 // When selecting the snap point for CSS scroll snapping, the velocity of the
 // scroll frame is clamped to this speed, in CSS pixels / s.
 pref("layout.css.scroll-snap.prediction-max-velocity", 2000);
@@ -5018,17 +5025,17 @@ pref("extensions.langpacks.signatures.re
 pref("extensions.webExtensionsMinPlatformVersion", "42.0a1");
 pref("extensions.legacy.enabled", true);
 
 // Other webextensions prefs
 pref("extensions.webextensions.keepStorageOnUninstall", false);
 pref("extensions.webextensions.keepUuidOnUninstall", false);
 // Redirect basedomain used by identity api
 pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org");
-pref("extensions.webextensions.restrictedDomains", "accounts-static.cdn.mozilla.net,accounts.firefox.com,addons.cdn.mozilla.net,addons.mozilla.org,api.accounts.firefox.com,content.cdn.mozilla.net,content.cdn.mozilla.net,discovery.addons.mozilla.org,input.mozilla.org,install.mozilla.org,oauth.accounts.firefox.com,profile.accounts.firefox.com,support.mozilla.org,sync.services.mozilla.com,testpilot.firefox.com");
+pref("extensions.webextensions.restrictedDomains", "accounts-static.cdn.mozilla.net,accounts.firefox.com,addons.cdn.mozilla.net,addons.mozilla.org,api.accounts.firefox.com,content.cdn.mozilla.net,discovery.addons.mozilla.org,input.mozilla.org,install.mozilla.org,oauth.accounts.firefox.com,profile.accounts.firefox.com,support.mozilla.org,sync.services.mozilla.com,testpilot.firefox.com");
 // Whether or not webextension icon theming is supported.
 pref("extensions.webextensions.themes.icons.enabled", false);
 pref("extensions.webextensions.remote", false);
 // Whether or not the moz-extension resource loads are remoted. For debugging
 // purposes only. Setting this to false will break moz-extension URI loading
 // unless other process sandboxing and extension remoting prefs are changed.
 pref("extensions.webextensions.protocol.remote", true);
 
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -565,19 +565,18 @@ bool PACProxyAlert(JSContext *cx, unsign
   if (!arg1)
     return false;
 
   nsAutoJSString message;
   if (!message.init(cx, arg1))
     return false;
 
   nsAutoString alertMessage;
-  alertMessage.SetCapacity(32 + message.Length());
-  alertMessage += NS_LITERAL_STRING("PAC-alert: ");
-  alertMessage += message;
+  alertMessage.AssignLiteral(u"PAC-alert: ");
+  alertMessage.Append(message);
   PACLogToConsole(alertMessage);
 
   args.rval().setUndefined();  /* return undefined */
   return true;
 }
 
 static const JSFunctionSpec PACGlobalFunctions[] = {
   JS_FN("dnsResolve", PACDnsResolve, 1, 0),
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -603,28 +603,16 @@
 #define NS_THROTTLEQUEUE_CID                            \
 { /* 4c39159c-cd90-4dd3-97a7-06af5e6d84c4 */            \
     0x4c39159c,                                         \
     0xcd90,                                             \
     0x4dd3,                                             \
     {0x97, 0xa7, 0x06, 0xaf, 0x5e, 0x6d, 0x84, 0xc4}    \
 }
 
-// Background channel registrar used for pairing HttpChannelParent
-// and its background channel
-#define NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID \
-    "@mozilla.org/network/background-channel-registrar;1"
-#define NS_BACKGROUNDCHANNELREGISTRAR_CID \
-{ /* 6907788a-17cc-4c2a-a7c5-59ad2d9cc079 */          \
-    0x6907788a,                                       \
-    0x17cc,                                           \
-    0x4c2a,                                           \
-    { 0xa7, 0xc5, 0x59, 0xad, 0x2d, 0x9c, 0xc0, 0x79} \
-}
-
 /******************************************************************************
  * netwerk/protocol/ftp/ classes
  */
 
 #define NS_FTPPROTOCOLHANDLER_CID \
 { /* 25029490-F132-11d2-9588-00805F369F95 */         \
     0x25029490,                                      \
     0xf132,                                          \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -32,16 +32,17 @@
 #include "nsMimeTypes.h"
 #include "nsDNSPrefetch.h"
 #include "nsAboutProtocolHandler.h"
 #include "nsXULAppAPI.h"
 #include "nsCategoryCache.h"
 #include "nsIContentSniffer.h"
 #include "Predictor.h"
 #include "nsIThreadPool.h"
+#include "mozilla/net/BackgroundChannelRegistrar.h"
 #include "mozilla/net/NeckoChild.h"
 #include "RedirectChannelRegistrar.h"
 
 #include "nsNetCID.h"
 
 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX)
 #define BUILD_NETWORK_INFO_SERVICE 1
 #endif
@@ -238,30 +239,28 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFt
 #undef LOG
 #undef LOG_ENABLED
 #include "nsHttpAuthManager.h"
 #include "nsHttpBasicAuth.h"
 #include "nsHttpDigestAuth.h"
 #include "nsHttpNTLMAuth.h"
 #include "nsHttpActivityDistributor.h"
 #include "ThrottleQueue.h"
-#include "BackgroundChannelRegistrar.h"
 #undef LOG
 #undef LOG_ENABLED
 namespace mozilla {
 namespace net {
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpNTLMAuth)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsHttpHandler, nsHttpHandler::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpsHandler, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpAuthManager, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR(ThrottleQueue)
-NS_GENERIC_FACTORY_CONSTRUCTOR(BackgroundChannelRegistrar)
 } // namespace net
 } // namespace mozilla
 
 #include "mozilla/net/Dashboard.h"
 namespace mozilla {
 namespace net {
   NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
 } // namespace net
@@ -635,16 +634,18 @@ static void nsNetShutdown()
 
     // Release the Websocket Admission Manager
     mozilla::net::WebSocketChannel::Shutdown();
 
     mozilla::net::Http2CompressionCleanup();
 
     mozilla::net::RedirectChannelRegistrar::Shutdown();
 
+    mozilla::net::BackgroundChannelRegistrar::Shutdown();
+
     delete gNetSniffers;
     gNetSniffers = nullptr;
     delete gDataSniffers;
     gDataSniffers = nullptr;
 }
 
 NS_DEFINE_NAMED_CID(NS_IOSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_STREAMTRANSPORTSERVICE_CID);
@@ -705,17 +706,16 @@ NS_DEFINE_NAMED_CID(NS_FILEPROTOCOLHANDL
 NS_DEFINE_NAMED_CID(NS_HTTPPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPSPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPBASICAUTH_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPDIGESTAUTH_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPNTLMAUTH_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPAUTHMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPACTIVITYDISTRIBUTOR_CID);
 NS_DEFINE_NAMED_CID(NS_THROTTLEQUEUE_CID);
-NS_DEFINE_NAMED_CID(NS_BACKGROUNDCHANNELREGISTRAR_CID);
 NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_RESPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_EXTENSIONPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURL_CID);
 NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURLMUTATOR_CID);
 NS_DEFINE_NAMED_CID(NS_ABOUTPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_SAFEABOUTPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_ABOUT_BLANK_MODULE_CID);
@@ -821,17 +821,16 @@ static const mozilla::Module::CIDEntry k
     { &kNS_HTTPPROTOCOLHANDLER_CID, false, nullptr, mozilla::net::nsHttpHandlerConstructor },
     { &kNS_HTTPSPROTOCOLHANDLER_CID, false, nullptr, mozilla::net::nsHttpsHandlerConstructor },
     { &kNS_HTTPBASICAUTH_CID, false, nullptr, mozilla::net::nsHttpBasicAuthConstructor },
     { &kNS_HTTPDIGESTAUTH_CID, false, nullptr, mozilla::net::nsHttpDigestAuthConstructor },
     { &kNS_HTTPNTLMAUTH_CID, false, nullptr, mozilla::net::nsHttpNTLMAuthConstructor },
     { &kNS_HTTPAUTHMANAGER_CID, false, nullptr, mozilla::net::nsHttpAuthManagerConstructor },
     { &kNS_HTTPACTIVITYDISTRIBUTOR_CID, false, nullptr, mozilla::net::nsHttpActivityDistributorConstructor },
     { &kNS_THROTTLEQUEUE_CID, false, nullptr, mozilla::net::ThrottleQueueConstructor },
-    { &kNS_BACKGROUNDCHANNELREGISTRAR_CID, false, nullptr, mozilla::net::BackgroundChannelRegistrarConstructor },
     { &kNS_FTPPROTOCOLHANDLER_CID, false, nullptr, nsFtpProtocolHandlerConstructor },
     { &kNS_RESPROTOCOLHANDLER_CID, false, nullptr, nsResProtocolHandlerConstructor },
     { &kNS_EXTENSIONPROTOCOLHANDLER_CID, false, nullptr, mozilla::ExtensionProtocolHandlerConstructor },
     { &kNS_SUBSTITUTINGURL_CID, false, nullptr, mozilla::SubstitutingURLMutatorConstructor }, // do_CreateInstance returns mutator
     { &kNS_SUBSTITUTINGURLMUTATOR_CID, false, nullptr, mozilla::SubstitutingURLMutatorConstructor },
     { &kNS_ABOUTPROTOCOLHANDLER_CID, false, nullptr, nsAboutProtocolHandlerConstructor },
     { &kNS_SAFEABOUTPROTOCOLHANDLER_CID, false, nullptr, nsSafeAboutProtocolHandlerConstructor },
     { &kNS_ABOUT_BLANK_MODULE_CID, false, nullptr, nsAboutBlank::Create },
@@ -943,17 +942,16 @@ static const mozilla::Module::ContractID
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &kNS_HTTPPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "https", &kNS_HTTPSPROTOCOLHANDLER_CID },
     { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "basic", &kNS_HTTPBASICAUTH_CID },
     { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "digest", &kNS_HTTPDIGESTAUTH_CID },
     { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "ntlm", &kNS_HTTPNTLMAUTH_CID },
     { NS_HTTPAUTHMANAGER_CONTRACTID, &kNS_HTTPAUTHMANAGER_CID },
     { NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &kNS_HTTPACTIVITYDISTRIBUTOR_CID },
     { NS_THROTTLEQUEUE_CONTRACTID, &kNS_THROTTLEQUEUE_CID },
-    { NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID, &kNS_BACKGROUNDCHANNELREGISTRAR_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &kNS_FTPPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "resource", &kNS_RESPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-extension", &kNS_EXTENSIONPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "about", &kNS_ABOUTPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-safe-about", &kNS_SAFEABOUTPROTOCOLHANDLER_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "blank", &kNS_ABOUT_BLANK_MODULE_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "cache", &kNS_ABOUT_CACHE_MODULE_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "cache-entry", &kNS_ABOUT_CACHE_ENTRY_MODULE_CID },
--- a/netwerk/protocol/about/nsAboutCacheEntry.cpp
+++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp
@@ -328,25 +328,27 @@ nsAboutCacheEntry::Channel::OnCacheEntry
     buffer.AppendLiteral("</td>\n" \
                          "  </tr>\n"); \
     PR_END_MACRO
 
 nsresult
 nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry)
 {
     nsresult rv;
-    nsCString buffer;
+    // This method appears to run in a situation where the run-time stack
+    // should have plenty of space, so allocating a large string on the
+    // stack is OK.
+    nsAutoCStringN<4097> buffer;
     uint32_t n;
 
     nsAutoCString str;
 
     rv = entry->GetKey(str);
     if (NS_FAILED(rv)) return rv;
 
-    buffer.SetCapacity(4096);
     buffer.AssignLiteral("<table>\n"
                          "  <tr>\n"
                          "    <th>key:</th>\n"
                          "    <td id=\"td-key\">");
 
     // Test if the key is actually a URI
     nsCOMPtr<nsIURI> uri;
     bool isJS = false;
--- a/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
@@ -5,16 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundChannelRegistrar.h"
 
 #include "HttpBackgroundChannelParent.h"
 #include "HttpChannelParent.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsXULAppAPI.h"
+#include "mozilla/StaticPtr.h"
+
+namespace {
+mozilla::StaticRefPtr<mozilla::net::BackgroundChannelRegistrar> gSingleton;
+}
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ISUPPORTS(BackgroundChannelRegistrar, nsIBackgroundChannelRegistrar)
 
 BackgroundChannelRegistrar::BackgroundChannelRegistrar()
 {
@@ -25,16 +30,34 @@ BackgroundChannelRegistrar::BackgroundCh
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 BackgroundChannelRegistrar::~BackgroundChannelRegistrar()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
+// static
+already_AddRefed<nsIBackgroundChannelRegistrar>
+BackgroundChannelRegistrar::GetOrCreate()
+{
+  if (!gSingleton) {
+    gSingleton = new BackgroundChannelRegistrar();
+  }
+  return do_AddRef(gSingleton);
+}
+
+// static
+void
+BackgroundChannelRegistrar::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  gSingleton = nullptr;
+}
+
 void
 BackgroundChannelRegistrar::NotifyChannelLinked(
   HttpChannelParent* aChannelParent,
   HttpBackgroundChannelParent* aBgParent)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aChannelParent);
   MOZ_ASSERT(aBgParent);
--- a/netwerk/protocol/http/BackgroundChannelRegistrar.h
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.h
@@ -4,16 +4,17 @@
  * 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 mozilla_net_BackgroundChannelRegistrar_h__
 #define mozilla_net_BackgroundChannelRegistrar_h__
 
 #include "nsIBackgroundChannelRegistrar.h"
 #include "nsRefPtrHashtable.h"
+#include "mozilla/AlreadyAddRefed.h"
 
 namespace mozilla {
 namespace net {
 
 class HttpBackgroundChannelParent;
 class HttpChannelParent;
 
 class BackgroundChannelRegistrar final : public nsIBackgroundChannelRegistrar
@@ -23,16 +24,21 @@ class BackgroundChannelRegistrar final :
   typedef nsRefPtrHashtable<nsUint64HashKey, HttpBackgroundChannelParent>
           BackgroundChannelHashtable;
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIBACKGROUNDCHANNELREGISTRAR
 
   explicit BackgroundChannelRegistrar();
 
+  // Singleton accessor
+  static already_AddRefed<nsIBackgroundChannelRegistrar> GetOrCreate();
+
+  static void Shutdown();
+
 private:
   virtual ~BackgroundChannelRegistrar();
 
   // A helper function for BackgroundChannelRegistrar itself to callback
   // HttpChannelParent and HttpBackgroundChannelParent when both objects are
   // ready. aChannelParent and aBgParent is the pair of HttpChannelParent and
   // HttpBackgroundChannelParent that should be linked together.
   void NotifyChannelLinked(HttpChannelParent* aChannelParent,
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -1091,17 +1091,16 @@ nsresult
 Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
                                    const nsACString &method, const nsACString &path,
                                    const nsACString &host, const nsACString &scheme,
                                    bool connectForm, nsACString &output)
 {
   mSetInitialMaxBufferSizeAllowed = false;
   mOutput = &output;
   output.Truncate();
-  output.SetCapacity(1024);
   mParsedContentLength = -1;
 
   // first thing's first - context size updates (if necessary)
   if (mBufferSizeChangeWaiting) {
     if (mLowestBufferSizeWaiting < mMaxBufferSetting) {
       EncodeTableSizeChange(mLowestBufferSizeWaiting);
     }
     EncodeTableSizeChange(mMaxBufferSetting);
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -551,17 +551,17 @@ Http2Stream::GenerateOpen()
     // case.
     LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
     return NS_ERROR_UNEXPECTED;
   }
 
   // Now we need to convert the flat http headers into a set
   // of HTTP/2 headers by writing to mTxInlineFrame{sz}
 
-  nsCString compressedData;
+  nsAutoCStringN<1025> compressedData;
   nsAutoCString authorityHeader;
   nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader);
   if (NS_FAILED(rv)) {
     MOZ_ASSERT(false);
     return rv;
   }
 
   nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -9,17 +9,17 @@
 #include "HttpLog.h"
 
 #include "HttpBackgroundChannelParent.h"
 
 #include "HttpChannelParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Unused.h"
-#include "nsIBackgroundChannelRegistrar.h"
+#include "mozilla/net/BackgroundChannelRegistrar.h"
 #include "nsNetCID.h"
 #include "nsQueryObject.h"
 #include "nsThreadUtils.h"
 
 using mozilla::dom::ContentParent;
 using mozilla::ipc::AssertIsInMainProcess;
 using mozilla::ipc::AssertIsOnBackgroundThread;
 using mozilla::ipc::BackgroundParent;
@@ -49,17 +49,17 @@ public:
   NS_IMETHOD Run() override
   {
     LOG(("HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p channelId=%"
          PRIu64 "]\n", mActor.get(), mChannelId));
     AssertIsInMainProcess();
     MOZ_ASSERT(NS_IsMainThread());
 
     nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
-      do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+      BackgroundChannelRegistrar::GetOrCreate();
     MOZ_ASSERT(registrar);
 
     registrar->LinkBackgroundChannel(mChannelId, mActor);
     return NS_OK;
   }
 
 private:
   RefPtr<HttpBackgroundChannelParent> mActor;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -21,17 +21,17 @@
 #include "mozilla/Unused.h"
 #include "HttpBackgroundChannelParent.h"
 #include "HttpChannelParentListener.h"
 #include "nsHttpHandler.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPriority.h"
 #include "nsIAuthPromptProvider.h"
-#include "nsIBackgroundChannelRegistrar.h"
+#include "mozilla/net/BackgroundChannelRegistrar.h"
 #include "nsSerializationHelper.h"
 #include "nsISerializable.h"
 #include "nsIApplicationCacheService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "SerializedLoadContext.h"
 #include "nsIAuthInformation.h"
 #include "nsIAuthPromptCallback.h"
@@ -257,17 +257,17 @@ HttpChannelParent::CleanupBackgroundChan
 
     if (!mChannel) {
       return;
     }
 
     // This HttpChannelParent might still have a reference from
     // BackgroundChannelRegistrar.
     nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
-      do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+      BackgroundChannelRegistrar::GetOrCreate();
     MOZ_ASSERT(registrar);
 
     registrar->DeleteChannel(mChannel->ChannelId());
 
     // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure
     // is still on going. we need to abort AsyncOpen with failure to destroy
     // PHttpChannel actor.
     if (mAsyncOpenBarrier) {
@@ -724,17 +724,17 @@ already_AddRefed<GenericPromise>
 HttpChannelParent::WaitForBgParent()
 {
   LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this));
   MOZ_ASSERT(!mBgParent);
   MOZ_ASSERT(mChannel);
 
 
   nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
-    do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+    BackgroundChannelRegistrar::GetOrCreate();
   MOZ_ASSERT(registrar);
   registrar->LinkHttpChannel(mChannel->ChannelId(), this);
 
   if (mBgParent) {
     RefPtr<GenericPromise> promise = mPromise.Ensure(__func__);
     // resolve promise immediatedly if bg channel is ready.
     mPromise.Resolve(true, __func__);
     return promise.forget();
@@ -1650,20 +1650,16 @@ HttpChannelParent::OnDataAvailable(nsIRe
   if (httpChannelImpl && httpChannelImpl->IsReadingFromCache()) {
     transportStatus = NS_NET_STATUS_READING;
   }
 
   static uint32_t const kCopyChunkSize = 128 * 1024;
   uint32_t toRead = std::min<uint32_t>(aCount, kCopyChunkSize);
 
   nsCString data;
-  if (!data.SetCapacity(toRead, fallible)) {
-    LOG(("  out of memory!"));
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
 
   int32_t count = static_cast<int32_t>(aCount);
 
   while (aCount) {
     nsresult rv = NS_ReadInputStreamToString(aInputStream, data, toRead);
     if (NS_FAILED(rv)) {
       return rv;
     }
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -34,16 +34,17 @@ EXPORTS += [
     'nsHttpHeaderArray.h',
     'nsHttpRequestHead.h',
     'nsHttpResponseHead.h',
 ]
 
 EXPORTS.mozilla.net += [
     'AltDataOutputStreamChild.h',
     'AltDataOutputStreamParent.h',
+    'BackgroundChannelRegistrar.h',
     'HttpAuthUtils.h',
     'HttpBackgroundChannelChild.h',
     'HttpBackgroundChannelParent.h',
     'HttpBaseChannel.h',
     'HttpChannelChild.h',
     'HttpChannelParent.h',
     'HttpInfo.h',
     'nsServerTiming.h',
--- a/netwerk/protocol/http/nsHttpDigestAuth.cpp
+++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp
@@ -410,17 +410,17 @@ nsHttpDigestAuth::CalculateResponse(cons
       len += 8; // length of "auth-int"
     else
       len += 4; // length of "auth"
   }
 
   nsAutoCString contents;
   contents.SetCapacity(len);
 
-  contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH);
+  contents.Append(ha1_digest, EXPANDED_DIGEST_LENGTH);
   contents.Append(':');
   contents.Append(nonce);
   contents.Append(':');
 
   if (qop & QOP_AUTH || qop & QOP_AUTH_INT) {
     contents.Append(nonce_count, NONCE_COUNT_LENGTH);
     contents.Append(':');
     contents.Append(cnonce);
@@ -474,19 +474,19 @@ nsHttpDigestAuth::CalculateHA1(const nsC
   int16_t len = username.Length() + password.Length() + realm.Length() + 2;
   if (algorithm & ALGO_MD5_SESS) {
     int16_t exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2;
     if (exlen > len)
         len = exlen;
   }
 
   nsAutoCString contents;
-  contents.SetCapacity(len + 1);
+  contents.SetCapacity(len);
 
-  contents.Assign(username);
+  contents.Append(username);
   contents.Append(':');
   contents.Append(realm);
   contents.Append(':');
   contents.Append(password);
 
   nsresult rv;
   rv = MD5Hash(contents.get(), contents.Length());
   if (NS_FAILED(rv))
--- a/netwerk/streamconv/converters/nsDirIndexParser.cpp
+++ b/netwerk/streamconv/converters/nsDirIndexParser.cpp
@@ -138,17 +138,16 @@ nsDirIndexParser::ParseFormat(const char
 
     if (! *aFormatStr)
       break;
 
     nsAutoCString name;
     int32_t     len = 0;
     while (aFormatStr[len] && !nsCRT::IsAsciiSpace(char16_t(aFormatStr[len])))
       ++len;
-    name.SetCapacity(len + 1);
     name.Append(aFormatStr, len);
     aFormatStr += len;
 
     // Okay, we're gonna monkey with the nsStr. Bold!
     name.SetLength(nsUnescapeCount(name.BeginWriting()));
 
     // All tokens are case-insensitive - http://www.mozilla.org/projects/netlib/dirindexformat.html
     if (name.LowerCaseEqualsLiteral("description"))
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/nodeutil.py
@@ -0,0 +1,134 @@
+# 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/.
+
+from __future__ import absolute_import
+
+import os
+import subprocess
+import platform
+from mozboot.util import get_state_dir
+import which
+
+from distutils.version import (
+    StrictVersion,
+)
+
+NODE_MIN_VERSION = StrictVersion("8.11.0")
+NPM_MIN_VERSION = StrictVersion("5.6.0")
+
+
+def find_node_paths():
+    """ Determines the possible paths for node executables.
+
+    Returns a list of paths, which includes the build state directory.
+    """
+    # Also add in the location to which `mach bootstrap` or
+    # `mach artifact toolchain` installs clang.
+    mozbuild_state_dir, _ = get_state_dir()
+
+    if platform.system() == "Windows":
+        mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node')
+    else:
+        mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node', 'bin')
+
+    # We still fallback to the PATH, since on OSes that don't have toolchain
+    # artifacts available to download, Node may be coming from $PATH.
+    paths = [mozbuild_node_path] + os.environ.get('PATH').split(os.pathsep)
+
+    if platform.system() == "Windows":
+        paths += [
+            "%s\\nodejs" % os.environ.get("SystemDrive"),
+            os.path.join(os.environ.get("ProgramFiles"), "nodejs"),
+            os.path.join(os.environ.get("PROGRAMW6432"), "nodejs"),
+            os.path.join(os.environ.get("PROGRAMFILES"), "nodejs")
+        ]
+
+    return paths
+
+
+def check_executable_version(exe):
+    """Determine the version of a Node executable by invoking it.
+
+    May raise ``subprocess.CalledProcessError`` or ``ValueError`` on failure.
+    """
+    out = subprocess.check_output([exe, "--version"]).lstrip('v').rstrip()
+    return StrictVersion(out)
+
+
+def simple_which(filename, path=None):
+    # Note: On windows, npm uses ".cmd"
+    exts = [".cmd", ".exe", ""] if platform.system() == "Windows" else [""]
+
+    for ext in exts:
+        try:
+            return which.which(filename + ext, path)
+        except which.WhichError:
+            pass
+
+    # If we got this far, we didn't find it with any of the extensions, so
+    # just return.
+    return None
+
+
+def find_node_executable(nodejs_exe=os.environ.get('NODEJS'), min_version=NODE_MIN_VERSION):
+    """Find a Node executable from the mozbuild directory.
+
+    Returns a tuple containing the the path to an executable binary and a
+    version tuple. Both tuple entries will be None if a Node executable
+    could not be resolved.
+    """
+    if nodejs_exe:
+        try:
+            version = check_executable_version(nodejs_exe)
+        except (subprocess.CalledProcessError, ValueError):
+            return None, None
+
+        if version >= min_version:
+            return nodejs_exe, version.version
+
+        return None, None
+
+    # "nodejs" is first in the tuple on the assumption that it's only likely to
+    # exist on systems (probably linux distros) where there is a program in the path
+    # called "node" that does something else.
+    return find_executable(['nodejs', 'node'], min_version)
+
+
+def find_npm_executable(min_version=NPM_MIN_VERSION):
+    """Find a Node executable from the mozbuild directory.
+
+    Returns a tuple containing the the path to an executable binary and a
+    version tuple. Both tuple entries will be None if a Node executable
+    could not be resolved.
+    """
+    return find_executable(["npm"], min_version)
+
+
+def find_executable(names, min_version):
+    paths = find_node_paths()
+
+    found_exe = None
+    for name in names:
+        try:
+            exe = simple_which(name, paths)
+        except which.WhichError:
+            continue
+
+        if not exe:
+            continue
+
+        if not found_exe:
+            found_exe = exe
+
+        # We always verify we can invoke the executable and its version is
+        # sane.
+        try:
+            version = check_executable_version(exe)
+        except (subprocess.CalledProcessError, ValueError):
+            continue
+
+        if version >= min_version:
+            return exe, version.version
+
+    return found_exe, None
--- a/python/mozlint/mozlint/parser.py
+++ b/python/mozlint/mozlint/parser.py
@@ -48,16 +48,19 @@ class Parser(object):
 
             if not isinstance(linter[attr], list) or \
                     not all(isinstance(a, basestring) for a in linter[attr]):
                 raise LinterParseError(relpath, "The {} directive must be a "
                                                 "list of strings!".format(attr))
             invalid_paths = set()
             for path in linter[attr]:
                 if '*' in path:
+                    if attr == 'include':
+                        raise LinterParseError(relpath, "Paths in the include directive cannot "
+                                                        "contain globs:\n  {}".format(path))
                     continue
 
                 abspath = path
                 if not os.path.isabs(abspath):
                     abspath = os.path.join(self.root, path)
 
                 if not os.path.exists(abspath):
                     invalid_paths.add('  ' + path)
@@ -96,10 +99,11 @@ class Parser(object):
             raise LinterParseError(path, "No lint definitions found!")
 
         linters = []
         for name, linter in config.iteritems():
             linter['name'] = name
             linter['path'] = path
             self._validate(linter)
             linter.setdefault('support-files', []).append(path)
+            linter.setdefault('include', ['.'])
             linters.append(linter)
         return linters
--- a/python/mozlint/mozlint/pathutils.py
+++ b/python/mozlint/mozlint/pathutils.py
@@ -10,25 +10,22 @@ from mozpack import path as mozpath
 from mozpack.files import FileFinder
 
 
 class FilterPath(object):
     """Helper class to make comparing and matching file paths easier."""
     def __init__(self, path, exclude=None):
         self.path = os.path.normpath(path)
         self._finder = None
-        self.exclude = exclude
 
     @property
     def finder(self):
         if self._finder:
             return self._finder
-        self._finder = FileFinder(
-            mozpath.normsep(self.path),
-            ignore=[mozpath.normsep(e) for e in self.exclude])
+        self._finder = FileFinder(mozpath.normsep(self.path))
         return self._finder
 
     @property
     def ext(self):
         return os.path.splitext(self.path)[1].strip('.')
 
     @property
     def exists(self):
@@ -89,41 +86,39 @@ def filterpaths(paths, linter, **lintarg
     if not lintargs.get('use_filters', True) or (not include and not exclude):
         return paths
 
     def normalize(path):
         if '*' not in path and not os.path.isabs(path):
             path = os.path.join(root, path)
         return FilterPath(path)
 
+    # Includes are always paths and should always exist.
     include = map(normalize, include)
-    exclude = map(normalize, exclude)
 
-    # Paths with and without globs will be handled separately,
+    # Exclude paths with and without globs will be handled separately,
     # pull them apart now.
-    includepaths = [p for p in include if p.exists]
+    exclude = map(normalize, exclude)
     excludepaths = [p for p in exclude if p.exists]
-
-    includeglobs = [p for p in include if not p.exists]
-    excludeglobs = [p for p in exclude if not p.exists]
+    excludeglobs = [p.path for p in exclude if not p.exists]
 
     extensions = linter.get('extensions')
     keep = set()
     discard = set()
     for path in map(FilterPath, paths):
         # Exclude bad file extensions
         if extensions and path.isfile and path.ext not in extensions:
             continue
 
         if path.match(excludeglobs):
             continue
 
         # First handle include/exclude directives
         # that exist (i.e don't have globs)
-        for inc in includepaths:
+        for inc in include:
             # Only excludes that are subdirectories of the include
             # path matter.
             excs = [e for e in excludepaths if inc.contains(e)]
 
             if path.contains(inc):
                 # If specified path is an ancestor of include path,
                 # then lint the include path.
                 keep.add(inc)
@@ -136,35 +131,21 @@ def filterpaths(paths, linter, **lintarg
 
             elif inc.contains(path):
                 # If the include path is an ancestor of the specified
                 # path, then add the specified path only if there are
                 # no exclude paths in-between them.
                 if not any(e.contains(path) for e in excs):
                     keep.add(path)
 
-        # Next handle include/exclude directives that
-        # contain globs.
-        if path.isfile:
-            # If the specified path is a file it must be both
-            # matched by an include directive and not matched
-            # by an exclude directive.
-            if not path.match(includeglobs) or any(e.contains(path) for e in excludepaths):
-                continue
-
-            keep.add(path)
-        elif path.isdir:
-            # If the specified path is a directory, use a
-            # FileFinder to resolve all relevant globs.
-            path.exclude = [os.path.relpath(e.path, root) for e in exclude]
-            for pattern in includeglobs:
-                for p, f in path.finder.find(pattern.path):
-                    if extensions and os.path.splitext(p)[1][1:] not in extensions:
-                        continue
-                    keep.add(path.join(p))
+        # Next expand excludes with globs in them so we can add them to
+        # the set of files to discard.
+        for pattern in excludeglobs:
+            for p, f in path.finder.find(pattern):
+                discard.add(path.join(p))
 
     # Only pass paths we couldn't exclude here to the underlying linter
     lintargs['exclude'] = [f.path for f in discard]
     return [f.path for f in keep]
 
 
 def findobject(path):
     """
new file mode 100644
--- /dev/null
+++ b/python/mozlint/test/linters/invalid_include_with_glob.yml
@@ -0,0 +1,6 @@
+---
+BadIncludeLinterWithGlob:
+    description: Has an invalid include directive.
+    include: ['**/*.js']
+    type: string
+    payload: foobar
--- a/python/mozlint/test/linters/raises.yml
+++ b/python/mozlint/test/linters/raises.yml
@@ -1,5 +1,6 @@
 ---
 RaisesLinter:
     description: Raises an exception
+    include: ['.']
     type: external
     payload: external:raises
--- a/python/mozlint/test/linters/regex.yml
+++ b/python/mozlint/test/linters/regex.yml
@@ -1,11 +1,10 @@
 ---
 RegexLinter:
     description: >-
         Make sure the string foobar never appears in a js variable
         file because it is bad.
     rule: no-foobar
-    include:
-        - '**/*.js'
-        - '**/*.jsm'
+    include: ['.']
+    extensions: ['js', '.jsm']
     type: regex
     payload: foobar
--- a/python/mozlint/test/linters/string.yml
+++ b/python/mozlint/test/linters/string.yml
@@ -1,12 +1,9 @@
 ---
 StringLinter:
     description: >-
         Make sure the string foobar never appears in browser js
         files because it is bad
     rule: no-foobar
-    include:
-        - '**/*.js'
-        - '**/*.jsm'
+    extensions: ['.js', 'jsm']
     type: string
-    extensions: ['.js', 'jsm']
     payload: foobar
--- a/python/mozlint/test/linters/warning.yml
+++ b/python/mozlint/test/linters/warning.yml
@@ -1,13 +1,11 @@
 ---
 WarningLinter:
     description: >-
         Make sure the string foobar never appears in browser js
         files because it is bad, but not too bad (just a warning)
     rule: no-foobar
     level: warning
-    include:
-        - '**/*.js'
-        - '**/*.jsm'
+    include: ['.']
     type: string
     extensions: ['.js', 'jsm']
     payload: foobar
--- a/python/mozlint/test/test_parser.py
+++ b/python/mozlint/test/test_parser.py
@@ -35,23 +35,26 @@ def test_parse_valid_linter(parse):
 
     lintobj = lintobj[0]
     assert isinstance(lintobj, dict)
     assert 'name' in lintobj
     assert 'description' in lintobj
     assert 'type' in lintobj
     assert 'payload' in lintobj
     assert 'extensions' in lintobj
+    assert 'include' in lintobj
+    assert lintobj['include'] == ['.']
     assert set(lintobj['extensions']) == set(['js', 'jsm'])
 
 
 @pytest.mark.parametrize('linter', [
     'invalid_type.yml',
     'invalid_extension.ym',
     'invalid_include.yml',
+    'invalid_include_with_glob.yml',
     'invalid_exclude.yml',
     'invalid_support_files.yml',
     'missing_attrs.yml',
     'missing_definition.yml',
     'non_existing_include.yml',
     'non_existing_exclude.yml',
     'non_existing_support_files.yml',
 ])
--- a/python/mozlint/test/test_pathutils.py
+++ b/python/mozlint/test/test_pathutils.py
@@ -52,52 +52,49 @@ def test_no_filter(filterpaths):
         'exclude': ['**/*.py'],
         'use_filters': False,
     }
 
     paths = filterpaths(**args)
     assert_paths(paths, args['paths'])
 
 
-def test_extensions(filterpaths):
-    args = {
-        'paths': ['a.py', 'a.js', 'subdir2'],
-        'include': ['**'],
-        'exclude': [],
-        'extensions': ['py'],
-    }
-
-    paths = filterpaths(**args)
-    assert_paths(paths, ['a.py', 'subdir2/c.py'])
-
-
 TEST_CASES = (
     {
         'paths': ['a.js', 'subdir1/subdir3/d.js'],
         'include': ['.'],
         'exclude': ['subdir1'],
         'expected': ['a.js'],
     },
     {
         'paths': ['a.js', 'subdir1/subdir3/d.js'],
         'include': ['subdir1/subdir3'],
         'exclude': ['subdir1'],
         'expected': ['subdir1/subdir3/d.js'],
     },
     {
         'paths': ['.'],
-        'include': ['**/*.py'],
+        'include': ['.'],
         'exclude': ['**/c.py', 'subdir1/subdir3'],
+        'extensions': ['py'],
+        'expected': ['.'],
+    },
+    {
+        'paths': ['a.py', 'a.js', 'subdir1/b.py', 'subdir2/c.py', 'subdir1/subdir3/d.py'],
+        'include': ['.'],
+        'exclude': ['**/c.py', 'subdir1/subdir3'],
+        'extensions': ['py'],
         'expected': ['a.py', 'subdir1/b.py'],
     },
     {
-        'paths': ['a.py', 'a.js', 'subdir1/b.py', 'subdir2/c.py', 'subdir1/subdir3/d.py'],
-        'include': ['**/*.py'],
-        'exclude': ['**/c.py', 'subdir1/subdir3'],
-        'expected': ['a.py', 'subdir1/b.py'],
+        'paths': ['a.py', 'a.js', 'subdir2'],
+        'include': ['.'],
+        'exclude': [],
+        'extensions': ['py'],
+        'expected': ['a.py', 'subdir2'],
     },
 )
 
 
 @pytest.mark.parametrize('test', TEST_CASES)
 def test_filterpaths(filterpaths, test):
     expected = test.pop('expected')
 
--- a/python/mozlint/test/test_roller.py
+++ b/python/mozlint/test/test_roller.py
@@ -56,20 +56,17 @@ def test_roll_from_subdir(lint, linters)
         result = lint.roll('no_foobar.js')
         assert len(result.issues) == 0
         assert len(result.failed) == 0
         assert result.returncode == 0
 
         # Path relative to root doesn't work
         result = lint.roll(os.path.join('files', 'no_foobar.js'))
         assert len(result.issues) == 0
-        # TODO Only the external linter is failing, the other two
-        # should be failing as well. Not using xfail so we don't
-        # lose coverage from the rest of the test.
-        assert len(result.failed) == 1
+        assert len(result.failed) == 3
         assert result.returncode == 1
 
         # Paths from vcs are always joined to root instead of cwd
         lint.mock_vcs([os.path.join('files', 'no_foobar.js')])
         result = lint.roll(outgoing=True)
         assert len(result.issues) == 0
         assert len(result.failed) == 0
         assert result.returncode == 0
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -498,51 +498,38 @@ ShowProtectedAuthPrompt(PK11SlotInfo* sl
 
   // Get protected auth dialogs
   nsCOMPtr<nsITokenDialogs> dialogs;
   nsresult nsrv = getNSSDialogs(getter_AddRefs(dialogs),
                                 NS_GET_IID(nsITokenDialogs),
                                 NS_TOKENDIALOGS_CONTRACTID);
   if (NS_SUCCEEDED(nsrv))
   {
-    nsProtectedAuthThread* protectedAuthRunnable = new nsProtectedAuthThread();
-    if (protectedAuthRunnable)
-    {
-      NS_ADDREF(protectedAuthRunnable);
-
-      protectedAuthRunnable->SetParams(slot);
+    RefPtr<nsProtectedAuthThread> protectedAuthRunnable = new nsProtectedAuthThread();
+    protectedAuthRunnable->SetParams(slot);
 
-      nsCOMPtr<nsIProtectedAuthThread> runnable = do_QueryInterface(protectedAuthRunnable);
-      if (runnable)
-      {
-        nsrv = dialogs->DisplayProtectedAuth(ir, runnable);
+    nsrv = dialogs->DisplayProtectedAuth(ir, protectedAuthRunnable);
 
-        // We call join on the thread,
-        // so we can be sure that no simultaneous access will happen.
-        protectedAuthRunnable->Join();
+    // We call join on the thread,
+    // so we can be sure that no simultaneous access will happen.
+    protectedAuthRunnable->Join();
 
-        if (NS_SUCCEEDED(nsrv))
-        {
-          SECStatus rv = protectedAuthRunnable->GetResult();
-          switch (rv)
-          {
-              case SECSuccess:
-                  protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED));
-                  break;
-              case SECWouldBlock:
-                  protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY));
-                  break;
-              default:
-                  protAuthRetVal = nullptr;
-                  break;
-          }
-        }
+    if (NS_SUCCEEDED(nsrv)) {
+      SECStatus rv = protectedAuthRunnable->GetResult();
+      switch (rv) {
+        case SECSuccess:
+          protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED));
+          break;
+        case SECWouldBlock:
+          protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY));
+          break;
+        default:
+          protAuthRetVal = nullptr;
+          break;
       }
-
-      NS_RELEASE(protectedAuthRunnable);
     }
   }
 
   return protAuthRetVal;
 }
 
 class PK11PasswordPromptRunnable : public SyncRunnableBase
 {
--- a/taskcluster/ci/build/android-stuff.yml
+++ b/taskcluster/ci/build/android-stuff.yml
@@ -78,18 +78,21 @@ android-test-ccov/opt:
         custom-build-variant-cfg: android-test-ccov
         tooltool-downloads: internal
     toolchains:
         - android-gradle-dependencies
         - android-sdk-linux
         - linux64-node
         - linux64-grcov
     fetches:
-        toolchain-linux64-grcov:
-            - grcov.tar.xz
+        # We use a fetch instead of toolchains here, because mozharness expects
+        # it there (since the code is shared with tests that don't support
+        # `toolchains`).
+        toolchain:
+            - linux64-grcov
 
 android-lint/opt:
     description: "Android lint"
     index:
         product: mobile
         job-name: android-lint
     treeherder:
         platform: android-4-0-armv7-api16/opt
--- a/taskcluster/ci/test/kind.yml
+++ b/taskcluster/ci/test/kind.yml
@@ -3,17 +3,16 @@ loader: taskgraph.loader.test:loader
 kind-dependencies:
     - build
     - build-signing
     - fetch
     - toolchain
 
 transforms:
     - taskgraph.transforms.tests:transforms
-    - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.coalesce:transforms
     - taskgraph.transforms.task:transforms
 
 # Each stanza in a file pointed to by 'jobs-from' describes a particular test
 # suite or sub-suite. These are processed through the transformations described
 # above to produce a bunch of tasks. See the schema in
 # `taskcluster/taskgraph/transforms/tests.py` for a description of the fields
--- a/taskcluster/docker/recipes/install-node.sh
+++ b/taskcluster/docker/recipes/install-node.sh
@@ -1,12 +1,15 @@
 #!/bin/bash
 # 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/.
 
 # This script installs Node v8.
+# XXX For now, this should match the version installed in
+# taskcluster/scripts/misc/repack-node.sh. Later we'll get the ESLint builder
+# to use the linux64-node toolchain directly.
 
-wget --progress=dot:mega https://nodejs.org/dist/v8.9.4/node-v8.9.4-linux-x64.tar.gz
-echo '21fb4690e349f82d708ae766def01d7fec1b085ce1f5ab30d9bda8ee126ca8fc  node-v8.9.4-linux-x64.tar.gz' | sha256sum -c
-tar -C /usr/local -xz --strip-components 1 < node-v8.9.4-linux-x64.tar.gz
+wget --progress=dot:mega https://nodejs.org/dist/v8.11.3/node-v8.11.3-linux-x64.tar.xz
+echo '08e2fcfea66746bd966ea3a89f26851f1238d96f86c33eaf6274f67fce58421a  node-v8.11.3-linux-x64.tar.xz' | sha256sum -c
+tar -C /usr/local -xJ --strip-components 1 < node-v8.11.3-linux-x64.tar.xz
 node -v  # verify
 npm -v
--- a/taskcluster/taskgraph/transforms/job/__init__.py
+++ b/taskcluster/taskgraph/transforms/job/__init__.py
@@ -145,24 +145,24 @@ def get_attribute(dict, key, attributes,
     is a corresponding value, set `key` in `dict` to that value.'''
     value = attributes.get(attribute_name)
     if value:
         dict[key] = value
 
 
 @transforms.add
 def use_fetches(config, jobs):
-    all_fetches = {}
+    artifact_names = {}
 
     for task in config.kind_dependencies_tasks:
-        if task.kind != 'fetch':
-            continue
-
-        name = task.label.replace('%s-' % task.kind, '')
-        get_attribute(all_fetches, name, task.attributes, 'fetch-artifact')
+        if task.kind in ('fetch', 'toolchain'):
+            get_attribute(
+                artifact_names, task.label, task.attributes,
+                '{kind}-artifact'.format(kind=task.kind),
+            )
 
     for job in jobs:
         fetches = job.pop('fetches', None)
         if not fetches:
             yield job
             continue
 
         # Hack added for `mach artifact toolchain` to support reading toolchain
@@ -170,35 +170,35 @@ def use_fetches(config, jobs):
         if 'fetch' in fetches and config.params.get('ignore_fetches'):
             fetches['fetch'][:] = []
 
         job_fetches = []
         name = job.get('name', job.get('label'))
         dependencies = job.setdefault('dependencies', {})
         prefix = get_artifact_prefix(job)
         for kind, artifacts in fetches.items():
-            if kind == 'fetch':
-                for fetch in artifacts:
-                    if fetch not in all_fetches:
+            if kind in ('fetch', 'toolchain'):
+                for fetch_name in artifacts:
+                    label = '{kind}-{name}'.format(kind=kind, name=fetch_name)
+                    if label not in artifact_names:
                         raise Exception('Missing fetch job for {kind}-{name}: {fetch}'.format(
-                            kind=config.kind, name=name, fetch=fetch))
+                            kind=config.kind, name=name, fetch=fetch_name))
 
-                    path = all_fetches[fetch]
+                    path = artifact_names[label]
                     if not path.startswith('public/'):
-                        raise Exception('Non-public artifacts not supported for {kind}-{name}: '
-                                        '{fetch}'.format(kind=config.kind, name=name, fetch=fetch))
+                        raise Exception(
+                            'Non-public artifacts not supported for {kind}-{name}: '
+                            '{fetch}'.format(kind=config.kind, name=name, fetch=fetch_name))
 
-                    dep = 'fetch-{}'.format(fetch)
-                    dependencies[dep] = dep
+                    dependencies[label] = label
                     job_fetches.append({
                         'artifact': path,
-                        'task': '<{dep}>'.format(dep=dep),
+                        'task': '<{label}>'.format(label=label),
                         'extract': True,
                     })
-
             else:
                 if kind not in dependencies:
                     raise Exception("{name} can't fetch {kind} artifacts because "
                                     "it has no {kind} dependencies!".format(name=name, kind=kind))
 
                 for artifact in artifacts:
                     if isinstance(artifact, basestring):
                         path = artifact
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -393,19 +393,16 @@ test_description_schema = Schema({
     # If None or not specified, a transform sets the target based on OS:
     # target.dmg (Mac), target.apk (Android), target.tar.bz2 (Linux),
     # or target.zip (Windows).
     Optional('target'): optionally_keyed_by(
         'test-platform',
         Any(basestring, None),
     ),
 
-    # A list of artifacts to install from 'toolchain' tasks.
-    Optional('toolchains'): [basestring],
-
     # A list of artifacts to install from 'fetch' tasks.
     Optional('fetches'): {
         basestring: [basestring],
     },
 }, required=True)
 
 
 @transforms.add
@@ -748,20 +745,17 @@ def enable_code_coverage(config, tests):
     for test in tests:
         if 'ccov' in test['build-platform']:
             # Do not run tests on fuzzing or opt build
             if 'opt' in test['build-platform'] or 'fuzzing' in test['build-platform']:
                 test['run-on-projects'] = []
                 continue
             # Skip this transform for android code coverage builds.
             if 'android' in test['build-platform']:
-                test.setdefault('fetches', {})\
-                    .setdefault('toolchain-linux64-grcov', [])\
-                    .append('grcov.tar.xz')
-                test.setdefault('toolchains', []).append('linux64-grcov')
+                test.setdefault('fetches', {}).setdefault('toolchain', []).append('linux64-grcov')
                 test['mozharness'].setdefault('extra-options', []).append('--java-code-coverage')
                 yield test
                 continue
             test['mozharness'].setdefault('extra-options', []).append('--code-coverage')
             test['instance-size'] = 'xlarge'
             # Ensure we always run on the projects defined by the build, unless the test
             # is try only or shouldn't run at all.
             if test['run-on-projects'] not in [[], ['try']]:
@@ -772,24 +766,22 @@ def enable_code_coverage(config, tests):
             test.pop('schedules-component', None)
             test.pop('when', None)
             test['optimization'] = None
 
             # Add a toolchain and a fetch task for the grcov binary.
             if any(p in test['build-platform'] for p in ('linux', 'osx', 'win')):
                 test.setdefault('fetches', {})
                 test['fetches'].setdefault('fetch', [])
-                test.setdefault('toolchains', [])
+                test['fetches'].setdefault('toolchain', [])
 
             if 'linux' in test['build-platform']:
-                test['fetches']['toolchain-linux64-grcov'] = ['grcov.tar.xz']
-                test['toolchains'].append('linux64-grcov')
+                test['fetches']['toolchain'].append('linux64-grcov')
             elif 'osx' in test['build-platform']:
-                test['fetches']['toolchain-macosx64-grcov'] = ['grcov.tar.xz']
-                test['toolchains'].append('macosx64-grcov')
+                test['fetches']['toolchain'].append('macosx64-grcov')
             elif 'win' in test['build-platform']:
                 test['fetches']['fetch'].append('grcov-win-x86_64')
 
             if 'talos' in test['test-name']:
                 test['max-run-time'] = 7200
                 if 'linux' in test['build-platform']:
                     test['docker-image'] = {"in-tree": "desktop1604-test"}
                 test['mozharness']['extra-options'].append('--add-option')
@@ -1064,19 +1056,16 @@ def make_job_description(config, tests):
         jobdesc['description'] = test['description']
         jobdesc['attributes'] = attributes
         jobdesc['dependencies'] = {'build': build_label}
         jobdesc['job-from'] = test['job-from']
 
         if test.get('fetches'):
             jobdesc['fetches'] = test['fetches']
 
-        if test.get('toolchains'):
-            jobdesc['toolchains'] = test.pop('toolchains')
-
         if test['mozharness']['requires-signed-builds'] is True:
             jobdesc['dependencies']['build-signing'] = test['build-signing-label']
 
         jobdesc['expires-after'] = test['expires-after']
         jobdesc['routes'] = []
         jobdesc['run-on-projects'] = test['run-on-projects']
         jobdesc['scopes'] = []
         jobdesc['tags'] = test.get('tags', {})
--- a/testing/mozbase/mozlog/mozlog/commandline.py
+++ b/testing/mozbase/mozlog/mozlog/commandline.py
@@ -55,16 +55,21 @@ def compact_wrapper(formatter, compact):
 def buffer_handler_wrapper(handler, buffer_limit):
     if buffer_limit == "UNLIMITED":
         buffer_limit = None
     else:
         buffer_limit = int(buffer_limit)
     return handlers.BufferHandler(handler, buffer_limit)
 
 
+def screenshot_wrapper(formatter, enable_screenshot):
+    formatter.enable_screenshot = enable_screenshot
+    return formatter
+
+
 def valgrind_handler_wrapper(handler):
     return handlers.ValgrindHandler(handler)
 
 
 def default_formatter_options(log_type, overrides):
     formatter_option_defaults = {
         "raw": {
             "level": "debug"
@@ -80,27 +85,33 @@ def default_formatter_options(log_type, 
     return rv
 
 
 fmt_options = {
     # <option name>: (<wrapper function>, description, <applicable formatters>, action)
     # "action" is used by the commandline parser in use.
     'verbose': (verbose_wrapper,
                 "Enables verbose mode for the given formatter.",
-                ["mach"], "store_true"),
+                {"mach"}, "store_true"),
     'compact': (compact_wrapper,
                 "Enables compact mode for the given formatter.",
-                ["tbpl"], "store_true"),
+                {"tbpl"}, "store_true"),
     'level': (level_filter_wrapper,
               "A least log level to subscribe to for the given formatter "
               "(debug, info, error, etc.)",
-              ["mach", "raw", "tbpl"], "store"),
+              {"mach", "raw", "tbpl"}, "store"),
     'buffer': (buffer_handler_wrapper,
                "If specified, enables message buffering at the given buffer size limit.",
                ["mach", "tbpl"], "store"),
+    'screenshot': (screenshot_wrapper,
+                   "Enable logging reftest-analyzer compatible screenshot data.",
+                   {"mach"}, "store_true"),
+    'no-screenshot': (screenshot_wrapper,
+                      "Disable logging reftest-analyzer compatible screenshot data.",
+                      {"mach"}, "store_false"),
 }
 
 
 def log_file(name):
     if name == "-":
         return sys.stdout
     # ensure we have a correct dirpath by using realpath
     dirpath = os.path.dirname(os.path.realpath(name))
@@ -151,22 +162,27 @@ def add_logging_group(parser, include_fo
         opt_log_type = log_file
         group_add = group.add_argument
 
     for name, (cls, help_str) in six.iteritems(log_formatters):
         if name in include_formatters:
             group_add("--log-" + name, action="append", type=opt_log_type,
                       help=help_str)
 
-    for optname, (cls, help_str, formatters_, action) in six.iteritems(fmt_options):
-        for fmt in formatters_:
-            # make sure fmt is in log_formatters and is accepted
-            if fmt in log_formatters and fmt in include_formatters:
-                group_add("--log-%s-%s" % (fmt, optname), action=action,
-                          help=help_str, default=None)
+    for fmt in include_formatters:
+        for optname, (cls, help_str, formatters_, action) in six.iteritems(fmt_options):
+            if fmt not in formatters_:
+                continue
+            if optname.startswith("no-") and action == "store_false":
+                dest = optname.split("-", 1)[1]
+            else:
+                dest = optname
+            dest = dest.replace("-", "_")
+            group_add("--log-%s-%s" % (fmt, optname), action=action,
+                      help=help_str, default=None, dest="log_%s_%s" % (fmt, dest))
 
 
 def setup_handlers(logger, formatters, formatter_options, allow_unused_options=False):
     """
     Add handlers to the given logger according to the formatters and
     options provided.
 
     :param logger: The logger configured by this function.
--- a/testing/mozbase/mozlog/mozlog/formatters/machformatter.py
+++ b/testing/mozbase/mozlog/mozlog/formatters/machformatter.py
@@ -6,46 +6,49 @@
 from __future__ import absolute_import
 
 import time
 
 from mozterm import Terminal
 
 from . import base
 from .process import strstatus
+from .tbplformatter import TbplFormatter
 from ..handlers import SummaryHandler
 import six
 from functools import reduce
 
 
 def format_seconds(total):
     """Format number of seconds to MM:SS.DD form."""
     minutes, seconds = divmod(total, 60)
     return '%2d:%05.2f' % (minutes, seconds)
 
 
 class MachFormatter(base.BaseFormatter):
 
     def __init__(self, start_time=None, write_interval=False, write_times=True,
                  terminal=None, disable_colors=False, summary_on_shutdown=False,
-                 verbose=False, **kwargs):
+                 verbose=False, enable_screenshot=False, **kwargs):
         super(MachFormatter, self).__init__(**kwargs)
 
         if start_time is None:
             start_time = time.time()
         start_time = int(start_time * 1000)
         self.start_time = start_time
         self.write_interval = write_interval
         self.write_times = write_times
         self.status_buffer = {}
         self.has_unexpected = {}
         self.last_time = None
         self.term = Terminal(disable_styling=disable_colors)
         self.verbose = verbose
         self._known_pids = set()
+        self.tbpl_formatter = None
+        self.enable_screenshot = enable_screenshot
 
         self.summary = SummaryHandler()
         self.summary_on_shutdown = summary_on_shutdown
 
     def __call__(self, data):
         self.summary(data)
 
         s = super(MachFormatter, self).__call__(data)
@@ -178,16 +181,18 @@ class MachFormatter(base.BaseFormatter):
 
         if "expected" in data:
             parent_unexpected = True
             expected_str = ", expected %s" % data["expected"]
         else:
             parent_unexpected = False
             expected_str = ""
 
+        has_screenshots = "reftest_screenshots" in data.get("extra", {})
+
         test = self._get_test_id(data)
 
         # Reset the counts to 0
         self.status_buffer[test] = {"count": 0, "unexpected": 0, "pass": 0}
         self.has_unexpected[test] = bool(subtests['unexpected'])
 
         if subtests["count"] != 0:
             rv = "Test %s%s. Subtests passed %i/%i. Unexpected %s" % (
@@ -210,17 +215,23 @@ class MachFormatter(base.BaseFormatter):
                     rv += self._format_status(data['test'], d)
 
         if "expected" not in data and not bool(subtests['unexpected']):
             color = self.term.green
         else:
             color = self.term.red
 
         action = color(data['action'].upper())
-        return "%s: %s" % (action, rv)
+        rv = "%s: %s" % (action, rv)
+        if has_screenshots and self.enable_screenshot:
+            if self.tbpl_formatter is None:
+                self.tbpl_formatter = TbplFormatter()
+            # Create TBPL-like output that can be pasted into the reftest analyser
+            rv = "\n".join((rv, self.tbpl_formatter.test_end(data)))
+        return rv
 
     def valgrind_error(self, data):
         rv = " " + data['primary'] + "\n"
         for line in data['secondary']:
             rv = rv + line + "\n"
 
         return rv
 
--- a/testing/mozbase/mozlog/mozlog/formatters/tbplformatter.py
+++ b/testing/mozbase/mozlog/mozlog/formatters/tbplformatter.py
@@ -208,52 +208,57 @@ class TbplFormatter(BaseFormatter):
         test_id = self.test_id(data["test"])
         duration_msg = ""
 
         if test_id in self.test_start_times:
             start_time = self.test_start_times.pop(test_id)
             time = data["time"] - start_time
             duration_msg = "took %ims" % time
 
+        screenshot_msg = ""
+        extra = data.get("extra", {})
+        if "reftest_screenshots" in extra:
+            screenshots = extra["reftest_screenshots"]
+            if len(screenshots) == 3:
+                screenshot_msg = ("\nREFTEST   IMAGE 1 (TEST): data:image/png;base64,%s\n"
+                                  "REFTEST   IMAGE 2 (REFERENCE): data:image/png;base64,%s") % (
+                                      screenshots[0]["screenshot"],
+                                      screenshots[2]["screenshot"])
+            elif len(screenshots) == 1:
+                screenshot_msg = ("\nREFTEST   IMAGE: data:image/png;base64,%s" %
+                                  screenshots[0]["screenshot"])
+
         if "expected" in data:
             message = data.get("message", "")
             if not message:
                 message = "expected %s" % data["expected"]
             if "stack" in data:
                 message += "\n%s" % data["stack"]
             if message and message[-1] == "\n":
                 message = message[:-1]
 
-            extra = data.get("extra", {})
-            if "reftest_screenshots" in extra:
-                screenshots = extra["reftest_screenshots"]
-                if len(screenshots) == 3:
-                    message += ("\nREFTEST   IMAGE 1 (TEST): data:image/png;base64,%s\n"
-                                "REFTEST   IMAGE 2 (REFERENCE): data:image/png;base64,%s") % (
-                                    screenshots[0]["screenshot"],
-                                    screenshots[2]["screenshot"])
-                elif len(screenshots) == 1:
-                    message += "\nREFTEST   IMAGE: data:image/png;base64,%s" \
-                               % screenshots[0]["screenshot"]
+            message += screenshot_msg
 
             failure_line = "TEST-UNEXPECTED-%s | %s | %s\n" % (
                 data["status"], test_id, message)
 
             if data["expected"] not in ("PASS", "OK"):
                 expected_msg = "expected %s | " % data["expected"]
             else:
                 expected_msg = ""
             info_line = "TEST-INFO %s%s\n" % (expected_msg, duration_msg)
 
             return failure_line + info_line
 
         sections = ["TEST-%s" % data['status'], test_id]
         if duration_msg:
             sections.append(duration_msg)
         rv.append(' | '.join(sections) + '\n')
+        if screenshot_msg:
+            rv.append(screenshot_msg[1:])
         return "".join(rv)
 
     def suite_end(self, data):
         start_time = self.suite_start_time
         time = int((data["time"] - start_time) / 1000)
 
         return "SUITE-END | took %is\n" % time
 
--- a/testing/mozharness/mozharness/mozilla/tooltool.py
+++ b/testing/mozharness/mozharness/mozilla/tooltool.py
@@ -13,16 +13,17 @@ TooltoolErrorList = PythonErrorList + [{
 TOOLTOOL_SERVERS = [
     'https://tooltool.mozilla-releng.net/',
 ]
 
 _here = os.path.abspath(os.path.dirname(__file__))
 _external_tools_path = os.path.normpath(os.path.join(_here, '..', '..',
                                                      'external_tools'))
 
+
 class TooltoolMixin(object):
     """Mixin class for handling tooltool manifests.
     To use a tooltool server other than the Mozilla server, override
     config['tooltool_servers'].  To specify a different authentication
     file than that used in releng automation,override
     config['tooltool_authentication_file']; set it to None to not pass
     any authentication information (OK for public files)
     """
@@ -86,16 +87,20 @@ class TooltoolMixin(object):
         else:
             cmd.extend(['fetch', '-m', manifest, '-o'])
 
         if cache:
             cmd.extend(['--cache-dir' if self.topsrcdir else '-c', cache])
 
         toolchains = os.environ.get('MOZ_TOOLCHAINS')
         if toolchains:
+            if not self.topsrcdir:
+                raise Exception(
+                    'MOZ_TOOLCHAINS is not supported for tasks without '
+                    'a source checkout.')
             cmd.extend(toolchains.split())
 
         timeout = self.config.get('tooltool_timeout', 10 * 60)
 
         self.retry(
             self.run_command,
             args=(cmd, ),
             kwargs={'cwd': output_dir,
--- a/testing/talos/talos/gecko_profile.py
+++ b/testing/talos/talos/gecko_profile.py
@@ -135,18 +135,17 @@ class GeckoProfile(object):
             "prefetchMaxSymbolsPerLib": 3,
             # Default symbol lookup directories
             "defaultApp": "FIREFOX",
             "defaultOs": "WINDOWS",
             # Paths to .SYM files, expressed internally as a
             # mapping of app or platform names to directories
             # Note: App & OS names from requests are converted
             # to all-uppercase internally
-            "symbolPaths": self.symbol_paths,
-            "platformsRequiringSymbols": ["Windows", "Microsoft"]
+            "symbolPaths": self.symbol_paths
         })
 
         if self.browser_config['symbols_path']:
             if mozfile.is_url(self.browser_config['symbols_path']):
                 symbolicator.integrate_symbol_zip_from_url(
                     self.browser_config['symbols_path']
                 )
             elif os.path.isfile(self.browser_config['symbols_path']):
--- a/testing/talos/talos/profiler/symbolication.py
+++ b/testing/talos/talos/profiler/symbolication.py
@@ -134,20 +134,18 @@ class ProfileSymbolicator:
             if platform.system() == "Darwin":
                 return OSXSymbolDumper()
             elif platform.system() == "Linux":
                 return LinuxSymbolDumper()
         except SymbolError:
             return None
 
     def integrate_symbol_zip_from_url(self, symbol_zip_url):
-        if platform.system() not in self.options['platformsRequiringSymbols']\
-                or self.have_integrated(symbol_zip_url):
+        if self.have_integrated(symbol_zip_url):
             return
-
         LogMessage("Retrieving symbol zip from {symbol_zip_url}...".format(
             symbol_zip_url=symbol_zip_url))
         try:
             io = urllib2.urlopen(symbol_zip_url, None, 30)
             with zipfile.ZipFile(cStringIO.StringIO(io.read())) as zf:
                 self.integrate_symbol_zip(zf)
             self._create_file_if_not_exists(self._marker_file(symbol_zip_url))
         except IOError:
--- a/testing/web-platform/mach_commands.py
+++ b/testing/web-platform/mach_commands.py
@@ -51,16 +51,19 @@ class WebPlatformTestsRunnerSetup(Mozbui
                 kwargs["ca_cert_path"] = os.path.join(cert_root, "cacert.pem")
 
             if kwargs["host_key_path"] is None:
                 kwargs["host_key_path"] = os.path.join(cert_root, "web-platform.test.key")
 
             if kwargs["host_cert_path"] is None:
                 kwargs["host_cert_path"] = os.path.join(cert_root, "web-platform.test.pem")
 
+        if kwargs["log_mach_screenshot"] is None:
+            kwargs["log_mach_screenshot"] = True
+
         kwargs["capture_stdio"] = True
 
         return kwargs
 
     def kwargs_firefox(self, kwargs):
         from wptrunner import wptcommandline
         kwargs = self.kwargs_common(kwargs)
 
--- a/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
+++ b/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
@@ -1,5 +1,5 @@
 [event-global-extra.window.html]
-  prefs: [dom.webcomponents.shadowdom.enabled:true]
+  prefs: [dom.webcomponents.shadowdom.enabled:true, dom.window.event.enabled:true]
   [Listener from a different global]
     expected: FAIL
 
--- a/testing/web-platform/meta/dom/events/event-global.html.ini
+++ b/testing/web-platform/meta/dom/events/event-global.html.ini
@@ -1,2 +1,2 @@
 [event-global.html]
-  prefs: [dom.webcomponents.shadowdom.enabled:true]
+  prefs: [dom.webcomponents.shadowdom.enabled:true, dom.window.event.enabled:true]
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -169,16 +169,17 @@
 
   [Range interface: existence and properties of interface prototype object]
     expected: FAIL
 
 
 [interfaces.html?include=Node]
 
 [interfaces.html?exclude=Node]
+  prefs: [dom.window.event.enabled:true]
 
   [Document interface: attribute origin]
     expected: FAIL
 
   [Document interface: new Document() must inherit property "origin" with the proper type]
     expected: FAIL
 
   [Document interface: xmlDoc must inherit property "origin" with the proper type]
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini
@@ -1,4 +1,5 @@
 [track-remove-track.html]
+  prefs: [dom.window.event.enabled:true]
   [Tests that the 'removetrack' event is NOT fired for inband TextTrack on a failed load.]
     expected: FAIL
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -791,16 +791,22 @@ AntiTrackingCommon::IsFirstPartyStorageA
          "pretending our channel isn't a tracking channel"));
     return true;
   }
 
   if (CheckContentBlockingAllowList(aChannel)) {
     return true;
   }
 
+  // Not a tracker.
+  if (!aChannel->GetIsTrackingResource()) {
+    LOG(("Our channel isn't a tracking channel"));
+    return true;
+  }
+
   nsIPrincipal* parentPrincipal = loadInfo->TopLevelStorageAreaPrincipal();
   if (!parentPrincipal) {
     LOG(("No top-level storage area principal at hand"));
 
     // parentPrincipal can be null if the parent window is not the top-level
     // window.
     if (loadInfo->TopLevelPrincipal()) {
       LOG(("Parent window is the top-level window, bail out early"));
@@ -810,22 +816,16 @@ AntiTrackingCommon::IsFirstPartyStorageA
     parentPrincipal = toplevelPrincipal;
     if (NS_WARN_IF(!parentPrincipal)) {
       LOG(("No triggering principal, this shouldn't be happening! Bail out early"));
       // Why we are here?!?
       return true;
     }
   }
 
-  // Not a tracker.
-  if (!aChannel->GetIsTrackingResource()) {
-    LOG(("Our channel isn't a tracking channel"));
-    return true;
-  }
-
   // Let's see if we have to grant the access for this particular channel.
 
   nsCOMPtr<nsIURI> trackingURI;
   rv = aChannel->GetURI(getter_AddRefs(trackingURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     LOG(("Failed to get the channel URI"));
     return true;
   }
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -36,17 +36,16 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
   AddonSettings: "resource://gre/modules/addons/AddonSettings.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
-  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm",
   ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
   ExtensionStorageIDB: "resource://gre/modules/ExtensionStorageIDB.jsm",
   ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm",
   ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm",
   FileSource: "resource://gre/modules/L10nRegistry.jsm",
   L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
   Log: "resource://gre/modules/Log.jsm",
@@ -107,21 +106,19 @@ const {
 
 XPCOMUtils.defineLazyGetter(this, "console", ExtensionCommon.getConsole);
 
 XPCOMUtils.defineLazyGetter(this, "LocaleData", () => ExtensionCommon.LocaleData);
 
 const {sharedData} = Services.ppmm;
 
 // The userContextID reserved for the extension storage (its purpose is ensuring that the IndexedDB
-// storage used by the browser.storage.local API is not directly accessible from the extension code).
-XPCOMUtils.defineLazyGetter(this, "WEBEXT_STORAGE_USER_CONTEXT_ID", () => {
-  return ContextualIdentityService.getDefaultPrivateIdentity(
-    "userContextIdInternal.webextStorageLocal").userContextId;
-});
+// storage used by the browser.storage.local API is not directly accessible from the extension code,
+// it is defined and reserved as "userContextIdInternal.webextStorageLocal" in ContextualIdentityService.jsm).
+const WEBEXT_STORAGE_USER_CONTEXT_ID = -1 >>> 0;
 
 // The maximum time to wait for extension child shutdown blockers to complete.
 const CHILD_SHUTDOWN_TIMEOUT_MS = 8000;
 
 /**
  * Classify an individual permission from a webextension manifest
  * as a host/origin permission, an api permission, or a regular permission.
  *
@@ -1829,25 +1826,16 @@ class Extension extends ExtensionData {
       // Select the storage.local backend if it is already known,
       // and start the data migration if needed.
       if (this.hasPermission("storage")) {
         if (!ExtensionStorageIDB.isBackendEnabled) {
           this.setSharedData("storageIDBBackend", false);
         } else if (ExtensionStorageIDB.isMigratedExtension(this)) {
           this.setSharedData("storageIDBBackend", true);
           this.setSharedData("storageIDBPrincipal", ExtensionStorageIDB.getStoragePrincipal(this));
-        } else {
-          // If the extension has to migrate backend, ensure that the data migration
-          // starts once Firefox is idle after the extension has been started.
-          this.once("ready", () => ChromeUtils.idleDispatch(() => {
-            if (this.hasShutdown) {
-              return;
-            }
-            ExtensionStorageIDB.selectBackend({extension: this});
-          }));
         }
       }
 
       // The "startup" Management event sent on the extension instance itself
       // is emitted just before the Management "startup" event,
       // and it is used to run code that needs to be executed before
       // any of the "startup" listeners.
       this.emit("startup", this);
--- a/toolkit/components/extensions/ExtensionStorageIDB.jsm
+++ b/toolkit/components/extensions/ExtensionStorageIDB.jsm
@@ -5,29 +5,26 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["ExtensionStorageIDB"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/IndexedDB.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
   getTrimmedString: "resource://gre/modules/ExtensionTelemetry.jsm",
   Services: "resource://gre/modules/Services.jsm",
   OS: "resource://gre/modules/osfile.jsm",
 });
 
 // The userContextID reserved for the extension storage (its purpose is ensuring that the IndexedDB
-// storage used by the browser.storage.local API is not directly accessible from the extension code).
-XPCOMUtils.defineLazyGetter(this, "WEBEXT_STORAGE_USER_CONTEXT_ID", () => {
-  return ContextualIdentityService.getDefaultPrivateIdentity(
-    "userContextIdInternal.webextStorageLocal").userContextId;
-});
+// storage used by the browser.storage.local API is not directly accessible from the extension code,
+// it is defined and reserved as "userContextIdInternal.webextStorageLocal" in ContextualIdentityService.jsm).
+const WEBEXT_STORAGE_USER_CONTEXT_ID = -1 >>> 0;
 
 const IDB_NAME = "webExtensions-storage-local";
 const IDB_DATA_STORENAME = "storage-local-data";
 const IDB_VERSION = 1;
 const IDB_MIGRATE_RESULT_HISTOGRAM = "WEBEXT_STORAGE_LOCAL_IDB_MIGRATE_RESULT_COUNT";
 
 // Whether or not the installed extensions should be migrated to the storage.local IndexedDB backend.
 const BACKEND_ENABLED_PREF = "extensions.webextensions.ExtensionStorageIDB.enabled";
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -1062,19 +1062,18 @@ def launcher(value, target):
     if enabled:
         return True
 
 set_config('MOZ_LAUNCHER_PROCESS', launcher)
 set_define('MOZ_LAUNCHER_PROCESS', launcher)
 
 # Prio
 # ==============================================================
-@depends(c_compiler, target)
-def libprio(info, target):
+@depends(c_compiler)
+def libprio(info):
     if info:
-      # TODO - re-enable Windows when bug 1489691 is fixed.
-      # Note that we will probably never support MSVC however.
-      if info.type in ('msvc',) or target.os in ('WINNT',):
+      # MSVC is not supported by libprio.
+      if info.type in ('msvc',):
         return None
     return True
 
 set_config('MOZ_LIBPRIO', libprio)
 
--- a/tools/lint/docs/create.rst
+++ b/tools/lint/docs/create.rst
@@ -21,18 +21,17 @@ be python code alongside the definition,
 Here's a trivial example:
 
 no-eval.yml
 
 .. code-block::
 
     EvalLinter:
         description: Ensures the string eval doesn't show up.
-        include:
-            - "**/*.js"
+        extensions: ['js']
         type: string
         payload: eval
 
 Now ``no-eval.yml`` gets passed into :func:`LintRoller.read`.
 
 
 Linter Types
 ------------
@@ -70,18 +69,18 @@ this case the signature for lint functio
 Linter Definition
 -----------------
 
 Each ``.yml`` file must have at least one linter defined in it. Here are the supported keys:
 
 * description - A brief description of the linter's purpose (required)
 * type - One of 'string', 'regex' or 'external' (required)
 * payload - The actual linting logic, depends on the type (required)
-* include - A list of glob patterns that must be matched (optional)
-* exclude - A list of glob patterns that must not be matched (optional)
+* include - A list of file paths that will be considered (optional)
+* exclude - A list of file paths or glob patterns that must not be matched (optional)
 * extensions - A list of file extensions to be considered (optional)
 * setup - A function that sets up external dependencies (optional)
 * support-files - A list of glob patterns matching configuration files (optional)
 
 In addition to the above, some ``.yml`` files correspond to a single lint rule. For these, the
 following additional keys may be specified:
 
 * message - A string to print on infraction (optional)
@@ -160,18 +159,18 @@ let's call the file ``flake8_lint.py``:
         return results
 
 Now here is the linter definition that would call it:
 
 .. code-block:: yml
 
     flake8:
         description: Python linter
-        include:
-            - '**/*.py'
+        include: ['.']
+        extensions: ['py']
         type: external
         payload: py.flake8:lint
         support-files:
             - '**/.flake8'
 
 Notice the payload has two parts, delimited by ':'. The first is the module
 path, which ``mozlint`` will attempt to import. The second is the object path
 within that module (e.g, the name of a function to call). It is up to consumers
@@ -195,18 +194,18 @@ Either way, to reduce the burden on user
 automated bootstrapping of all their dependencies. To help with this,
 ``mozlint`` allows linters to define a ``setup`` config, which has the same
 path object format as an external payload. For example:
 
 .. code-block:: yml
 
     flake8:
         description: Python linter
-        include:
-            - '**/*.py'
+        include: ['.']
+        extensions: ['py']
         type: external
         payload: py.flake8:lint
         setup: py.flake8:setup
 
 The setup function takes a single argument, the root of the repository being
 linted. In the case of ``flake8``, it might look like:
 
 .. code-block:: python
--- a/tools/lint/eslint/__init__.py
+++ b/tools/lint/eslint/__init__.py
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import json
 import os
 import signal
 import sys
 sys.path.append(os.path.join(os.path.dirname(__file__), "eslint"))
 import setup_helper
+from mozbuild.nodeutil import find_node_executable
 
 from mozprocess import ProcessHandler
 
 from mozlint import result
 
 ESLINT_ERROR_MESSAGE = """
 An error occurred running eslint. Please check the following error messages:
 
@@ -47,29 +48,25 @@ def lint(paths, config, binary=None, fix
     module_path = setup_helper.get_project_root()
 
     # Valid binaries are:
     #  - Any provided by the binary argument.
     #  - Any pointed at by the ESLINT environmental variable.
     #  - Those provided by |mach lint --setup|.
 
     if not binary:
-        binary = os.environ.get('ESLINT', None)
-
-        if not binary:
-            binary = os.path.join(module_path, "node_modules", ".bin", "eslint")
-            if not os.path.isfile(binary):
-                binary = None
+        binary, _ = find_node_executable()
 
     if not binary:
         print(ESLINT_NOT_FOUND_MESSAGE)
         return 1
 
     extra_args = lintargs.get('extra_args') or []
     cmd_args = [binary,
+                os.path.join(module_path, "node_modules", "eslint", "bin", "eslint.js"),
                 # Enable the HTML plugin.
                 # We can't currently enable this in the global config file
                 # because it has bad interactions with the SublimeText
                 # ESLint plugin (bug 1229874).
                 '--plugin', 'html',
                 # This keeps ext as a single argument.
                 '--ext', '[{}]'.format(','.join(config['extensions'])),
                 '--format', 'json',
--- a/tools/lint/eslint/setup_helper.py
+++ b/tools/lint/eslint/setup_helper.py
@@ -9,49 +9,49 @@ import json
 import os
 import platform
 import re
 from mozfile.mozfile import remove as mozfileremove
 import subprocess
 import sys
 import shutil
 import tempfile
-from distutils.version import LooseVersion
+from distutils.version import LooseVersion, StrictVersion
+from mozbuild.nodeutil import (find_node_executable, find_npm_executable,
+                               NPM_MIN_VERSION, NODE_MIN_VERSION)
 sys.path.append(os.path.join(
     os.path.dirname(__file__), "..", "..", "..", "third_party", "python", "which"))
-import which
-
-NODE_MIN_VERSION = "8.9.1"
-NPM_MIN_VERSION = "5.5.1"
 
 NODE_MACHING_VERSION_NOT_FOUND_MESSAGE = """
-nodejs is out of date. You currently have node v%s but v%s is required.
-Please update nodejs from https://nodejs.org and try again.
+Could not find Node.js executable later than %s.
+
+Executing `mach bootstrap --no-system-changes` should
+install a compatible version in ~/.mozbuild on most platforms.
 """.strip()
 
 NPM_MACHING_VERSION_NOT_FOUND_MESSAGE = """
-npm is out of date. You currently have npm v%s but v%s is required.
-You can usually update npm with:
+Could not find npm executable later than %s.
 
-npm i -g npm
+Executing `mach bootstrap --no-system-changes` should
+install a compatible version in ~/.mozbuild on most platforms.
 """.strip()
 
 NODE_NOT_FOUND_MESSAGE = """
 nodejs is either not installed or is installed to a non-standard path.
-Please install nodejs from https://nodejs.org and try again.
 
-Valid installation paths:
+Executing `mach bootstrap --no-system-changes` should
+install a compatible version in ~/.mozbuild on most platforms.
 """.strip()
 
 NPM_NOT_FOUND_MESSAGE = """
 Node Package Manager (npm) is either not installed or installed to a
-non-standard path. Please install npm from https://nodejs.org (it comes as an
-option in the node installation) and try again.
+non-standard path.
 
-Valid installation paths:
+Executing `mach bootstrap --no-system-changes` should
+install a compatible version in ~/.mozbuild on most platforms.
 """.strip()
 
 
 VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$")
 CARET_VERSION_RANGE_RE = re.compile(r"^\^((\d+)\.\d+\.\d+)$")
 
 project_root = None
 
@@ -83,28 +83,28 @@ def eslint_setup(should_clobber=False):
         node_modules_path = os.path.join(project_root, "node_modules")
         print("Clobbering node_modules...")
         if sys.platform.startswith('win') and have_winrm():
             process = subprocess.Popen(['winrm', '-rf', node_modules_path])
             process.wait()
         else:
             mozfileremove(node_modules_path)
 
-    npm_path, version = get_node_or_npm_path("npm")
+    npm_path, version = find_npm_executable()
     if not npm_path:
         return 1
 
     extra_parameters = ["--loglevel=error"]
 
     package_lock_json_path = os.path.join(get_project_root(), "package-lock.json")
     package_lock_json_tmp_path = os.path.join(tempfile.gettempdir(), "package-lock.json.tmp")
 
     # If we have an npm version newer than 5.8.0, just use 'ci', as that's much
     # simpler and does exactly what we want.
-    npm_is_older_version = version < LooseVersion("5.8.0")
+    npm_is_older_version = version < StrictVersion("5.8.0").version
 
     if npm_is_older_version:
         cmd = [npm_path, "install"]
         shutil.copy2(package_lock_json_path, package_lock_json_tmp_path)
     else:
         cmd = [npm_path, "ci"]
 
     cmd.extend(extra_parameters)
@@ -267,84 +267,16 @@ def get_possible_node_paths_win():
     return list({
         "%s\\nodejs" % os.environ.get("SystemDrive"),
         os.path.join(os.environ.get("ProgramFiles"), "nodejs"),
         os.path.join(os.environ.get("PROGRAMW6432"), "nodejs"),
         os.path.join(os.environ.get("PROGRAMFILES"), "nodejs")
     })
 
 
-def simple_which(filename, path=None):
-    exts = [".cmd", ".exe", ""] if platform.system() == "Windows" else [""]
-
-    for ext in exts:
-        try:
-            return which.which(filename + ext, path)
-        except which.WhichError:
-            pass
-
-    # If we got this far, we didn't find it with any of the extensions, so
-    # just return.
-    return None
-
-
-def which_path(filename):
-    """
-    Return the nodejs or npm path.
-    """
-    # Look in the system path first.
-    path = simple_which(filename)
-    if path is not None:
-        return path
-
-    if platform.system() == "Windows":
-        # If we didn't find it fallback to the non-system paths.
-        path = simple_which(filename, get_possible_node_paths_win())
-    elif filename == "node":
-        path = simple_which("nodejs")
-
-    return path
-
-
-def get_node_or_npm_path(filename, minversion=None):
-    node_or_npm_path = which_path(filename)
-
-    if not node_or_npm_path:
-        if filename in ('node', 'nodejs'):
-            print(NODE_NOT_FOUND_MESSAGE)
-        elif filename == "npm":
-            print(NPM_NOT_FOUND_MESSAGE)
-
-        if platform.system() == "Windows":
-            app_paths = get_possible_node_paths_win()
-
-            for p in app_paths:
-                print("  - %s" % p)
-        elif platform.system() == "Darwin":
-            print("  - /usr/local/bin/{}".format(filename))
-        elif platform.system() == "Linux":
-            print("  - /usr/bin/{}".format(filename))
-
-        return None, None
-
-    version_str = get_version(node_or_npm_path).lstrip('v')
-
-    version = LooseVersion(version_str)
-
-    if not minversion or version > minversion:
-        return node_or_npm_path, version
-
-    if filename == "npm":
-        print(NPM_MACHING_VERSION_NOT_FOUND_MESSAGE % (version_str.strip(), minversion))
-    else:
-        print(NODE_MACHING_VERSION_NOT_FOUND_MESSAGE % (version_str.strip(), minversion))
-
-    return None, None
-
-
 def get_version(path):
     try:
         version_str = subprocess.check_output([path, "--version"],
                                               stderr=subprocess.STDOUT)
         return version_str
     except (subprocess.CalledProcessError, OSError):
         return None
 
@@ -388,23 +320,30 @@ def get_project_root():
     return project_root
 
 
 def get_eslint_module_path():
     return os.path.join(get_project_root(), "tools", "lint", "eslint")
 
 
 def check_node_executables_valid():
-    # eslint requires at least node 6.9.1
-    node_path = get_node_or_npm_path("node", LooseVersion(NODE_MIN_VERSION))
+    node_path, version = find_node_executable()
     if not node_path:
+        print(NODE_NOT_FOUND_MESSAGE)
+        return False
+    if not version:
+        print(NODE_MACHING_VERSION_NOT_FOUND_MESSAGE % NODE_MIN_VERSION)
         return False
 
-    npm_path = get_node_or_npm_path("npm", LooseVersion(NPM_MIN_VERSION))
+    npm_path, version = find_npm_executable()
     if not npm_path:
+        print(NPM_NOT_FOUND_MESSAGE)
+        return False
+    if not version:
+        print(NPM_MACHING_VERSION_NOT_FOUND_MESSAGE % NPM_MIN_VERSION)
         return False
 
     return True
 
 
 def have_winrm():
     # `winrm -h` should print 'winrm version ...' and exit 1
     try:
--- a/tools/lint/flake8.yml
+++ b/tools/lint/flake8.yml
@@ -35,16 +35,17 @@ flake8:
         - testing/marionette/puppeteer
         - testing/mochitest
         - testing/mozbase
         - testing/mozharness/configs
         - testing/mozharness/mozfile
         - testing/mozharness/mozharness/mozilla/l10n/locales.py
         - testing/mozharness/mozharness/mozilla/mar.py
         - testing/mozharness/mozharness/mozilla/testing/
+        - testing/mozharness/mozharness/mozilla/tooltool.py
         - testing/mozharness/mozinfo
         - testing/mozharness/scripts
         - testing/raptor
         - testing/remotecppunittests.py
         - testing/runcppunittests.py
         - testing/talos/
         - testing/xpcshell
         - toolkit/components/telemetry
--- a/tools/lint/test-disable.yml
+++ b/tools/lint/test-disable.yml
@@ -1,15 +1,14 @@
 ---
 no-comment-disable:
     description: >
       "Use 'disabled=<reason>' to disable a test instead of a
       comment"
-    include:
-        - "**/*.ini"
+    include: ['.']
     exclude:
         - "**/application.ini"
         - "**/l10n.ini"
         - dom/canvas/test/webgl-conf/mochitest-errata.ini
         - testing/mozbase/manifestparser/tests
         - testing/web-platform
         - third_party
         - xpcom/tests/unit/data
--- a/uriloader/exthandler/ExternalHelperAppChild.cpp
+++ b/uriloader/exthandler/ExternalHelperAppChild.cpp
@@ -42,19 +42,16 @@ ExternalHelperAppChild::OnDataAvailable(
 {
   if (NS_FAILED(mStatus))
     return mStatus;
 
   static uint32_t const kCopyChunkSize = 128 * 1024;
   uint32_t toRead = std::min<uint32_t>(count, kCopyChunkSize);
 
   nsCString data;
-  if (NS_WARN_IF(!data.SetCapacity(toRead, fallible))) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
 
   while (count) {
     nsresult rv = NS_ReadInputStreamToString(input, data, toRead);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (NS_WARN_IF(!SendOnDataAvailable(data, offset, toRead))) {
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -325,18 +325,17 @@ nsOSHelperAppService::GetTypeAndDescript
                                   getter_AddRefs(mimeTypes),
                                   cBuf, &netscapeFormat, &more);
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsAutoString extensions;
-  nsString entry;
-  entry.SetCapacity(100);
+  nsAutoStringN<101> entry;
   nsAString::const_iterator majorTypeStart, majorTypeEnd,
                             minorTypeStart, minorTypeEnd,
                             descriptionStart, descriptionEnd;
 
   do {
     CopyASCIItoUTF16(cBuf, buf);
     // read through, building up an entry.  If we finish an entry, check for
     // a match and return out of the loop if we match
@@ -491,18 +490,17 @@ nsOSHelperAppService::GetExtensionsAndDe
   nsresult rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile),
                                   getter_AddRefs(mimeTypes),
                                   cBuf, &netscapeFormat, &more);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsAutoString extensions;
-  nsString entry;
-  entry.SetCapacity(100);
+  nsAutoStringN<101> entry;
   nsAString::const_iterator majorTypeStart, majorTypeEnd,
                             minorTypeStart, minorTypeEnd,
                             descriptionStart, descriptionEnd;
 
   do {
     CopyASCIItoUTF16(cBuf, buf);
     // read through, building up an entry.  If we finish an entry, check for
     // a match and return out of the loop if we match
@@ -942,20 +940,19 @@ nsOSHelperAppService::GetHandlerAndDescr
 
   nsCOMPtr<nsILineInputStream> mailcap (do_QueryInterface(mailcapFile, &rv));
 
   if (NS_FAILED(rv)) {
     LOG(("Interface trouble in stream land!"));
     return rv;
   }
 
-  nsString entry, buffer;
-  nsAutoCString cBuffer;
-  entry.SetCapacity(128);
-  cBuffer.SetCapacity(80);
+  nsAutoStringN<129> entry;
+  nsAutoStringN<81> buffer;
+  nsAutoCStringN<81> cBuffer;
   rv = mailcap->ReadLine(cBuffer, &more);
   if (NS_FAILED(rv)) {
     mailcapFile->Close();
     return rv;
   }
 
   do {  // return on end-of-file in the loop