merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 18 Feb 2016 12:01:24 +0100
changeset 321007 1150ac4755c7bb35df4fc7504f6f1b6c257f400e
parent 320898 3b35f0a98eba9f1c9be7d793650b3d5bec6c8fdb (current diff)
parent 321006 de3cd971372ca5ebd4ad328e0d712de888f1d3b3 (diff)
child 321010 fcd35e10fa17d9fd11d92be48ae9698c2a900f1c
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
first release with
nightly linux32
1150ac4755c7 / 47.0a1 / 20160218030349 / files
nightly linux64
1150ac4755c7 / 47.0a1 / 20160218030349 / files
nightly mac
1150ac4755c7 / 47.0a1 / 20160218030349 / files
nightly win32
1150ac4755c7 / 47.0a1 / 20160218030349 / files
nightly win64
1150ac4755c7 / 47.0a1 / 20160218030349 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/modules/webrtcUI.jsm
media/ffvpx/libavcodec/imgconvert.h
media/ffvpx/libavutil/x86_cpu.h
testing/docker/tester/bin/entrypoint
testing/docker/tester/bin/pull_gaia.sh
testing/taskcluster/README.md
testing/taskcluster/scripts/tester/test-mulet.sh
testing/taskcluster/tasks/branches/base_job_flags.yml
testing/taskcluster/tasks/branches/base_jobs.yml
testing/taskcluster/tasks/tests/b2g_gaia_unit_oop.yml
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +1,16 @@
 # 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/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=46.0a1
+MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=2.6.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -274,16 +274,17 @@ function getHost(uri, href) {
 }
 
 function prompt(aBrowser, aRequest) {
   let {audioDevices: audioDevices, videoDevices: videoDevices,
        sharingScreen: sharingScreen, sharingAudio: sharingAudio,
        requestTypes: requestTypes} = aRequest;
   let uri = Services.io.newURI(aRequest.documentURI, null, null);
   let host = getHost(uri);
+  let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
   let chromeDoc = aBrowser.ownerDocument;
   let chromeWin = chromeDoc.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
   let stringId = "getUserMedia.share" + requestTypes.join("And") + ".message";
   let message = stringBundle.getFormattedString(stringId, [host]);
 
   let mainLabel;
   if (sharingScreen || sharingAudio) {
@@ -369,17 +370,26 @@ function prompt(aBrowser, aRequest) {
       // to avoid granting permissions automatically to background tabs.
       if (aRequest.secure) {
         let perms = Services.perms;
 
         let micPerm = perms.testExactPermission(uri, "microphone");
         if (micPerm == perms.PROMPT_ACTION)
           micPerm = perms.UNKNOWN_ACTION;
 
+        let camPermanentPerm = perms.testExactPermanentPermission(principal, "camera");
         let camPerm = perms.testExactPermission(uri, "camera");
+
+        // Session approval given but never used to allocate a camera, remove
+        // and ask again
+        if (camPerm && !camPermanentPerm) {
+          perms.remove(uri, "camera");
+          camPerm = perms.UNKNOWN_ACTION;
+        }
+
         if (camPerm == perms.PROMPT_ACTION)
           camPerm = perms.UNKNOWN_ACTION;
 
         // Screen sharing shouldn't follow the camera permissions.
         if (videoDevices.length && sharingScreen)
           camPerm = perms.UNKNOWN_ACTION;
 
         // We don't check that permissions are set to ALLOW_ACTION in this
@@ -510,21 +520,24 @@ function prompt(aBrowser, aRequest) {
 
       this.mainAction.callback = function(aRemember) {
         let allowedDevices = [];
         let perms = Services.perms;
         if (videoDevices.length) {
           let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
           let videoDeviceIndex = chromeDoc.getElementById(listId).value;
           let allowCamera = videoDeviceIndex != "-1";
-          if (allowCamera)
+          if (allowCamera) {
             allowedDevices.push(videoDeviceIndex);
-          if (aRemember) {
-            perms.add(uri, "camera",
-                      allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
+            // Session permission will be removed after use
+            // (it's really one-shot, not for the entire session)
+            perms.add(uri, "camera", perms.ALLOW_ACTION,
+                      aRemember ? perms.EXPIRE_NEVER : perms.EXPIRE_SESSION);
+          } else if (aRemember) {
+            perms.add(uri, "camera", perms.DENY_ACTION);
           }
         }
         if (audioDevices.length) {
           if (!sharingAudio) {
             let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
             let allowMic = audioDeviceIndex != "-1";
             if (allowMic)
               allowedDevices.push(audioDeviceIndex);
--- a/dom/animation/CSSPseudoElement.h
+++ b/dom/animation/CSSPseudoElement.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_CSSPseudoElement_h
 #define mozilla_dom_CSSPseudoElement_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Element.h"
 #include "mozilla/RefPtr.h"
 #include "nsCSSPseudoElements.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class Animation;
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -18,33 +18,51 @@ using namespace mozilla::dom;
 FormData::FormData(nsISupports* aOwner)
   : nsFormSubmission(NS_LITERAL_CSTRING("UTF-8"), nullptr)
   , mOwner(aOwner)
 {
 }
 
 namespace {
 
-already_AddRefed<Blob>
-GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename,
-                          ErrorResult& aRv)
+already_AddRefed<File>
+GetOrCreateFileCalledBlob(Blob& aBlob, ErrorResult& aRv)
 {
-  if (!aFilename.WasPassed()) {
-    RefPtr<Blob> blob = &aBlob;
-    return blob.forget();
+  // If this is file, we can just use it
+  RefPtr<File> file = aBlob.ToFile();
+  if (file) {
+    return file.forget();
   }
 
-  RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
+  // Forcing 'blob' as filename
+  file = aBlob.ToFile(NS_LITERAL_STRING("blob"), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return file.forget();
 }
 
+already_AddRefed<File>
+GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename,
+                          ErrorResult& aRv)
+{
+  // Forcing a filename
+  if (aFilename.WasPassed()) {
+    RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    return file.forget();
+  }
+
+  return GetOrCreateFileCalledBlob(aBlob, aRv);
+}
+
 } // namespace
 
 // -------------------------------------------------------------------------
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FormData)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FormData)
@@ -97,22 +115,22 @@ FormData::Append(const nsAString& aName,
   AddNameValuePair(aName, aValue);
 }
 
 void
 FormData::Append(const nsAString& aName, Blob& aBlob,
                  const Optional<nsAString>& aFilename,
                  ErrorResult& aRv)
 {
-  RefPtr<Blob> blob = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
+  RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  AddNameBlobPair(aName, blob);
+  AddNameBlobPair(aName, file);
 }
 
 void
 FormData::Delete(const nsAString& aName)
 {
   // We have to use this slightly awkward for loop since uint32_t >= 0 is an
   // error for being always true.
   for (uint32_t i = mFormData.Length(); i-- > 0; ) {
@@ -160,18 +178,24 @@ FormData::Has(const nsAString& aName)
   return false;
 }
 
 nsresult
 FormData::AddNameBlobPair(const nsAString& aName, Blob* aBlob)
 {
   MOZ_ASSERT(aBlob);
 
+  ErrorResult rv;
+  RefPtr<File> file = GetOrCreateFileCalledBlob(*aBlob, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
   FormDataTuple* data = mFormData.AppendElement();
-  SetNameBlobPair(data, aName, aBlob);
+  SetNameFilePair(data, aName, file);
   return NS_OK;
 }
 
 FormData::FormDataTuple*
 FormData::RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName)
 {
   FormDataTuple* lastFoundTuple = nullptr;
   uint32_t lastFoundIndex = mFormData.Length();
@@ -194,22 +218,22 @@ FormData::RemoveAllOthersAndGetFirstForm
 
 void
 FormData::Set(const nsAString& aName, Blob& aBlob,
               const Optional<nsAString>& aFilename,
               ErrorResult& aRv)
 {
   FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
   if (tuple) {
-    RefPtr<Blob> blob = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
+    RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
-    SetNameBlobPair(tuple, aName, blob);
+    SetNameFilePair(tuple, aName, file);
   } else {
     Append(aName, aBlob, aFilename, aRv);
   }
 }
 
 void
 FormData::Set(const nsAString& aName, const nsAString& aValue,
               ErrorResult& aRv)
@@ -248,25 +272,25 @@ FormData::SetNameValuePair(FormDataTuple
                            const nsAString& aValue)
 {
   MOZ_ASSERT(aData);
   aData->name = aName;
   aData->value.SetAsUSVString() = aValue;
 }
 
 void
-FormData::SetNameBlobPair(FormDataTuple* aData,
+FormData::SetNameFilePair(FormDataTuple* aData,
                           const nsAString& aName,
-                          Blob* aBlob)
+                          File* aFile)
 {
   MOZ_ASSERT(aData);
-  MOZ_ASSERT(aBlob);
+  MOZ_ASSERT(aFile);
 
   aData->name = aName;
-  aData->value.SetAsBlob() = aBlob;
+  aData->value.SetAsBlob() = aFile;
 }
 
 // -------------------------------------------------------------------------
 // nsIDOMFormData
 
 NS_IMETHODIMP
 FormData::Append(const nsAString& aName, nsIVariant* aValue)
 {
--- a/dom/base/FormData.h
+++ b/dom/base/FormData.h
@@ -42,19 +42,19 @@ private:
   // no element with aName was found.
   FormDataTuple*
   RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName);
 
   void SetNameValuePair(FormDataTuple* aData,
                         const nsAString& aName,
                         const nsAString& aValue);
 
-  void SetNameBlobPair(FormDataTuple* aData,
+  void SetNameFilePair(FormDataTuple* aData,
                        const nsAString& aName,
-                       Blob* aBlob);
+                       File* aFile);
 
 public:
   explicit FormData(nsISupports* aOwner = nullptr);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(FormData,
                                                          nsIDOMFormData)
 
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -1,16 +1,15 @@
 /* -*- 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 "ImageEncoder.h"
-#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/unused.h"
 #include "gfxUtils.h"
@@ -486,29 +485,62 @@ ImageEncoder::GetImageEncoder(nsAString&
     aType.AssignLiteral("image/png");
     nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
     encoder = do_CreateInstance(PNGEncoderCID.get());
   }
 
   return encoder.forget();
 }
 
+class EncoderThreadPoolTerminator final : public nsIObserver
+{
+  public:
+    NS_DECL_ISUPPORTS
+
+    NS_IMETHODIMP Observe(nsISupports *, const char *topic, const char16_t *) override
+    {
+      NS_ASSERTION(!strcmp(topic, "xpcom-shutdown-threads"),
+                   "Unexpected topic");
+      if (ImageEncoder::sThreadPool) {
+        ImageEncoder::sThreadPool->Shutdown();
+        ImageEncoder::sThreadPool = nullptr;
+      }
+      return NS_OK;
+    }
+  private:
+    ~EncoderThreadPoolTerminator() {}
+};
+
+NS_IMPL_ISUPPORTS(EncoderThreadPoolTerminator, nsIObserver)
+
+static void
+RegisterEncoderThreadPoolTerminatorObserver()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  NS_ASSERTION(os, "do_GetService failed");
+  os->AddObserver(new EncoderThreadPoolTerminator(),
+                  "xpcom-shutdown-threads",
+                  false);
+}
+
 /* static */
 nsresult
 ImageEncoder::EnsureThreadPool()
 {
   if (!sThreadPool) {
     nsCOMPtr<nsIThreadPool> threadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
     sThreadPool = threadPool;
+
     if (!NS_IsMainThread()) {
       NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
-        ClearOnShutdown(&sThreadPool);
+        RegisterEncoderThreadPoolTerminatorObserver();
       }));
     } else {
-      ClearOnShutdown(&sThreadPool);
+      RegisterEncoderThreadPoolTerminatorObserver();
     }
 
     const uint32_t kThreadLimit = 2;
     const uint32_t kIdleThreadLimit = 1;
     const uint32_t kIdleThreadTimeoutMs = 30000;
 
     nsresult rv = sThreadPool->SetName(NS_LITERAL_CSTRING("EncodingRunnable"));
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/base/ImageEncoder.h
+++ b/dom/base/ImageEncoder.h
@@ -109,16 +109,17 @@ private:
   static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
 
   static nsresult EnsureThreadPool();
 
   // Thread pool for dispatching EncodingRunnable.
   static StaticRefPtr<nsIThreadPool> sThreadPool;
 
   friend class EncodingRunnable;
+  friend class EncoderThreadPoolTerminator;
 };
 
 /**
  *  The callback interface of ExtractDataAsync and ExtractDataFromLayersImageAsync.
  *  ReceiveBlob() is called on main thread when encoding is complete.
  */
 class EncodeCompleteCallback
 {
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -502,18 +502,23 @@ AutoJSAPI::ReportException()
   }
 
   // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null
   // compartment when the destructor is called. However, the JS engine
   // requires us to be in a compartment when we fetch the pending exception.
   // In this case, we enter the privileged junk scope and don't dispatch any
   // error events.
   JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
-  if (!errorGlobal)
-    errorGlobal = xpc::PrivilegedJunkScope();
+  if (!errorGlobal) {
+    if (mIsMainThread) {
+      errorGlobal = xpc::PrivilegedJunkScope();
+    } else {
+      errorGlobal = workers::GetCurrentThreadWorkerGlobal();
+    }
+  }
   JSAutoCompartment ac(cx(), errorGlobal);
   JS::Rooted<JS::Value> exn(cx());
   js::ErrorReport jsReport(cx());
   if (StealException(&exn) && jsReport.init(cx(), exn)) {
     if (mIsMainThread) {
       RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
       RefPtr<nsGlobalWindow> win = xpc::WindowGlobalOrNull(errorGlobal);
       nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -431,16 +431,17 @@ LOCAL_INCLUDES += [
     '/caps',
     '/docshell/base',
     '/dom/base',
     '/dom/geolocation',
     '/dom/html',
     '/dom/ipc',
     '/dom/storage',
     '/dom/svg',
+    '/dom/u2f',
     '/dom/workers',
     '/dom/xbl',
     '/dom/xml',
     '/dom/xslt/xpath',
     '/dom/xul',
     '/gfx/2d',
     '/image',
     '/js/xpconnect/src',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -221,16 +221,17 @@
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
+#include "mozilla/dom/U2F.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
@@ -1880,16 +1881,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
 
   tmp->TraverseHostObjectURIs(cb);
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -1954,16 +1956,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
 
   tmp->UnlinkHostObjectURIs();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -4343,16 +4346,33 @@ nsGlobalWindow::GetCrypto(ErrorResult& a
 
   if (!mCrypto) {
     mCrypto = new Crypto();
     mCrypto->Init(this);
   }
   return mCrypto;
 }
 
+mozilla::dom::U2F*
+nsGlobalWindow::GetU2f(ErrorResult& aError)
+{
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+
+  if (!mU2F) {
+    RefPtr<U2F> u2f = new U2F();
+    u2f->Init(AsInner(), aError);
+    if (NS_WARN_IF(aError.Failed())) {
+      return nullptr;
+    }
+
+    mU2F = u2f;
+  }
+  return mU2F;
+}
+
 nsIControllers*
 nsGlobalWindow::GetControllersOuter(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   if (!mControllers) {
     nsresult rv;
     mControllers = do_CreateInstance(kXULControllersCID, &rv);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -101,27 +101,28 @@ namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
 class Console;
 class Crypto;
 class External;
 class Function;
 class Gamepad;
-class VRDevice;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
 class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
+class U2F;
+class VRDevice;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class WindowOrientationObserver;
 #endif
 namespace cache {
 class CacheStorage;
 } // namespace cache
 class IDBFactory;
@@ -1066,16 +1067,17 @@ public:
 #endif
   already_AddRefed<nsICSSDeclaration>
     GetDefaultComputedStyle(mozilla::dom::Element& aElt,
                             const nsAString& aPseudoElt,
                             mozilla::ErrorResult& aError);
   void SizeToContentOuter(mozilla::ErrorResult& aError, bool aCallerIsChrome);
   void SizeToContent(mozilla::ErrorResult& aError);
   mozilla::dom::Crypto* GetCrypto(mozilla::ErrorResult& aError);
+  mozilla::dom::U2F* GetU2f(mozilla::ErrorResult& aError);
   nsIControllers* GetControllersOuter(mozilla::ErrorResult& aError);
   nsIControllers* GetControllers(mozilla::ErrorResult& aError);
   nsresult GetControllers(nsIControllers** aControllers) override;
   mozilla::dom::Element* GetRealFrameElementOuter();
   mozilla::dom::Element* GetRealFrameElement(mozilla::ErrorResult& aError);
   float GetMozInnerScreenXOuter();
   float GetMozInnerScreenX(mozilla::ErrorResult& aError);
   float GetMozInnerScreenYOuter();
@@ -1761,16 +1763,17 @@ protected:
   RefPtr<mozilla::dom::BarProp> mPersonalbar;
   RefPtr<mozilla::dom::BarProp> mStatusbar;
   RefPtr<mozilla::dom::BarProp> mScrollbars;
   RefPtr<nsDOMWindowUtils>    mWindowUtils;
   nsString                      mStatus;
   nsString                      mDefaultStatus;
   RefPtr<nsGlobalWindowObserver> mObserver; // Inner windows only.
   RefPtr<mozilla::dom::Crypto>  mCrypto;
+  RefPtr<mozilla::dom::U2F> mU2F;
   RefPtr<mozilla::dom::cache::CacheStorage> mCacheStorage;
   RefPtr<mozilla::dom::Console> mConsole;
   // We need to store an nsISupports pointer to this object because the
   // mozilla::dom::External class doesn't exist on b2g and using the type
   // forward declared here means that ~nsGlobalWindow wouldn't compile because
   // it wouldn't see the ~External function's declaration.
   nsCOMPtr<nsISupports>         mExternal;
 
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -2752,32 +2752,37 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
     // Disable Necko-internal response timeouts.
     internalHttpChannel->SetResponseTimeoutEnabled(false);
   }
 
   if (!mIsAnon) {
     AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
   }
 
-  // When we are sync loading, we need to bypass the local cache when it would
-  // otherwise block us waiting for exclusive access to the cache.  If we don't
-  // do this, then we could dead lock in some cases (see bug 309424).
-  //
-  // Also don't block on the cache entry on async if it is busy - favoring parallelism
-  // over cache hit rate for xhr. This does not disable the cache everywhere -
-  // only in cases where more than one channel for the same URI is accessed
-  // simultanously.
-
-  AddLoadFlags(mChannel,
-               nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
-
-  // While it would be optimal to bypass the cache in case of POST requests
-  // since they are never cached, our ServiceWorker interception implementation
-  // on single-process systems is implemented via the HTTP cache, so DO NOT
-  // bypass the cache based on method!
+  // Bypass the network cache in cases where it makes no sense:
+  // POST responses are always unique, and we provide no API that would
+  // allow our consumers to specify a "cache key" to access old POST
+  // responses, so they are not worth caching.
+  if (method.EqualsLiteral("POST")) {
+    AddLoadFlags(mChannel,
+                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
+                 nsIRequest::INHIBIT_CACHING);
+  } else {
+    // When we are sync loading, we need to bypass the local cache when it would
+    // otherwise block us waiting for exclusive access to the cache.  If we don't
+    // do this, then we could dead lock in some cases (see bug 309424).
+    //
+    // Also don't block on the cache entry on async if it is busy - favoring parallelism
+    // over cache hit rate for xhr. This does not disable the cache everywhere -
+    // only in cases where more than one channel for the same URI is accessed
+    // simultanously.
+
+    AddLoadFlags(mChannel,
+                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
+  }
 
   // Since we expect XML data, set the type hint accordingly
   // if the channel doesn't know any content type.
   // This means that we always try to parse local files as XML
   // ignoring return value, as this is not critical
   nsAutoCString contentType;
   if (NS_FAILED(mChannel->GetContentType(contentType)) ||
       contentType.IsEmpty() ||
--- a/dom/base/test/fileutils.js
+++ b/dom/base/test/fileutils.js
@@ -31,17 +31,17 @@ function testFile(file, contents, test) 
 
   // Send file to server using FormData and XMLHttpRequest
   xhr = new XMLHttpRequest();
   xhr.onload = function(event) {
     checkMPSubmission(JSON.parse(event.target.responseText),
                       [{ name: "hello", value: "world"},
                        { name: "myfile",
                          value: contents,
-                         fileName: file.name || "",
+                         fileName: file.name || "blob",
                          contentType: file.type || "application/octet-stream" }]);
     testHasRun();
   }
   xhr.open("POST", "../../../dom/html/test/form_submit_server.sjs");
   var fd = new FormData;
   fd.append("hello", "world");
   fd.append("myfile", file);
   xhr.send(fd);
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3242,15 +3242,14 @@ DeprecationWarning(JSContext* aCx, JSObj
 namespace binding_detail {
 JSObject*
 UnprivilegedJunkScopeOrWorkerGlobal()
 {
   if (NS_IsMainThread()) {
     return xpc::UnprivilegedJunkScope();
   }
 
-  return workers::GetCurrentThreadWorkerPrivate()->
-    GlobalScope()->GetGlobalJSObject();
+  return workers::GetCurrentThreadWorkerGlobal();
 }
 } // namespace binding_detail
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1131,21 +1131,23 @@ class CGHeaders(CGWrapper):
             # getExtendedAttribute() returns a list, extract the entry.
             funcList = desc.interface.getExtendedAttribute("Func")
             if funcList is not None:
                 addHeaderForFunc(funcList[0])
 
             if desc.interface.maplikeOrSetlikeOrIterable:
                 # We need ToJSValue.h for maplike/setlike type conversions
                 bindingHeaders.add("mozilla/dom/ToJSValue.h")
-                # Add headers for the key and value types of the maplike, since
-                # they'll be needed for convenience functions
-                addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
-                                   desc, None))
-                if desc.interface.maplikeOrSetlikeOrIterable.valueType:
+                # Add headers for the key and value types of the
+                # maplike/setlike/iterable, since they'll be needed for
+                # convenience functions
+                if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
+                    addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
+                                       desc, None))
+                if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
                     addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
                                        desc, None))
 
         for d in dictionaries:
             if d.parent:
                 declareIncludes.add(self.getDeclarationFilename(d.parent))
             bindingHeaders.add(self.getDeclarationFilename(d))
 
@@ -2276,44 +2278,59 @@ class MethodDefiner(PropertyDefiner):
                 "name": "@@iterator",
                 "methodInfo": False,
                 "selfHostedName": "ArrayValues",
                 "length": 0,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": MemberCondition()
             })
 
-        # Generate the maplike/setlike iterator, if one wasn't already
-        # generated by a method. If we already have an @@iterator symbol, fail.
-        if descriptor.interface.maplikeOrSetlikeOrIterable:
-            if hasIterator(methods, self.regular):
-                raise TypeError("Cannot have maplike/setlike/iterable interface with "
-                                "other members that generate @@iterator "
-                                "on interface %s, such as indexed getters "
-                                "or aliased functions." %
-                                self.descriptor.interface.identifier.name)
-            for m in methods:
-                if (m.isMaplikeOrSetlikeOrIterableMethod() and
-                    (((m.maplikeOrSetlikeOrIterable.isMaplike() or
-                       (m.maplikeOrSetlikeOrIterable.isIterable() and
-                        m.maplikeOrSetlikeOrIterable.hasValueType())) and
-                       m.identifier.name == "entries") or
-                    (((m.maplikeOrSetlikeOrIterable.isSetlike() or
-                       (m.maplikeOrSetlikeOrIterable.isIterable() and
-                        not m.maplikeOrSetlikeOrIterable.hasValueType()))) and
-                       m.identifier.name == "values"))):
-                    self.regular.append({
-                        "name": "@@iterator",
-                        "methodName": m.identifier.name,
-                        "length": methodLength(m),
-                        "flags": "0",
-                        "condition": PropertyDefiner.getControllingCondition(m,
-                                                                             descriptor),
-                    })
-                    break
+        # Generate the keys/values/entries aliases for value iterables.
+        maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
+        if (not static and
+            maplikeOrSetlikeOrIterable and
+            maplikeOrSetlikeOrIterable.isIterable() and
+            maplikeOrSetlikeOrIterable.isValueIterator()):
+            # Add our keys/values/entries/forEach
+            self.regular.append({
+                "name": "keys",
+                "methodInfo": False,
+                "selfHostedName": "ArrayKeys",
+                "length": 0,
+                "flags": "JSPROP_ENUMERATE",
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+            self.regular.append({
+                "name": "values",
+                "methodInfo": False,
+                "selfHostedName": "ArrayValues",
+                "length": 0,
+                "flags": "JSPROP_ENUMERATE",
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+            self.regular.append({
+                "name": "entries",
+                "methodInfo": False,
+                "selfHostedName": "ArrayEntries",
+                "length": 0,
+                "flags": "JSPROP_ENUMERATE",
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+            self.regular.append({
+                "name": "forEach",
+                "methodInfo": False,
+                "selfHostedName": "ArrayForEach",
+                "length": 0,
+                "flags": "JSPROP_ENUMERATE",
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
             if (stringifier and
                 unforgeable == MemberIsUnforgeable(stringifier, descriptor) and
                 isMaybeExposedIn(stringifier, descriptor)):
                 toStringDesc = {
                     "name": "toString",
@@ -13068,18 +13085,19 @@ class CGForwardDeclarations(CGWrapper):
             builder.add(d.nativeType)
             # If we're an interface and we have a maplike/setlike declaration,
             # we'll have helper functions exposed to the native side of our
             # bindings, which will need to show up in the header. If either of
             # our key/value types are interfaces, they'll be passed as
             # arguments to helper functions, and they'll need to be forward
             # declared in the header.
             if d.interface.maplikeOrSetlikeOrIterable:
-                builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
-                                              config)
+                if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
+                    builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
+                                                  config)
                 if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
                     builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
                                                   config)
 
         # We just about always need NativePropertyHooks
         builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
         builder.addInMozillaDom("ProtoAndIfaceCache")
         # Add the atoms cache type, even if we don't need it.
@@ -15797,16 +15815,41 @@ class CGMaplikeOrSetlikeHelperGenerator(
 class CGIterableMethodGenerator(CGGeneric):
     """
     Creates methods for iterable interfaces. Unwrapping/wrapping
     will be taken care of by the usual method generation machinery in
     CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
     using CGCallGenerator.
     """
     def __init__(self, descriptor, iterable, methodName):
+        if methodName == "forEach":
+            CGGeneric.__init__(self, fill(
+                """
+                if (!JS::IsCallable(arg0)) {
+                  ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach");
+                  return false;
+                }
+                JS::AutoValueArray<3> callArgs(cx);
+                callArgs[2].setObject(*obj);
+                JS::Rooted<JS::Value> ignoredReturnVal(cx);
+                for (size_t i = 0; i < self->GetIterableLength(); ++i) {
+                  if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) {
+                    return false;
+                  }
+                  if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) {
+                    return false;
+                  }
+                  if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
+                                &ignoredReturnVal)) {
+                    return false;
+                  }
+                }
+                """,
+                ifaceName=descriptor.interface.identifier.name))
+            return
         CGGeneric.__init__(self, fill(
             """
             typedef ${iterClass} itrType;
             RefPtr<itrType> result(new itrType(self,
                                                  itrType::IterableIteratorType::${itrMethod},
                                                  &${ifaceName}IteratorBinding::Wrap));
             """,
             iterClass=iteratorNativeType(descriptor),
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -903,13 +903,10 @@ def getAllTypes(descriptors, dictionarie
             yield (t, None, dictionary)
     for callback in callbacks:
         for t in getTypesFromCallback(callback):
             yield (t, None, None)
 
 def iteratorNativeType(descriptor):
     assert descriptor.interface.isIterable()
     iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
-    if iterableDecl.valueType is None:
-        iterClass = "OneTypeIterableIterator"
-    else:
-        iterClass = "TwoTypeIterableIterator"
-    return "mozilla::dom::%s<%s>" % (iterClass, descriptor.nativeType)
+    assert iterableDecl.isPairIterator()
+    return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
--- a/dom/bindings/IterableIterator.h
+++ b/dom/bindings/IterableIterator.h
@@ -1,29 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 /**
  * The IterableIterator class is used for WebIDL interfaces that have a
- * iterable<> member defined. It handles the ES6 Iterator-like functions that
- * are generated for the iterable interface.
+ * iterable<> member defined with two types (so a pair iterator). It handles
+ * the ES6 Iterator-like functions that are generated for the iterable
+ * interface.
  *
- * For iterable interfaces, the implementation class will need to
- * implement these two functions:
+ * For iterable interfaces with a pair iterator, the implementation class will
+ * need to implement these two functions:
  *
  * - size_t GetIterableLength()
  *   - Returns the number of elements available to iterate over
  * - [type] GetValueAtIndex(size_t index)
  *   - Returns the value at the requested index.
- *
- * If this is a two-type iterator, then the implementation class will also need to implement:
- *
  * - [type] GetKeyAtIndex(size_t index)
  *   - Returns the key at the requested index
  *
  * Examples of iterable interface implementations can be found in the bindings
  * test directory.
  */
 
 #ifndef mozilla_dom_IterableIterator_h
@@ -55,23 +53,87 @@ public:
   
 protected:
   virtual ~IterableIteratorBase() {}
   virtual void UnlinkHelper() = 0;
   virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) = 0;
 };
 
 template <typename T>
-class IterableIterator : public IterableIteratorBase
+class IterableIterator final : public IterableIteratorBase
 {
 public:
-  explicit IterableIterator(T* aIterableObj)
+  typedef bool (*WrapFunc)(JSContext* aCx,
+                           IterableIterator<T>* aObject,
+                           JS::Handle<JSObject*> aGivenProto,
+                           JS::MutableHandle<JSObject*> aReflector);
+
+  explicit IterableIterator(T* aIterableObj,
+                            IterableIteratorType aIteratorType,
+                            WrapFunc aWrapFunc)
     : mIterableObj(aIterableObj)
+    , mIteratorType(aIteratorType)
+    , mWrapFunc(aWrapFunc)
+    , mIndex(0)
   {
     MOZ_ASSERT(mIterableObj);
+    MOZ_ASSERT(mWrapFunc);
+  }
+
+  void
+  Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
+  {
+    JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
+    if (mIndex >= this->mIterableObj->GetIterableLength()) {
+      DictReturn(aCx, aResult, true, value, aRv);
+      return;
+    }
+    switch (mIteratorType) {
+    case IterableIteratorType::Keys:
+    {
+      if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+      DictReturn(aCx, aResult, false, value, aRv);
+      break;
+    }
+    case IterableIteratorType::Values:
+    {
+      if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+      DictReturn(aCx, aResult, false, value, aRv);
+      break;
+    }
+    case IterableIteratorType::Entries:
+    {
+      JS::Rooted<JS::Value> key(aCx);
+      if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+      if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+      KeyAndValueReturn(aCx, key, value, aResult, aRv);
+      break;
+    }
+    default:
+      MOZ_CRASH("Invalid iterator type!");
+    }
+    ++mIndex;
+  }
+
+  bool
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
+  {
+    return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
   }
 
 protected:
   static void
   DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
              bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv)
   {
     RootedDictionary<IterableKeyOrValueResult> dict(aCx);
@@ -123,171 +185,16 @@ protected:
   virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override
   {
     IterableIterator<T>* tmp = this;
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
   }
 
   // Binding Implementation object that we're iterating over.
   RefPtr<T> mIterableObj;
-};
-
-template<typename T>
-class OneTypeIterableIterator final : public IterableIterator<T>
-{
-public:
-  typedef typename IterableIterator<T>::IterableIteratorType IterableIteratorType;
-  using IterableIterator<T>::DictReturn;
-  using IterableIterator<T>::KeyAndValueReturn;
-  typedef bool (*WrapFunc)(JSContext* aCx,
-                           OneTypeIterableIterator<T>* aObject,
-                           JS::Handle<JSObject*> aGivenProto,
-                           JS::MutableHandle<JSObject*> aReflector);
-
-  OneTypeIterableIterator(T* aIterableObj,
-                          IterableIteratorType aIteratorType,
-                          WrapFunc aWrapFunc)
-    : IterableIterator<T>(aIterableObj)
-    , mIteratorType(aIteratorType)
-    , mWrapFunc(aWrapFunc)
-    , mIndex(0)
-  {
-    MOZ_ASSERT(mWrapFunc);
-  }
-
-  void
-  Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
-  {
-    JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
-    if (mIndex >= this->mIterableObj->GetIterableLength()) {
-      DictReturn(aCx, aResult, true, value, aRv);
-      return;
-    }
-
-    switch (mIteratorType) {
-    case IterableIteratorType::Keys:
-    case IterableIteratorType::Values:
-    {
-      if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      DictReturn(aCx, aResult, false, value, aRv);
-      break;
-    }
-    case IterableIteratorType::Entries:
-    {
-      if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      KeyAndValueReturn(aCx, value, value, aResult, aRv);
-      break;
-    }
-    default:
-      MOZ_CRASH("Invalid iterator type!");
-    }
-    ++mIndex;
-  }
-
-  bool
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
-  {
-    return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
-  }
-
-protected:
-  virtual ~OneTypeIterableIterator() {}
-
-  // Tells whether this is a key, value, or entries iterator.
-  IterableIteratorType mIteratorType;
-  // Function pointer to binding-type-specific Wrap() call for this iterator.
-  WrapFunc mWrapFunc;
-  // Current index of iteration.
-  uint32_t mIndex;
-};
-
-template<typename T>
-class TwoTypeIterableIterator final : public IterableIterator<T>
-{
-public:
-  typedef typename IterableIterator<T>::IterableIteratorType IterableIteratorType;
-  using IterableIterator<T>::DictReturn;
-  using IterableIterator<T>::KeyAndValueReturn;
-  typedef bool (*WrapFunc)(JSContext* aCx,
-                           TwoTypeIterableIterator<T>* aObject,
-                           JS::Handle<JSObject*> aGivenProto,
-                           JS::MutableHandle<JSObject*> aReflector);
-
-  TwoTypeIterableIterator(T* aIterableObj, IterableIteratorType aIteratorType,
-                          WrapFunc aWrapFunc)
-    : IterableIterator<T>(aIterableObj)
-    , mIteratorType(aIteratorType)
-    , mWrapFunc(aWrapFunc)
-    , mIndex(0)
-  {
-    MOZ_ASSERT(mWrapFunc);
-  }
-
-  void
-  Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
-  {
-    JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
-    if (mIndex >= this->mIterableObj->GetIterableLength()) {
-      DictReturn(aCx, aResult, true, value, aRv);
-      return;
-    }
-    switch (mIteratorType) {
-    case IterableIteratorType::Keys:
-    {
-      if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      DictReturn(aCx, aResult, false, value, aRv);
-      break;
-    }
-    case IterableIteratorType::Values:
-    {
-      if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      DictReturn(aCx, aResult, false, value, aRv);
-      break;
-    }
-    case IterableIteratorType::Entries:
-    {
-      JS::Rooted<JS::Value> key(aCx);
-      if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      KeyAndValueReturn(aCx, key, value, aResult, aRv);
-      break;
-    }
-    default:
-      MOZ_CRASH("Invalid iterator type!");
-    }
-    ++mIndex;
-  }
-
-  bool
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
-  {
-    return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
-  }
-
-protected:
-  virtual ~TwoTypeIterableIterator() {}
-
   // Tells whether this is a key, value, or entries iterator.
   IterableIteratorType mIteratorType;
   // Function pointer to binding-type-specific Wrap() call for this iterator.
   WrapFunc mWrapFunc;
   // Current index of iteration.
   uint32_t mIndex;
 };
 
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1093,17 +1093,17 @@ class IDLInterface(IDLObjectWithScope, I
                     raise WebIDLError("Interface with [Global] inherits from "
                                       "interface with [OverrideBuiltins]",
                                       [self.location, parent.location])
                 parent._isOnGlobalProtoChain = True
                 parent = parent.parent
 
     def validate(self):
         # We don't support consequential unforgeable interfaces.  Need to check
-        # this here, becaue in finish() an interface might not know yet that
+        # this here, because in finish() an interface might not know yet that
         # it's consequential.
         if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
             raise WebIDLError(
                 "%s is an unforgeable consequential interface" %
                 self.identifier.name,
                 [self.location] +
                 list(i.location for i in
                      (self.interfacesBasedOnSelf - {self})))
@@ -1112,28 +1112,35 @@ class IDLInterface(IDLObjectWithScope, I
         if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
             locations = ([self.location] +
                          list(i.location for i in
                               self.interfacesBasedOnSelf if i.parent == self))
             raise WebIDLError("%s is an unforgeable ancestor interface" %
                               self.identifier.name,
                               locations)
 
+        indexedGetter = None
+        hasLengthAttribute = False
         for member in self.members:
             member.validate()
 
             if self.isCallback() and member.getExtendedAttribute("Replaceable"):
                 raise WebIDLError("[Replaceable] used on an attribute on "
                                   "interface %s which is a callback interface" %
                                   self.identifier.name,
                                   [self.location, member.location])
 
             # Check that PutForwards refers to another attribute and that no
-            # cycles exist in forwarded assignments.
+            # cycles exist in forwarded assignments.  Also check for a
+            # integer-typed "length" attribute.
             if member.isAttr():
+                if (member.identifier.name == "length" and
+                    member.type.isInteger()):
+                    hasLengthAttribute = True
+
                 iface = self
                 attr = member
                 putForwards = attr.getExtendedAttribute("PutForwards")
                 if putForwards and self.isCallback():
                     raise WebIDLError("[PutForwards] used on an attribute "
                                       "on interface %s which is a callback "
                                       "interface" % self.identifier.name,
                                       [self.location, member.location])
@@ -1161,18 +1168,21 @@ class IDLInterface(IDLObjectWithScope, I
                                           (attr.identifier.name, iface, putForwards),
                                           [attr.location])
 
                     iface = forwardIface
                     attr = fowardAttr
                     putForwards = attr.getExtendedAttribute("PutForwards")
 
             # Check that the name of an [Alias] doesn't conflict with an
-            # interface member.
+            # interface member and whether we support indexed properties.
             if member.isMethod():
+                if member.isGetter() and member.isIndexed():
+                    indexedGetter = member
+
                 for alias in member.aliases:
                     if self.isOnGlobalProtoChain():
                         raise WebIDLError("[Alias] must not be used on a "
                                           "[Global] interface operation",
                                           [member.location])
                     if (member.getExtendedAttribute("Exposed") or
                         member.getExtendedAttribute("ChromeOnly") or
                         member.getExtendedAttribute("Pref") or
@@ -1223,16 +1233,45 @@ class IDLInterface(IDLObjectWithScope, I
         # interface object, unless they're navigator properties.
         if (self.isExposedConditionally() and
             not self.hasInterfaceObject() and
             not self.getNavigatorProperty()):
             raise WebIDLError("Interface with no interface object is "
                               "exposed conditionally",
                               [self.location])
 
+        # Value iterators are only allowed on interfaces with indexed getters,
+        # and pair iterators are only allowed on interfaces without indexed
+        # getters.
+        if self.isIterable():
+            iterableDecl = self.maplikeOrSetlikeOrIterable
+            if iterableDecl.isValueIterator():
+                if not indexedGetter:
+                    raise WebIDLError("Interface with value iterator does not "
+                                      "support indexed properties",
+                                      [self.location])
+
+                if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
+                    raise WebIDLError("Iterable type does not match indexed "
+                                      "getter type",
+                                      [iterableDecl.location,
+                                       indexedGetter.location])
+
+                if not hasLengthAttribute:
+                    raise WebIDLError('Interface with value iterator does not '
+                                      'have an integer-typed "length" attribute',
+                                      [self.location])
+            else:
+                assert iterableDecl.isPairIterator()
+                if indexedGetter:
+                    raise WebIDLError("Interface with pair iterator supports "
+                                      "indexed properties",
+                                      [self.location, iterableDecl.location,
+                                       indexedGetter.location])
+
     def isInterface(self):
         return True
 
     def isExternal(self):
         return False
 
     def setIsConsequentialInterfaceOf(self, other):
         self._consequential = True
@@ -3421,17 +3460,20 @@ class IDLInterfaceMember(IDLObjectWithId
                               [self.location])
         self.aliases.append(alias)
 
 
 class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
 
     def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
         IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
-        assert isinstance(keyType, IDLType)
+        if keyType is not None:
+            assert isinstance(keyType, IDLType)
+        else:
+            assert valueType is not None
         assert ifaceType in ['maplike', 'setlike', 'iterable']
         if valueType is not None:
             assert isinstance(valueType, IDLType)
         self.keyType = keyType
         self.valueType = valueType
         self.maplikeOrSetlikeOrIterableType = ifaceType
         self.disallowedMemberNames = []
         self.disallowedNonMethodNames = []
@@ -3440,16 +3482,19 @@ class IDLMaplikeOrSetlikeOrIterableBase(
         return self.maplikeOrSetlikeOrIterableType == "maplike"
 
     def isSetlike(self):
         return self.maplikeOrSetlikeOrIterableType == "setlike"
 
     def isIterable(self):
         return self.maplikeOrSetlikeOrIterableType == "iterable"
 
+    def hasKeyType(self):
+        return self.keyType is not None
+
     def hasValueType(self):
         return self.valueType is not None
 
     def checkCollisions(self, members, isAncestor):
         for member in members:
             # Check that there are no disallowed members
             if (member.identifier.name in self.disallowedMemberNames and
                 not ((member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) or
@@ -3464,17 +3509,18 @@ class IDLMaplikeOrSetlikeOrIterableBase(
                 member.identifier.name in self.disallowedNonMethodNames):
                 raise WebIDLError("Member '%s' conflicts "
                                   "with reserved %s method." %
                                   (member.identifier.name,
                                    self.maplikeOrSetlikeOrIterableType),
                                   [self.location, member.location])
 
     def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
-                  chromeOnly=False, isPure=False, affectsNothing=False, newObject=False):
+                  chromeOnly=False, isPure=False, affectsNothing=False, newObject=False,
+                  isIteratorAlias=False):
         """
         Create an IDLMethod based on the parameters passed in.
 
         - members is the member list to add this function to, since this is
           called during the member expansion portion of interface object
           building.
 
         - chromeOnly is only True for read-only js implemented classes, to
@@ -3524,26 +3570,30 @@ class IDLMaplikeOrSetlikeOrIterableBase(
         # them pure, since they return a new object each time they are run.
         if affectsNothing:
             method.addExtendedAttributes(
                 [IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
                  IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
         if newObject:
             method.addExtendedAttributes(
                 [IDLExtendedAttribute(self.location, ("NewObject",))])
+        if isIteratorAlias:
+            method.addExtendedAttributes(
+                [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
         members.append(method)
 
     def resolve(self, parentScope):
-        self.keyType.resolveType(parentScope)
+        if self.keyType:
+            self.keyType.resolveType(parentScope)
         if self.valueType:
             self.valueType.resolveType(parentScope)
 
     def finish(self, scope):
         IDLInterfaceMember.finish(self, scope)
-        if not self.keyType.isComplete():
+        if self.keyType and not self.keyType.isComplete():
             t = self.keyType.complete(scope)
 
             assert not isinstance(t, IDLUnresolvedType)
             assert not isinstance(t, IDLTypedefType)
             assert not isinstance(t.name, IDLUnresolvedIdentifier)
             self.keyType = t
         if self.valueType and not self.valueType.isComplete():
             t = self.valueType.complete(scope)
@@ -3555,19 +3605,33 @@ class IDLMaplikeOrSetlikeOrIterableBase(
 
     def validate(self):
         IDLInterfaceMember.validate(self)
 
     def handleExtendedAttribute(self, attr):
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
     def _getDependentObjects(self):
+        deps = set()
+        if self.keyType:
+            deps.add(self.keyType)
         if self.valueType:
-            return set([self.keyType, self.valueType])
-        return set([self.keyType])
+            deps.add(self.valueType)
+        return deps
+
+    def getForEachArguments(self):
+        return [IDLArgument(self.location,
+                            IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+                                                    "callback"),
+                            BuiltinTypes[IDLBuiltinType.Types.object]),
+                IDLArgument(self.location,
+                            IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+                                                    "thisArg"),
+                            BuiltinTypes[IDLBuiltinType.Types.any],
+                            optional=True)]
 
 # Iterable adds ES6 iterator style functions and traits
 # (keys/values/entries/@@iterator) to an interface.
 class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
 
     def __init__(self, location, identifier, keyType, valueType=None, scope=None):
         IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier,
                                                    "iterable", keyType, valueType,
@@ -3578,26 +3642,43 @@ class IDLIterable(IDLMaplikeOrSetlikeOrI
         return "declared iterable with key '%s' and value '%s'" % (self.keyType, self.valueType)
 
     def expand(self, members, isJSImplemented):
         """
         In order to take advantage of all of the method machinery in Codegen,
         we generate our functions as if they were part of the interface
         specification during parsing.
         """
+        # We only need to add entries/keys/values here if we're a pair iterator.
+        # Value iterators just copy these from %ArrayPrototype% instead.
+        if not self.isPairIterator():
+            return
+
         # object entries()
         self.addMethod("entries", members, False, self.iteratorType,
-                       affectsNothing=True, newObject=True)
+                       affectsNothing=True, newObject=True,
+                       isIteratorAlias=True)
         # object keys()
         self.addMethod("keys", members, False, self.iteratorType,
                        affectsNothing=True, newObject=True)
         # object values()
         self.addMethod("values", members, False, self.iteratorType,
                        affectsNothing=True, newObject=True)
 
+        # void forEach(callback(valueType, keyType), optional any thisArg)
+        self.addMethod("forEach", members, False,
+                       BuiltinTypes[IDLBuiltinType.Types.void],
+                       self.getForEachArguments())
+
+    def isValueIterator(self):
+        return not self.isPairIterator()
+
+    def isPairIterator(self):
+        return self.hasKeyType()
+
 # MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
 class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
 
     def __init__(self, location, identifier, maplikeOrSetlikeType,
                  readonly, keyType, valueType):
         IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType,
                                                    keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike)
         self.readonly = readonly
@@ -3624,36 +3705,27 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSe
                                     IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
                                     BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
                                     True,
                                     maplikeOrSetlike=self))
         self.reserved_ro_names = ["size"]
 
         # object entries()
         self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
-                       affectsNothing=True)
+                       affectsNothing=True, isIteratorAlias=self.isMaplike())
         # object keys()
         self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
                        affectsNothing=True)
         # object values()
         self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
-                       affectsNothing=True)
+                       affectsNothing=True, isIteratorAlias=self.isSetlike())
 
         # void forEach(callback(valueType, keyType), thisVal)
-        foreachArguments = [IDLArgument(self.location,
-                                        IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
-                                                                "callback"),
-                                        BuiltinTypes[IDLBuiltinType.Types.object]),
-                            IDLArgument(self.location,
-                                        IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
-                                                                "thisArg"),
-                                        BuiltinTypes[IDLBuiltinType.Types.any],
-                                        optional=True)]
         self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void],
-                       foreachArguments)
+                       self.getForEachArguments())
 
         def getKeyArg():
             return IDLArgument(self.location,
                                IDLUnresolvedIdentifier(self.location, "key"),
                                self.keyType)
 
         # boolean has(keyType key)
         self.addMethod("has", members, False, BuiltinTypes[IDLBuiltinType.Types.boolean],
@@ -5433,20 +5505,23 @@ class Parser(Tokenizer):
     def p_Iterable(self, p):
         """
             Iterable : ITERABLE LT Type GT SEMICOLON
                      | ITERABLE LT Type COMMA Type GT SEMICOLON
         """
         location = self.getLocation(p, 2)
         identifier = IDLUnresolvedIdentifier(location, "__iterable",
                                              allowDoubleUnderscore=True)
-        keyType = p[3]
-        valueType = None
         if (len(p) > 6):
+            keyType = p[3]
             valueType = p[5]
+        else:
+            keyType = None
+            valueType = p[3]
+
         p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
 
     def p_Setlike(self, p):
         """
             Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
         """
         readonly = p[1]
         maplikeOrSetlikeType = p[2]
@@ -6506,17 +6581,17 @@ class Parser(Tokenizer):
             # We haven't run finish() on the interface yet, so we don't know
             # whether our interface is maplike/setlike/iterable or not. This
             # means we have to loop through the members to see if we have an
             # iterable member.
             for m in iface.members:
                 if isinstance(m, IDLIterable):
                     iterable = m
                     break
-            if iterable:
+            if iterable and iterable.isPairIterator():
                 def simpleExtendedAttr(str):
                     return IDLExtendedAttribute(iface.location, (str, ))
                 nextMethod = IDLMethod(
                     iface.location,
                     IDLUnresolvedIdentifier(iface.location, "next"),
                     BuiltinTypes[IDLBuiltinType.Types.object], [])
                 nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
                 itr_ident = IDLUnresolvedIdentifier(iface.location,
--- a/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
+++ b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
@@ -40,68 +40,72 @@ def WebIDLTest(parser, harness):
         except WebIDL.WebIDLError, e:
             harness.ok(True,
                        prefix + " - Interface failed as expected")
         except Exception, e:
             harness.ok(False,
                        prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
 
     iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
-                                                       "values"]]
-    setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "forEach"]] +
+                                                       "values", "forEach"]]
+    setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
                     [("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
                     iterableMembers)
     setROMembers.extend([("size", WebIDL.IDLAttribute)])
     setRWMembers = ([(x, WebIDL.IDLMethod) for x in ["add",
                                                      "clear",
                                                      "delete"]] +
                     setROMembers)
     setROChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
                                                            "__clear",
                                                            "__delete"]] +
                           setROMembers)
     setRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
                                                            "__clear",
                                                            "__delete"]] +
                           setRWMembers)
-    mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "forEach"]] +
+    mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
                     [("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
                     iterableMembers)
     mapROMembers.extend([("size", WebIDL.IDLAttribute)])
     mapRWMembers = ([(x, WebIDL.IDLMethod) for x in ["set",
                                                      "clear",
                                                      "delete"]] + mapROMembers)
     mapRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__set",
                                                            "__clear",
                                                            "__delete"]] +
                           mapRWMembers)
 
     # OK, now that we've used iterableMembers to set up the above, append
     # __iterable to it for the iterable<> case.
     iterableMembers.append(("__iterable", WebIDL.IDLIterable))
 
+    valueIterableMembers = [("__iterable", WebIDL.IDLIterable)]
+    valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod))
+    valueIterableMembers.append(("length", WebIDL.IDLAttribute))
+
     disallowedIterableNames = ["keys", "entries", "values"]
     disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames
     mapDisallowedMemberNames = ["get"] + disallowedMemberNames
     disallowedNonMethodNames = ["clear", "delete"]
     mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
     setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames
 
     #
     # Simple Usage Tests
     #
 
     shouldPass("Iterable (key only)",
                """
                interface Foo1 {
                iterable<long>;
+               readonly attribute unsigned long length;
+               getter long(unsigned long index);
                };
-               """, iterableMembers,
-               # numProductions == 2 because of the generated iterator iface,
-               numProductions=2)
+               """, valueIterableMembers)
 
     shouldPass("Iterable (key and value)",
                """
                interface Foo1 {
                iterable<long, long>;
                };
                """, iterableMembers,
                # numProductions == 2 because of the generated iterator iface,
--- a/dom/bindings/test/TestInterfaceIterableSingle.cpp
+++ b/dom/bindings/test/TestInterfaceIterableSingle.cpp
@@ -18,17 +18,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInt
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceIterableSingle)
 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 TestInterfaceIterableSingle::TestInterfaceIterableSingle(nsPIDOMWindowInner* aParent)
   : mParent(aParent)
 {
-  for(int i = 0; i < 3; ++i) {
+  for (int i = 0; i < 3; ++i) {
     mValues.AppendElement(i);
   }
 }
 
 //static
 already_AddRefed<TestInterfaceIterableSingle>
 TestInterfaceIterableSingle::Constructor(const GlobalObject& aGlobal,
                                          ErrorResult& aRv)
@@ -50,23 +50,28 @@ TestInterfaceIterableSingle::WrapObject(
 }
 
 nsPIDOMWindowInner*
 TestInterfaceIterableSingle::GetParentObject() const
 {
   return mParent;
 }
 
-size_t
-TestInterfaceIterableSingle::GetIterableLength() const
+uint32_t
+TestInterfaceIterableSingle::Length() const
 {
   return mValues.Length();
 }
 
-uint32_t
-TestInterfaceIterableSingle::GetValueAtIndex(uint32_t index) const
+int32_t
+TestInterfaceIterableSingle::IndexedGetter(uint32_t aIndex, bool& aFound) const
 {
-  MOZ_ASSERT(index < mValues.Length());
-  return mValues.ElementAt(index);
+  if (aIndex >= mValues.Length()) {
+    aFound = false;
+    return 0;
+  }
+
+  aFound = true;
+  return mValues[aIndex];
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/test/TestInterfaceIterableSingle.h
+++ b/dom/bindings/test/TestInterfaceIterableSingle.h
@@ -31,20 +31,21 @@ public:
 
   explicit TestInterfaceIterableSingle(nsPIDOMWindowInner* aParent);
   nsPIDOMWindowInner* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
   static already_AddRefed<TestInterfaceIterableSingle>
     Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
 
-  size_t GetIterableLength() const;
-  uint32_t GetValueAtIndex(uint32_t aIndex) const;
+  uint32_t Length() const;
+  int32_t IndexedGetter(uint32_t aIndex, bool& aFound) const;
+
 private:
   virtual ~TestInterfaceIterableSingle() {}
   nsCOMPtr<nsPIDOMWindowInner> mParent;
-  nsTArray<uint32_t> mValues;
+  nsTArray<int32_t> mValues;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TestInterfaceIterableSingle_h
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -66,9 +66,10 @@ skip-if = debug == false
 [test_crossOriginWindowSymbolAccess.html]
 [test_primitive_this.html]
 [test_callback_exceptions.html]
 [test_bug1123516_maplikesetlike.html]
 skip-if = debug == false
 [test_jsimplemented_eventhandler.html]
 skip-if = debug == false
 [test_iterable.html]
-skip-if = debug == false
\ No newline at end of file
+skip-if = debug == false
+[test_oom_reporting.html]
--- a/dom/bindings/test/test_bug1123516_maplikesetlike.html
+++ b/dom/bindings/test/test_bug1123516_maplikesetlike.html
@@ -88,17 +88,18 @@
              is(m.size, 1, "SimpleMap: size should be 1");
              iterable = false;
              for (var e of m) {
                  iterable = true;
                  is(e[0], "test", "SimpleMap: iterable first array element should be key");
                  is(e[1], 1, "SimpleMap: iterable second array element should be value");
              }
              is(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length");
-             is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleMap: @@iterator symbol has correct name");
+             is(m[Symbol.iterator].name, "entries", "SimpleMap: @@iterator symbol has correct name");
+             is(m[Symbol.iterator], m.entries, 'SimpleMap: @@iterator is an alias for "entries"');
              ok(iterable, "SimpleMap: @@iterator symbol resolved correctly");
              for (var k of m.keys()) {
                  is(k, "test", "SimpleMap: first keys element should be 'test'");
              }
              for (var v of m.values()) {
                  is(v, 1, "SimpleMap: first values elements should be 1");
              }
              for (var e of m.entries()) {
@@ -134,17 +135,18 @@
              is(m.delete("test2"), true, "SimpleSet: maplike deletion should return true");
              is(m.size, 1, "SimpleSet: size should be 1");
              iterable = false;
              for (var e of m) {
                  iterable = true;
                  is(e, "test", "SimpleSet: iterable first array element should be key");
              }
              is(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length");
-             is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleSet: @@iterator symbol has correct name");
+             is(m[Symbol.iterator].name, "values", "SimpleSet: @@iterator symbol has correct name");
+             is(m[Symbol.iterator], m.values, 'SimpleSet: @@iterator is an alias for "values"');
              ok(iterable, "SimpleSet: @@iterator symbol resolved correctly");
              for (var k of m.keys()) {
                  is(k, "test", "SimpleSet: first keys element should be 'test'");
              }
              for (var v of m.values()) {
                  is(v, "test", "SimpleSet: first values elements should be 'test'");
              }
              for (var e of m.entries()) {
--- a/dom/bindings/test/test_iterable.html
+++ b/dom/bindings/test/test_iterable.html
@@ -9,17 +9,18 @@
   </head>
   <body>
     <script class="testbody" type="application/javascript">
      SimpleTest.waitForExplicitFinish();
      SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
 
        base_properties = [["entries", "function", 0],
                           ["keys", "function", 0],
-                          ["values", "function", 0]]
+                          ["values", "function", 0],
+                          ["forEach", "function", 1]]
        var testExistence = function testExistence(prefix, obj, properties) {
          for (var [name, type, args] of properties) {
            // Properties are somewhere up the proto chain, hasOwnProperty won't work
            isnot(obj[name], undefined,
                  `${prefix} object has property ${name}`);
 
            is(typeof obj[name], type,
               `${prefix} object property ${name} is a ${type}`);
@@ -45,16 +46,26 @@
          }
        }
 
        var itr;
        // Simple single type iterable creation and functionality test
        info("IterableSingle: Testing simple iterable creation and functionality");
        itr = new TestInterfaceIterableSingle();
        testExistence("IterableSingle: ", itr, base_properties);
+       is(itr[Symbol.iterator], Array.prototype[Symbol.iterator],
+          "IterableSingle: Should be using %ArrayIterator% for @@iterator");
+       is(itr.keys, Array.prototype.keys,
+          "IterableSingle: Should be using %ArrayIterator% for 'keys'");
+       is(itr.entries, Array.prototype.entries,
+          "IterableSingle: Should be using %ArrayIterator% for 'entries'");
+       is(itr.values, itr[Symbol.iterator],
+          "IterableSingle: Should be using @@iterator for 'values'");
+       is(itr.forEach, Array.prototype.forEach,
+          "IterableSingle: Should be using %ArrayIterator% for 'forEach'");
        var keys = [...itr.keys()];
        var values = [...itr.values()];
        var entries = [...itr.entries()];
        var key_itr = itr.keys();
        var value_itr = itr.values();
        var entries_itr = itr.entries();
        for (var i = 0; i < 3; ++i) {
          var key = key_itr.next();
@@ -68,33 +79,52 @@
             "IterableSingle: Value iterator value should match destructuring " + i);
          is(entry.value[0], i, "IterableSingle: Entry iterator value 0 should be " + i);
          is(entry.value[1], i, "IterableSingle: Entry iterator value 1 should be " + i);
          is(entry.value[0], entries[i][0],
             "IterableSingle: Entry iterator value 0 should match destructuring " + i);
          is(entry.value[1], entries[i][1],
             "IterableSingle: Entry iterator value 1 should match destructuring " + i);
        }
+
+       var callsToForEachCallback = 0;
+       var thisArg = {};
+       itr.forEach(function(value, index, obj) {
+         is(index, callsToForEachCallback,
+            `IterableSingle: Should have the right index at ${callsToForEachCallback} calls to forEach callback`);
+         is(value, values[index],
+            `IterableSingle: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
+         is(this, thisArg,
+            "IterableSingle: Should have the right this value for forEach callback");
+         is(obj, itr,
+            "IterableSingle: Should have the right third arg for forEach callback");
+         ++callsToForEachCallback;
+       }, thisArg);
+       is(callsToForEachCallback, 3,
+          "IterableSingle: Should have right total number of calls to forEach callback");
+
        var key = key_itr.next();
        var value = value_itr.next();
        var entry = entries_itr.next();
        is(key.value, undefined, "IterableSingle: Key iterator value should be undefined");
        is(key.done, true, "IterableSingle: Key iterator done should be true");
        is(value.value, undefined, "IterableSingle: Value iterator value should be undefined");
        is(value.done, true, "IterableSingle: Value iterator done should be true");
        is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
        is(entry.done, true, "IterableSingle: Entry iterator done should be true");
        is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
-          "[object TestInterfaceIterableSingleIteratorPrototype]",
+          "[object Array Iterator]",
           "iterator prototype should have the right brand");
 
        // Simple dual type iterable creation and functionality test
        info("IterableDouble: Testing simple iterable creation and functionality");
        itr = new TestInterfaceIterableDouble();
        testExistence("IterableDouble: ", itr, base_properties);
+       is(itr.entries, itr[Symbol.iterator],
+          "IterableDouble: Should be using @@iterator for 'entries'");
        var elements = [["a", "b"], ["c", "d"], ["e", "f"]]
        var keys = [...itr.keys()];
        var values = [...itr.values()];
        var entries = [...itr.entries()];
        var key_itr = itr.keys();
        var value_itr = itr.values();
        var entries_itr = itr.entries();
        for (var i = 0; i < 3; ++i) {
@@ -109,16 +139,33 @@
             "IterableDouble: Value iterator value should match destructuring " + i);
          is(entry.value[0], elements[i][0], "IterableDouble: Entry iterator value 0 should be " + elements[i][0]);
          is(entry.value[1], elements[i][1], "IterableDouble: Entry iterator value 1 should be " + elements[i][1]);
          is(entry.value[0], entries[i][0],
             "IterableDouble: Entry iterator value 0 should match destructuring " + i);
          is(entry.value[1], entries[i][1],
             "IterableDouble: Entry iterator value 1 should match destructuring " + i);
        }
+
+       callsToForEachCallback = 0;
+       thisArg = {};
+       itr.forEach(function(value, key, obj) {
+         is(key, keys[callsToForEachCallback],
+            `IterableDouble: Should have the right key at ${callsToForEachCallback} calls to forEach callback`);
+         is(value, values[callsToForEachCallback],
+            `IterableDouble: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
+         is(this, thisArg,
+            "IterableDouble: Should have the right this value for forEach callback");
+         is(obj, itr,
+            "IterableSingle: Should have the right third arg for forEach callback");
+         ++callsToForEachCallback;
+       }, thisArg);
+       is(callsToForEachCallback, 3,
+          "IterableDouble: Should have right total number of calls to forEach callback");
+
        var key = key_itr.next();
        var value = value_itr.next();
        var entry = entries_itr.next()
        is(key.value, undefined, "IterableDouble: Key iterator value should be undefined");
        is(key.done, true, "IterableDouble: Key iterator done should be true");
        is(value.value, undefined, "IterableDouble: Value iterator value should be undefined");
        is(value.done, true, "IterableDouble: Value iterator done should be true");
        is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_oom_reporting.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug </title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug  **/
+  SimpleTest.waitForExplicitFinish();
+
+  window.length = 0xffffffff;
+
+  SimpleTest.expectUncaughtException();
+  setTimeout(Array.prototype.splice, 0, undefined);
+
+  addEventListener("error", function(e) {
+    is(e.type, "error", "Should have an error event");
+    is(e.message, "uncaught exception: out of memory",
+       "Should have the right error message");
+    // Make sure we finish async, in case the expectUncaughtException assertion
+    // about having seen the exception runs after our listener
+    SimpleTest.executeSoon(SimpleTest.finish);
+  });
+
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/crypto/CryptoBuffer.cpp
+++ b/dom/crypto/CryptoBuffer.cpp
@@ -23,16 +23,23 @@ CryptoBuffer::Assign(const CryptoBuffer&
 
 uint8_t*
 CryptoBuffer::Assign(const uint8_t* aData, uint32_t aLength)
 {
   return ReplaceElementsAt(0, Length(), aData, aLength, fallible);
 }
 
 uint8_t*
+CryptoBuffer::Assign(const nsACString& aString)
+{
+  return Assign(reinterpret_cast<uint8_t const*>(aString.BeginReading()),
+                aString.Length());
+}
+
+uint8_t*
 CryptoBuffer::Assign(const SECItem* aItem)
 {
   MOZ_ASSERT(aItem);
   return Assign(aItem->data, aItem->len);
 }
 
 uint8_t*
 CryptoBuffer::Assign(const ArrayBuffer& aData)
--- a/dom/crypto/CryptoBuffer.h
+++ b/dom/crypto/CryptoBuffer.h
@@ -17,16 +17,17 @@ namespace dom {
 class ArrayBufferViewOrArrayBuffer;
 class OwningArrayBufferViewOrArrayBuffer;
 
 class CryptoBuffer : public FallibleTArray<uint8_t>
 {
 public:
   uint8_t* Assign(const CryptoBuffer& aData);
   uint8_t* Assign(const uint8_t* aData, uint32_t aLength);
+  uint8_t* Assign(const nsACString& aString);
   uint8_t* Assign(const SECItem* aItem);
   uint8_t* Assign(const ArrayBuffer& aData);
   uint8_t* Assign(const ArrayBufferView& aData);
   uint8_t* Assign(const ArrayBufferViewOrArrayBuffer& aData);
   uint8_t* Assign(const OwningArrayBufferViewOrArrayBuffer& aData);
 
   template<typename T,
            JSObject* UnwrapArray(JSObject*),
--- a/dom/gamepad/windows/WindowsGamepad.cpp
+++ b/dom/gamepad/windows/WindowsGamepad.cpp
@@ -877,23 +877,29 @@ WindowsGamepadService::Shutdown()
 
 void
 WindowsGamepadService::Cleanup()
 {
   if (mXInputPollTimer) {
     mXInputPollTimer->Cancel();
   }
   mGamepads.Clear();
+  if (mObserver) {
+    mObserver->Stop();
+    mObserver = nullptr;
+  }
 }
 
 void
 WindowsGamepadService::DevicesChanged(DeviceChangeType type)
 {
   if (type == DeviceChangeNotification) {
-    mObserver->SetDeviceChangeTimer();
+    if (mObserver) {
+      mObserver->SetDeviceChangeTimer();
+    }
   } else if (type == DeviceChangeStable) {
     ScanForDevices();
   }
 }
 
 NS_IMETHODIMP
 Observer::Observe(nsISupports* aSubject,
                   const char* aTopic,
--- a/dom/html/test/formData_test.js
+++ b/dom/html/test/formData_test.js
@@ -157,17 +157,17 @@ function testSend(doneCb) {
     var response = xhr.response;
 
     for (var entry of response) {
       is(entry.body, 'hey');
       is(entry.headers['Content-Type'], 'text/plain');
     }
 
     is(response[0].headers['Content-Disposition'],
-        'form-data; name="empty"; filename=""');
+        'form-data; name="empty"; filename="blob"');
 
     is(response[1].headers['Content-Disposition'],
         'form-data; name="explicit"; filename="explicit-file-name"');
 
     is(response[2].headers['Content-Disposition'],
         'form-data; name="explicit-empty"; filename=""');
 
     is(response[3].headers['Content-Disposition'],
--- a/dom/html/test/test_formSubmission.html
+++ b/dom/html/test/test_formSubmission.html
@@ -585,17 +585,17 @@ var expectedAugment = [
   { name: "aNameNum", value: "9.2" },
   { name: "aNameFile1", value: placeholder_myFile1 },
   { name: "aNameFile2", value: placeholder_myFile2 },
   //{ name: "aNameObj", value: "[object XMLHttpRequest]" },
   //{ name: "aNameNull", value: "null" },
   //{ name: "aNameUndef", value: "undefined" },
 ];
 
-function checkMPSubmission(sub, expected, test) {
+function checkMPSubmission(sub, expected, test, isFormData = false) {
   function getPropCount(o) {
     var x, l = 0;
     for (x in o) ++l;
     return l;
   }
   function mpquote(s) {
     return s.replace(/\r\n/g, " ")
             .replace(/\r/g, " ")
@@ -620,17 +620,17 @@ function checkMPSubmission(sub, expected
           "Wrong number of headers in " + test);
       is(sub[i].body,
          expected[i].value.replace(/\r\n|\r|\n/, "\r\n"),
          "Correct value in " + test);
     }
     else {
       is(sub[i].headers["Content-Disposition"],
          "form-data; name=\"" + mpquote(expected[i].name) + "\"; filename=\"" +
-           mpquote(expected[i].fileName) + "\"",
+           mpquote(expected[i].fileName != "" || !isFormData ? expected[i].fileName : "blob") + "\"",
          "Correct name in " + test);
       is(sub[i].headers["Content-Type"],
          expected[i].contentType,
          "Correct content type in " + test);
       is (getPropCount(sub[i].headers), 2,
           "Wrong number of headers in " + test);
       is(sub[i].body,
          expected[i].value,
@@ -777,48 +777,48 @@ function runTest() {
   checkURLSubmission(submission, expectedSub);
 
   // Send form using XHR and FormData
   xhr = new XMLHttpRequest();
   xhr.onload = function() { gen.next(); };
   xhr.open("POST", "form_submit_server.sjs");
   xhr.send(new FormData(form));
   yield undefined; // Wait for XHR load
-  checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData");
+  checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData", true);
   
   // Send disabled form using XHR and FormData
   setDisabled(document.querySelectorAll("input, select, textarea"), true);
   xhr.open("POST", "form_submit_server.sjs");
   xhr.send(new FormData(form));
   yield undefined;
-  checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData");
+  checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData", true);
   setDisabled(document.querySelectorAll("input, select, textarea"), false);
   
   // Send FormData
   function addToFormData(fd) {
     fd.append("aName", "aValue");
     fd.append("aNameNum", 9.2);
     fd.append("aNameFile1", myFile1);
     fd.append("aNameFile2", myFile2);
   }
   var fd = new FormData();
   addToFormData(fd);
   xhr.open("POST", "form_submit_server.sjs");
   xhr.send(fd);
   yield undefined;
-  checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData");
+  checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData", true);
 
   // Augment <form> using FormData
   fd = new FormData(form);
   addToFormData(fd);
   xhr.open("POST", "form_submit_server.sjs");
   xhr.send(fd);
   yield undefined;
   checkMPSubmission(JSON.parse(xhr.responseText),
-                    expectedSub.concat(expectedAugment), "send augmented FormData");
+                    expectedSub.concat(expectedAugment), "send augmented FormData", true);
 
   SimpleTest.finish();
   yield undefined;
 }
 
 </script>
 </pre>
 </body>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/psm/PSMContentListener.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/plugins/PluginModuleParent.h"
@@ -1255,16 +1256,29 @@ ContentChild::AllocPContentBridgeParent(
 
 PGMPServiceChild*
 ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess)
 {
   return GMPServiceChild::Create(aTransport, aOtherProcess);
 }
 
+PAPZChild*
+ContentChild::AllocPAPZChild(const TabId& aTabId)
+{
+  return APZChild::Create(aTabId);
+}
+
+bool
+ContentChild::DeallocPAPZChild(PAPZChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 PCompositorChild*
 ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess)
 {
   return CompositorChild::Create(aTransport, aOtherProcess);
 }
 
 PSharedBufferManagerChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -139,16 +139,21 @@ public:
   PContentBridgeChild*
   AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
                            base::ProcessId otherProcess) override;
 
   PGMPServiceChild*
   AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
                         base::ProcessId otherProcess) override;
 
+  PAPZChild*
+  AllocPAPZChild(const TabId& aTabId) override;
+  bool
+  DeallocPAPZChild(PAPZChild* aActor) override;
+
   PCompositorChild*
   AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                         base::ProcessId aOtherProcess) override;
 
   PSharedBufferManagerChild*
   AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                   base::ProcessId aOtherProcess) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -78,16 +78,17 @@
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/layers/PAPZParent.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/Move.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/plugins/PluginBridge.h"
@@ -2023,21 +2024,71 @@ std::map<ContentParent*, std::set<uint64
 NestedBrowserLayerIds()
 {
   MOZ_ASSERT(NS_IsMainThread());
   static std::map<ContentParent*, std::set<uint64_t> > sNestedBrowserIds;
   return sNestedBrowserIds;
 }
 } // namespace
 
+/* static */
 bool
-ContentParent::RecvAllocateLayerTreeId(uint64_t* aId)
+ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId)
+{
+  return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(),
+                             aTabParent, aTabParent->GetTabId(), aId);
+}
+
+/* static */
+bool
+ContentParent::AllocateLayerTreeId(ContentParent* aContent,
+                                   TabParent* aTopLevel, const TabId& aTabId,
+                                   uint64_t* aId)
 {
   *aId = CompositorParent::AllocateLayerTreeId();
 
+  if (!gfxPlatform::AsyncPanZoomEnabled()) {
+    return true;
+  }
+
+  if (!aContent || !aTopLevel) {
+    return false;
+  }
+
+  return CompositorParent::UpdateRemoteContentController(*aId, aContent,
+                                                         aTabId, aTopLevel);
+}
+
+bool
+ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
+                                       const TabId& aTabId, uint64_t* aId)
+{
+  // Protect against spoofing by a compromised child. aCpId must either
+  // correspond to the process that this ContentParent represents or be a
+  // child of it.
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  if (ChildID() != aCpId) {
+    ContentParentId parent;
+    if (!cpm->GetParentProcessId(aCpId, &parent) ||
+        ChildID() != parent) {
+      return false;
+    }
+  }
+
+  // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId
+  // lives in the process for aCpId.
+  RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
+  RefPtr<TabParent> browserParent =
+    cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId);
+  MOZ_ASSERT(contentParent && browserParent);
+
+  if (!AllocateLayerTreeId(contentParent, browserParent, aTabId, aId)) {
+    return false;
+  }
+
   auto iter = NestedBrowserLayerIds().find(this);
   if (iter == NestedBrowserLayerIds().end()) {
     std::set<uint64_t> ids;
     ids.insert(*aId);
     NestedBrowserLayerIds().insert(IDPair(this, ids));
   } else {
     iter->second.insert(*aId);
   }
@@ -3393,16 +3444,31 @@ ContentParent::Observe(nsISupports* aSub
 
 PGMPServiceParent*
 ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
                                       base::ProcessId aOtherProcess)
 {
   return GMPServiceParent::Create(aTransport, aOtherProcess);
 }
 
+PAPZParent*
+ContentParent::AllocPAPZParent(const TabId& aTabId)
+{
+  // The PAPZParent should just be created in the main process and then an IPDL
+  // constructor message sent to hook it up.
+  MOZ_CRASH("This shouldn't be called");
+  return nullptr;
+}
+
+bool
+ContentParent::DeallocPAPZParent(PAPZParent* aActor)
+{
+  return true;
+}
+
 PCompositorParent*
 ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                                       base::ProcessId aOtherProcess)
 {
   return CompositorParent::Create(aTransport, aOtherProcess);
 }
 
 gfx::PVRManagerParent*
@@ -5693,16 +5759,28 @@ ContentParent::RecvProfile(const nsCStri
 bool
 ContentParent::RecvGetGraphicsDeviceInitData(DeviceInitData* aOut)
 {
   gfxPlatform::GetPlatform()->GetDeviceInitData(aOut);
   return true;
 }
 
 bool
+ContentParent::RecvGraphicsError(const nsCString& aError)
+{
+  gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder();
+  if (lf) {
+    std::stringstream message;
+    message << "CP+" << aError.get();
+    lf->UpdateStringsVector(message.str());
+  }
+  return true;
+}
+
+bool
 ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed)
 {
   // Only one driver crash guard should be active at a time, per-process.
   MOZ_ASSERT(!mDriverCrashGuard);
 
   UniquePtr<gfx::DriverCrashGuard> guard;
   switch (gfx::CrashGuardType(aGuardType)) {
   case gfx::CrashGuardType::D3D11Layers:
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -501,16 +501,18 @@ public:
                                 const nsString& aName,
                                 const nsCString& aFeatures,
                                 const nsCString& aBaseURI,
                                 nsresult* aResult,
                                 bool* aWindowIsNew,
                                 InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                                 nsCString* aURLToLoad) override;
 
+  static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId);
+
 protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   void OnNuwaForkTimeout();
 
   bool ShouldContinueFromReplyTimeout() override;
@@ -645,20 +647,29 @@ private:
   // manager and null out mMessageManager.
   void ShutDownMessageManager();
 
   // Start the force-kill timer on shutdown.
   void StartForceKillTimer();
 
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
 
+  static bool AllocateLayerTreeId(ContentParent* aContent,
+                                  TabParent* aTopLevel, const TabId& aTabId,
+                                  uint64_t* aId);
+
   PGMPServiceParent*
   AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
                          base::ProcessId aOtherProcess) override;
 
+  PAPZParent*
+  AllocPAPZParent(const TabId& aTabId) override;
+  bool
+  DeallocPAPZParent(PAPZParent* aActor) override;
+
   PCompositorParent*
   AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                          base::ProcessId aOtherProcess) override;
 
   PImageBridgeParent*
   AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) override;
 
@@ -999,24 +1010,28 @@ private:
 
   virtual bool RecvCopyFavicon(const URIParams& aOldURI,
                                const URIParams& aNewURI,
                                const IPC::Principal& aLoadingPrincipal,
                                const bool& aInPrivateBrowsing) override;
 
   virtual void ProcessingError(Result aCode, const char* aMsgName) override;
 
-  virtual bool RecvAllocateLayerTreeId(uint64_t* aId) override;
+  virtual bool RecvAllocateLayerTreeId(const ContentParentId& aCpId,
+                                       const TabId& aTabId,
+                                       uint64_t* aId) override;
 
   virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override;
 
   virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                             int32_t* aStatus,
                                             bool* aSuccess) override;
 
+  virtual bool RecvGraphicsError(const nsCString& aError) override;
+
   virtual bool
   RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
                             bool* aOutCrashed) override;
 
   virtual bool RecvEndDriverCrashGuard(const uint32_t& aGuardType) override;
 
   virtual bool RecvAddIdleObserver(const uint64_t& observerId,
                                    const uint32_t& aIdleTimeInS) override;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -51,23 +51,19 @@ using class mozilla::WidgetWheelEvent fr
 using class mozilla::WidgetDragEvent from "ipc/nsGUIEventIPC.h";
 using struct nsRect from "nsRect.h";
 using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetPluginEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
-using mozilla::CSSPoint from "Units.h";
 using mozilla::CSSToScreenScale from "Units.h";
 using mozilla::CommandInt from "mozilla/EventForwards.h";
-using mozilla::Modifiers from "mozilla/EventForwards.h";
-using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
 using mozilla::WritingMode from "mozilla/WritingModes.h";
-using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using nsIWidget::TouchPointerState from "nsIWidget.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
 using class mozilla::dom::ipc::StructuredCloneData from "ipc/IPCMessageUtils.h";
 using mozilla::EventMessage from "mozilla/EventForwards.h";
 using nsEventStatus from "mozilla/EventForwards.h";
 using nsSizeMode from "nsIWidgetListener.h";
 using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
@@ -413,53 +409,16 @@ parent:
      *
      * @param opener the PBrowser whose content called window.open.
      */
     sync BrowserFrameOpenWindow(PBrowser opener, nsString aURL,
                                 nsString aName, nsString aFeatures)
       returns (bool windowOpened);
 
     /**
-     * Instructs the TabParent to forward a request to zoom to a rect given in
-     * CSS pixels. This rect is relative to the document.
-     */
-    async ZoomToRect(uint32_t aPresShellId, ViewID aViewId, CSSRect aRect, uint32_t aFlags);
-
-    /**
-     * We know for sure that content has either preventDefaulted or not
-     * preventDefaulted. This applies to an entire batch of touch events. It is
-     * expected that, if there are any DOM touch listeners, touch events will be
-     * batched and only processed for panning and zooming if content does not
-     * preventDefault.
-     */
-    async ContentReceivedInputBlock(ScrollableLayerGuid aGuid, uint64_t aInputBlockId, bool aPreventDefault);
-
-    /**
-     * Notifies the APZ code of the results of the gecko hit-test for a
-     * particular input block. Each target corresponds to one touch point in the
-     * touch event.
-     */
-    async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
-
-    /**
-     * Notifies the APZ code of the allowed touch-behaviours for a particular
-     * input block. Each item in the aFlags array corresponds to one touch point
-     * in the touch event.
-     */
-    async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aFlags);
-
-    /**
-     * Updates the zoom constraints for a scrollable frame in this tab.
-     * The zoom controller code lives on the parent side and so this allows it to
-     * have up-to-date zoom constraints.
-     */
-    async UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId,
-                                MaybeZoomConstraints aConstraints);
-
-    /**
      * Tells the containing widget whether the given input block results in a
      * swipe. Should be called in response to a WidgetWheelEvent that has
      * mFlags.mCanTriggerSwipe set on it.
      */
     async RespondStartSwipeEvent(uint64_t aInputBlockId, bool aStartSwipe);
 
     /**
      * Brings up the auth prompt dialog.
@@ -533,19 +492,16 @@ parent:
      * dimensions has been requested, likely through win.moveTo or resizeTo
      */
     async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
 
     prio(high) sync DispatchWheelEvent(WidgetWheelEvent event);
     prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
     prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
 
-    // Start an APZ drag on a scrollbar
-    async StartScrollbarDrag(AsyncDragMetrics aDragMetrics);
-
     async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
                             nsCString visualData, uint32_t width, uint32_t height,
                             uint32_t stride, uint8_t format,
                             int32_t dragAreaX, int32_t dragAreaY);
 
     async AudioChannelActivityNotification(uint32_t aAudioChannel,
                                            bool aActive);
 
@@ -571,30 +527,16 @@ child:
     async OpenURI(URIParams uri, uint32_t flags);
 
     async CacheFileDescriptor(nsString path, FileDescriptor fd);
 
     async UpdateDimensions(CSSRect rect, CSSSize size, nsSizeMode sizeMode,
                            ScreenOrientationInternal orientation,
                            LayoutDeviceIntPoint chromeDisp) compressall;
 
-    async UpdateFrame(FrameMetrics frame);
-
-    // The following methods correspond to functions on the GeckoContentController
-    // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
-    // in that file for these functions.
-    async RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination);
-    async AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
-    async HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
-    async HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
-    async HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
-    async NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
-    async NotifyFlushComplete();
-
-
     /**
      * Sending an activate message moves focus to the child.
      */
     async Activate();
 
     async Deactivate();
 
     async ParentActivated(bool aActivated);
@@ -656,17 +598,17 @@ child:
                    int32_t aKeyCode,
                    int32_t aCharCode,
                    int32_t aModifiers,
                    bool aPreventDefault);
 
     /**
      * APZ notification for mouse scroll testing events.
      */
-    async MouseScrollTestEvent(ViewID aScrollId, nsString aEvent);
+    async MouseScrollTestEvent(uint64_t aLayersId, ViewID aScrollId, nsString aEvent);
 
     async CompositionEvent(WidgetCompositionEvent event);
 
     async SelectionEvent(WidgetSelectionEvent event);
 
     /**
      * Activate event forwarding from client to parent.
      */
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 protocol PAPZ;
 include protocol PBackground;
 include protocol PBlob;
 include protocol PBluetooth;
 include protocol PBrowser;
 include protocol PCellBroadcast;
 include protocol PCompositor;
 include protocol PContentBridge;
 include protocol PContentPermissionRequest;
@@ -460,16 +461,17 @@ prio(normal upto urgent) sync protocol P
     parent opens PCompositor;
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     parent opens PGMPService;
     parent opens PVRManager;
     child opens PBackground;
 
+    manages PAPZ;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
     manages PCrashReporter;
     manages PCycleCollectWithLogs;
     manages PDeviceStorageRequest;
@@ -570,16 +572,18 @@ child:
      * nsIMemoryInfoDumper.idl
      */
     async PCycleCollectWithLogs(bool dumpAllTraces,
                                 FileDescriptor gcLog,
                                 FileDescriptor ccLog);
 
     async PTestShell();
 
+    async PAPZ(TabId tabId);
+
     async RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
                          OverrideMapping[] overrides, nsCString locale, bool reset);
     async RegisterChromeItem(ChromeRegistryItem item);
 
     async SetOffline(bool offline);
     async SetConnectivity(bool connectivity);
 
     async NotifyVisited(URIParams uri);
@@ -988,17 +992,17 @@ parent:
     sync KeywordToURI(nsCString keyword)
         returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri);
 
     sync NotifyKeywordSearchLoading(nsString providerName, nsString keyword);
 
     async CopyFavicon(URIParams oldURI, URIParams newURI, Principal aLoadingPrincipal, bool isPrivate);
 
     // Tell the compositor to allocate a layer tree id for nested remote mozbrowsers.
-    sync AllocateLayerTreeId()
+    sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId)
         returns (uint64_t id);
     async DeallocateLayerTreeId(uint64_t id);
 
     sync SpeakerManagerForceSpeaker(bool aEnable);
 
     sync SpeakerManagerGetSpeakerStatus()
         returns (bool value);
 
@@ -1011,16 +1015,19 @@ parent:
      */
     async RecordingDeviceEvents(nsString recordingStatus,
                                 nsString pageURL,
                                 bool isAudio,
                                 bool isVideo);
 
     sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, bool aSuccess);
 
+    // Graphics errors
+    async GraphicsError(nsCString aError);
+
     // Driver crash guards. aGuardType must be a member of CrashGuardType.
     sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
     sync EndDriverCrashGuard(uint32_t aGuardType);
 
     async AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
     async RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
 
     /**
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -11,37 +11,40 @@
 #include "gfxPrefs.h"
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/DocAccessibleChild.h"
 #endif
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/BrowserElementParent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/plugins/PluginWidgetChild.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/URIUtils.h"
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 #include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/DoubleTapToZoom.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layout/RenderFrameChild.h"
+#include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Move.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
@@ -584,30 +587,31 @@ TabChild::TabChild(nsIContentChild* aMan
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
   , mIPCOpen(true)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
+  , mAPZChild(nullptr)
 {
   // In the general case having the TabParent tell us if APZ is enabled or not
   // doesn't really work because the TabParent itself may not have a reference
   // to the owning widget during initialization. Instead we assume that this
   // TabChild corresponds to a widget type that would have APZ enabled, and just
   // check the other conditions necessary for enabling APZ.
   mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
 
   nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this)));  // for capture by the lambda
   mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId,
                                                    const nsTArray<TouchBehaviorFlags>& aFlags)
   {
     if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
-      static_cast<TabChild*>(tabChild.get())->SendSetAllowedTouchBehavior(aInputBlockId, aFlags);
+      static_cast<TabChild*>(tabChild.get())->SetAllowedTouchBehavior(aInputBlockId, aFlags);
     }
   };
 
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
@@ -705,31 +709,63 @@ TabChild::Observe(nsISupports *aSubject,
       mAudioChannelsActive[audioChannel] = active;
       Unused << SendAudioChannelActivityNotification(audioChannel, active);
     }
   }
 
   return NS_OK;
 }
 
+void
+TabChild::ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+                                    uint64_t aInputBlockId,
+                                    bool aPreventDefault) const
+{
+  if (mAPZChild) {
+    mAPZChild->SendContentReceivedInputBlock(aGuid, aInputBlockId,
+                                             aPreventDefault);
+  }
+}
+
+void
+TabChild::SetTargetAPZC(uint64_t aInputBlockId,
+                        const nsTArray<ScrollableLayerGuid>& aTargets) const
+{
+  if (mAPZChild) {
+    mAPZChild->SendSetTargetAPZC(aInputBlockId, aTargets);
+  }
+}
+
+void
+TabChild::SetAllowedTouchBehavior(uint64_t aInputBlockId,
+                                  const nsTArray<TouchBehaviorFlags>& aTargets) const
+{
+  if (mAPZChild) {
+    mAPZChild->SendSetAllowedTouchBehavior(aInputBlockId, aTargets);
+  }
+}
+
 bool
 TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId,
                                   const ViewID& aViewId,
                                   const Maybe<ZoomConstraints>& aConstraints)
 {
   if (sPreallocatedTab == this) {
     // If we're the preallocated tab, bail out because doing IPC will crash.
     // Once we get used for something we'll get another zoom constraints update
     // and all will be well.
     return true;
   }
 
-  return SendUpdateZoomConstraints(aPresShellId,
-                                   aViewId,
-                                   aConstraints);
+  if (!mAPZChild) {
+    return false;
+  }
+
+  return mAPZChild->SendUpdateZoomConstraints(aPresShellId, aViewId,
+                                              aConstraints);
 }
 
 nsresult
 TabChild::Init()
 {
   nsCOMPtr<nsIWebBrowser> webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
   if (!webBrowser) {
     NS_ERROR("Couldn't create a nsWebBrowser?");
@@ -806,17 +842,17 @@ TabChild::Init()
 
   nsWeakPtr weakPtrThis = do_GetWeakReference(static_cast<nsITabChild*>(this));  // for capture by the lambda
   ContentReceivedInputBlockCallback callback(
       [weakPtrThis](const ScrollableLayerGuid& aGuid,
                     uint64_t aInputBlockId,
                     bool aPreventDefault)
       {
         if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
-          static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
+          static_cast<TabChild*>(tabChild.get())->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
         }
       });
   mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
 
   return NS_OK;
 }
 
 void
@@ -1628,17 +1664,17 @@ TabChild::RecvUpdateDimensions(const CSS
     mPuppetWidget->Resize(screenRect.x + chromeDisp.x,
                           screenRect.y + chromeDisp.y,
                           screenSize.width, screenSize.height, true);
 
     return true;
 }
 
 bool
-TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
+TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics)
 {
   return TabChildBase::UpdateFrameHandler(aFrameMetrics);
 }
 
 bool
 TabChild::RecvSuppressDisplayport(const bool& aEnabled)
 {
   if (aEnabled) {
@@ -1647,101 +1683,104 @@ TabChild::RecvSuppressDisplayport(const 
     mActiveSuppressDisplayport--;
   }
 
   MOZ_ASSERT(mActiveSuppressDisplayport >= 0);
   APZCCallbackHelper::SuppressDisplayport(aEnabled);
   return true;
 }
 
-bool
-TabChild::RecvRequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
-                               const mozilla::CSSPoint& aDestination)
-{
-  APZCCallbackHelper::RequestFlingSnap(aScrollId, aDestination);
-  return true;
-}
-
-bool
-TabChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
-                                      const uint32_t& aScrollGeneration)
-{
-  APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
-  return true;
-}
-
-bool
-TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
+void
+TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
+                          const ScrollableLayerGuid& aGuid)
 {
-    TABC_LOG("Handling double tap at %s with %p %p\n",
-      Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
-
-    if (!mGlobal || !mTabChildGlobal) {
-        return true;
-    }
-
-    // Note: there is nothing to do with the modifiers here, as we are not
-    // synthesizing any sort of mouse event.
-    CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
-    nsCOMPtr<nsIDocument> document = GetDocument();
-    CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
-    // The double-tap can be dispatched by any scroll frame (so |aGuid| could be
-    // the guid of any scroll frame), but the zoom-to-rect operation must be
-    // performed by the root content scroll frame, so query its identifiers
-    // for the SendZoomToRect() call rather than using the ones from |aGuid|.
-    uint32_t presShellId;
-    ViewID viewId;
-    if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
-        document->GetDocumentElement(), &presShellId, &viewId)) {
-      SendZoomToRect(presShellId, viewId, zoomToRect, DEFAULT_BEHAVIOR);
-    }
-
-    return true;
+  TABC_LOG("Handling double tap at %s with %p %p\n",
+    Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
+
+  if (!mGlobal || !mTabChildGlobal) {
+    return;
+  }
+
+  // Note: there is nothing to do with the modifiers here, as we are not
+  // synthesizing any sort of mouse event.
+  CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
+  nsCOMPtr<nsIDocument> document = GetDocument();
+  CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
+  // The double-tap can be dispatched by any scroll frame (so |aGuid| could be
+  // the guid of any scroll frame), but the zoom-to-rect operation must be
+  // performed by the root content scroll frame, so query its identifiers
+  // for the SendZoomToRect() call rather than using the ones from |aGuid|.
+  uint32_t presShellId;
+  ViewID viewId;
+  if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+      document->GetDocumentElement(), &presShellId, &viewId) &&
+      mAPZChild) {
+    mAPZChild->SendZoomToRect(presShellId, viewId, zoomToRect,
+                              DEFAULT_BEHAVIOR);
+  }
 }
 
-bool
-TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
+void
+TabChild::HandleSingleTap(const CSSPoint& aPoint,
+                          const Modifiers& aModifiers,
+                          const ScrollableLayerGuid& aGuid,
+                          bool aCallTakeFocusForClickFromTap)
 {
+  if (aCallTakeFocusForClickFromTap && mRemoteFrame) {
+    mRemoteFrame->SendTakeFocusForClickFromTap();
+  }
   if (mGlobal && mTabChildGlobal) {
     mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
   }
-  return true;
 }
 
-bool
-TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
+void
+TabChild::HandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
+                        const ScrollableLayerGuid& aGuid,
+                        const uint64_t& aInputBlockId)
 {
   if (mGlobal && mTabChildGlobal) {
     mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
         aInputBlockId);
   }
-  return true;
 }
 
 bool
-TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
-                                   const APZStateChange& aChange,
-                                   const int& aArg)
+TabChild::NotifyAPZStateChange(const ViewID& aViewId,
+                               const layers::GeckoContentController::APZStateChange& aChange,
+                               const int& aArg)
 {
   mAPZEventState->ProcessAPZStateChange(GetDocument(), aViewId, aChange, aArg);
-  if (aChange == APZStateChange::TransformEnd) {
+  if (aChange == layers::GeckoContentController::APZStateChange::TransformEnd) {
     // This is used by tests to determine when the APZ is done doing whatever
     // it's doing. XXX generify this as needed when writing additional tests.
     DispatchMessageManagerMessage(
       NS_LITERAL_STRING("APZ:TransformEnd"),
       NS_LITERAL_STRING("{}"));
   }
   return true;
 }
 
-bool
-TabChild::RecvNotifyFlushComplete()
+void
+TabChild::StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics)
 {
-  APZCCallbackHelper::NotifyFlushComplete();
-  return true;
+  if (mAPZChild) {
+    mAPZChild->SendStartScrollbarDrag(aDragMetrics);
+  }
+}
+
+void
+TabChild::ZoomToRect(const uint32_t& aPresShellId,
+                     const FrameMetrics::ViewID& aViewId,
+                     const CSSRect& aRect,
+                     const uint32_t& aFlags)
+{
+  if (mAPZChild) {
+    mAPZChild->SendZoomToRect(aPresShellId, aViewId, aRect, aFlags);
+  }
 }
 
 bool
 TabChild::RecvActivate()
 {
   nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation());
   browser->Activate();
   return true;
@@ -1849,18 +1888,31 @@ TabChild::RecvMouseWheelEvent(const Widg
 
   if (aEvent.mFlags.mHandledByAPZ) {
     mAPZEventState->ProcessWheelEvent(event, aGuid, aInputBlockId);
   }
   return true;
 }
 
 bool
-TabChild::RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+TabChild::RecvMouseScrollTestEvent(const uint64_t& aLayersId,
+                                   const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
 {
+  if (aLayersId != mLayersId) {
+    RefPtr<TabParent> browser = TabParent::GetTabParentFromLayersId(aLayersId);
+    if (!browser) {
+      return false;
+    }
+    NS_DispatchToMainThread(NS_NewRunnableFunction(
+      [aLayersId, browser, aScrollId, aEvent] () -> void {
+        Unused << browser->SendMouseScrollTestEvent(aLayersId, aScrollId, aEvent);
+      }));
+    return true;
+  }
+
   APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
   return true;
 }
 
 bool
 TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
                              const ScrollableLayerGuid& aGuid,
                              const uint64_t& aInputBlockId,
@@ -2617,17 +2669,20 @@ TabChild::MakeHidden()
   if (mPuppetWidget) {
     mPuppetWidget->Show(false);
   }
 }
 
 void
 TabChild::UpdateHitRegion(const nsRegion& aRegion)
 {
-    mRemoteFrame->SendUpdateHitRegion(aRegion);
+  mRemoteFrame->SendUpdateHitRegion(aRegion);
+  if (mAPZChild) {
+    mAPZChild->SendUpdateHitRegion(aRegion);
+  }
 }
 
 NS_IMETHODIMP
 TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult)
 {
   if (mTabChildGlobal) {
     NS_ADDREF(*aResult = mTabChildGlobal);
     return NS_OK;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -32,27 +32,30 @@
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "nsIWebBrowserChrome3.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "AudioChannelService.h"
 #include "PuppetWidget.h"
+#include "mozilla/layers/GeckoContentController.h"
 
 class nsICachedFileDescriptorListener;
 class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 } // namespace layout
 
 namespace layers {
+class APZChild;
 class APZEventState;
+class AsyncDragMetrics;
 class ImageCompositeNotification;
 } // namespace layers
 
 namespace widget {
 struct AutoCacheNativeKeyCommands;
 } // namespace widget
 
 namespace plugins {
@@ -327,49 +330,16 @@ public:
 
   virtual bool
   RecvUpdateDimensions(const CSSRect& aRect,
                        const CSSSize& aSize,
                        const nsSizeMode& aSizeMode,
                        const ScreenOrientationInternal& aOrientation,
                        const LayoutDeviceIntPoint& aChromeDisp) override;
 
-  virtual bool
-  RecvUpdateFrame(const layers::FrameMetrics& aFrameMetrics) override;
-
-  virtual bool
-  RecvRequestFlingSnap(const ViewID& aScrollId,
-                       const CSSPoint& aDestination) override;
-
-  virtual bool
-  RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
-                              const uint32_t& aScrollGeneration) override;
-
-  virtual bool
-  RecvHandleDoubleTap(const CSSPoint& aPoint,
-                      const Modifiers& aModifiers,
-                      const mozilla::layers::ScrollableLayerGuid& aGuid) override;
-
-  virtual bool
-  RecvHandleSingleTap(const CSSPoint& aPoint,
-                      const Modifiers& aModifiers,
-                      const mozilla::layers::ScrollableLayerGuid& aGuid) override;
-
-  virtual bool
-  RecvHandleLongTap(const CSSPoint& aPoint,
-                    const Modifiers& aModifiers,
-                    const mozilla::layers::ScrollableLayerGuid& aGuid,
-                    const uint64_t& aInputBlockId) override;
-
-  virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
-                                        const APZStateChange& aChange,
-                                        const int& aArg) override;
-
-  virtual bool RecvNotifyFlushComplete() override;
-
   virtual bool RecvActivate() override;
 
   virtual bool RecvDeactivate() override;
 
   virtual bool RecvMouseEvent(const nsString& aType,
                               const float& aX,
                               const float& aY,
                               const int32_t& aButton,
@@ -413,17 +383,18 @@ public:
                          const nsEventStatus& aApzResponse) override;
 
   virtual bool RecvKeyEvent(const nsString& aType,
                             const int32_t& aKeyCode,
                             const int32_t& aCharCode,
                             const int32_t& aModifiers,
                             const bool& aPreventDefault) override;
 
-  virtual bool RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId,
+  virtual bool RecvMouseScrollTestEvent(const uint64_t& aLayersId,
+                                        const FrameMetrics::ViewID& aScrollId,
                                         const nsString& aEvent) override;
 
   virtual bool RecvNativeSynthesisResponse(const uint64_t& aObserverId,
                                            const nsCString& aResponse) override;
 
   virtual bool RecvPluginEvent(const WidgetPluginEvent& aEvent) override;
 
   virtual bool
@@ -617,16 +588,50 @@ public:
   virtual ScreenIntSize GetInnerSize() override;
 
   // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
   void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                   const uint64_t& aLayersId,
                   PRenderFrameChild* aRenderFrame,
                   const ShowInfo& aShowInfo);
 
+  void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+                                 uint64_t aInputBlockId,
+                                 bool aPreventDefault) const;
+  void SetTargetAPZC(uint64_t aInputBlockId,
+                    const nsTArray<ScrollableLayerGuid>& aTargets) const;
+  void HandleDoubleTap(const CSSPoint& aPoint,
+                       const Modifiers& aModifiers,
+                       const mozilla::layers::ScrollableLayerGuid& aGuid);
+  void HandleSingleTap(const CSSPoint& aPoint,
+                       const Modifiers& aModifiers,
+                       const mozilla::layers::ScrollableLayerGuid& aGuid,
+                       bool aCallTakeFocusForClickFromTap);
+  void HandleLongTap(const CSSPoint& aPoint,
+                     const Modifiers& aModifiers,
+                     const mozilla::layers::ScrollableLayerGuid& aGuid,
+                     const uint64_t& aInputBlockId);
+  void SetAllowedTouchBehavior(uint64_t aInputBlockId,
+                               const nsTArray<TouchBehaviorFlags>& aFlags) const;
+
+  bool UpdateFrame(const FrameMetrics& aFrameMetrics);
+  bool NotifyAPZStateChange(const ViewID& aViewId,
+                            const layers::GeckoContentController::APZStateChange& aChange,
+                            const int& aArg);
+  void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics);
+  void ZoomToRect(const uint32_t& aPresShellId,
+                  const FrameMetrics::ViewID& aViewId,
+                  const CSSRect& aRect,
+                  const uint32_t& aFlags);
+
+  void SetAPZChild(layers::APZChild* aAPZChild)
+  {
+      mAPZChild = aAPZChild;
+  }
+
 protected:
   virtual ~TabChild();
 
   virtual PRenderFrameChild* AllocPRenderFrameChild() override;
 
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual bool RecvDestroy() override;
@@ -731,15 +736,19 @@ private:
   bool mIPCOpen;
   bool mParentIsActive;
   bool mAsyncPanZoomEnabled;
   CSSSize mUnscaledInnerSize;
   bool mDidSetRealShowInfo;
 
   AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
 
+  // APZChild clears this pointer from its destructor, so it shouldn't be a
+  // dangling pointer.
+  layers::APZChild* mAPZChild;
+
   DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -991,24 +991,16 @@ TabParent::UpdateDimensions(const nsIntR
     CSSSize unscaledSize = devicePixelSize / widgetScale;
     Unused << SendUpdateDimensions(unscaledRect, unscaledSize,
                                    widget->SizeMode(),
                                    orientation, chromeOffset);
   }
 }
 
 void
-TabParent::UpdateFrame(const FrameMetrics& aFrameMetrics)
-{
-  if (!mIsDestroyed) {
-    Unused << SendUpdateFrame(aFrameMetrics);
-  }
-}
-
-void
 TabParent::UIResolutionChanged()
 {
   if (!mIsDestroyed) {
     // TryCacheDPIAndScale()'s cache is keyed off of
     // mDPI being greater than 0, so this invalidates it.
     mDPI = -1;
     TryCacheDPIAndScale();
     // If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale
@@ -1038,86 +1030,16 @@ TabParent::HandleAccessKey(nsTArray<uint
                            const int32_t& aModifierMask)
 {
   if (!mIsDestroyed) {
     Unused << SendHandleAccessKey(aCharCodes, aIsTrusted, aModifierMask);
   }
 }
 
 void
-TabParent::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
-                            const mozilla::CSSPoint& aDestination)
-{
-  if (!mIsDestroyed) {
-    Unused << SendRequestFlingSnap(aScrollId, aDestination);
-  }
-}
-
-void
-TabParent::AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration)
-{
-  if (!mIsDestroyed) {
-    Unused << SendAcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
-  }
-}
-
-void TabParent::HandleDoubleTap(const CSSPoint& aPoint,
-                                Modifiers aModifiers,
-                                const ScrollableLayerGuid &aGuid)
-{
-  if (!mIsDestroyed) {
-    Unused << SendHandleDoubleTap(aPoint, aModifiers, aGuid);
-  }
-}
-
-void TabParent::HandleSingleTap(const CSSPoint& aPoint,
-                                Modifiers aModifiers,
-                                const ScrollableLayerGuid &aGuid)
-{
-  if (!mIsDestroyed) {
-    Unused << SendHandleSingleTap(aPoint, aModifiers, aGuid);
-  }
-}
-
-void TabParent::HandleLongTap(const CSSPoint& aPoint,
-                              Modifiers aModifiers,
-                              const ScrollableLayerGuid &aGuid,
-                              uint64_t aInputBlockId)
-{
-  if (!mIsDestroyed) {
-    Unused << SendHandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
-  }
-}
-
-void TabParent::NotifyAPZStateChange(ViewID aViewId,
-                                     APZStateChange aChange,
-                                     int aArg)
-{
-  if (!mIsDestroyed) {
-    Unused << SendNotifyAPZStateChange(aViewId, aChange, aArg);
-  }
-}
-
-void
-TabParent::NotifyMouseScrollTestEvent(const ViewID& aScrollId, const nsString& aEvent)
-{
-  if (!mIsDestroyed) {
-    Unused << SendMouseScrollTestEvent(aScrollId, aEvent);
-  }
-}
-
-void
-TabParent::NotifyFlushComplete()
-{
-  if (!mIsDestroyed) {
-    Unused << SendNotifyFlushComplete();
-  }
-}
-
-void
 TabParent::Activate()
 {
   if (!mIsDestroyed) {
     Unused << SendActivate();
   }
 }
 
 void
@@ -1395,43 +1317,16 @@ TabParent::SendRealDragEvent(WidgetDragE
   return PBrowserParent::SendRealDragEvent(event, aDragAction, aDropEffect);
 }
 
 CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
 {
   return aPoint + (LayoutDevicePoint(GetChildProcessOffset()) * GetLayoutDeviceToCSSScale());
 }
 
-bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
-{
-  if (mIsDestroyed) {
-    return false;
-  }
-
-  return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
-}
-
-bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
-{
-  if (mIsDestroyed) {
-    return false;
-  }
-
-  return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid, aInputBlockId);
-}
-
-bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
-{
-  if (mIsDestroyed) {
-    return false;
-  }
-
-  return PBrowserParent::SendHandleDoubleTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
-}
-
 bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
 
   ScrollableLayerGuid guid;
   uint64_t blockId;
@@ -2792,88 +2687,25 @@ TabParent::RecvBrowserFrameOpenWindow(PB
   BrowserElementParent::OpenWindowResult opened =
     BrowserElementParent::OpenWindowOOP(TabParent::GetFrom(aOpener),
                                         this, aURL, aName, aFeatures);
   *aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
   return true;
 }
 
 bool
-TabParent::RecvZoomToRect(const uint32_t& aPresShellId,
-                          const ViewID& aViewId,
-                          const CSSRect& aRect,
-                          const uint32_t& aFlags)
-{
-  if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
-  }
-  return true;
-}
-
-bool
-TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
-                                     const ViewID& aViewId,
-                                     const MaybeZoomConstraints& aConstraints)
-{
-  if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->UpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
-  }
-  return true;
-}
-
-bool
 TabParent::RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
                                       const bool& aStartSwipe)
 {
   if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
     widget->ReportSwipeStarted(aInputBlockId, aStartSwipe);
   }
   return true;
 }
 
-bool
-TabParent::RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
-                                         const uint64_t& aInputBlockId,
-                                         const bool& aPreventDefault)
-{
-  if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
-  }
-  return true;
-}
-
-bool
-TabParent::RecvSetTargetAPZC(const uint64_t& aInputBlockId,
-                             nsTArray<ScrollableLayerGuid>&& aTargets)
-{
-  if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->SetTargetAPZC(aInputBlockId, aTargets);
-  }
-  return true;
-}
-
-bool
-TabParent::RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
-{
-  if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->StartScrollbarDrag(aDragMetrics);
-  }
-  return true;
-}
-
-bool
-TabParent::RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
-                                       nsTArray<TouchBehaviorFlags>&& aFlags)
-{
-  if (RenderFrameParent* rfp = GetRenderFrame()) {
-    rfp->SetAllowedTouchBehavior(aInputBlockId, aFlags);
-  }
-  return true;
-}
-
 already_AddRefed<nsILoadContext>
 TabParent::GetLoadContext()
 {
   nsCOMPtr<nsILoadContext> loadContext;
   if (mLoadContext) {
     loadContext = mLoadContext;
   } else {
     loadContext = new LoadContext(GetOwnerElement(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -45,18 +45,16 @@ namespace a11y {
 class DocAccessibleParent;
 }
 
 namespace jsipc {
 class CpowHolder;
 } // namespace jsipc
 
 namespace layers {
-class AsyncDragMetrics;
-struct FrameMetrics;
 struct TextureFactoryIdentifier;
 } // namespace layers
 
 namespace layout {
 class RenderFrameParent;
 } // namespace layout
 
 namespace widget {
@@ -85,17 +83,16 @@ class TabParent final : public PBrowserP
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
                       , public nsSupportsWeakReference
                       , public TabContext
                       , public nsAPostRefreshObserver
                       , public nsIWebBrowserPersistable
 {
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
-  typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics;
 
   virtual ~TabParent();
 
 public:
   // Helper class for ContentParent::RecvCreateWindow.
   struct AutoUseNewTab;
 
   // nsITabParent
@@ -292,53 +289,28 @@ public:
   virtual bool RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override;
 
   virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) override;
 
   virtual bool RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override;
 
   virtual bool RecvDispatchFocusToTopLevelWindow() override;
 
-  virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
-                              const ViewID& aViewId,
-                              const CSSRect& aRect,
-                              const uint32_t& aFlags) override;
-
-  virtual bool
-  RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
-                            const ViewID& aViewId,
-                            const MaybeZoomConstraints& aConstraints) override;
-
   virtual bool RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
                                           const bool& aStartSwipe) override;
 
   virtual bool
-  RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
-                                const uint64_t& aInputBlockId,
-                                const bool& aPreventDefault) override;
-
-  virtual bool RecvSetTargetAPZC(const uint64_t& aInputBlockId,
-                                 nsTArray<ScrollableLayerGuid>&& aTargets) override;
-
-  virtual bool
-  RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
-                              nsTArray<TouchBehaviorFlags>&& aTargets) override;
-
-  virtual bool
   RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override;
 
   virtual bool
   RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) override;
 
   virtual bool
   RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
 
-  virtual bool
-  RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
-
   virtual PColorPickerParent*
   AllocPColorPickerParent(const nsString& aTitle,
                           const nsString& aInitialColor) override;
 
   virtual bool
   DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override;
 
   virtual PDocAccessibleParent*
@@ -360,54 +332,24 @@ public:
 
   // XXX/cjones: it's not clear what we gain by hiding these
   // message-sending functions under a layer of indirection and
   // eating the return values
   void Show(const ScreenIntSize& aSize, bool aParentIsActive);
 
   void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
 
-  void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
-
   void UIResolutionChanged();
 
   void ThemeChanged();
 
   void HandleAccessKey(nsTArray<uint32_t>& aCharCodes,
                        const bool& aIsTrusted,
                        const int32_t& aModifierMask);
 
-  void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
-                        const mozilla::CSSPoint& aDestination);
-
-  void AcknowledgeScrollUpdate(const ViewID& aScrollId,
-                               const uint32_t& aScrollGeneration);
-
-  void HandleDoubleTap(const CSSPoint& aPoint,
-                       Modifiers aModifiers,
-                       const ScrollableLayerGuid& aGuid);
-
-  void HandleSingleTap(const CSSPoint& aPoint,
-                       Modifiers aModifiers,
-                       const ScrollableLayerGuid& aGuid);
-
-  void HandleLongTap(const CSSPoint& aPoint,
-                     Modifiers aModifiers,
-                     const ScrollableLayerGuid& aGuid,
-                     uint64_t aInputBlockId);
-
-  void NotifyAPZStateChange(ViewID aViewId,
-                            APZStateChange aChange,
-                            int aArg);
-
-  void NotifyMouseScrollTestEvent(const ViewID& aScrollId,
-                                  const nsString& aEvent);
-
-  void NotifyFlushComplete();
-
   void Activate();
 
   void Deactivate();
 
   bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent);
 
   void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
                                           mozilla::WidgetEvent* aEvent);
@@ -477,29 +419,16 @@ public:
                          uint32_t aDropEffect);
 
   bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event);
 
   bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
 
   bool SendRealTouchEvent(WidgetTouchEvent& event);
 
-  bool SendHandleSingleTap(const CSSPoint& aPoint,
-                           const Modifiers& aModifiers,
-                           const ScrollableLayerGuid& aGuid);
-
-  bool SendHandleLongTap(const CSSPoint& aPoint,
-                         const Modifiers& aModifiers,
-                         const ScrollableLayerGuid& aGuid,
-                         const uint64_t& aInputBlockId);
-
-  bool SendHandleDoubleTap(const CSSPoint& aPoint,
-                           const Modifiers& aModifiers,
-                           const ScrollableLayerGuid& aGuid);
-
   virtual PDocumentRendererParent*
   AllocPDocumentRendererParent(const nsRect& documentRect,
                                const gfx::Matrix& transform,
                                const nsString& bgcolor,
                                const uint32_t& renderFlags,
                                const bool& flushLayout,
                                const nsIntSize& renderSize) override;
 
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -47,17 +47,16 @@ struct AutoProfilerUnregisterThread
   }
 };
 
 GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
   : mIterationStart(0),
     mIterationEnd(0),
     mGraphImpl(aGraphImpl),
     mWaitState(WAITSTATE_RUNNING),
-    mAudioInput(nullptr),
     mCurrentTimeStamp(TimeStamp::Now()),
     mPreviousDriver(nullptr),
     mNextDriver(nullptr)
 { }
 
 void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver,
                                GraphTime aLastSwitchNextIterationStart,
                                GraphTime aLastSwitchNextIterationEnd)
@@ -541,16 +540,17 @@ StreamAndPromiseForOperation::StreamAndP
   , mOperation(aOperation)
 {
   // MOZ_ASSERT(aPromise);
 }
 
 AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
   : GraphDriver(aGraphImpl)
   , mSampleRate(0)
+  , mInputChannels(1)
   , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
   , mStarted(false)
   , mAudioInput(nullptr)
   , mAudioChannel(aGraphImpl->AudioChannel())
   , mAddedMixer(false)
   , mInCallback(false)
   , mMicrophoneActive(false)
 #ifdef XP_MACOSX
@@ -599,17 +599,17 @@ AudioCallbackDriver::Init()
   }
 
   if (cubeb_get_min_latency(CubebUtils::GetCubebContext(), output, &latency) != CUBEB_OK) {
     NS_WARNING("Could not get minimal latency from cubeb.");
     return;
   }
 
   input = output;
-  input.channels = 1; // change to support optional stereo capture
+  input.channels = mInputChannels; // change to support optional stereo capture
 
   cubeb_stream* stream;
   // XXX Only pass input input if we have an input listener.  Always
   // set up output because it's easier, and it will just get silence.
   // XXX Add support for adding/removing an input listener later.
   if (cubeb_stream_init(CubebUtils::GetCubebContext(), &stream,
                         "AudioCallbackDriver",
                         mGraphImpl->mInputDeviceID,
@@ -917,24 +917,24 @@ AudioCallbackDriver::DataCallback(const 
   mBuffer.BufferFilled();
 
   // Callback any observers for the AEC speaker data.  Note that one
   // (maybe) of these will be full-duplex, the others will get their input
   // data off separate cubeb callbacks.  Take care with how stuff is
   // removed/added to this list and TSAN issues, but input and output will
   // use separate callback methods.
   mGraphImpl->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
-                               ChannelCount);
+                               mSampleRate, ChannelCount);
 
   // Process mic data if any/needed -- after inserting far-end data for AEC!
   if (aInputBuffer) {
     if (mAudioInput) { // for this specific input-only or full-duplex stream
       mAudioInput->NotifyInputData(mGraphImpl, aInputBuffer,
                                    static_cast<size_t>(aFrames),
-                                   ChannelCount);
+                                   mSampleRate, mInputChannels);
     }
   }
 
   bool switching = false;
   {
     MonitorAutoLock mon(mGraphImpl->GetMonitor());
     switching = !!NextDriver();
   }
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -192,25 +192,16 @@ public:
   void EnsureNextIterationLocked();
 
   MediaStreamGraphImpl* GraphImpl() {
     return mGraphImpl;
   }
 
   virtual bool OnThread() = 0;
 
-  // These are invoked on the MSG thread (or MainThread in shutdown)
-  virtual void SetInputListener(AudioDataListener *aListener) {
-    mAudioInput = aListener;
-  }
-  // XXX do we need the param?  probably no
-  virtual void RemoveInputListener(AudioDataListener *aListener) {
-    mAudioInput = nullptr;
-  }
-
 protected:
   GraphTime StateComputedTime() const;
 
   // Time of the start of this graph iteration. This must be accessed while
   // having the monitor.
   GraphTime mIterationStart;
   // Time of the end of this graph iteration. This must be accessed while having
   // the monitor.
@@ -231,19 +222,16 @@ protected:
     WAITSTATE_WAITING_INDEFINITELY,
     // Something has signaled RunThread() to wake up immediately,
     // but it hasn't done so yet
     WAITSTATE_WAKING_UP
   };
   // This must be access with the monitor.
   WaitState mWaitState;
 
-  // Callback for mic data, if any
-  AudioDataListener *mAudioInput;
-
   // This is used on the main thread (during initialization), and the graph
   // thread. No monitor needed because we know the graph thread does not run
   // during the initialization.
   TimeStamp mCurrentTimeStamp;
   // This is non-null only when this driver has recently switched from an other
   // driver, and has not cleaned it up yet (for example because the audio stream
   // is currently calling the callback during initialization).
   //
@@ -416,16 +404,27 @@ public:
   /* This function gets called when the graph has produced the audio frames for
    * this iteration. */
   void MixerCallback(AudioDataValue* aMixedBuffer,
                      AudioSampleFormat aFormat,
                      uint32_t aChannels,
                      uint32_t aFrames,
                      uint32_t aSampleRate) override;
 
+  // These are invoked on the MSG thread (we don't call this if not LIFECYCLE_RUNNING)
+  virtual void SetInputListener(AudioDataListener *aListener) {
+    MOZ_ASSERT(OnThread());
+    mAudioInput = aListener;
+  }
+  // XXX do we need the param?  probably no
+  virtual void RemoveInputListener(AudioDataListener *aListener) {
+    MOZ_ASSERT(OnThread());
+    mAudioInput = nullptr;
+  }
+
   AudioCallbackDriver* AsAudioCallbackDriver() override {
     return this;
   }
 
   /* Enqueue a promise that is going to be resolved when a specific operation
    * occurs on the cubeb stream. */
   void EnqueueStreamAndPromiseForOperation(MediaStream* aStream,
                                          void* aPromise,
@@ -481,16 +480,19 @@ private:
    * callback thread. */
   AudioCallbackBufferWrapper<AudioDataValue, ChannelCount> mBuffer;
   /* cubeb stream for this graph. This is guaranteed to be non-null after Init()
    * has been called, and is synchronized internaly. */
   nsAutoRef<cubeb_stream> mAudioStream;
   /* The sample rate for the aforementionned cubeb stream. This is set on
    * initialization and can be read safely afterwards. */
   uint32_t mSampleRate;
+  /* The number of input channels from cubeb.  Should be set before opening cubeb
+   * and then be static. */
+  uint32_t mInputChannels;
   /* Approximation of the time between two callbacks. This is used to schedule
    * video frames. This is in milliseconds. Only even used (after
    * inizatialization) on the audio callback thread. */
   uint32_t mIterationDurationMS;
   /* cubeb_stream_init calls the audio callback to prefill the buffers. The
    * previous driver has to be kept alive until the audio stream has been
    * started, because it is responsible to call cubeb_stream_start, so we delay
    * the cleanup of the previous driver until it has started the audio stream.
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -629,23 +629,25 @@ VideoDevice::GetSource()
 
 AudioDevice::Source*
 AudioDevice::GetSource()
 {
   return static_cast<Source*>(&*mSource);
 }
 
 nsresult VideoDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
-                               const MediaEnginePrefs &aPrefs) {
-  return GetSource()->Allocate(aConstraints, aPrefs, mID);
+                               const MediaEnginePrefs &aPrefs,
+                               const nsACString& aOrigin) {
+  return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin);
 }
 
 nsresult AudioDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
-                               const MediaEnginePrefs &aPrefs) {
-  return GetSource()->Allocate(aConstraints, aPrefs, mID);
+                               const MediaEnginePrefs &aPrefs,
+                               const nsACString& aOrigin) {
+  return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin);
 }
 
 nsresult VideoDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
                               const MediaEnginePrefs &aPrefs) {
   return GetSource()->Restart(aConstraints, aPrefs, mID);
 }
 
 nsresult AudioDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
@@ -1198,26 +1200,28 @@ public:
     MOZ_ASSERT(mDeviceChosen);
 
     // Allocate a video or audio device and return a MediaStream via
     // a GetUserMediaStreamRunnable.
 
     nsresult rv;
 
     if (mAudioDevice) {
-      rv = mAudioDevice->Allocate(GetInvariant(mConstraints.mAudio), mPrefs);
+      rv = mAudioDevice->Allocate(GetInvariant(mConstraints.mAudio),
+                                  mPrefs, mOrigin);
       if (NS_FAILED(rv)) {
         LOG(("Failed to allocate audiosource %d",rv));
         Fail(NS_LITERAL_STRING("SourceUnavailableError"),
              NS_LITERAL_STRING("Failed to allocate audiosource"));
         return;
       }
     }
     if (mVideoDevice) {
-      rv = mVideoDevice->Allocate(GetInvariant(mConstraints.mVideo), mPrefs);
+      rv = mVideoDevice->Allocate(GetInvariant(mConstraints.mVideo),
+                                  mPrefs, mOrigin);
       if (NS_FAILED(rv)) {
         LOG(("Failed to allocate videosource %d\n",rv));
         if (mAudioDevice) {
           mAudioDevice->GetSource()->Deallocate();
         }
         Fail(NS_LITERAL_STRING("SourceUnavailableError"),
              NS_LITERAL_STRING("Failed to allocate videosource"));
         return;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -89,31 +89,33 @@ class VideoDevice : public MediaDevice
 {
 public:
   typedef MediaEngineVideoSource Source;
 
   explicit VideoDevice(Source* aSource);
   NS_IMETHOD GetType(nsAString& aType);
   Source* GetSource();
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
-                    const MediaEnginePrefs &aPrefs);
+                    const MediaEnginePrefs &aPrefs,
+                    const nsACString& aOrigin);
   nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
                    const MediaEnginePrefs &aPrefs);
 };
 
 class AudioDevice : public MediaDevice
 {
 public:
   typedef MediaEngineAudioSource Source;
 
   explicit AudioDevice(Source* aSource);
   NS_IMETHOD GetType(nsAString& aType);
   Source* GetSource();
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
-                    const MediaEnginePrefs &aPrefs);
+                    const MediaEnginePrefs &aPrefs,
+                    const nsACString& aOrigin);
   nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
                    const MediaEnginePrefs &aPrefs);
 };
 
 /**
  * This class is an implementation of MediaStreamListener. This is used
  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
  * are assigned and deassigned in content.
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -933,33 +933,47 @@ MediaStreamGraphImpl::PlayVideo(MediaStr
     aStream->mLastPlayedVideoFrame.SetNull();
   }
 }
 
 void
 MediaStreamGraphImpl::OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
                                          AudioDataListener *aListener)
 {
- // Bug 1238038 Need support for multiple mics at once
-  MOZ_ASSERT(!mInputWanted);
-  if (mInputWanted) {
+  // Bug 1238038 Need support for multiple mics at once
+  if (mInputDeviceUsers.Count() > 0 &&
+      !mInputDeviceUsers.Get(aListener, nullptr)) {
+    NS_ASSERTION(false, "Input from multiple mics not yet supported; bug 1238038");
     // Need to support separate input-only AudioCallback drivers; they'll
     // call us back on "other" threads.  We will need to echo-cancel them, though.
     return;
   }
   mInputWanted = true;
+
+  // Add to count of users for this ID.
+  // XXX Since we can't rely on IDs staying valid (ugh), use the listener as
+  // a stand-in for the ID.  Fix as part of support for multiple-captures
+  // (Bug 1238038)
+  uint32_t count = 0;
+  mInputDeviceUsers.Get(aListener, &count); // ok if this fails
+  count++;
+  mInputDeviceUsers.Put(aListener, count); // creates a new entry in the hash if needed
+
   // aID is a cubeb_devid, and we assume that opaque ptr is valid until
   // we close cubeb.
   mInputDeviceID = aID;
-  mAudioInputs.AppendElement(aListener); // always monitor speaker data
+  if (count == 1) { // first open for this listener
+    mAudioInputs.AppendElement(aListener); // always monitor speaker data
+  }
 
   // Switch Drivers since we're adding input (to input-only or full-duplex)
   MonitorAutoLock mon(mMonitor);
   if (mLifecycleState == LIFECYCLE_RUNNING) {
     AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+    driver->SetInputListener(aListener);
     CurrentDriver()->SwitchAtNextIteration(driver);
   }
 }
 
 nsresult
 MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID,
                                      AudioDataListener *aListener)
 {
@@ -980,26 +994,38 @@ MediaStreamGraphImpl::OpenAudioInput(Cub
       mGraph->OpenAudioInputImpl(mID, mListener);
     }
     MediaStreamGraphImpl *mGraph;
     // aID is a cubeb_devid, and we assume that opaque ptr is valid until
     // we close cubeb.
     CubebUtils::AudioDeviceID mID;
     RefPtr<AudioDataListener> mListener;
   };
+  // XXX Check not destroyed!
   this->AppendMessage(MakeUnique<Message>(this, aID, aListener));
   return NS_OK;
 }
 
 void
 MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
 {
+  uint32_t count;
+  DebugOnly<bool> result = mInputDeviceUsers.Get(aListener, &count);
+  MOZ_ASSERT(result);
+  if (--count > 0) {
+    mInputDeviceUsers.Put(aListener, count);
+    return; // still in use
+  }
+  mInputDeviceUsers.Remove(aListener);
   mInputDeviceID = nullptr;
   mInputWanted = false;
-  CurrentDriver()->RemoveInputListener(aListener);
+  AudioCallbackDriver *driver = CurrentDriver()->AsAudioCallbackDriver();
+  if (driver) {
+    driver->RemoveInputListener(aListener);
+  }
   mAudioInputs.RemoveElement(aListener);
 
   // Switch Drivers since we're adding or removing an input (to nothing/system or output only)
   bool audioTrackPresent = false;
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
     // If this is a AudioNodeStream, force a AudioCallbackDriver.
     if (stream->AsAudioNodeStream()) {
@@ -1054,20 +1080,20 @@ MediaStreamGraphImpl::CloseAudioInput(Au
   };
   this->AppendMessage(MakeUnique<Message>(this, aListener));
 }
 
 
 // All AudioInput listeners get the same speaker data (at least for now).
 void
 MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
-                                   uint32_t aChannels)
+                                   TrackRate aRate, uint32_t aChannels)
 {
   for (auto& listener : mAudioInputs) {
-    listener->NotifyOutputData(this, aBuffer, aFrames, aChannels);
+    listener->NotifyOutputData(this, aBuffer, aFrames, aRate, aChannels);
   }
 }
 
 bool
 MediaStreamGraphImpl::ShouldUpdateMainThread()
 {
   if (mRealtime) {
     return true;
@@ -2290,19 +2316,42 @@ MediaStream::AddMainThreadListener(MainT
 
     RefPtr<MediaStream> mStream;
   };
 
   RefPtr<nsRunnable> runnable = new NotifyRunnable(this);
   NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
 }
 
+nsresult
+SourceMediaStream::OpenAudioInput(CubebUtils::AudioDeviceID aID,
+                                  AudioDataListener *aListener)
+{
+  if (GraphImpl()) {
+    mInputListener = aListener;
+    return GraphImpl()->OpenAudioInput(aID, aListener);
+  }
+  return NS_ERROR_FAILURE;
+}
+
+void
+SourceMediaStream::CloseAudioInput()
+{
+  // Destroy() may have run already and cleared this
+  if (GraphImpl() && mInputListener) {
+    GraphImpl()->CloseAudioInput(mInputListener);
+  }
+  mInputListener = nullptr;
+}
+
 void
 SourceMediaStream::DestroyImpl()
 {
+  CloseAudioInput();
+
   // Hold mMutex while mGraph is reset so that other threads holding mMutex
   // can null-check know that the graph will not destroyed.
   MutexAutoLock lock(mMutex);
   MediaStream::DestroyImpl();
 }
 
 void
 SourceMediaStream::SetPullEnabled(bool aEnabled)
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -192,24 +192,24 @@ public:
   /* These are for cubeb audio input & output streams: */
   /**
    * Output data to speakers, for use as the "far-end" data for echo
    * cancellation.  This is not guaranteed to be in any particular size
    * chunks.
    */
   virtual void NotifyOutputData(MediaStreamGraph* aGraph,
                                 AudioDataValue* aBuffer, size_t aFrames,
-                                uint32_t aChannels) = 0;
+                                TrackRate aRate, uint32_t aChannels) = 0;
   /**
    * Input data from a microphone (or other audio source.  This is not
    * guaranteed to be in any particular size chunks.
    */
   virtual void NotifyInputData(MediaStreamGraph* aGraph,
                                const AudioDataValue* aBuffer, size_t aFrames,
-                               uint32_t aChannels) = 0;
+                               TrackRate aRate, uint32_t aChannels) = 0;
 };
 
 class AudioDataListener : public AudioDataListenerInterface {
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioDataListener() {}
 
 public:
@@ -729,16 +729,26 @@ public:
     mPullEnabled(false),
     mUpdateFinished(false),
     mNeedsMixing(false)
   {}
 
   SourceMediaStream* AsSourceStream() override { return this; }
 
   // Media graph thread only
+
+  // Users of audio inputs go through the stream so it can track when the
+  // last stream referencing an input goes away, so it can close the cubeb
+  // input.  Also note: callable on any thread (though it bounces through
+  // MainThread to set the command if needed).
+  nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
+                          AudioDataListener *aListener);
+  // Note: also implied when Destroy() happens
+  void CloseAudioInput();
+
   void DestroyImpl() override;
 
   // Call these on any thread.
   /**
    * Enable or disable pulling. When pulling is enabled, NotifyPull
    * gets called on MediaStreamListeners for this stream during the
    * MediaStreamGraph control loop. Pulling is initially disabled.
    * Due to unavoidable race conditions, after a call to SetPullEnabled(false)
@@ -915,16 +925,22 @@ protected:
    * Notify direct consumers of new data to one of the stream tracks.
    * The data doesn't have to be resampled (though it may be).  This is called
    * from AppendToTrack on the thread providing the data, and will call
    * the Listeners on this thread.
    */
   void NotifyDirectConsumers(TrackData *aTrack,
                              MediaSegment *aSegment);
 
+  // Only accessed on the MSG thread.  Used so to ask the MSGImpl to usecount
+  // users of a specific input.
+  // XXX Should really be a CubebUtils::AudioDeviceID, but they aren't
+  // copyable (opaque pointers)
+  RefPtr<AudioDataListener> mInputListener;
+
   // This must be acquired *before* MediaStreamGraphImpl's lock, if they are
   // held together.
   Mutex mMutex;
   // protected by mMutex
   StreamTime mUpdateKnownTracksTime;
   nsTArray<TrackData> mUpdateTracks;
   nsTArray<TrackData> mPendingTracks;
   nsTArray<RefPtr<MediaStreamDirectListener> > mDirectListeners;
@@ -1293,17 +1309,17 @@ public:
   already_AddRefed<MediaInputPort> ConnectToCaptureStream(
     uint64_t aWindowId, MediaStream* aMediaStream);
 
   /**
    * Data going to the speakers from the GraphDriver's DataCallback
    * to notify any listeners (for echo cancellation).
    */
   void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
-                        uint32_t aChannels);
+                        TrackRate aRate, uint32_t aChannels);
 
 protected:
   explicit MediaStreamGraph(TrackRate aSampleRate)
     : mSampleRate(aSampleRate)
   {
     MOZ_COUNT_CTOR(MediaStreamGraph);
   }
   virtual ~MediaStreamGraph()
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -3,16 +3,18 @@
  * 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_MEDIASTREAMGRAPHIMPL_H_
 #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 
 #include "MediaStreamGraph.h"
 
+#include "nsDataHashtable.h"
+
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIMemoryReporter.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "nsIAsyncShutdown.h"
 #include "Latency.h"
 #include "mozilla/UniquePtr.h"
@@ -624,16 +626,19 @@ public:
   /**
    * Devices to use for cubeb input & output, or NULL for no input (void*),
    * and boolean to control if we want input/output
    */
   bool mInputWanted;
   CubebUtils::AudioDeviceID mInputDeviceID;
   bool mOutputWanted;
   CubebUtils::AudioDeviceID mOutputDeviceID;
+  // Maps AudioDataListeners to a usecount of streams using the listener
+  // so we can know when it's no longer in use.
+  nsDataHashtable<nsPtrHashKey<AudioDataListener>, uint32_t> mInputDeviceUsers;
 
   // True if the graph needs another iteration after the current iteration.
   Atomic<bool> mNeedAnotherIteration;
   // GraphDriver may need a WakeUp() if something changes
   Atomic<bool> mGraphDriverAsleep;
 
   // mMonitor guards the data below.
   // MediaStreamGraph normally does its work without holding mMonitor, so it is
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -164,17 +164,17 @@ TrackBuffersManager::AbortAppendData()
     mon.Wait();
   }
 }
 
 void
 TrackBuffersManager::ResetParserState()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
+  MOZ_DIAGNOSTIC_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
   MSE_DEBUG("");
 
   // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed.
   // SourceBuffer.abort() has ensured that all complete coded frames have been
   // processed. As such, we don't need to check for the value of mAppendState.
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState);
   GetTaskQueue()->Dispatch(task.forget());
@@ -182,17 +182,17 @@ TrackBuffersManager::ResetParserState()
   // 7. Set append state to WAITING_FOR_SEGMENT.
   SetAppendState(AppendState::WAITING_FOR_SEGMENT);
 }
 
 RefPtr<TrackBuffersManager::RangeRemovalPromise>
 TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running");
+  MOZ_DIAGNOSTIC_ASSERT(!mAppendRunning, "Append is running");
   MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
 
   mEnded = false;
 
   return InvokeAsync(GetTaskQueue(), this, __func__,
                      &TrackBuffersManager::CodedFrameRemovalWithPromise,
                      TimeInterval(aStart, aEnd));
 }
@@ -296,17 +296,17 @@ TrackBuffersManager::Detach()
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
 }
 
 void
 TrackBuffersManager::CompleteResetParserState()
 {
   MOZ_ASSERT(OnTaskQueue());
-  MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning);
+  MOZ_DIAGNOSTIC_ASSERT(!mSegmentParserLoopRunning);
   MSE_DEBUG("");
 
   for (auto& track : GetTracksList()) {
     // 2. Unset the last decode timestamp on all track buffers.
     // 3. Unset the last frame duration on all track buffers.
     // 4. Unset the highest end timestamp on all track buffers.
     // 5. Set the need random access point flag on all track buffers to true.
     track->ResetAppendState();
@@ -534,17 +534,17 @@ TrackBuffersManager::UpdateBufferedRange
 
   mOfficialGroupEndTimestamp = mGroupEndTimestamp;
 }
 
 RefPtr<TrackBuffersManager::AppendPromise>
 TrackBuffersManager::InitSegmentParserLoop()
 {
   MOZ_ASSERT(OnTaskQueue());
-  MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mAppendPromise.IsEmpty());
   MSE_DEBUG("");
 
   RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__);
 
   AppendIncomingBuffers();
   SegmentParserLoop();
 
   return p;
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -152,17 +152,17 @@ public:
   CreateDXVAManagerEvent(LayersBackend aBackend, nsCString& aFailureReason)
     : mBackend(aBackend)
     , mFailureReason(aFailureReason)
   {}
 
   NS_IMETHOD Run() {
     NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
     if (mBackend == LayersBackend::LAYERS_D3D11 &&
-        Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", false) &&
+        Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", true) &&
         IsWin8OrLater()) {
       mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(mFailureReason);
     } else {
       mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(mFailureReason);
     }
     return NS_OK;
   }
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -346,23 +346,25 @@ CamerasChild::RecvReplyGetCaptureDevice(
   monitor.Notify();
   return true;
 }
 
 int
 CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
                                     const char* unique_idUTF8,
                                     const unsigned int unique_idUTF8Length,
-                                    int& capture_id)
+                                    int& capture_id,
+                                    const nsACString& aOrigin)
 {
   LOG((__PRETTY_FUNCTION__));
   nsCString unique_id(unique_idUTF8);
+  nsCString origin(aOrigin);
   nsCOMPtr<nsIRunnable> runnable =
-    media::NewRunnableFrom([this, aCapEngine, unique_id]() -> nsresult {
-      if (this->SendAllocateCaptureDevice(aCapEngine, unique_id)) {
+    media::NewRunnableFrom([this, aCapEngine, unique_id, origin]() -> nsresult {
+      if (this->SendAllocateCaptureDevice(aCapEngine, unique_id, origin)) {
         return NS_OK;
       }
       return NS_ERROR_FAILURE;
     });
   LockAndDispatch<> dispatcher(this, __func__, runnable);
   if (dispatcher.Success()) {
     LOG(("Capture Device allocated: %d", mReplyInteger));
     capture_id = mReplyInteger;
--- a/dom/media/systemservices/CamerasChild.h
+++ b/dom/media/systemservices/CamerasChild.h
@@ -176,17 +176,18 @@ public:
                            const int capture_id);
   int StartCapture(CaptureEngine aCapEngine,
                    const int capture_id, webrtc::CaptureCapability& capability,
                    webrtc::ExternalRenderer* func);
   int StopCapture(CaptureEngine aCapEngine, const int capture_id);
   int AllocateCaptureDevice(CaptureEngine aCapEngine,
                             const char* unique_idUTF8,
                             const unsigned int unique_idUTF8Length,
-                            int& capture_id);
+                            int& capture_id,
+                            const nsACString& aOrigin);
   int GetCaptureCapability(CaptureEngine aCapEngine,
                            const char* unique_idUTF8,
                            const unsigned int capability_number,
                            webrtc::CaptureCapability& capability);
   int GetCaptureDevice(CaptureEngine aCapEngine,
                        unsigned int list_number, char* device_nameUTF8,
                        const unsigned int device_nameUTF8Length,
                        char* unique_idUTF8,
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -6,20 +6,24 @@
 
 #include "CamerasParent.h"
 #include "CamerasUtils.h"
 #include "MediaEngine.h"
 #include "MediaUtils.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/unused.h"
+#include "mozilla/Services.h"
 #include "mozilla/Logging.h"
 #include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Preferences.h"
+#include "nsIPermissionManager.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
+#include "nsNetUtil.h"
 
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 
 #undef LOG
 #undef LOG_ENABLED
 mozilla::LazyLogModule gCamerasParentLog("CamerasParent");
 #define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
@@ -636,49 +640,137 @@ CamerasParent::RecvGetCaptureDevice(cons
         });
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
+static nsresult
+GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
+{
+  nsAutoCString originNoSuffix;
+  mozilla::PrincipalOriginAttributes attrs;
+  if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+  principal.forget(aPrincipal);
+  return NS_OK;
+}
+
+// Find out whether the given origin has permission to use the
+// camera. If the permission is not persistent, we'll make it
+// a one-shot by removing the (session) permission.
+static bool
+HasCameraPermission(const nsCString& aOrigin)
+{
+  // Name used with nsIPermissionManager
+  static const char* cameraPermission = "camera";
+  bool allowed = false;
+  bool permanent = false;
+  nsresult rv;
+  nsCOMPtr<nsIPermissionManager> mgr =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIIOService> ioServ(do_GetIOService());
+    nsCOMPtr<nsIURI> uri;
+    rv = ioServ->NewURI(aOrigin, nullptr, nullptr, getter_AddRefs(uri));
+    if (NS_SUCCEEDED(rv)) {
+      // Permanent permissions are only retrievable via principal, not uri
+      nsCOMPtr<nsIPrincipal> principal;
+      rv = GetPrincipalFromOrigin(aOrigin, getter_AddRefs(principal));
+      if (NS_SUCCEEDED(rv)) {
+        uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
+        rv = mgr->TestExactPermissionFromPrincipal(principal,
+                                                   cameraPermission,
+                                                   &video);
+        if (NS_SUCCEEDED(rv)) {
+          allowed = (video == nsIPermissionManager::ALLOW_ACTION);
+          // Was allowed, now see if this is a persistent permission
+          // or a session one.
+          if (allowed) {
+            rv = mgr->TestExactPermanentPermission(principal,
+                                                   cameraPermission,
+                                                   &video);
+            if (NS_SUCCEEDED(rv)) {
+              permanent = (video == nsIPermissionManager::ALLOW_ACTION);
+            }
+          }
+        }
+        // Session permissions are removed after one use.
+        if (allowed && !permanent) {
+          mgr->RemoveFromPrincipal(principal, cameraPermission);
+        }
+      }
+    }
+  }
+  return allowed;
+}
+
 bool
 CamerasParent::RecvAllocateCaptureDevice(const int& aCapEngine,
-                                         const nsCString& unique_id)
+                                         const nsCString& unique_id,
+                                         const nsCString& aOrigin)
 {
-  LOG((__PRETTY_FUNCTION__));
-
+  LOG(("%s: Verifying permissions for %s", __PRETTY_FUNCTION__, aOrigin.get()));
   RefPtr<CamerasParent> self(this);
-  RefPtr<nsRunnable> webrtc_runnable =
-    media::NewRunnableFrom([self, aCapEngine, unique_id]() -> nsresult {
-      int numdev = -1;
-      int error = -1;
-      if (self->EnsureInitialized(aCapEngine)) {
-        error = self->mEngines[aCapEngine].mPtrViECapture->AllocateCaptureDevice(
-          unique_id.get(), MediaEngineSource::kMaxUniqueIdLength, numdev);
+  RefPtr<nsRunnable> mainthread_runnable =
+    media::NewRunnableFrom([self, aCapEngine, unique_id, aOrigin]() -> nsresult {
+      // Verify whether the claimed origin has received permission
+      // to use the camera, either persistently or this session (one shot).
+      bool allowed = HasCameraPermission(aOrigin);
+      if (!allowed) {
+        // Developer preference for turning off permission check.
+        if (Preferences::GetBool("media.navigator.permission.disabled", false)
+            || Preferences::GetBool("media.navigator.permission.fake")) {
+          allowed = true;
+          LOG(("No permission but checks are disabled or fake sources active"));
+        } else {
+          LOG(("No camera permission for this origin"));
+        }
       }
-      RefPtr<nsIRunnable> ipc_runnable =
-        media::NewRunnableFrom([self, numdev, error]() -> nsresult {
-          if (self->IsShuttingDown()) {
-            return NS_ERROR_FAILURE;
-          }
-          if (error) {
-            Unused << self->SendReplyFailure();
-            return NS_ERROR_FAILURE;
-          } else {
-            LOG(("Allocated device nr %d", numdev));
-            Unused << self->SendReplyAllocateCaptureDevice(numdev);
-            return NS_OK;
-          }
+      // After retrieving the permission (or not) on the main thread,
+      // bounce to the WebRTC thread to allocate the device (or not),
+      // then bounce back to the IPC thread for the reply to content.
+      RefPtr<nsRunnable> webrtc_runnable =
+      media::NewRunnableFrom([self, allowed, aCapEngine, unique_id]() -> nsresult {
+        int numdev = -1;
+        int error = -1;
+        if (allowed && self->EnsureInitialized(aCapEngine)) {
+          error = self->mEngines[aCapEngine].mPtrViECapture->AllocateCaptureDevice(
+                    unique_id.get(), MediaEngineSource::kMaxUniqueIdLength, numdev);
+        }
+        RefPtr<nsIRunnable> ipc_runnable =
+          media::NewRunnableFrom([self, numdev, error]() -> nsresult {
+            if (self->IsShuttingDown()) {
+              return NS_ERROR_FAILURE;
+            }
+            if (error) {
+              Unused << self->SendReplyFailure();
+              return NS_ERROR_FAILURE;
+            } else {
+              LOG(("Allocated device nr %d", numdev));
+              Unused << self->SendReplyAllocateCaptureDevice(numdev);
+              return NS_OK;
+            }
+          });
+        self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+        return NS_OK;
         });
-      self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+      self->DispatchToVideoCaptureThread(webrtc_runnable);
       return NS_OK;
     });
-  DispatchToVideoCaptureThread(webrtc_runnable);
+  NS_DispatchToMainThread(mainthread_runnable);
   return true;
 }
 
 int
 CamerasParent::ReleaseCaptureDevice(const int& aCapEngine,
                                     const int& capnum)
 {
   int error = -1;
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -81,17 +81,17 @@ class CamerasParent :  public PCamerasPa
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
 public:
   static already_AddRefed<CamerasParent> Create();
 
   // Messages received form the child. These run on the IPC/PBackground thread.
-  virtual bool RecvAllocateCaptureDevice(const int&, const nsCString&) override;
+  virtual bool RecvAllocateCaptureDevice(const int&, const nsCString&, const nsCString&) override;
   virtual bool RecvReleaseCaptureDevice(const int&, const int &) override;
   virtual bool RecvNumberOfCaptureDevices(const int&) override;
   virtual bool RecvNumberOfCapabilities(const int&, const nsCString&) override;
   virtual bool RecvGetCaptureCapability(const int&, const nsCString&, const int&) override;
   virtual bool RecvGetCaptureDevice(const int&, const int&) override;
   virtual bool RecvStartCapture(const int&, const int&, const CaptureCapability&) override;
   virtual bool RecvStopCapture(const int&, const int&) override;
   virtual bool RecvReleaseFrame(mozilla::ipc::Shmem&&) override;
--- a/dom/media/systemservices/PCameras.ipdl
+++ b/dom/media/systemservices/PCameras.ipdl
@@ -40,17 +40,17 @@ child:
 
 parent:
   async NumberOfCaptureDevices(int engine);
   async NumberOfCapabilities(int engine, nsCString deviceUniqueIdUTF8);
 
   async GetCaptureCapability(int engine, nsCString unique_idUTF8, int capability_number);
   async GetCaptureDevice(int engine, int num);
 
-  async AllocateCaptureDevice(int engine, nsCString unique_idUTF8);
+  async AllocateCaptureDevice(int engine, nsCString unique_idUTF8, nsCString origin);
   async ReleaseCaptureDevice(int engine, int numdev);
   async StartCapture(int engine, int numdev, CaptureCapability capability);
   async StopCapture(int engine, int numdev);
   // transfers frame back
   async ReleaseFrame(Shmem s);
 
   // Ask parent to delete us
   async AllDone();
--- a/dom/media/test/test_referer.html
+++ b/dom/media/test/test_referer.html
@@ -62,16 +62,17 @@ for (var i=0; i<gSmallTests.length; ++i)
     type = "audio";
   }
   var v = document.createElement(type);
   if (!v.canPlayType(test.type)) {
     continue;
   }
   // ensure metadata is loaded for default preload is none on b2g 
   v.preload = "metadata";
+  v.autoplay = "true";
   v._complete = false;
   v.addEventListener("error", loadError, false);
   v.addEventListener("loadedmetadata", loadedMetadata, false);
   v.src = 'referer.sjs?name=' + test.name + '&type=' + test.type;
   document.body.appendChild(v); // Will start load.
   media.push(v);
 }
 
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -166,17 +166,18 @@ public:
 
   void SetHasFakeTracks(bool aHasFakeTracks) {
     mHasFakeTracks = aHasFakeTracks;
   }
 
   /* This call reserves but does not start the device. */
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs,
-                            const nsString& aDeviceId) = 0;
+                            const nsString& aDeviceId,
+                            const nsACString& aOrigin) = 0;
 
   virtual uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) = 0;
 
 protected:
   // Only class' own members can be initialized in constructor initializer list.
   explicit MediaEngineSource(MediaEngineState aState)
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -82,17 +82,18 @@ MediaEngineDefaultVideoSource::GetBestFi
   }
 #endif
   return distance;
 }
 
 nsresult
 MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                         const MediaEnginePrefs &aPrefs,
-                                        const nsString& aDeviceId)
+                                        const nsString& aDeviceId,
+                                        const nsACString& aOrigin)
 {
   if (mState != kReleased) {
     return NS_ERROR_FAILURE;
   }
 
   mOpts = aPrefs;
   mOpts.mWidth = mOpts.mWidth ? mOpts.mWidth : MediaEngine::DEFAULT_43_VIDEO_WIDTH;
   mOpts.mHeight = mOpts.mHeight ? mOpts.mHeight : MediaEngine::DEFAULT_43_VIDEO_HEIGHT;
@@ -391,17 +392,18 @@ MediaEngineDefaultAudioSource::GetBestFi
   }
 #endif
   return distance;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                         const MediaEnginePrefs &aPrefs,
-                                        const nsString& aDeviceId)
+                                        const nsString& aDeviceId,
+                                        const nsACString& aOrigin)
 {
   if (mState != kReleased) {
     return NS_ERROR_FAILURE;
   }
 
   mState = kAllocated;
   // generate sine wave (default 1KHz)
   mSineGenerator = new SineWaveGenerator(AUDIO_RATE,
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -40,17 +40,18 @@ public:
 
   void Shutdown() override {};
 
   void GetName(nsAString&) override;
   void GetUUID(nsACString&) override;
 
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                     const MediaEnginePrefs &aPrefs,
-                    const nsString& aDeviceId) override;
+                    const nsString& aDeviceId,
+                    const nsACString& aOrigin) override;
   nsresult Deallocate() override;
   nsresult Start(SourceMediaStream*, TrackID) override;
   nsresult Stop(SourceMediaStream*, TrackID) override;
   nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
                    const MediaEnginePrefs &aPrefs,
                    const nsString& aDeviceId) override;
   void SetDirectListeners(bool aHasDirectListeners) override {};
   void NotifyPull(MediaStreamGraph* aGraph,
@@ -109,17 +110,18 @@ public:
 
   void Shutdown() override {};
 
   void GetName(nsAString&) override;
   void GetUUID(nsACString&) override;
 
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                     const MediaEnginePrefs &aPrefs,
-                    const nsString& aDeviceId) override;
+                    const nsString& aDeviceId,
+                    const nsACString& aOrigin) override;
   nsresult Deallocate() override;
   nsresult Start(SourceMediaStream*, TrackID) override;
   nsresult Stop(SourceMediaStream*, TrackID) override;
   nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
                    const MediaEnginePrefs &aPrefs,
                    const nsString& aDeviceId) override;
   void SetDirectListeners(bool aHasDirectListeners) override {};
   void AppendToSegment(AudioSegment& aSegment, TrackTicks aSamples);
@@ -133,21 +135,21 @@ public:
     NS_WARN_IF_FALSE(!data || data->IsEnded() ||
                      aDesiredTime <= aSource->GetEndOfAppendedData(aId),
                      "MediaEngineDefaultAudioSource data underrun");
 #endif
   }
 
   void NotifyOutputData(MediaStreamGraph* aGraph,
                         AudioDataValue* aBuffer, size_t aFrames,
-                        uint32_t aChannels) override
+                        TrackRate aRate, uint32_t aChannels) override
   {}
   void NotifyInputData(MediaStreamGraph* aGraph,
                        const AudioDataValue* aBuffer, size_t aFrames,
-                       uint32_t aChannels) override
+                       TrackRate aRate, uint32_t aChannels) override
   {}
   bool IsFake() override {
     return true;
   }
 
   dom::MediaSourceEnum GetMediaSource() const override {
     return dom::MediaSourceEnum::Microphone;
   }
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -95,17 +95,18 @@ MediaEngineRemoteVideoSource::Shutdown()
   mState = kReleased;
   mInitDone = false;
   return;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                        const MediaEnginePrefs& aPrefs,
-                                       const nsString& aDeviceId)
+                                       const nsString& aDeviceId,
+                                       const nsACString& aOrigin)
 {
   LOG((__PRETTY_FUNCTION__));
   AssertIsOnOwningThread();
 
   if (!mInitDone) {
     LOG(("Init not done"));
     return NS_ERROR_FAILURE;
   }
@@ -115,21 +116,22 @@ MediaEngineRemoteVideoSource::Allocate(c
     // (This may change depending on spec changes for Constraints/settings)
 
     if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     if (mozilla::camera::GetChildAndCall(
       &mozilla::camera::CamerasChild::AllocateCaptureDevice,
-      mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex)) {
+      mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex, aOrigin)) {
       return NS_ERROR_FAILURE;
     }
     mState = kAllocated;
-    LOG(("Video device %d allocated", mCaptureIndex));
+    LOG(("Video device %d allocated for %s", mCaptureIndex,
+         PromiseFlatCString(aOrigin).get()));
   } else if (MOZ_LOG_TEST(GetMediaManagerLog(), mozilla::LogLevel::Debug)) {
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
       LOG(("Video device %d reallocated", mCaptureIndex));
     } else {
       LOG(("Video device %d allocated shared", mCaptureIndex));
     }
   }
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h
@@ -68,17 +68,18 @@ public:
 
   // MediaEngineCameraVideoSource
   MediaEngineRemoteVideoSource(int aIndex, mozilla::camera::CaptureEngine aCapEngine,
                                dom::MediaSourceEnum aMediaSource,
                                const char* aMonitorName = "RemoteVideo.Monitor");
 
   nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                     const MediaEnginePrefs& aPrefs,
-                    const nsString& aDeviceId) override;
+                    const nsString& aDeviceId,
+                    const nsACString& aOrigin) override;
   nsresult Deallocate() override;;
   nsresult Start(SourceMediaStream*, TrackID) override;
   nsresult Stop(SourceMediaStream*, TrackID) override;
   nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
                    const MediaEnginePrefs &aPrefs,
                    const nsString& aDeviceId) override;
   void NotifyPull(MediaStreamGraph* aGraph,
                   SourceMediaStream* aSource,
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -123,17 +123,18 @@ MediaEngineTabVideoSource::GetUUID(nsACS
 
 #define DEFAULT_TABSHARE_VIDEO_MAX_WIDTH 4096
 #define DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT 4096
 #define DEFAULT_TABSHARE_VIDEO_FRAMERATE 30
 
 nsresult
 MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                     const MediaEnginePrefs& aPrefs,
-                                    const nsString& aDeviceId)
+                                    const nsString& aDeviceId,
+                                    const nsACString& aOrigin)
 {
   // windowId is not a proper constraint, so just read it.
   // It has no well-defined behavior in advanced, so ignore it there.
 
   mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
               aConstraints.mBrowserWindow.Value() : -1;
 
   return Restart(aConstraints, aPrefs, aDeviceId);
--- a/dom/media/webrtc/MediaEngineTabVideoSource.h
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.h
@@ -18,17 +18,18 @@ class MediaEngineTabVideoSource : public
     NS_DECL_NSITIMERCALLBACK
     MediaEngineTabVideoSource();
 
     void Shutdown() override {};
     void GetName(nsAString_internal&) override;
     void GetUUID(nsACString_internal&) override;
     nsresult Allocate(const dom::MediaTrackConstraints &,
                       const mozilla::MediaEnginePrefs&,
-                      const nsString& aDeviceId) override;
+                      const nsString& aDeviceId,
+                      const nsACString& aOrigin) override;
     nsresult Deallocate() override;
     nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override;
     void SetDirectListeners(bool aHasDirectListeners) override {};
     void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
     nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
     nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
                      const mozilla::MediaEnginePrefs& aPrefs,
                      const nsString& aDeviceId) override;
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -9,16 +9,17 @@
 #include "prthread.h"
 #include "prprf.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "DOMMediaStream.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsRefPtrHashtable.h"
 
 #include "VideoUtils.h"
@@ -67,17 +68,18 @@ public:
   explicit MediaEngineWebRTCAudioCaptureSource(const char* aUuid)
     : MediaEngineAudioSource(kReleased)
   {
   }
   void GetName(nsAString& aName) override;
   void GetUUID(nsACString& aUUID) override;
   nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                     const MediaEnginePrefs& aPrefs,
-                    const nsString& aDeviceId) override
+                    const nsString& aDeviceId,
+                    const nsACString& aOrigin) override
   {
     // Nothing to do here, everything is managed in MediaManager.cpp
     return NS_OK;
   }
   nsresult Deallocate() override
   {
     // Nothing to do here, everything is managed in MediaManager.cpp
     return NS_OK;
@@ -90,21 +92,21 @@ public:
   nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override;
   nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
                    const MediaEnginePrefs &aPrefs,
                    const nsString& aDeviceId) override;
   void SetDirectListeners(bool aDirect) override
   {}
   void NotifyOutputData(MediaStreamGraph* aGraph,
                         AudioDataValue* aBuffer, size_t aFrames,
-                        uint32_t aChannels) override
+                        TrackRate aRate, uint32_t aChannels) override
   {}
   void NotifyInputData(MediaStreamGraph* aGraph,
                        const AudioDataValue* aBuffer, size_t aFrames,
-                       uint32_t aChannels) override
+                       TrackRate aRate, uint32_t aChannels) override
   {}
   void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource,
                   TrackID aID, StreamTime aDesiredTime) override
   {}
   dom::MediaSourceEnum GetMediaSource() const override
   {
     return dom::MediaSourceEnum::AudioCapture;
   }
@@ -133,32 +135,32 @@ public:
   // Threadsafe because it's referenced from an MicrophoneSource, which can
   // had references to it on other threads.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInput)
 
   virtual int GetNumOfRecordingDevices(int& aDevices) = 0;
   virtual int GetRecordingDeviceName(int aIndex, char aStrNameUTF8[128],
                                      char aStrGuidUTF8[128]) = 0;
   virtual int GetRecordingDeviceStatus(bool& aIsAvailable) = 0;
-  virtual void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) = 0;
-  virtual void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) = 0;
+  virtual void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) = 0;
+  virtual void StopRecording(SourceMediaStream *aStream) = 0;
   virtual int SetRecordingDevice(int aIndex) = 0;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioInput() {}
 
   webrtc::VoiceEngine* mVoiceEngine;
 };
 
 class AudioInputCubeb final : public AudioInput
 {
 public:
   explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) :
-    AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUse(false)
+    AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0)
   {
     if (!mDeviceIndexes) {
       mDeviceIndexes = new nsTArray<int>;
       mDeviceNames = new nsTArray<nsCString>;
     }
   }
 
   static void CleanupGlobalData()
@@ -209,50 +211,54 @@ public:
   int GetRecordingDeviceStatus(bool& aIsAvailable)
   {
     // With cubeb, we only expose devices of type CUBEB_DEVICE_TYPE_INPUT,
     // so unless it was removed, say it's available
     aIsAvailable = true;
     return 0;
   }
 
-  void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
+  void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener)
   {
     MOZ_ASSERT(mDevices);
 
-    ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
-    ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
-    if (ptrVoERender) {
-      ptrVoERender->SetExternalRecordingStatus(true);
+    if (mInUseCount == 0) {
+      ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
+      ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
+      if (ptrVoERender) {
+        ptrVoERender->SetExternalRecordingStatus(true);
+      }
+      mAnyInUse = true;
     }
-    aGraph->OpenAudioInput(mDevices->device[mSelectedDevice]->devid, aListener);
-    mInUse = true;
-    mAnyInUse = true;
+    mInUseCount++;
+    // Always tell the stream we're using it for input
+    aStream->OpenAudioInput(mDevices->device[mSelectedDevice]->devid, aListener);
   }
 
-  void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
+  void StopRecording(SourceMediaStream *aStream)
   {
-    aGraph->CloseAudioInput(aListener);
-    mInUse = false;
-    mAnyInUse = false;
+    aStream->CloseAudioInput();
+    if (--mInUseCount == 0) {
+      mAnyInUse = false;
+    }
   }
 
   int SetRecordingDevice(int aIndex)
   {
     int32_t devindex = DeviceIndex(aIndex);
     if (!mDevices || devindex < 0) {
       return 1;
     }
     mSelectedDevice = devindex;
     return 0;
   }
 
 protected:
   ~AudioInputCubeb() {
-    MOZ_RELEASE_ASSERT(!mInUse);
+    MOZ_RELEASE_ASSERT(mInUseCount == 0);
   }
 
 private:
   // It would be better to watch for device-change notifications
   void UpdateDeviceList()
   {
     cubeb_device_collection *devices = nullptr;
 
@@ -265,20 +271,24 @@ private:
     for (auto& device_index : (*mDeviceIndexes)) {
       device_index = -1; // unmapped
     }
     // We keep all the device names, but wipe the mappings and rebuild them
 
     // Calculate translation from existing mDevices to new devices. Note we
     // never end up with less devices than before, since people have
     // stashed indexes.
+    // For some reason the "fake" device for automation is marked as DISABLED,
+    // so white-list it.
     for (uint32_t i = 0; i < devices->count; i++) {
       if (devices->device[i]->type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
           (devices->device[i]->state == CUBEB_DEVICE_STATE_ENABLED ||
-           devices->device[i]->state == CUBEB_DEVICE_STATE_UNPLUGGED))
+           devices->device[i]->state == CUBEB_DEVICE_STATE_UNPLUGGED ||
+           (devices->device[i]->state == CUBEB_DEVICE_STATE_DISABLED &&
+            strcmp(devices->device[i]->friendly_name, "Sine source at 440 Hz") == 0)))
       {
         auto j = mDeviceNames->IndexOf(devices->device[i]->device_id);
         if (j != nsTArray<nsCString>::NoIndex) {
           // match! update the mapping
           (*mDeviceIndexes)[j] = i;
         } else {
           // new device, add to the array
           mDeviceIndexes->AppendElement(i);
@@ -295,17 +305,17 @@ private:
 
   // We have an array, which consists of indexes to the current mDevices
   // list.  This is updated on mDevices updates.  Many devices in mDevices
   // won't be included in the array (wrong type, etc), or if a device is
   // removed it will map to -1 (and opens of this device will need to check
   // for this - and be careful of threading access.  The mappings need to
   // updated on each re-enumeration.
   int mSelectedDevice;
-  bool mInUse; // for assertions about listener lifetime
+  uint32_t mInUseCount;
 
   // pointers to avoid static constructors
   static nsTArray<int>* mDeviceIndexes;
   static nsTArray<nsCString>* mDeviceNames;
   static cubeb_device_collection *mDevices;
   static bool mAnyInUse;
 };
 
@@ -342,18 +352,18 @@ public:
     ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
     if (!ptrVoEHw)  {
       return 1;
     }
     ptrVoEHw->GetRecordingDeviceStatus(aIsAvailable);
     return 0;
   }
 
-  void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) {}
-  void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) {}
+  void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {}
+  void StopRecording(SourceMediaStream *aStream) {}
 
   int SetRecordingDevice(int aIndex)
   {
     ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw;
     ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
     if (!ptrVoEHw)  {
       return 1;
     }
@@ -374,25 +384,25 @@ protected:
 public:
   explicit WebRTCAudioDataListener(MediaEngineAudioSource* aAudioSource) :
     mAudioSource(aAudioSource)
   {}
 
   // AudioDataListenerInterface methods
   virtual void NotifyOutputData(MediaStreamGraph* aGraph,
                                 AudioDataValue* aBuffer, size_t aFrames,
-                                uint32_t aChannels) override
+                                TrackRate aRate, uint32_t aChannels) override
   {
-    mAudioSource->NotifyOutputData(aGraph, aBuffer, aFrames, aChannels);
+    mAudioSource->NotifyOutputData(aGraph, aBuffer, aFrames, aRate, aChannels);
   }
   virtual void NotifyInputData(MediaStreamGraph* aGraph,
                                const AudioDataValue* aBuffer, size_t aFrames,
-                               uint32_t aChannels) override
+                               TrackRate aRate, uint32_t aChannels) override
   {
-    mAudioSource->NotifyInputData(aGraph, aBuffer, aFrames, aChannels);
+    mAudioSource->NotifyInputData(aGraph, aBuffer, aFrames, aRate, aChannels);
   }
 
 private:
   RefPtr<MediaEngineAudioSource> mAudioSource;
 };
 
 class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource,
                                           public webrtc::VoEMediaProcess,
@@ -416,51 +426,53 @@ public:
     , mInitDone(false)
     , mStarted(false)
     , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
     , mEchoOn(false), mAgcOn(false), mNoiseOn(false)
     , mEchoCancel(webrtc::kEcDefault)
     , mAGC(webrtc::kAgcDefault)
     , mNoiseSuppress(webrtc::kNsDefault)
     , mPlayoutDelay(0)
-    , mNullTransport(nullptr) {
+    , mNullTransport(nullptr)
+    , mInputBufferLen(0) {
     MOZ_ASSERT(aVoiceEnginePtr);
     MOZ_ASSERT(aAudioInput);
     mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
     mDeviceUUID.Assign(uuid);
     mListener = new mozilla::WebRTCAudioDataListener(this);
     Init();
   }
 
   void GetName(nsAString& aName) override;
   void GetUUID(nsACString& aUUID) override;
 
   nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                     const MediaEnginePrefs& aPrefs,
-                    const nsString& aDeviceId) override;
+                    const nsString& aDeviceId,
+                    const nsACString& aOrigin) override;
   nsresult Deallocate() override;
   nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
   nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
   nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
                    const MediaEnginePrefs &aPrefs,
                    const nsString& aDeviceId) override;
   void SetDirectListeners(bool aHasDirectListeners) override {};
 
   void NotifyPull(MediaStreamGraph* aGraph,
                   SourceMediaStream* aSource,
                   TrackID aId,
                   StreamTime aDesiredTime) override;
 
   // AudioDataListenerInterface methods
   void NotifyOutputData(MediaStreamGraph* aGraph,
                         AudioDataValue* aBuffer, size_t aFrames,
-                        uint32_t aChannels) override;
+                        TrackRate aRate, uint32_t aChannels) override;
   void NotifyInputData(MediaStreamGraph* aGraph,
                        const AudioDataValue* aBuffer, size_t aFrames,
-                       uint32_t aChannels) override;
+                       TrackRate aRate, uint32_t aChannels) override;
 
   bool IsFake() override {
     return false;
   }
 
   dom::MediaSourceEnum GetMediaSource() const override {
     return dom::MediaSourceEnum::Microphone;
   }
@@ -519,16 +531,20 @@ private:
   uint32_t mSampleFrequency;
   bool mEchoOn, mAgcOn, mNoiseOn;
   webrtc::EcModes  mEchoCancel;
   webrtc::AgcModes mAGC;
   webrtc::NsModes  mNoiseSuppress;
   int32_t mPlayoutDelay;
 
   NullTransport *mNullTransport;
+
+  // For full_duplex packetizer output
+  size_t mInputBufferLen;
+  UniquePtr<int16_t[]> mInputBuffer;
 };
 
 class MediaEngineWebRTC : public MediaEngine
 {
 public:
   explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
 
   // Clients should ensure to clean-up sources video/audio sources
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -215,17 +215,18 @@ uint32_t MediaEngineWebRTCMicrophoneSour
     break; // distance is read from first entry only
   }
   return distance;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                             const MediaEnginePrefs &aPrefs,
-                                            const nsString& aDeviceId)
+                                            const nsString& aDeviceId,
+                                            const nsACString& aOrigin)
 {
   AssertIsOnOwningThread();
   if (mState == kReleased) {
     if (mInitDone) {
       if (mAudioInput->SetRecordingDevice(mCapIndex)) {
         return NS_ERROR_FAILURE;
       }
       mState = kAllocated;
@@ -353,41 +354,45 @@ MediaEngineWebRTCMicrophoneSource::Start
   aStream->AddAudioTrack(aID, mSampleFrequency, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
 
   // XXX Make this based on the pref.
   aStream->RegisterForAudioMixing();
   LOG(("Start audio for stream %p", aStream));
 
   if (mState == kStarted) {
     MOZ_ASSERT(aID == mTrackID);
+    // Make sure we're associated with this stream
+    mAudioInput->StartRecording(aStream, mListener);
     return NS_OK;
   }
   mState = kStarted;
   mTrackID = aID;
 
   // Make sure logger starts before capture
   AsyncLatencyLogger::Get(true);
 
   // Register output observer
   // XXX
   MOZ_ASSERT(gFarendObserver);
   gFarendObserver->Clear();
 
   if (mVoEBase->StartReceive(mChannel)) {
     return NS_ERROR_FAILURE;
   }
+
+  // Must be *before* StartSend() so it will notice we selected external input (full_duplex)
+  mAudioInput->StartRecording(aStream, mListener);
+
   if (mVoEBase->StartSend(mChannel)) {
     return NS_ERROR_FAILURE;
   }
 
   // Attach external media processor, so this::Process will be called.
   mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
 
-  mAudioInput->StartRecording(aStream->Graph(), mListener);
-
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
   AssertIsOnOwningThread();
   {
@@ -396,29 +401,30 @@ MediaEngineWebRTCMicrophoneSource::Stop(
     if (!mSources.RemoveElement(aSource)) {
       // Already stopped - this is allowed
       return NS_OK;
     }
 
     aSource->EndTrack(aID);
 
     if (!mSources.IsEmpty()) {
+      mAudioInput->StopRecording(aSource);
       return NS_OK;
     }
     if (mState != kStarted) {
       return NS_ERROR_FAILURE;
     }
     if (!mVoEBase) {
       return NS_ERROR_FAILURE;
     }
 
     mState = kStopped;
   }
 
-  mAudioInput->StopRecording(aSource->Graph(), mListener);
+  mAudioInput->StopRecording(aSource);
 
   mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel);
 
   if (mVoEBase->StopSend(mChannel)) {
     return NS_ERROR_FAILURE;
   }
   if (mVoEBase->StopReceive(mChannel)) {
     return NS_ERROR_FAILURE;
@@ -435,42 +441,49 @@ MediaEngineWebRTCMicrophoneSource::Notif
   // Ignore - we push audio data
   LOG_FRAMES(("NotifyPull, desired = %ld", (int64_t) aDesiredTime));
 }
 
 void
 MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph,
                                                     AudioDataValue* aBuffer,
                                                     size_t aFrames,
+                                                    TrackRate aRate,
                                                     uint32_t aChannels)
 {
 }
 
 // Called back on GraphDriver thread
 void
 MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph,
                                                    const AudioDataValue* aBuffer,
                                                    size_t aFrames,
+                                                   TrackRate aRate,
                                                    uint32_t aChannels)
 {
   // This will call Process() with data coming out of the AEC/NS/AGC/etc chain
   if (!mPacketizer ||
-      mPacketizer->PacketSize() != mSampleFrequency/100 ||
+      mPacketizer->PacketSize() != aRate/100u ||
       mPacketizer->Channels() != aChannels) {
     // It's ok to drop the audio still in the packetizer here.
-    mPacketizer = new AudioPacketizer<AudioDataValue, int16_t>(mSampleFrequency/100, aChannels);
-   }
+    mPacketizer = new AudioPacketizer<AudioDataValue, int16_t>(aRate/100, aChannels);
+  }
 
   mPacketizer->Input(aBuffer, static_cast<uint32_t>(aFrames));
 
   while (mPacketizer->PacketsAvailable()) {
     uint32_t samplesPerPacket = mPacketizer->PacketSize() *
                                 mPacketizer->Channels();
-    int16_t* packet = mPacketizer->Output();
-    mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, mSampleFrequency, 0);
+    if (mInputBufferLen < samplesPerPacket) {
+      mInputBuffer = MakeUnique<int16_t[]>(samplesPerPacket);
+    }
+    int16_t *packet = mInputBuffer.get();
+    mPacketizer->Output(packet);
+
+    mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, aRate, 0);
   }
 }
 
 void
 MediaEngineWebRTCMicrophoneSource::Init()
 {
   mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
 
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -109,16 +109,17 @@ DIRS += [
     'xbl',
     'xml',
     'xslt',
     'xul',
     'resourcestats',
     'manifest',
     'vr',
     'newapps',
+    'u2f',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['plugins/ipc/hangui']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DIRS += [
         'speakermanager',
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_authident.js
@@ -0,0 +1,6 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var am = Cc["@mozilla.org/network/http-auth-manager;1"].
+         getService(Ci.nsIHttpAuthManager);
+am.setAuthIdentity("http", "mochi.test", 8888, "basic", "testrealm", "",
+    "mochi.test", "user1", "password1");
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -1,13 +1,14 @@
 [DEFAULT]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #b2g-desktop(tests that use plugins)
 support-files =
   307-xo-redirect.sjs
   crashing_subpage.html
+  file_authident.js
   file_bug738396.html
   file_bug771202.html
   file_bug863792.html
   large-pic.jpg
   loremipsum.txt
   loremipsum.xtest
   loremipsum.xtest^headers^
   loremipsum_file.txt
@@ -61,17 +62,16 @@ skip-if = (!crashreporter) || true # Bug
 [test_CrashService_crash.html]
 skip-if = !crashreporter || e10s
 [test_CrashService_hang.html]
 skip-if = !crashreporter || e10s
 [test_defaultValue.html]
 [test_enumerate.html]
 [test_fullpage.html]
 [test_getauthenticationinfo.html]
-skip-if = e10s # Bug 1237834
 [test_hanging.html]
 skip-if = !crashreporter || e10s
 [test_instance_re-parent.html]
 skip-if = release_build # Bug 1172627
 [test_instance_unparent1.html]
 [test_instance_unparent2.html]
 [test_instance_unparent3.html]
 [test_instantiation.html]
--- a/dom/plugins/test/mochitest/test_getauthenticationinfo.html
+++ b/dom/plugins/test/mochitest/test_getauthenticationinfo.html
@@ -56,20 +56,26 @@ function iframeLoad() {
     ok(true, "expected exception caught");
   }
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
+// Authentication info is added twice here. In the non-e10s case, this does
+// nothing. In the e10s case, we need to add auth info in both the child process,
+// which the plugin checks for auth validity, and the parent process, which the
+// http network objects use.
+// TODO: Clean this up once HTTPAuthManager is made e10s compliant in bug 1249172
 var iframe = document.getElementById("iframe");
 var am = Cc["@mozilla.org/network/http-auth-manager;1"].
          getService(Ci.nsIHttpAuthManager);
 am.setAuthIdentity("http", "mochi.test", 8888, "basic", "testrealm", "", 
     "mochi.test", "user1", "password1");
+SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("file_authident.js"));
 iframe.onload = iframeLoad;
 iframe.src = "http://mochi.test:8888/tests/toolkit/components/passwordmgr/" +
     "test/authenticate.sjs?user=user1&pass=password1&realm=testrealm&plugin=1";
 
 </script>
 </body>
 </html>
--- a/dom/power/WakeLock.h
+++ b/dom/power/WakeLock.h
@@ -10,17 +10,17 @@
 #include "nsCOMPtr.h"
 #include "nsIDOMEventListener.h"
 #include "nsIObserver.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
 #include "nsWrapperCache.h"
 #include "mozilla/ErrorResult.h"
 
-class nsIDOMWindow;
+class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class ContentParent;
 
 class WakeLock final
   : public nsIDOMEventListener
--- a/dom/svg/test/mochitest.ini
+++ b/dom/svg/test/mochitest.ini
@@ -93,13 +93,15 @@ skip-if = android_version == '18' # bug 
 [test_text.html]
 [test_text_lengthAdjust.html]
 [test_text_scaled.html]
 [test_text_selection.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' # b2g(Mouse selection not workin on b2g) b2g-debug(Mouse selection not workin on b2g) b2g-desktop(Mouse selection not workin on b2g)
 [test_text_update.html]
 [test_transform.xhtml]
 [test_transformParsing.html]
+[test_use_with_hsts.html]
+support-files = use-with-hsts-helper.html use-with-hsts-helper.html^headers^
 [test_valueAsString.xhtml]
 [test_valueLeaks.xhtml]
 [test_viewport.html]
 [test_zoom.xhtml]
 
new file mode 100644
--- /dev/null
+++ b/dom/svg/test/test_use_with_hsts.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1247733
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1247733</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247733">Mozilla Bug 1247733</a>
+<p id="display">
+  <iframe id="myIframe"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test"></pre>
+<script type="application/javascript">
+  /** Test for Bug 1247733 **/
+
+  /**
+   * This test ensures that we render the SVG 'use' element correctly, in
+   * pages that have been upgraded from HTTP to HTTPS using strict transport
+   * security (HSTS)
+   *
+   * Specifically:
+   *  (1) We load a file using HTTPS, in an iframe. The file gets sent
+   *      with a Strict-Transport-Security flag.
+   *  (2) We load the same file again, but now over HTTP (which should get
+   *      upgraded to HTTPS, since we received the Strict-Transport-Security
+   *      flag during the first load).
+   *  (3) After each of the above loads, we take a snapshot of the iframe
+   *      and ensure that it renders as fully lime (which the 'use' element
+   *      is responsible for). If the 'use' element fails to render, the iframe
+   *      will be fully red, and we'll fail an "assertSnapshots" check.
+   */
+  SimpleTest.waitForExplicitFinish();
+
+  const iframe = document.getElementById("myIframe");
+  const iframeWin = iframe.contentWindow;
+
+  // URI for our testcase with 'use' element, via HTTP and HTTPS:
+  const insecureURI = "http://example.com/tests/dom/svg/test/use-with-hsts-helper.html";
+  const secureURI   = "https://example.com/tests/dom/svg/test/use-with-hsts-helper.html";
+
+  // Snapshots that we'll populate below:
+  var blankSnapshot;  // Snapshot of blank iframe.
+  var refSnapshot;    // Snapshot of lime reference rendering in iframe.
+  var secureSnapshot; // Snapshot of testcase using HTTPS.
+  var upgradedSnapshot; // Snapshot of testcase using HTTP-upgraded-to-HTTPS.
+
+  // Bookkeeping to be sure receiveMessage is called as many times as we expect:
+  var numPostMessageCalls = 0;
+  const expectedNumPostMessageCalls = 2; // (We load the helper file twice.)
+
+  // Helper function, called via postMessage, to check iframe's actual location:
+  function receiveMessage(event) {
+    is(event.data, secureURI, "iframe should end up viewing secure URI");
+    numPostMessageCalls++;
+  }
+
+  // TEST CODE BEGINS HERE.
+  // Execution basically proceeds top-to-bottom through the functions
+  // from this point on, via a chain of iframe onload-callbacks.
+  function runTest() {
+    // Capture a snapshot with nothing in the iframe, so we can do a
+    // sanity-check not-equal comparison against our reference case, to be
+    // sure we're rendering anything at all:
+    blankSnapshot = snapshotWindow(iframeWin);
+
+    // Point iframe at a reference case:
+    iframe.onload = captureRefSnapshot;
+    iframe.src = "data:text/html,<body style='background:lime'>";
+  }
+
+  function captureRefSnapshot() {
+    // Capture the reference screenshot:
+    refSnapshot = snapshotWindow(iframeWin);
+
+    // Ensure reference-case looks different from blank snapshot:
+    assertSnapshots(refSnapshot, blankSnapshot,
+                    false /*not equal*/, null /*no fuzz*/,
+                    "refSnapshot", "blankSnapshot");
+
+    // OK, assuming we've got a valid refSnapshot, we can now proceed to
+    // capture test screenshots.
+
+    // Register a postMessage handler, so that iframe can report its location:
+    window.addEventListener("message", receiveMessage, false);
+
+    // Point iframe at secure (HTTPS) version of testcase, & wait for callback:
+    iframe.onload = captureSecureSnapshot;
+    iframe.src = secureURI;
+  }
+
+  function captureSecureSnapshot() {
+    // Capture snapshot of iframe showing always-HTTPS version of testcase:
+    secureSnapshot = snapshotWindow(iframeWin);
+    assertSnapshots(secureSnapshot, refSnapshot,
+                    true /*equal*/, null /*no fuzz*/,
+                    "secureSnapshot", "refSnapshot");
+
+    // Point iframe at insecure (HTTP) version of testcase (which should get
+    // automatically upgraded to secure (HTTPS) under the hood), & wait for
+    // callback:
+    iframe.onload = captureUpgradedSnapshot;
+    iframe.src = insecureURI;
+  }
+
+  function captureUpgradedSnapshot() {
+    // Double-check that iframe is really pointed at insecure URI, to be sure
+    // we're actually exercising HSTS. (Note that receiveMessage() will make
+    // sure it's been upgraded to a secure HTTPS URI under the hood.)
+    is(iframe.src, insecureURI,
+       "test should've attempted to load insecure HTTP URI, to exercise HSTS");
+
+    // Capture snapshot of iframe showing upgraded-to-HTTPS version of testcase:
+    upgradedSnapshot = snapshotWindow(iframeWin);
+    assertSnapshots(upgradedSnapshot, refSnapshot,
+                    true /*equal*/, null /*no fuzz*/,
+                    "upgradedSnapshot", "refSnapshot");
+    cleanupAndFinish();
+  }
+
+  function cleanupAndFinish() {
+    is(numPostMessageCalls, expectedNumPostMessageCalls,
+      "didn't receive as many messages from child iframe as expected");
+    SpecialPowers.cleanUpSTSData("http://example.com");
+    SimpleTest.finish();
+  }
+
+  runTest();
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/svg/test/use-with-hsts-helper.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script>
+    // Notify parent of our final URI:
+    window.parent.postMessage(window.location.href, "*");
+  </script>
+  <style>
+    html, body {
+      margin: 0;
+      height: 100%;
+    }
+    svg {
+      display: block;
+      height: 100%;
+    }
+  </style>
+</head>
+<body>
+  <svg xmlns="http://www.w3.org/2000/svg"
+       xmlns:xlink="http://www.w3.org/1999/xlink"
+       version="1.1">
+    <defs>
+      <rect id="limeRect" width="100%" height="100%" fill="lime"/>
+    </defs>
+    <rect width="100%" height="100%" fill="red"/>
+    <use xlink:href="#limeRect"/>
+  </svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/svg/test/use-with-hsts-helper.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Strict-Transport-Security: max-age=60
--- a/dom/tests/mochitest/fetch/test_fetch_basic_http.js
+++ b/dom/tests/mochitest/fetch/test_fetch_basic_http.js
@@ -164,17 +164,17 @@ function testFormDataSend() {
         if (entry.headers['Content-Disposition'] != 'form-data; name="string"') {
           is(entry.headers['Content-Type'], 'text/plain');
         }
 
         is(entry.body, 'hey');
       }
 
       is(response[1].headers['Content-Disposition'],
-          'form-data; name="empty"; filename=""');
+          'form-data; name="empty"; filename="blob"');
 
       is(response[2].headers['Content-Disposition'],
           'form-data; name="explicit"; filename="explicit-file-name"');
 
       is(response[3].headers['Content-Disposition'],
           'form-data; name="explicit-empty"; filename=""');
 
       is(response[4].headers['Content-Disposition'],
--- a/dom/tests/mochitest/fetch/test_request.js
+++ b/dom/tests/mochitest/fetch/test_request.js
@@ -339,16 +339,17 @@ function testFormDataBodyCreation() {
   f1.append("blob", new Blob([text]));
   var r2 = new Request("", { method: 'post', body: f1 });
   f1.delete("key");
   var p2 = r2.formData().then(function(fd) {
     ok(fd instanceof FormData, "Valid FormData extracted.");
     ok(fd.has("more"), "more should exist.");
 
     var b = fd.get("blob");
+    ok(b.name, "blob", "blob entry should be a Blob.");
     ok(b instanceof Blob, "blob entry should be a Blob.");
 
     return readAsText(b).then(function(output) {
       is(output, text, "Blob contents should match.");
     });
   });
 
   return Promise.all([p1, p2]);
@@ -412,27 +413,29 @@ function testFormDataBodyExtraction() {
   f1.append("blob", new Blob([text]));
   var r2 = new Request("", { method: 'post', body: f1 });
   var p2 = r2.formData().then(function(fd) {
     ok(fd.has("key"), "Has entry 'key'.");
     ok(fd.has("foo"), "Has entry 'foo'.");
     ok(fd.has("blob"), "Has entry 'blob'.");
     var entries = fd.getAll("blob");
     is(entries.length, 1, "getAll returns all items.");
+    is(entries[0].name, "blob", "Filename should be blob.");
     ok(entries[0] instanceof Blob, "getAll returns blobs.");
   });
 
   var ws = "\r\n\r\n\r\n\r\n";
   f1.set("key", new File([ws], 'file name has spaces.txt', { type: 'new/lines' }));
   var r3 = new Request("", { method: 'post', body: f1 });
   var p3 = r3.formData().then(function(fd) {
     ok(fd.has("foo"), "Has entry 'foo'.");
     ok(fd.has("blob"), "Has entry 'blob'.");
     var entries = fd.getAll("blob");
     is(entries.length, 1, "getAll returns all items.");
+    is(entries[0].name, "blob", "Filename should be blob.");
     ok(entries[0] instanceof Blob, "getAll returns blobs.");
 
     ok(fd.has("key"), "Has entry 'key'.");
     var f = fd.get("key");
     ok(f instanceof File, "entry should be a File.");
     is(f.name, "file name has spaces.txt", "File name should match.");
     is(f.type, "new/lines", "File type should match.");
     is(f.size, ws.length, "File size should match.");
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1373,16 +1373,18 @@ var interfaceNamesInGlobalScope =
     {name: "TVProgram", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TVScanningStateChangedEvent", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TVSource", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TVTuner", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "U2F", release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "UDPMessageEvent", b2g: true, permission: ["udp-socket"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "UDPSocket", b2g: true, permission: ["udp-socket"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UIEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UndoManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/u2f/NSSToken.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "NSSToken.h"
+
+#include "nsNSSComponent.h"
+#include "pk11pub.h"
+
+namespace mozilla {
+namespace dom {
+
+const nsString NSSToken::mVersion = NS_LITERAL_STRING("U2F_V2");
+
+const uint32_t kParamLen = 32;
+const uint32_t kPublicKeyLen = 65;
+const uint32_t kSignedDataLen = (2 * kParamLen) + 1 + 4;
+
+NSSToken::NSSToken()
+  : mInitialized(false)
+  , mMutex("NSSToken::mMutex")
+{}
+
+NSSToken::~NSSToken()
+{
+  nsNSSShutDownPreventionLock locker;
+
+  if (isAlreadyShutDown()) {
+    return;
+  }
+
+  destructorSafeDestroyNSSReference();
+  shutdown(calledFromObject);
+}
+
+void
+NSSToken::virtualDestroyNSSReference()
+{
+  destructorSafeDestroyNSSReference();
+}
+
+void
+NSSToken::destructorSafeDestroyNSSReference()
+{
+  mSlot = nullptr;
+}
+
+nsresult
+NSSToken::Init()
+{
+  MOZ_ASSERT(!mInitialized);
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  MutexAutoLock lock(mMutex);
+
+  if (!EnsureNSSInitializedChromeOrContent()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSlot = PK11_GetInternalSlot();
+  if (!mSlot.get()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mInitialized = true;
+  return NS_OK;
+}
+
+bool
+NSSToken::IsCompatibleVersion(const nsString& aVersionParam) const
+{
+  MOZ_ASSERT(mInitialized);
+  return mVersion == aVersionParam;
+}
+
+/*
+ * IsRegistered determines if the provided key handle is usable by this token.
+ */
+bool
+NSSToken::IsRegistered(const CryptoBuffer& aKeyHandle) const
+{
+  MOZ_ASSERT(mInitialized);
+  return false;
+}
+
+/*
+ * A U2F Register operation causes a new key pair to be generated by the token.
+ * The token then returns the public key of the key pair, and a handle to the
+ * private key.  The input parameters are used only for attestation, which this
+ * token does not provide.  (We'll see how that works!)
+ *
+ * The format of the return registration data is as follows:
+ *
+ * Bytes  Value
+ *   1    0x05
+ *  65    public key
+ *   1    key handle length
+ *   *    key handle
+ *   *    attestation certificate (omitted for now)
+ *   *    attestation signature (omitted for now)
+ *
+ */
+nsresult
+NSSToken::Register(const CryptoBuffer& /* aChallengeParam */,
+                   const CryptoBuffer& /* aApplicationParam */,
+                   CryptoBuffer& aRegistrationData)
+{
+  MOZ_ASSERT(mInitialized);
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mInitialized) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  return NS_OK;
+}
+
+/*
+ * A U2F Sign operation creates a signature over the "param" arguments (plus
+ * some other stuff) using the private key indicated in the key handle argument.
+ *
+ * The format of the signed data is as follows:
+ *
+ *  32    Application parameter
+ *   1    User presence (0x01)
+ *   4    Counter
+ *  32    Challenge parameter
+ *
+ * The format of the signature data is as follows:
+ *
+ *   1    User presence
+ *   4    Counter
+ *   *    Signature
+ *
+ */
+nsresult
+NSSToken::Sign(const CryptoBuffer& aApplicationParam,
+               const CryptoBuffer& aChallengeParam,
+               const CryptoBuffer& aKeyHandle,
+               CryptoBuffer& aSignatureData)
+{
+  MOZ_ASSERT(mInitialized);
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mInitialized) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/u2f/NSSToken.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_NSSToken_h
+#define mozilla_dom_NSSToken_h
+
+#include "mozilla/dom/CryptoBuffer.h"
+#include "mozilla/Mutex.h"
+#include "nsNSSShutDown.h"
+#include "ScopedNSSTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+// NSSToken will support FIDO U2F operations using NSS for the crypto layer.
+// This is a stub. It will be implemented in bug 1244960.
+class NSSToken final : public nsNSSShutDownObject
+{
+public:
+  NSSToken();
+
+  ~NSSToken();
+
+  nsresult Init();
+
+  bool IsCompatibleVersion(const nsString& aVersionParam) const;
+
+  bool IsRegistered(const CryptoBuffer& aKeyHandle) const;
+
+  nsresult Register(const CryptoBuffer& aApplicationParam,
+                    const CryptoBuffer& aChallengeParam,
+                    CryptoBuffer& aRegistrationData);
+
+  nsresult Sign(const CryptoBuffer& aApplicationParam,
+                const CryptoBuffer& aChallengeParam,
+                const CryptoBuffer& aKeyHandle,
+                CryptoBuffer& aSignatureData);
+
+  // For nsNSSShutDownObject
+  virtual void virtualDestroyNSSReference() override;
+  void destructorSafeDestroyNSSReference();
+
+private:
+  bool mInitialized;
+  ScopedPK11SlotInfo mSlot;
+  mozilla::Mutex mMutex;
+
+  static const nsString mVersion;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_NSSToken_h
new file mode 100644
--- /dev/null
+++ b/dom/u2f/U2F.cpp
@@ -0,0 +1,583 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/dom/CryptoBuffer.h"
+#include "mozilla/dom/U2F.h"
+#include "mozilla/dom/U2FBinding.h"
+#include "mozilla/Preferences.h"
+#include "nsContentUtils.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsURLParsers.h"
+#include "nsNetCID.h"
+#include "pk11pub.h"
+
+namespace mozilla {
+namespace dom {
+
+// These enumerations are defined in the FIDO U2F Javascript API under the
+// interface "ErrorCode" as constant integers, and thus in the U2F.webidl file.
+// Any changes to these must occur in both locations.
+enum class ErrorCode {
+  OK = 0,
+  OTHER_ERROR = 1,
+  BAD_REQUEST = 2,
+  CONFIGURATION_UNSUPPORTED = 3,
+  DEVICE_INELIGIBLE = 4,
+  TIMEOUT = 5
+};
+
+#define PREF_U2F_SOFTTOKEN_ENABLED "security.webauth.u2f.softtoken"
+#define PREF_U2F_USBTOKEN_ENABLED  "security.webauth.u2f.usbtoken"
+
+const nsString
+U2F::FinishEnrollment = NS_LITERAL_STRING("navigator.id.finishEnrollment");
+
+const nsString
+U2F::GetAssertion = NS_LITERAL_STRING("navigator.id.getAssertion");
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
+
+U2F::U2F()
+{}
+
+U2F::~U2F()
+{
+  nsNSSShutDownPreventionLock locker;
+
+  if (isAlreadyShutDown()) {
+    return;
+  }
+  shutdown(calledFromObject);
+}
+
+/* virtual */ JSObject*
+U2F::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return U2FBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+U2F::Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv)
+{
+  MOZ_ASSERT(!mParent);
+  mParent = do_QueryInterface(aParent);
+  MOZ_ASSERT(mParent);
+
+  nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
+  MOZ_ASSERT(doc);
+
+  nsIPrincipal* principal = doc->NodePrincipal();
+  aRv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  if (NS_WARN_IF(mOrigin.IsEmpty())) {
+    return;
+  }
+
+  if (!EnsureNSSInitializedChromeOrContent()) {
+    return;
+  }
+
+  aRv = mSoftToken.Init();
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  aRv = mUSBToken.Init();
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+}
+
+nsresult
+U2F::AssembleClientData(const nsAString& aTyp,
+                        const nsAString& aChallenge,
+                        CryptoBuffer& aClientData) const
+{
+  ClientData clientDataObject;
+  clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
+  clientDataObject.mChallenge.Construct(aChallenge);
+  clientDataObject.mOrigin.Construct(mOrigin);
+
+  nsAutoString json;
+  if (NS_WARN_IF(!clientDataObject.ToJSON(json))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(!aClientData.Assign(NS_ConvertUTF16toUTF8(json)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+bool
+U2F::ValidAppID(/* in/out */ nsString& aAppId) const
+{
+  nsCOMPtr<nsIURLParser> urlParser =
+      do_GetService(NS_STDURLPARSER_CONTRACTID);
+  nsCOMPtr<nsIEffectiveTLDService> tldService =
+      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+
+  MOZ_ASSERT(urlParser);
+  MOZ_ASSERT(tldService);
+
+  uint32_t facetSchemePos;
+  int32_t facetSchemeLen;
+  uint32_t facetAuthPos;
+  int32_t facetAuthLen;
+  // Facet is the specification's way of referring to the web origin.
+  nsAutoCString facetUrl = NS_ConvertUTF16toUTF8(mOrigin);
+  nsresult rv = urlParser->ParseURL(facetUrl.get(), mOrigin.Length(),
+                                    &facetSchemePos, &facetSchemeLen,
+                                    &facetAuthPos, &facetAuthLen,
+                                    nullptr, nullptr);      // ignore path
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoCString facetScheme(Substring(facetUrl, facetSchemePos, facetSchemeLen));
+  nsAutoCString facetAuth(Substring(facetUrl, facetAuthPos, facetAuthLen));
+
+  uint32_t appIdSchemePos;
+  int32_t appIdSchemeLen;
+  uint32_t appIdAuthPos;
+  int32_t appIdAuthLen;
+  nsAutoCString appIdUrl = NS_ConvertUTF16toUTF8(aAppId);
+  rv = urlParser->ParseURL(appIdUrl.get(), aAppId.Length(),
+                           &appIdSchemePos, &appIdSchemeLen,
+                           &appIdAuthPos, &appIdAuthLen,
+                           nullptr, nullptr);      // ignore path
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoCString appIdScheme(Substring(appIdUrl, appIdSchemePos, appIdSchemeLen));
+  nsAutoCString appIdAuth(Substring(appIdUrl, appIdAuthPos, appIdAuthLen));
+
+  // If the facetId (origin) is not HTTPS, reject
+  if (!facetScheme.LowerCaseEqualsLiteral("https")) {
+    return false;
+  }
+
+  // If the appId is empty or null, overwrite it with the facetId and accept
+  if (aAppId.IsEmpty() || aAppId.EqualsLiteral("null")) {
+    aAppId.Assign(mOrigin);
+    return true;
+  }
+
+  // if the appId URL is not HTTPS, reject.
+  if (!appIdScheme.LowerCaseEqualsLiteral("https")) {
+    return false;
+  }
+
+  // If the facetId and the appId auths match, accept
+  if (facetAuth == appIdAuth) {
+    return true;
+  }
+
+  nsAutoCString appIdTld;
+  nsAutoCString facetTld;
+
+  rv = tldService->GetBaseDomainFromHost(appIdAuth, 0, appIdTld);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+  rv = tldService->GetBaseDomainFromHost(facetAuth, 0, facetTld);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  // If this AppID's registered domain matches the Facet's, accept
+  if (!facetTld.IsEmpty() && !appIdTld.IsEmpty() &&
+      (facetTld == appIdTld)) {
+    return true;
+  }
+
+  // TODO(Bug 1244959) Implement the remaining algorithm.
+  return false;
+}
+
+template <class CB, class Rsp>
+void
+SendError(CB& aCallback, ErrorCode aErrorCode)
+{
+  Rsp response;
+  response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
+
+  ErrorResult rv;
+  aCallback.Call(response, rv);
+  NS_WARN_IF(rv.Failed());
+  // Useful exceptions already got reported.
+  rv.SuppressException();
+}
+
+void
+U2F::Register(const nsAString& aAppId,
+              const Sequence<RegisterRequest>& aRegisterRequests,
+              const Sequence<RegisteredKey>& aRegisteredKeys,
+              U2FRegisterCallback& aCallback,
+              const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
+              ErrorResult& aRv)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                     ErrorCode::OTHER_ERROR);
+    return;
+  }
+
+  const bool softTokenEnabled =
+    Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
+
+  const bool usbTokenEnabled =
+    Preferences::GetBool(PREF_U2F_USBTOKEN_ENABLED);
+
+  nsAutoString appId(aAppId);
+
+  // Verify the global appId first.
+  if (!ValidAppID(appId)) {
+    SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                     ErrorCode::BAD_REQUEST);
+    return;
+  }
+
+  for (size_t i = 0; i < aRegisteredKeys.Length(); ++i) {
+    RegisteredKey request(aRegisteredKeys[i]);
+
+    // Check for equired attributes
+    if (!(request.mKeyHandle.WasPassed() &&
+          request.mVersion.WasPassed())) {
+      continue;
+    }
+
+    // Verify the appId for this Registered Key, if set
+    if (request.mAppId.WasPassed() &&
+        !ValidAppID(request.mAppId.Value())) {
+      continue;
+    }
+
+    // Decode the key handle
+    CryptoBuffer keyHandle;
+    nsresult rv = keyHandle.FromJwkBase64(request.mKeyHandle.Value());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                       ErrorCode::BAD_REQUEST);
+      return;
+    }
+
+    // We ignore mTransports, as it is intended to be used for sorting the
+    // available devices by preference, but is not an exclusion factor.
+
+    // Determine if the provided keyHandle is registered at any device. If so,
+    // then we'll return DEVICE_INELIGIBLE to signify we're already registered.
+    if (usbTokenEnabled &&
+        mUSBToken.IsCompatibleVersion(request.mVersion.Value()) &&
+        mUSBToken.IsRegistered(keyHandle)) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                  ErrorCode::DEVICE_INELIGIBLE);
+      return;
+    }
+
+    if (softTokenEnabled &&
+        mSoftToken.IsCompatibleVersion(request.mVersion.Value()) &&
+        mSoftToken.IsRegistered(keyHandle)) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                  ErrorCode::DEVICE_INELIGIBLE);
+      return;
+    }
+  }
+
+  // Search the requests in order for the first some token can fulfill
+  for (size_t i = 0; i < aRegisterRequests.Length(); ++i) {
+    RegisterRequest request(aRegisterRequests[i]);
+
+    // Check for equired attributes
+    if (!(request.mVersion.WasPassed() &&
+        request.mChallenge.WasPassed())) {
+      continue;
+    }
+
+    CryptoBuffer clientData;
+    nsresult rv = AssembleClientData(FinishEnrollment,
+                                     request.mChallenge.Value(),
+                                     clientData);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                       ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    // Hash the AppID and the ClientData into the AppParam and ChallengeParam
+    SECStatus srv;
+    nsCString cAppId = NS_ConvertUTF16toUTF8(appId);
+    CryptoBuffer appParam;
+    CryptoBuffer challengeParam;
+    if (!appParam.SetLength(SHA256_LENGTH, fallible) ||
+        !challengeParam.SetLength(SHA256_LENGTH, fallible)) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                       ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(),
+                       reinterpret_cast<const uint8_t*>(cAppId.BeginReading()),
+                       cAppId.Length());
+    if (srv != SECSuccess) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                       ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    srv = PK11_HashBuf(SEC_OID_SHA256, challengeParam.Elements(),
+                       clientData.Elements(), clientData.Length());
+    if (srv != SECSuccess) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                       ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    // Get the registration data from the token
+    CryptoBuffer registrationData;
+    bool registerSuccess = false;
+
+    if (usbTokenEnabled &&
+        mUSBToken.IsCompatibleVersion(request.mVersion.Value())) {
+      rv = mUSBToken.Register(opt_aTimeoutSeconds, challengeParam,
+                              appParam, registrationData);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                      ErrorCode::OTHER_ERROR);
+        return;
+      }
+      registerSuccess = true;
+    }
+
+    if (!registerSuccess && softTokenEnabled &&
+        mSoftToken.IsCompatibleVersion(request.mVersion.Value())) {
+      rv = mSoftToken.Register(challengeParam, appParam, registrationData);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                        ErrorCode::OTHER_ERROR);
+        return;
+      }
+      registerSuccess = true;
+    }
+
+    if (!registerSuccess) {
+      // Try another request
+      continue;
+    }
+
+    // Assemble a response object to return
+    nsString clientDataBase64, registrationDataBase64;
+    nsresult rvClientData =
+      clientData.ToJwkBase64(clientDataBase64);
+    nsresult rvRegistrationData =
+      registrationData.ToJwkBase64(registrationDataBase64);
+    if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
+        NS_WARN_IF(NS_FAILED(rvRegistrationData))) {
+      SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                       ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    RegisterResponse response;
+    response.mClientData.Construct(clientDataBase64);
+    response.mRegistrationData.Construct(registrationDataBase64);
+    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
+
+    ErrorResult result;
+    aCallback.Call(response, result);
+    NS_WARN_IF(result.Failed());
+    // Useful exceptions already got reported.
+    result.SuppressException();
+    return;
+  }
+
+  // Nothing could satisfy
+  SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
+                                                   ErrorCode::BAD_REQUEST);
+  return;
+}
+
+void
+U2F::Sign(const nsAString& aAppId,
+          const nsAString& aChallenge,
+          const Sequence<RegisteredKey>& aRegisteredKeys,
+          U2FSignCallback& aCallback,
+          const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
+          ErrorResult& aRv)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    SendError<U2FSignCallback, SignResponse>(aCallback,
+                                             ErrorCode::OTHER_ERROR);
+    return;
+  }
+
+  const bool softTokenEnabled =
+    Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
+
+  const bool usbTokenEnabled =
+    Preferences::GetBool(PREF_U2F_USBTOKEN_ENABLED);
+
+  nsAutoString appId(aAppId);
+
+  // Verify the global appId first.
+  if (!ValidAppID(appId)) {
+    SendError<U2FSignCallback, SignResponse>(aCallback,
+                                             ErrorCode::BAD_REQUEST);
+    return;
+  }
+
+  // Search the requests for one a token can fulfill
+  for (size_t i = 0; i < aRegisteredKeys.Length(); i += 1) {
+    RegisteredKey request(aRegisteredKeys[i]);
+
+    // Check for required attributes
+    if (!(request.mVersion.WasPassed() &&
+          request.mKeyHandle.WasPassed())) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      continue;
+    }
+
+    // Allow an individual RegisteredKey to assert a different AppID
+    nsAutoString regKeyAppId(appId);
+    if (request.mAppId.WasPassed()) {
+      regKeyAppId.Assign(request.mAppId.Value());
+      if (!ValidAppID(regKeyAppId)) {
+        continue;
+      }
+    }
+
+    // Assemble a clientData object
+    CryptoBuffer clientData;
+    nsresult rv = AssembleClientData(GetAssertion, aChallenge, clientData);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    // Hash the AppID and the ClientData into the AppParam and ChallengeParam
+    SECStatus srv;
+    nsCString cAppId = NS_ConvertUTF16toUTF8(regKeyAppId);
+    CryptoBuffer appParam;
+    CryptoBuffer challengeParam;
+    if (!appParam.SetLength(SHA256_LENGTH, fallible) ||
+        !challengeParam.SetLength(SHA256_LENGTH, fallible)) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(),
+                       reinterpret_cast<const uint8_t*>(cAppId.BeginReading()),
+                       cAppId.Length());
+    if (srv != SECSuccess) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    srv = PK11_HashBuf(SEC_OID_SHA256, challengeParam.Elements(),
+                       clientData.Elements(), clientData.Length());
+    if (srv != SECSuccess) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    // Decode the key handle
+    CryptoBuffer keyHandle;
+    rv = keyHandle.FromJwkBase64(request.mKeyHandle.Value());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      return;
+    }
+
+    // Get the signature from the token
+    CryptoBuffer signatureData;
+    bool signSuccess = false;
+
+    // We ignore mTransports, as it is intended to be used for sorting the
+    // available devices by preference, but is not an exclusion factor.
+
+    if (usbTokenEnabled &&
+        mUSBToken.IsCompatibleVersion(request.mVersion.Value())) {
+      rv = mUSBToken.Sign(opt_aTimeoutSeconds, appParam, challengeParam,
+                          keyHandle, signatureData);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        SendError<U2FSignCallback, SignResponse>(aCallback,
+                                                 ErrorCode::OTHER_ERROR);
+        return;
+      }
+      signSuccess = true;
+    }
+
+    if (!signSuccess && softTokenEnabled &&
+        mSoftToken.IsCompatibleVersion(request.mVersion.Value())) {
+      rv = mSoftToken.Sign(appParam, challengeParam, keyHandle, signatureData);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        SendError<U2FSignCallback, SignResponse>(aCallback,
+                                                 ErrorCode::OTHER_ERROR);
+        return;
+      }
+      signSuccess = true;
+    }
+
+    if (!signSuccess) {
+      // Try another request
+      continue;
+    }
+
+    // Assemble a response object to return
+    nsString clientDataBase64, signatureDataBase64;
+    nsresult rvClientData =
+      clientData.ToJwkBase64(clientDataBase64);
+    nsresult rvSignatureData =
+      signatureData.ToJwkBase64(signatureDataBase64);
+    if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
+        NS_WARN_IF(NS_FAILED(rvSignatureData))) {
+      SendError<U2FSignCallback, SignResponse>(aCallback,
+                                               ErrorCode::OTHER_ERROR);
+      return;
+    }
+    SignResponse response;
+    response.mKeyHandle.Construct(request.mKeyHandle.Value());
+    response.mClientData.Construct(clientDataBase64);
+    response.mSignatureData.Construct(signatureDataBase64);
+    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
+
+    ErrorResult result;
+    aCallback.Call(response, result);
+    NS_WARN_IF(result.Failed());
+    // Useful exceptions already got reported.
+    result.SuppressException();
+    return;
+  }
+
+  // Nothing could satisfy
+  SendError<U2FSignCallback, SignResponse>(aCallback,
+                                           ErrorCode::DEVICE_INELIGIBLE);
+  return;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/u2f/U2F.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_U2F_h
+#define mozilla_dom_U2F_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsPIDOMWindow.h"
+#include "nsWrapperCache.h"
+
+#include "NSSToken.h"
+#include "USBToken.h"
+
+namespace mozilla {
+namespace dom {
+
+struct RegisterRequest;
+struct RegisteredKey;
+class U2FRegisterCallback;
+class U2FSignCallback;
+
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+
+class U2F final : public nsISupports,
+                  public nsWrapperCache,
+                  public nsNSSShutDownObject
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(U2F)
+
+  U2F();
+
+  nsPIDOMWindowInner*
+  GetParentObject() const
+  {
+    return mParent;
+  }
+
+  void
+  Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv);
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  void
+  Register(const nsAString& aAppId,
+           const Sequence<RegisterRequest>& aRegisterRequests,
+           const Sequence<RegisteredKey>& aRegisteredKeys,
+           U2FRegisterCallback& aCallback,
+           const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
+           ErrorResult& aRv);
+
+  void
+  Sign(const nsAString& aAppId,
+       const nsAString& aChallenge,
+       const Sequence<RegisteredKey>& aRegisteredKeys,
+       U2FSignCallback& aCallback,
+       const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
+       ErrorResult& aRv);
+
+  // No NSS resources to release.
+  virtual
+  void virtualDestroyNSSReference() override {};
+
+private:
+  nsCOMPtr<nsPIDOMWindowInner> mParent;
+  nsString mOrigin;
+  NSSToken mSoftToken;
+  USBToken mUSBToken;
+
+  static const nsString FinishEnrollment;
+  static const nsString GetAssertion;
+
+  ~U2F();
+
+  nsresult
+  AssembleClientData(const nsAString& aTyp,
+                     const nsAString& aChallenge,
+                     CryptoBuffer& aClientData) const;
+
+  // ValidAppID determines whether the supplied FIDO AppID is valid for
+  // the current FacetID, e.g., the current origin. If the supplied
+  // aAppId param is null or empty, it will be filled in per the algorithm.
+  // See https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-appid-and-facets.html
+  // for a description of the algorithm.
+  bool
+  ValidAppID(/* in/out */ nsString& aAppId) const;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_U2F_h
new file mode 100644
--- /dev/null
+++ b/dom/u2f/USBToken.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "USBToken.h"
+
+namespace mozilla {
+namespace dom {
+
+USBToken::USBToken()
+  : mInitialized(false)
+{}
+
+USBToken::~USBToken()
+{}
+
+nsresult
+USBToken::Init()
+{
+  // This routine does nothing at present, but Bug 1245527 will
+  // integrate the upcoming USB HID service here, which will likely
+  // require an initialization upon load.
+  MOZ_ASSERT(!mInitialized);
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  mInitialized = true;
+  return NS_OK;
+}
+
+const nsString USBToken::mVersion = NS_LITERAL_STRING("U2F_V2");
+
+bool
+USBToken::IsCompatibleVersion(const nsString& aVersionParam) const
+{
+  MOZ_ASSERT(mInitialized);
+  return mVersion == aVersionParam;
+}
+
+bool
+USBToken::IsRegistered(const CryptoBuffer& aKeyHandle) const
+{
+  MOZ_ASSERT(mInitialized);
+  return false;
+}
+
+nsresult
+USBToken::Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
+                   const CryptoBuffer& /* aChallengeParam */,
+                   const CryptoBuffer& /* aApplicationParam */,
+                   CryptoBuffer& aRegistrationData) const
+{
+  MOZ_ASSERT(mInitialized);
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+USBToken::Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
+               const CryptoBuffer& aApplicationParam,
+               const CryptoBuffer& aChallengeParam,
+               const CryptoBuffer& aKeyHandle,
+               CryptoBuffer& aSignatureData) const
+{
+  MOZ_ASSERT(mInitialized);
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/u2f/USBToken.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_USBToken_h
+#define mozilla_dom_USBToken_h
+
+#include "mozilla/dom/CryptoBuffer.h"
+
+namespace mozilla {
+namespace dom {
+
+// USBToken implements FIDO operations using a USB device.
+class USBToken final
+{
+public:
+  USBToken();
+
+  ~USBToken();
+
+  nsresult Init();
+
+  bool IsCompatibleVersion(const nsString& aVersionParam) const;
+
+  bool IsRegistered(const CryptoBuffer& aKeyHandle) const;
+
+  nsresult Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
+                    const CryptoBuffer& aApplicationParam,
+                    const CryptoBuffer& aChallengeParam,
+                    CryptoBuffer& aRegistrationData) const;
+
+  nsresult Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
+                const CryptoBuffer& aApplicationParam,
+                const CryptoBuffer& aChallengeParam,
+                const CryptoBuffer& aKeyHandle,
+                CryptoBuffer& aSignatureData) const;
+
+private:
+  bool mInitialized;
+
+  static const nsString mVersion;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_USBToken_h
new file mode 100644
--- /dev/null
+++ b/dom/u2f/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; c-basic-offset: 4; 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/.
+
+EXPORTS.mozilla.dom += [
+    'NSSToken.h',
+    'U2F.h',
+    'USBToken.h',
+]
+
+UNIFIED_SOURCES += [
+    'NSSToken.cpp',
+    'U2F.cpp',
+    'USBToken.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+    '/dom/base',
+    '/dom/crypto',
+    '/security/manager/ssl',
+    '/security/pkix/include',
+]
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList-good
@@ -0,0 +1,8 @@
+{
+  "trustedFacets" : [{
+    "version": { "major": 1, "minor" : 0 },
+    "ids": [
+     "https://fido.example.com"
+    ]
+  }]
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList-good^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/fido.trusted-apps+json
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList-invalid_format
@@ -0,0 +1,6 @@
+# This file isn't actually JSON, so it shouldn't successfully parse.
+{
+  "trustedFacets" : [{
+    "version": { "major": 1, "minor" : 0 },
+  },{}]
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList-invalid_format^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/fido.trusted-apps+json
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList-no_overlap
@@ -0,0 +1,9 @@
+{
+  "trustedFacets" : [{
+    "version": { "major": 1, "minor" : 0 },
+    "ids": [
+     "https://example.net",
+     "http://www.example.com"
+    ]
+  }]
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList-no_overlap^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/fido.trusted-apps+json
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/facet/facetList.txt
@@ -0,0 +1,8 @@
+{
+  "trustedFacets" : [{
+    "version": { "major": 1, "minor" : 0 },
+    "ids": [
+     "https://fido.example.com"
+    ]
+  }]
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/mochitest.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+support-files =
+  u2futil.js
+  test_frame_appid_facet.html
+  test_frame_register.html
+  test_frame_appid_facet_remoteload.html
+  test_frame_appid_facet_insecure.html
+  test_frame_appid_facet_subdomain.html
+  facet/facetList.txt
+  facet/facetList-good
+  facet/facetList-good^headers^
+  facet/facetList-no_overlap
+  facet/facetList-no_overlap^headers^
+  facet/facetList-invalid_format
+  facet/facetList-invalid_format^headers^
+
+[test_util_methods.html]
+[test_no_token.html]
+[test_frame.html]
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_frame.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+<p id="display"></p>
+
+<div id="content" style="display: none"></div>
+
+<div id="framediv">
+  <iframe id="testing_frame"></iframe>
+</div>
+
+<pre id="log"></pre>
+
+
+<script class="testbody" type="text/javascript">
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+
+var testList = [
+  "https://example.com/tests/dom/u2f/tests/test_frame_register.html",
+  "http://mochi.test:8888/tests/dom/u2f/tests/test_frame_appid_facet_insecure.html",
+  "https://example.com/tests/dom/u2f/tests/test_frame_appid_facet.html",
+  "https://example.com/tests/dom/u2f/tests/test_frame_appid_facet_remoteload.html",
+  "https://test1.example.com/tests/dom/u2f/tests/test_frame_appid_facet_subdomain.html"
+];
+
+function log(msg) {
+  document.getElementById("log").textContent += "\n" + msg;
+}
+
+function nextTest() {
+  if (testList.length < 1) {
+    SimpleTest.finish();
+    return;
+  }
+
+  document.getElementById('testing_frame').src = testList.shift();
+}
+
+// listen for a messages from the mixed content test harness
+function receiveMessage(event) {
+  if ("test" in event.data) {
+    var summary = event.data.test + ": " + event.data.msg;
+    log(event.data.status + ": " + summary);
+    ok(event.data.status, summary);
+  } else if ("done" in event.data) {
+    nextTest();
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage, false);
+nextTest();
+
+</script>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_frame_appid_facet.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <script src="u2futil.js"></script>
+</head>
+<body>
+<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+
+local_is(window.location.origin, "https://example.com", "Is loaded correctly");
+
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+
+u2f.register(null, [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "Null AppID should work.");
+});
+
+u2f.register("", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "Empty AppID should work.");
+});
+
+// Test: Correct TLD, but incorrect scheme
+u2f.register("http://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "HTTP scheme is disallowed");
+});
+
+// Test: Correct TLD, and also HTTPS
+u2f.register("https://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "HTTPS origin for example.com should work");
+});
+
+// Test: Dynamic origin
+u2f.register(window.location.origin + "/otherAppId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "Direct window origin should work");
+});
+
+// eTLD+1 check
+u2f.register("https://test1.example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "Subdomain AppID should work");
+});
+
+local_finished();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_frame_appid_facet_insecure.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <script src="u2futil.js"></script>
+</head>
+<body>
+<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+
+local_is(window.location.origin, "http://mochi.test:8888", "Is loaded correctly");
+
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+
+u2f.register(null, [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "Insecure origin disallowed for null AppID");
+});
+
+u2f.register("", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "Insecure origin disallowed for empty AppID");
+});
+
+u2f.register("http://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP AppID");
+});
+
+u2f.register("https://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTPS AppID from HTTP origin");
+});
+
+u2f.register(window.location.origin + "/otherAppId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP origin");
+});
+
+local_finished();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_frame_appid_facet_remoteload.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <script src="u2futil.js"></script>
+</head>
+<body>
+<p>Test for Remote AppId Load behavior for FIDO Universal Second Factor</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+
+local_is(window.location.origin, "https://example.com", "Is loaded correctly");
+
+// TODO: Must support remote loads of AppID manifests first.
+//
+// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList.txt", [{
+//   version: version,
+//   challenge: bytesToBase64UrlSafe(challenge),
+// }], [], function(res){
+//   local_is(res.errorCode, 2, "Should not permit this AppId contentType");
+// });
+
+// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetListMissing", [{
+//   version: version,
+//   challenge: bytesToBase64UrlSafe(challenge),
+// }], [], function(res){
+//   local_is(res.errorCode, 2, "Should not permit with a missing AppID list");
+// });
+
+// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-good", [{
+//   version: version,
+//   challenge: bytesToBase64UrlSafe(challenge),
+// }], [], function(res){
+//   local_is(res.errorCode, 0, "The AppId should permit example.com");
+// });
+
+// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-no_overlap", [{
+//   version: version,
+//   challenge: bytesToBase64UrlSafe(challenge),
+// }], [], function(res){
+//   local_is(res.errorCode, 2, "Should not permit with a missing AppID list");
+// });
+
+// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-invalid_format", [{
+//   version: version,
+//   challenge: bytesToBase64UrlSafe(challenge),
+// }], [], function(res){
+//   local_is(res.errorCode, 2, "Should not fail gracefully on invalid formatted facet lists");
+// });
+
+local_finished();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_frame_appid_facet_subdomain.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <script src="u2futil.js"></script>
+</head>
+<body>
+<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+
+local_is(window.location.origin, "https://test1.example.com", "Is loaded correctly");
+
+// eTLD+1 check
+u2f.register("https://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "AppID should work from a subdomain");
+});
+
+u2f.register("https://example.net/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "AppID should not work from other domains");
+});
+
+local_finished();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_frame_register.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <script src="u2futil.js"></script>
+</head>
+<body>
+<p>Test for Register behavior for FIDO Universal Second Factor</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+
+local_is(window.location.origin, "https://example.com", "Is loaded correctly");
+
+// eTLD+1 check
+u2f.register("https://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 0, "AppID should work from a subdomain");
+});
+
+u2f.register("https://example.net/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_is(res.errorCode, 2, "AppID should not work from other domains");
+});
+
+u2f.register("", [], [], function(res){
+  local_is(res.errorCode, 2, "Empty register requests");
+});
+
+local_doesThrow(function(){
+  u2f.register("", null, [], null);
+}, "Non-array register requests");
+
+local_doesThrow(function(){
+  u2f.register("", [], null, null);
+}, "Non-array sign requests");
+
+local_doesThrow(function(){
+  u2f.register("", null, null, null);
+}, "Non-array for both arguments");
+
+u2f.register("", [{}], [], function(res){
+  local_is(res.errorCode, 2, "Empty request");
+});
+
+u2f.register("https://example.net/appId", [{
+    version: version,
+  }], [], function(res){
+  local_is(res.errorCode, 2, "Missing challenge");
+});
+
+u2f.register("https://example.net/appId", [{
+    challenge: bytesToBase64UrlSafe(challenge),
+  }], [], function(res){
+  local_is(res.errorCode, 2, "Missing version");
+});
+
+u2f.register("https://example.net/appId", [{
+    version: "a_version_00",
+    challenge: bytesToBase64UrlSafe(challenge),
+  }], [], function(res){
+  local_is(res.errorCode, 2, "Invalid version");
+});
+
+local_finished();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_no_token.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for FIDO Universal Second Factor No Token</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", false);
+SpecialPowers.setBoolPref("security.webauth.u2f.usbtoken", false);
+
+var challenge = new Uint8Array(16);
+window.crypto.getRandomValues(challenge);
+
+var regRequest = {
+  version: "U2F_V2",
+  challenge: bytesToBase64UrlSafe(challenge),
+};
+
+u2f.register(window.location.origin, [regRequest], [], function (regResponse) {
+  isnot(regResponse.errorCode, 0, "The registration should be rejected.");
+
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_util_methods.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for Utility Methods for other FIDO Universal Second Factor tests</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/dom/u2f/tests/u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.setBoolPref("security.webauth.u2f", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
+SpecialPowers.setBoolPref("security.webauth.u2f.usbtoken", false);
+
+// Example from:
+// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html
+//
+// Run this example from the console to check that the u2futil methods work
+var pubKey = hexDecode("04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d");
+var appId = "https://gstatic.com/securitykey/a/example.com";
+var clientData = string2buffer('{"typ":"navigator.id.getAssertion","challenge":"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}');
+var presenceAndCounter = hexDecode("0100000001");
+var signature = hexDecode("304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f");
+
+// Import the key
+// Assemble the client data
+// Verify
+Promise.all([
+  importPublicKey(pubKey),
+  assembleSignedData(appId, presenceAndCounter, clientData)
+])
+.then(function(results) {
+  var importedKey = results[0];
+  var signedData = new Uint8Array(results[1]);
+  return verifySignature(importedKey, signedData, signature);
+})
+.then(function(verified) {
+  console.log("verified:", verified);
+  ok(true, "Utility methods work")
+  SimpleTest.finish();
+})
+.catch(function(err) {
+  console.log("error:", err);
+  ok(false, "Utility methods failed")
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/u2futil.js
@@ -0,0 +1,144 @@
+function local_is(value, expected, message) {
+  if (value === expected) {
+    local_ok(true, message);
+  } else {
+    local_ok(false, message + " unexpectedly: " + value + " !== " + expected);
+  }
+}
+
+function local_isnot(value, expected, message) {
+  if (value !== expected) {
+    local_ok(true, message);
+  } else {
+    local_ok(false, message + " unexpectedly: " + value + " === " + expected);
+  }
+}
+
+function local_ok(expression, message) {
+  let body = {"test": this.location.pathname, "status":expression, "msg": message}
+  parent.postMessage(body, "http://mochi.test:8888");
+}
+
+function local_doesThrow(fn, name) {
+  var gotException = false;
+  try {
+    fn();
+  } catch (ex) { gotException = true; }
+  local_ok(gotException, name);
+};
+
+function local_finished() {
+  parent.postMessage({"done":true}, "http://mochi.test:8888");
+}
+
+function string2buffer(str) {
+  return (new Uint8Array(str.length)).map((x, i) => str.charCodeAt(i));
+}
+
+function buffer2string(buf) {
+  var str = "";
+  buf.map(x => str += String.fromCharCode(x));
+  return str;
+}
+
+function bytesToBase64(u8a){
+  var CHUNK_SZ = 0x8000;
+  var c = [];
+  for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
+    c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
+  }
+  return window.btoa(c.join(""));
+}
+
+function base64ToBytes(b64encoded) {
+  return new Uint8Array(window.atob(b64encoded).split("").map(function(c) {
+    return c.charCodeAt(0);
+  }));
+}
+
+function bytesToBase64UrlSafe(buf) {
+  return bytesToBase64(buf)
+                 .replace(/\+/g, "-")
+                 .replace(/\//g, "_")
+                 .replace(/=/g, "");
+}
+
+function base64ToBytesUrlSafe(str) {
+  if (str.length % 4 == 1) {
+    throw "Improper b64 string";
+  }
+
+  var b64 = str.replace(/\-/g, "+").replace(/\_/g, "/");
+  while (b64.length % 4 != 0) {
+    b64 += "=";
+  }
+  return base64ToBytes(b64);
+}
+
+function hexEncode(buf) {
+  return Array.from(buf)
+              .map(x => ("0"+x.toString(16)).substr(-2))
+              .join("");
+}
+
+function hexDecode(str) {
+  return new Uint8Array(str.match(/../g).map(x => parseInt(x, 16)));
+}
+
+function importPublicKey(keyBytes) {
+  if (keyBytes[0] != 0x04 || keyBytes.byteLength != 65) {
+    throw "Bad public key octet string";
+  }
+  var jwk = {
+    kty: "EC",
+    crv: "P-256",
+    x: bytesToBase64UrlSafe(keyBytes.slice(1, 33)),
+    y: bytesToBase64UrlSafe(keyBytes.slice(33))
+  };
+  return crypto.subtle.importKey("jwk", jwk, {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"])
+}
+
+function assembleSignedData(appId, presenceAndCounter, clientData) {
+  var appIdBuf = string2buffer(appId);
+  return Promise.all([
+    crypto.subtle.digest("SHA-256", appIdBuf),
+    crypto.subtle.digest("SHA-256", clientData)
+  ])
+  .then(function(digests) {
+    var appParam = new Uint8Array(digests[0]);
+    var clientParam = new Uint8Array(digests[1]);
+
+    var signedData = new Uint8Array(32 + 1 + 4 + 32);
+    appParam.map((x, i) => signedData[0 + i] = x);
+    presenceAndCounter.map((x, i) => signedData[32 + i] = x);
+    clientParam.map((x, i) => signedData[37 + i] = x);
+    return signedData;
+  });
+}
+
+function verifySignature(key, data, derSig) {
+  if (derSig.byteLength < 70) {
+    console.log("bad sig: " + hexEncode(derSig))
+    throw "Invalid signature length: " + derSig.byteLength;
+  }
+
+  // Poor man's ASN.1 decode
+  // R and S are always 32 bytes.  If ether has a DER
+  // length > 32, it's just zeros we can chop off.
+  var lenR = derSig[3];
+  var lenS = derSig[3 + lenR + 2];
+  var padR = lenR - 32;
+  var padS = lenS - 32;
+  var sig = new Uint8Array(64);
+  derSig.slice(4 + padR, 4 + lenR).map((x, i) => sig[i] = x);
+  derSig.slice(4 + lenR + 2 + padS, 4 + lenR + 2 + lenS).map(
+    (x, i) => sig[32 + i] = x
+  );
+
+  console.log("data: " + hexEncode(data));
+  console.log("der:  " + hexEncode(derSig));
+  console.log("raw:  " + hexEncode(sig));
+
+  var alg = {name: "ECDSA", hash: "SHA-256"};
+  return crypto.subtle.verify(alg, key, sig, data);
+}
\ No newline at end of file
--- a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl
+++ b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl
@@ -45,16 +45,18 @@ interface TestInterfaceSetlike {
 interface TestInterfaceSetlikeNode {
   setlike<Node>;
 };
 
 [Constructor(),
  Pref="dom.expose_test_interfaces"]
 interface TestInterfaceIterableSingle {
   iterable<long>;
+  getter long(unsigned long index);
+  readonly attribute unsigned long length;
 };
 
 [Constructor(),
  Pref="dom.expose_test_interfaces"]
 interface TestInterfaceIterableDouble {
   iterable<DOMString, DOMString>;
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/U2F.webidl
@@ -0,0 +1,95 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is a combination of the FIDO U2F Raw Message Formats:
+ * https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html
+ * and the U2F JavaScript API v1.1, not yet published. While v1.1 is not published,
+ * v1.0, is located here:
+ * https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-javascript-api.html
+ */
+
+[NoInterfaceObject]
+interface GlobalU2F {
+  [Throws, Pref="security.webauth.u2f"]
+  readonly attribute U2F u2f;
+};
+
+typedef unsigned short ErrorCode;
+typedef sequence<Transport> Transports;
+
+enum Transport {
+    "bt",
+    "ble",
+    "nfc",
+    "usb"
+};
+
+dictionary ClientData {
+    DOMString             typ; // Spelling is from the specification
+    DOMString             challenge;
+    DOMString             origin;
+    // cid_pubkey for Token Binding is not implemented
+};
+
+dictionary RegisterRequest {
+    DOMString version;
+    DOMString challenge;
+};
+
+dictionary RegisterResponse {
+    DOMString version;
+    DOMString registrationData;
+    DOMString clientData;
+
+    // From Error
+    ErrorCode? errorCode;
+    DOMString? errorMessage;
+};
+
+dictionary RegisteredKey {
+    DOMString   version;
+    DOMString   keyHandle;
+    Transports? transports;
+    DOMString?  appId;
+};
+
+dictionary SignResponse {
+    DOMString keyHandle;
+    DOMString signatureData;
+    DOMString clientData;
+
+    // From Error
+    ErrorCode? errorCode;
+    DOMString? errorMessage;
+};
+
+callback U2FRegisterCallback = void(RegisterResponse response);
+callback U2FSignCallback = void(SignResponse response);
+
+interface U2F {
+  // These enumerations are defined in the FIDO U2F Javascript API under the
+  // interface "ErrorCode" as constant integers, and also in the U2F.cpp file.
+  // Any changes to these must occur in both locations.
+  const unsigned short OK = 0;
+  const unsigned short OTHER_ERROR = 1;
+  const unsigned short BAD_REQUEST = 2;
+  const unsigned short CONFIGURATION_UNSUPPORTED = 3;
+  const unsigned short DEVICE_INELIGIBLE = 4;
+  const unsigned short TIMEOUT = 5;
+
+  [Throws]
+  void register (DOMString appId,
+                 sequence<RegisterRequest> registerRequests,
+                 sequence<RegisteredKey> registeredKeys,
+                 U2FRegisterCallback callback,
+                 optional long? opt_timeoutSeconds);
+
+  [Throws]
+  void sign (DOMString appId,
+             DOMString challenge,
+             sequence<RegisteredKey> registeredKeys,
+             U2FSignCallback callback,
+             optional long? opt_timeoutSeconds);
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -241,16 +241,19 @@ callback FrameRequestCallback = void (DO
 // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
 partial interface Window {
   [Replaceable, Pure, StoreInSlot] readonly attribute Performance? performance;
 };
 
 // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
 Window implements GlobalCrypto;
 
+// https://fidoalliance.org/specifications/download/
+Window implements GlobalU2F;
+
 #ifdef MOZ_WEBSPEECH
 // http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
 [NoInterfaceObject]
 interface SpeechSynthesisGetter {
   [Throws, Pref="media.webspeech.synth.enabled"] readonly attribute SpeechSynthesis speechSynthesis;
 };
 
 Window implements SpeechSynthesisGetter;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -567,16 +567,17 @@ WEBIDL_FILES = [
     'TreeColumn.webidl',
     'TreeColumns.webidl',
     'TreeWalker.webidl',
     'TVChannel.webidl',
     'TVManager.webidl',
     'TVProgram.webidl',
     'TVSource.webidl',
     'TVTuner.webidl',
+    'U2F.webidl',
     'UDPMessageEvent.webidl',
     'UDPSocket.webidl',
     'UIEvent.webidl',
     'UndoManager.webidl',
     'URL.webidl',
     'URLSearchParams.webidl',
     'USSDSession.webidl',
     'ValidityState.webidl',
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1364,16 +1364,22 @@ IsCurrentThreadRunningChromeWorker()
 }
 
 JSContext*
 GetCurrentThreadJSContext()
 {
   return GetCurrentThreadWorkerPrivate()->GetJSContext();
 }
 
+JSObject*
+GetCurrentThreadWorkerGlobal()
+{
+  return GetCurrentThreadWorkerPrivate()->GlobalScope()->GetGlobalJSObject();
+}
+
 END_WORKERS_NAMESPACE
 
 struct RuntimeService::IdleThreadInfo
 {
   RefPtr<WorkerThread> mThread;
   mozilla::TimeStamp mExpirationTime;
 };
 
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1441,16 +1441,19 @@ WorkerPrivate*
 GetCurrentThreadWorkerPrivate();
 
 bool
 IsCurrentThreadRunningChromeWorker();
 
 JSContext*
 GetCurrentThreadJSContext();
 
+JSObject*
+GetCurrentThreadWorkerGlobal();
+
 class AutoSyncLoopHolder
 {
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mTarget;
   uint32_t mIndex;
 
 public:
   explicit AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate)
new file mode 100644
--- /dev/null
+++ b/dom/xslt/crashtests/1243337.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" standalone="yes" ?>
+<?xml-stylesheet type="text/xsl" href="1243337.xsl" ?>
+<root/>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/crashtests/1243337.xsl
@@ -0,0 +1,6 @@
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="/">
+  <xsl:value-of select="generate-id(5)"/>
+  <html><body/></html>
+ </xsl:template>
+</xsl:stylesheet>
--- a/dom/xslt/crashtests/crashtests.list
+++ b/dom/xslt/crashtests/crashtests.list
@@ -12,8 +12,9 @@ load 528488.xml
 load 528963.xml
 load 545927.html
 load 601543.html
 load 602115.html
 load 603844.html
 load 667315.xml
 load 1089049.html
 load 1205163.xml
+load 1243337.xml
--- a/dom/xslt/xslt/txGenerateIdFunctionCall.cpp
+++ b/dom/xslt/xslt/txGenerateIdFunctionCall.cpp
@@ -36,17 +36,17 @@ GenerateIdFunctionCall::evaluate(txIEval
     *aResult = nullptr;
     if (!requireParams(0, 1, aContext))
         return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
 
     txExecutionState* es = 
         static_cast<txExecutionState*>(aContext->getPrivateContext());
     if (!es) {
         NS_ERROR(
-            "called xslt extension function \"current\" with wrong context");
+            "called xslt extension function \"generate-id\" with wrong context");
         return NS_ERROR_UNEXPECTED;
     }
 
     nsresult rv = NS_OK;
     if (mParams.IsEmpty()) {
         StringResult* strRes;
         rv = aContext->recycler()->getStringResult(&strRes);
         NS_ENSURE_SUCCESS(rv, rv);
@@ -87,16 +87,20 @@ Expr::ResultType
 GenerateIdFunctionCall::getReturnType()
 {
     return STRING_RESULT;
 }
 
 bool
 GenerateIdFunctionCall::isSensitiveTo(ContextSensitivity aContext)
 {
+    if (aContext & PRIVATE_CONTEXT) {
+        return true;
+    }
+
     if (mParams.IsEmpty()) {
         return !!(aContext & NODE_CONTEXT);
     }
 
     return argsSensitiveTo(aContext);
 }
 
 #ifdef TX_TO_STRING
--- a/editor/libeditor/tests/chrome.ini
+++ b/editor/libeditor/tests/chrome.ini
@@ -23,16 +23,18 @@ skip-if = buildapp == 'mulet'
 [test_bug1053048.html]
 [test_bug1100966.html]
 [test_bug1102906.html]
 [test_bug1101392.html]
 [test_bug1140105.html]
 [test_bug1140617.xul]
 [test_bug1153237.html]
 [test_bug1154791.html]
+[test_bug1248128.html]
+[test_bug1248185.html]
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_text_input_handling.html]
 [test_dragdrop.html]
 skip-if = buildapp == 'mulet'
 [test_htmleditor_keyevent_handling.html]
 [test_selection_move_commands.xul]
 [test_texteditor_keyevent_handling.html]
 skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1248128.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1248128
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1248128</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  SimpleTest.waitForExplicitFinish();
+
+  SimpleTest.waitForFocus(function() {
+    var outer = document.querySelector("html");
+    ok(outer.scrollTop == 0, "scrollTop is zero: got " + outer.scrollTop);
+
+    var input = document.getElementById("testInput");
+    input.focus();
+
+    var scroll = outer.scrollTop;
+    ok(scroll > 0, "element has scrolled: new value " + scroll);
+
+    const kMoveLeft = "cmd_moveLeft";
+    var controller =
+      top.document.commandDispatcher.getControllerForCommand(kMoveLeft);
+    ok((controller && controller.isCommandEnabled(kMoveLeft)),
+       "have " + kMoveLeft + " command");
+
+    try {
+      controller.doCommand(kMoveLeft);
+	  ok(false, "should not be able to do kMoveLeft");
+    }
+	catch (e) {
+	  ok(true, "unable to perform kMoveLeft");
+	}
+
+    ok(outer.scrollTop == scroll,
+       "scroll is unchanged: got " + outer.scrollTop + ", expected " + scroll);
+
+    SimpleTest.finish();
+  });
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1248128">Mozilla Bug 1248128</a>
+<div style="height: 2000px;"></div>
+<input type="text" id="testInput"></input>
+<div style="height: 200px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1248185.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1248185
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1248185</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  SimpleTest.waitForExplicitFinish();
+
+  // Avoid platform selection differences
+  SimpleTest.waitForFocus(function() {
+    SpecialPowers.pushPrefEnv({
+      "set": [["layout.word_select.eat_space_to_next_word", true]]
+    }, runTests);
+  });
+
+  function runTests()
+  {
+    var editor = document.querySelector("#test");
+    editor.focus();
+
+    var sel = window.getSelection();
+
+    const kMoveRight2 = "cmd_moveRight2";
+    var controller =
+      top.document.commandDispatcher.getControllerForCommand(kMoveRight2);
+    ok((controller && controller.isCommandEnabled(kMoveRight2)),
+       "have " + kMoveRight2 + " command");
+
+    // we assume the controller for these is the same
+    const kSelectLeft2 = "cmd_selectLeft2";
+    const kSelectRight2 = "cmd_selectRight2";
+
+    controller.doCommand(kMoveRight2);
+    controller.doCommand(kMoveRight2);
+    controller.doCommand(kMoveRight2);
+    controller.doCommand(kSelectRight2);
+    ok(sel.toString() == "three ", "expected 'three ' to be selected");
+
+    controller.doCommand(kMoveRight2);
+    controller.doCommand(kMoveRight2);
+    controller.doCommand(kMoveRight2);
+    ok(sel.toString() == "", "expected empty selection");
+
+    controller.doCommand(kSelectLeft2);
+    ok(sel.toString() == "five", "expected 'five' to be selected");
+
+    SimpleTest.finish();
+  }
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1248185">Mozilla Bug 1248185</a>
+<body>
+<div style="font: 12px monospace; width: 45ch;">
+<span contenteditable="" id="test">blablablablablablablablablablablablablabla one two three four five</span>
+<div>
+<span>foo</span>
+</div>
+</div>
+</body>
+</html>
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -904,23 +904,45 @@ DrawTargetSkia::InitWithGrContext(GrCont
 
   mCanvas = gpuSurface->getCanvas();
 
   return true;
 }
 
 #endif
 
+#ifdef DEBUG
+bool
+VerifyRGBXFormat(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+  // We should've initialized the data to be opaque already
+  // On debug builds, verify that this is actually true.
+  int height = aSize.height;
+  int width = aSize.width;
+
+  for (int row = 0; row < height; ++row) {
+    for (int column = 0; column < width; column += 4) {
+#ifdef IS_BIG_ENDIAN
+      MOZ_ASSERT(aData[column] == 0xFF);
+#else
+      MOZ_ASSERT(aData[column + 3] == 0xFF);
+#endif
+    }
+    aData += aStride;
+  }
+
+  return true;
+}
+#endif
+
 void
 DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat)
 {
-  if (aFormat == SurfaceFormat::B8G8R8X8) {
-    // We have to manually set the A channel to be 255 as Skia doesn't understand BGRX
-    ConvertBGRXToBGRA(aData, aSize, aStride);
-  }
+  MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) ||
+              VerifyRGBXFormat(aData, aSize, aStride, aFormat));
 
   SkBitmap bitmap;
   bitmap.setInfo(MakeSkiaImageInfo(aSize, aFormat), aStride);
   bitmap.setPixels(aData);
 
   mCanvas.adopt(new SkCanvas(bitmap));
 
   mSize = aSize;
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -125,16 +125,17 @@ enum class LogReason : int {
   FilterInputFormat,
   FilterNodeD2D1Target,
   FilterNodeD2D1Backend,
   SourceSurfaceIncompatible,
   GlyphAllocFailedCairo,
   GlyphAllocFailedCG,
   InvalidRect,
   CannotDraw3D, // 20
+  IncompatibleBasicTexturedEffect,
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger
 {
   // For efficiency, this method exists and copies the logic of the
   // OutputMessage below.  If making any changes here, also make it
@@ -205,16 +206,17 @@ typedef mozilla::Tuple<int32_t,std::stri
 // Implement this interface and init the Factory with an instance to
 // forward critical logs.
 typedef std::vector<LoggingRecordEntry> LoggingRecord;
 class LogForwarder {
 public:
   virtual ~LogForwarder() {}
   virtual void Log(const std::string &aString) = 0;
   virtual void CrashAction(LogReason aReason) = 0;
+  virtual bool UpdateStringsVector(const std::string& aString) = 0;
 
   // Provide a copy of the logs to the caller.
   virtual LoggingRecord LoggingRecordCopy() = 0;
 };
 
 class NoLog
 {
 public:
--- a/gfx/layers/AtomicRefCountedWithFinalize.h
+++ b/gfx/layers/AtomicRefCountedWithFinalize.h
@@ -61,17 +61,17 @@ protected:
     }
 
 public:
     // Mark user classes that are considered flawless.
     template<class U>
     friend class ::mozilla::StaticRefPtr;
 
     template<class U>
-    friend class ::RefPtr;
+    friend struct mozilla::RefPtrTraits;
 
     template<class U>
     friend struct ::RunnableMethodTraits;
 
     template<typename U>
     friend class ::mozilla::gl::RefSet;
 
     template<typename U>
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -364,19 +364,21 @@ MemoryTextureData::Serialize(SurfaceDesc
 
 static bool InitBuffer(uint8_t* buf, size_t bufSize, TextureAllocationFlags aAllocFlags)
 {
   if (!buf) {
     gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize << " bytes";
     return false;
   }
 
-  if (aAllocFlags & ALLOC_CLEAR_BUFFER) {
+  if ((aAllocFlags & ALLOC_CLEAR_BUFFER) ||
+      (aAllocFlags & ALLOC_CLEAR_BUFFER_BLACK)) {
     memset(buf, 0, bufSize);
   }
+
   if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
     memset(buf, 0xFF, bufSize);
   }
 
   return true;
 }
 
 MemoryTextureData*
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -7,26 +7,32 @@
 #ifndef mozilla_layers_GeckoContentController_h
 #define mozilla_layers_GeckoContentController_h
 
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "Units.h"                      // for CSSPoint, CSSRect, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/EventForwards.h"      // for Modifiers
 #include "nsISupportsImpl.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
 
 class Task;
 
 namespace mozilla {
 namespace layers {
 
 class GeckoContentController
 {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoContentController)
+  /**
+   * At least one class deriving from GeckoContentController needs to do
+   * synchronous cleanup on the main thread, so we use
+   * NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION.
+   */
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GeckoContentController)
 
   /**
    * Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
    * Implementations per-platform are responsible for actually handling this.
    * This method will always be called on the Gecko main thread.
    */
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
 
@@ -83,16 +89,17 @@ public:
 
   /**
    * APZ uses |FrameMetrics::mCompositionBounds| for hit testing. Sometimes,
    * widget code has knowledge of a touch-sensitive region that should
    * additionally constrain hit testing for all frames associated with the
    * controller. This method allows APZ to query the controller for such a
    * region. A return value of true indicates that the controller has such a
    * region, and it is returned in |aOutRegion|.
+   * This method needs to be called on the main thread.
    * TODO: once bug 928833 is implemented, this should be removed, as
    * APZ can then get the correct touch-sensitive region for each frame
    * directly from the layer.
    */
   virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion)
   {
     return false;
   }
@@ -143,16 +150,20 @@ public:
    * Notify content that the repaint requests have been flushed.
    */
   virtual void NotifyFlushComplete() = 0;
 
   virtual void UpdateOverscrollVelocity(const float aX, const float aY) {}
   virtual void UpdateOverscrollOffset(const float aX,const  float aY) {}
 
   GeckoContentController() {}
+  virtual void ChildAdopted() {}
+  /**
+   * Needs to be called on the main thread.
+   */
   virtual void Destroy() {}
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~GeckoContentController() {}
 };
 
 } // namespace layers
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -29,16 +29,17 @@ namespace mozilla {
 using namespace mozilla::gfx;
 
 namespace layers {
 
 class DataTextureSourceBasic : public DataTextureSource
                              , public TextureSourceBasic
 {
 public:
+  virtual const char* Name() const override { return "DataTextureSourceBasic"; }
 
   virtual TextureSourceBasic* AsSourceBasic() override { return this; }
 
   virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
 
   SurfaceFormat GetFormat() const override
   {
     return mSurface->GetFormat();
@@ -170,16 +171,21 @@ DrawSurfaceWithTextureCoords(DrawTarget 
                              const gfx::Rect& aDestRect,
                              SourceSurface *aSource,
                              const gfx::Rect& aTextureCoords,
                              gfx::Filter aFilter,
                              const DrawOptions& aOptions,
                              SourceSurface *aMask,
                              const Matrix* aMaskTransform)
 {
+  if (!aSource) {
+    gfxWarning() << "DrawSurfaceWithTextureCoords problem " << gfx::hexa(aSource) << " and " << gfx::hexa(aMask);
+    return;
+  }
+
   // Convert aTextureCoords into aSource's coordinate space
   gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width,
                      aTextureCoords.y * aSource->GetSize().height,
                      aTextureCoords.width * aSource->GetSize().width,
                      aTextureCoords.height * aSource->GetSize().height);
 
   // Floating point error can accumulate above and we know our visible region
   // is integer-aligned, so round it out.
@@ -391,16 +397,19 @@ BasicCompositor::DrawQuad(const gfx::Rec
   newTransform.PostTranslate(-offset.x, -offset.y);
   buffer->SetTransform(newTransform);
 
   RefPtr<SourceSurface> sourceMask;
   Matrix maskTransform;
   if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
     EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
     sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest);
+    if (!sourceMask) {
+      gfxWarning() << "Invalid sourceMask effect";
+    }
     MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
     MOZ_ASSERT(!effectMask->mIs3D);
     maskTransform = effectMask->mMaskTransform.As2D();
     maskTransform.PreTranslate(-offset.x, -offset.y);
   }
 
   CompositionOp blendMode = CompositionOp::OP_OVER;
   if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
@@ -425,37 +434,43 @@ BasicCompositor::DrawQuad(const gfx::Rec
       }
       break;
     }
     case EffectTypes::RGB: {
       TexturedEffect* texturedEffect =
           static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
       TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();
 
-      if (texturedEffect->mPremultiplied) {
+      if (source && texturedEffect->mPremultiplied) {
           DrawSurfaceWithTextureCoords(dest, aRect,
                                        source->GetSurface(dest),
                                        texturedEffect->mTextureCoords,
                                        texturedEffect->mFilter,
                                        DrawOptions(aOpacity, blendMode),
                                        sourceMask, &maskTransform);
-      } else {
-          RefPtr<DataSourceSurface> srcData = source->GetSurface(dest)->GetDataSurface();
+      } else if (source) {
+        SourceSurface* srcSurf = source->GetSurface(dest);
+        if (srcSurf) {
+          RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();
 
           // Yes, we re-create the premultiplied data every time.
           // This might be better with a cache, eventually.
           RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData);
 
           DrawSurfaceWithTextureCoords(dest, aRect,
                                        premultData,
                                        texturedEffect->mTextureCoords,
                                        texturedEffect->mFilter,
                                        DrawOptions(aOpacity, blendMode),
                                        sourceMask, &maskTransform);
+        }
+      } else {
+        gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask);
       }
+
       break;
     }
     case EffectTypes::YCBCR: {
       NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
       break;
     }
     case EffectTypes::RENDER_TARGET: {
       EffectRenderTarget* effectRenderTarget =
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -18,16 +18,18 @@ class BasicCompositingRenderTarget : pub
 {
 public:
   BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget, const gfx::IntRect& aRect)
     : CompositingRenderTarget(aRect.TopLeft())
     , mDrawTarget(aDrawTarget)
     , mSize(aRect.Size())
   { }
 
+  virtual const char* Name() const override { return "BasicCompositingRenderTarget"; }
+
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   void BindRenderTarget();
 
   virtual gfx::SurfaceFormat GetFormat() const override
   {
     return mDrawTarget ? mDrawTarget->GetFormat()
                        : gfx::SurfaceFormat(gfx::SurfaceFormat::UNKNOWN);
--- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
@@ -26,16 +26,18 @@ class MacIOSurfaceTextureSourceBasic
   : public TextureSourceBasic,
     public TextureSource
 {
 public:
   MacIOSurfaceTextureSourceBasic(BasicCompositor* aCompositor,
                                  MacIOSurface* aSurface);
   virtual ~MacIOSurfaceTextureSourceBasic();
 
+  virtual const char* Name() const override { return "MacIOSurfaceTextureSourceBasic"; }
+
   virtual TextureSourceBasic* AsSourceBasic() override { return this; }
 
   virtual gfx::IntSize GetSize() const override;
   virtual gfx::SurfaceFormat GetFormat() const override;
   virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
 
   virtual void DeallocateDeviceData() override { }
 
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -17,16 +17,18 @@ namespace layers {
 
 // TextureSource for Image-backed surfaces.
 class X11DataTextureSourceBasic : public DataTextureSource
                                 , public TextureSourceBasic
 {
 public:
   X11DataTextureSourceBasic() {};
 
+  virtual const char* Name() const override { return "X11DataTextureSourceBasic"; }
+
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   virtual TextureSourceBasic* AsSourceBasic() override;
 
   virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
 
--- a/gfx/layers/basic/X11TextureSourceBasic.h
+++ b/gfx/layers/basic/X11TextureSourceBasic.h
@@ -19,16 +19,18 @@ class BasicCompositor;
 // TextureSource for Xlib-backed surfaces.
 class X11TextureSourceBasic
   : public TextureSourceBasic
   , public X11TextureSource
 {
 public:
   X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface);
 
+  virtual const char* Name() const override { return "X11TextureSourceBasic"; }
+
   virtual X11TextureSourceBasic* AsSourceBasic() override { return this; }
 
   virtual gfx::IntSize GetSize() const override;
 
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
 
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -285,20 +285,25 @@ ContentClientRemoteBuffer::BuildTextureC
 
   CreateBackBuffer(mBufferRect);
 }
 
 void
 ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
 {
   // gfx::BackendType::NONE means fallback to the content backend
+  TextureAllocationFlags textureAllocFlags
+                         = (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ?
+                            TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
+                            TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
+
   mTextureClient = CreateTextureClientForDrawing(
     mSurfaceFormat, mSize, BackendSelector::Content,
     mTextureFlags | ExtraTextureFlags(),
-    TextureAllocationFlags::ALLOC_CLEAR_BUFFER
+    textureAllocFlags
   );
   if (!mTextureClient || !AddTextureClient(mTextureClient)) {
     AbortTextureClientCreation();
     return;
   }
 
   if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
     mTextureClientOnWhite = mTextureClient->CreateSimilar(
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -828,16 +828,23 @@ TextureClient::CreateForRawBufferAccess(
   if (aAllocFlags & ALLOC_DISALLOW_BUFFERTEXTURECLIENT) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
     return nullptr;
   }
 
+  if (aFormat == SurfaceFormat::B8G8R8X8 &&
+      (aAllocFlags != TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK) &&
+      aMoz2DBackend == gfx::BackendType::SKIA) {
+    // skia requires alpha component of RGBX textures to be 255.
+    aAllocFlags = TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE;
+  }
+
   TextureData* texData = BufferTextureData::Create(aSize, aFormat, aMoz2DBackend,
                                                    aTextureFlags, aAllocFlags,
                                                    aAllocator);
   if (!texData) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator);
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -64,25 +64,26 @@ class TextureClientPool;
 class KeepAlive;
 
 /**
  * TextureClient is the abstraction that allows us to share data between the
  * content and the compositor side.
  */
 
 enum TextureAllocationFlags {
-  ALLOC_DEFAULT = 0x0,
-  ALLOC_CLEAR_BUFFER = 0x1,
-  ALLOC_CLEAR_BUFFER_WHITE = 0x2,
-  ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 0x4,
+  ALLOC_DEFAULT = 0,
+  ALLOC_CLEAR_BUFFER = 1 << 1,  // Clear the buffer to whatever is best for the draw target
+  ALLOC_CLEAR_BUFFER_WHITE = 1 << 2,  // explicit all white
+  ALLOC_CLEAR_BUFFER_BLACK = 1 << 3,  // explicit all black
+  ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 1 << 4,
 
   // Allocate the texture for out-of-band content updates. This is mostly for
   // TextureClientD3D11, which may otherwise choose D3D10 or non-KeyedMutex
   // surfaces when used on the main thread.
-  ALLOC_FOR_OUT_OF_BAND_CONTENT = 0x8
+  ALLOC_FOR_OUT_OF_BAND_CONTENT = 1 << 5,
 };
 
 #ifdef XP_WIN
 typedef void* SyncHandle;
 #else
 typedef uintptr_t SyncHandle;
 #endif // XP_WIN
 
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -369,16 +369,23 @@ TextureSource::TextureSource()
     MOZ_COUNT_CTOR(TextureSource);
 }
 
 TextureSource::~TextureSource()
 {
     MOZ_COUNT_DTOR(TextureSource);
 }
 
+const char*
+TextureSource::Name() const
+{
+  MOZ_CRASH("TextureSource without class name");
+  return "TextureSource";
+}
+  
 BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
                                      TextureFlags aFlags)
 : TextureHost(aFlags)
 , mCompositor(nullptr)
 , mUpdateSerial(1)
 , mLocked(false)
 , mNeedsFullUpdate(false)
 {
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -83,16 +83,18 @@ class TextureSource: public RefCounted<T
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureSource)
 
   TextureSource();
 
   virtual ~TextureSource();
 
+  virtual const char* Name() const = 0;
+
   /**
    * Should be overridden in order to deallocate the data that is associated
    * with the rendering backend, such as GL textures.
    */
   virtual void DeallocateDeviceData() {}
 
 
   /**
@@ -228,16 +230,18 @@ typedef CompositableTextureRef<TextureHo
  */
 class DataTextureSource : public TextureSource
 {
 public:
   DataTextureSource()
     : mUpdateSerial(0)
   {}
 
+  virtual const char* Name() const override { return "DataTextureSource"; }
+
   virtual DataTextureSource* AsDataTextureSource() override { return this; }
 
   /**
    * Upload a (portion of) surface to the TextureSource.
    *
    * The DataTextureSource doesn't own aSurface, although it owns and manage
    * the device texture it uploads to internally.
    */
@@ -719,16 +723,18 @@ public:
 
   explicit CompositingRenderTarget(const gfx::IntPoint& aOrigin)
     : mClearOnBind(false)
     , mOrigin(aOrigin)
     , mHasComplexProjection(false)
   {}
   virtual ~CompositingRenderTarget() {}
 
+  virtual const char* Name() const override { return "CompositingRenderTarget"; }
+
 #ifdef MOZ_DUMP_PAINTING
   virtual already_AddRefed<gfx::DataSourceSurface> Dump(Compositor* aCompositor) { return nullptr; }
 #endif
 
   /**
    * Perform a clear when recycling a non opaque surface.
    * The clear is deferred to when the render target is bound.
    */
--- a/gfx/layers/composite/X11TextureHost.h
+++ b/gfx/layers/composite/X11TextureHost.h
@@ -16,16 +16,18 @@ namespace mozilla {
 namespace layers {
 
 class X11TextureSource : public TextureSource
 {
 public:
   // Called when the underlying X surface has been changed.
   // Useful for determining whether to rebind a GLXPixmap to a texture.
   virtual void Updated() = 0;
+
+  virtual const char* Name() const override { return "X11TextureSource"; }
 };
 
 // TextureHost for Xlib-backed TextureSources.
 class X11TextureHost : public TextureHost
 {
 public:
   X11TextureHost(TextureFlags aFlags,
                  const SurfaceDescriptorX11& aDescriptor);
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -209,16 +209,17 @@ public:
   DataTextureSourceD3D11(gfx::SurfaceFormat aFormat, CompositorD3D11* aCompositor,
                          TextureFlags aFlags);
 
   DataTextureSourceD3D11(gfx::SurfaceFormat aFormat, CompositorD3D11* aCompositor,
                          ID3D11Texture2D* aTexture);
 
   virtual ~DataTextureSourceD3D11();
 
+  virtual const char* Name() const override { return "DataTextureSourceD3D11"; }
 
   // DataTextureSource
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   // TextureSource
@@ -364,16 +365,18 @@ protected:
 class CompositingRenderTargetD3D11 : public CompositingRenderTarget,
                                      public TextureSourceD3D11
 {
 public:
   CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
                                const gfx::IntPoint& aOrigin,
                                DXGI_FORMAT aFormatOverride = DXGI_FORMAT_UNKNOWN);
 
+  virtual const char* Name() const override { return "CompositingRenderTargetD3D11"; }
+
   virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
 
   void BindRenderTarget(ID3D11DeviceContext* aContext);
 
   virtual gfx::IntSize GetSize() const override;
 
   void SetSize(const gfx::IntSize& aSize) { mSize = aSize; }
 
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -107,16 +107,18 @@ public:
   DataTextureSourceD3D9(gfx::SurfaceFormat aFormat,
                         gfx::IntSize aSize,
                         CompositorD3D9* aCompositor,
                         IDirect3DTexture9* aTexture,
                         TextureFlags aFlags = TextureFlags::DEFAULT);
 
   virtual ~DataTextureSourceD3D9();
 
+  virtual const char* Name() const override { return "DataTextureSourceD3D9"; }
+
   // DataTextureSource
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   // TextureSource
 
@@ -393,16 +395,18 @@ public:
                               SurfaceInitMode aInit,
                               const gfx::IntRect& aRect);
   // use for rendering to the main window, cannot be rendered as a texture
   CompositingRenderTargetD3D9(IDirect3DSurface9* aSurface,
                               SurfaceInitMode aInit,
                               const gfx::IntRect& aRect);
   virtual ~CompositingRenderTargetD3D9();
 
+  virtual const char* Name() const override { return "CompositingRenderTargetD3D9"; }
+
   virtual TextureSourceD3D9* AsSourceD3D9() override
   {
     MOZ_ASSERT(mTexture,
                "No texture, can't be indirectly rendered. Is this the screen backbuffer?");
     return this;
   }
 
   virtual gfx::IntSize GetSize() const override;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et 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 "mozilla/layers/APZChild.h"
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * There are cases where we try to create the APZChild before the corresponding
+ * TabChild has been created, we use an observer for the "tab-child-created"
+ * topic to set the TabChild in the APZChild when it has been created.
+ */
+class TabChildCreatedObserver : public nsIObserver
+{
+public:
+  TabChildCreatedObserver(APZChild* aAPZChild, const dom::TabId& aTabId)
+    : mAPZChild(aAPZChild),
+      mTabId(aTabId)
+  {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+private:
+  virtual ~TabChildCreatedObserver()
+  {}
+
+  // TabChildCreatedObserver is owned by mAPZChild, and mAPZChild outlives its
+  // TabChildCreatedObserver, so the raw pointer is fine.
+  APZChild* mAPZChild;
+  dom::TabId mTabId;
+};
+
+NS_IMPL_ISUPPORTS(TabChildCreatedObserver, nsIObserver)
+
+NS_IMETHODIMP
+TabChildCreatedObserver::Observe(nsISupports* aSubject,
+                                 const char* aTopic,
+                                 const char16_t* aData)
+{
+  MOZ_ASSERT(strcmp(aTopic, "tab-child-created") == 0);
+
+  nsCOMPtr<nsITabChild> tabChild(do_QueryInterface(aSubject));
+  NS_ENSURE_TRUE(tabChild, NS_ERROR_FAILURE);
+
+  dom::TabChild* browser = static_cast<dom::TabChild*>(tabChild.get());
+  if (browser->GetTabId() == mTabId) {
+    mAPZChild->SetBrowser(browser);
+  }
+  return NS_OK;
+}
+
+APZChild*
+APZChild::Create(const dom::TabId& aTabId)
+{
+  RefPtr<dom::TabChild> browser = dom::TabChild::FindTabChild(aTabId);
+  nsAutoPtr<APZChild> apz(new APZChild);
+  if (browser) {
+    apz->SetBrowser(browser);
+  } else {
+    RefPtr<TabChildCreatedObserver> observer =
+      new TabChildCreatedObserver(apz, aTabId);
+    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+    if (!os ||
+        NS_FAILED(os->AddObserver(observer, "tab-child-created", false))) {
+      return nullptr;
+    }
+    apz->SetObserver(observer);
+  }
+
+  return apz.forget();
+}
+
+APZChild::~APZChild()
+{
+  if (mObserver) {
+    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+    os->RemoveObserver(mObserver, "tab-child-created");
+  } else {
+    mBrowser->SetAPZChild(nullptr);
+  }
+}
+
+bool
+APZChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
+{
+  return mBrowser->UpdateFrame(aFrameMetrics);
+}
+
+bool
+APZChild::RecvRequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
+                               const mozilla::CSSPoint& aDestination)
+{
+  APZCCallbackHelper::RequestFlingSnap(aScrollId, aDestination);
+  return true;
+}
+
+bool
+APZChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
+                                      const uint32_t& aScrollGeneration)
+{
+  APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
+  return true;
+}
+
+bool
+APZChild::RecvHandleDoubleTap(const CSSPoint& aPoint,
+                              const Modifiers& aModifiers,
+                              const ScrollableLayerGuid& aGuid)
+{
+  mBrowser->HandleDoubleTap(aPoint, aModifiers, aGuid);
+  return true;
+}
+
+bool
+APZChild::RecvHandleSingleTap(const CSSPoint& aPoint,
+                              const Modifiers& aModifiers,
+                              const ScrollableLayerGuid& aGuid,
+                              const bool& aCallTakeFocusForClickFromTap)
+{
+  mBrowser->HandleSingleTap(aPoint, aModifiers, aGuid,
+                            aCallTakeFocusForClickFromTap);
+  return true;
+}
+
+bool
+APZChild::RecvHandleLongTap(const CSSPoint& aPoint,
+                            const Modifiers& aModifiers,
+                            const ScrollableLayerGuid& aGuid,
+                            const uint64_t& aInputBlockId)
+{
+  mBrowser->HandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
+  return true;
+}
+
+bool
+APZChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
+                                   const APZStateChange& aChange,
+                                   const int& aArg)
+{
+  return mBrowser->NotifyAPZStateChange(aViewId, aChange, aArg);
+}
+
+bool
+APZChild::RecvNotifyFlushComplete()
+{
+  APZCCallbackHelper::NotifyFlushComplete();
+  return true;
+}
+
+void
+APZChild::SetObserver(nsIObserver* aObserver)
+{
+  MOZ_ASSERT(!mBrowser);
+  mObserver = aObserver;
+}
+
+void
+APZChild::SetBrowser(dom::TabChild* aBrowser)
+{
+  MOZ_ASSERT(!mBrowser);
+  if (mObserver) {
+    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+    os->RemoveObserver(mObserver, "tab-child-created");
+    mObserver = nullptr;
+  }
+  mBrowser = aBrowser;
+  mBrowser->SetAPZChild(this);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et 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/. */
+
+#ifndef mozilla_layers_APZChild_h
+#define mozilla_layers_APZChild_h
+
+#include "mozilla/layers/PAPZChild.h"
+
+class nsIObserver;
+
+namespace mozilla {
+
+namespace dom {
+class TabChild;
+} // namespace dom
+
+namespace layers {
+
+class APZChild final : public PAPZChild
+{
+public:
+  static APZChild* Create(const dom::TabId& aTabId);
+
+  ~APZChild();
+
+  virtual bool RecvUpdateFrame(const FrameMetrics& frame) override;
+
+  virtual bool RecvRequestFlingSnap(const ViewID& aScrollID,
+                                    const CSSPoint& aDestination) override;
+
+  virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
+                                           const uint32_t& aScrollGeneration) override;
+
+  virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
+                                   const Modifiers& aModifiers,
+                                   const ScrollableLayerGuid& aGuid) override;
+
+  virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
+                                   const Modifiers& aModifiers,
+                                   const ScrollableLayerGuid& aGuid,
+                                   const bool& aCallTakeFocusForClickFromTap) override;
+
+  virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
+                                 const Modifiers& aModifiers,
+                                 const ScrollableLayerGuid& aGuid,
+                                 const uint64_t& aInputBlockId) override;
+
+  virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
+                                        const APZStateChange& aChange,
+                                        const int& aArg) override;
+
+  virtual bool RecvNotifyFlushComplete() override;
+
+  void SetBrowser(dom::TabChild* aBrowser);
+
+private:
+  APZChild() {};
+
+  void SetObserver(nsIObserver* aObserver);
+
+  RefPtr<dom::TabChild> mBrowser;
+  RefPtr<nsIObserver> mObserver;
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_APZChild_h
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -20,16 +20,17 @@
 #include "gfxPlatform.h"                // for gfxPlatform
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"             // for gfxPlatform
 #endif
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "mozilla/AutoRestore.h"        // for AutoRestore
 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
 #include "mozilla/DebugOnly.h"          // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/gfx/2D.h"          // for DrawTarget
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"          // for IntSize
 #include "VRManager.h"                  // for VRManager
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "mozilla/layers/APZThreadUtils.h"  // for APZCTreeManager
 #include "mozilla/layers/AsyncCompositionManager.h"
@@ -38,17 +39,19 @@
 #include "mozilla/layers/CompositorLRU.h"  // for CompositorLRU
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FrameUniformityData.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
 #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
+#include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_WIDGET_GTK
 #include "basic/X11BasicCompositor.h" // for X11BasicCompositor
 #endif
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
@@ -73,16 +76,20 @@
 #endif
 #include "mozilla/VsyncDispatcher.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "GeckoTouchDispatcher.h"
 #include "nsScreenManagerGonk.h"
 #endif
 
+#ifdef MOZ_ANDROID_APZ
+#include "AndroidBridge.h"
+#endif
+
 #include "LayerScope.h"
 
 namespace mozilla {
 
 namespace gfx {
 // See VRManagerChild.cpp
 void ReleaseVRManagerParentSingleton();
 } // namespace gfx
@@ -1649,33 +1656,67 @@ bool
 CompositorParent::RecvNotifyChildCreated(const uint64_t& child)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   NotifyChildCreated(child);
   return true;
 }
 
 void
-CompositorParent::NotifyChildCreated(const uint64_t& aChild)
+CompositorParent::NotifyChildCreated(uint64_t aChild)
 {
   sIndirectLayerTreesLock->AssertCurrentThreadOwns();
   sIndirectLayerTrees[aChild].mParent = this;
   sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
 }
 
+/* static */ bool
+CompositorParent::UpdateRemoteContentController(uint64_t aLayersId,
+                                                dom::ContentParent* aContent,
+                                                const dom::TabId& aTabId,
+                                                dom::TabParent* aTopLevel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+  // RemoteContentController needs to know the layers id and the top level
+  // TabParent, so we pass that to its constructor here and then set up the
+  // PAPZ protocol by calling SendPAPZConstructor (and pass in the tab id for
+  // the PBrowser that it corresponds to).
+  RefPtr<RemoteContentController> controller =
+    new RemoteContentController(aLayersId, aTopLevel);
+  if (!aContent->SendPAPZConstructor(controller, aTabId)) {
+    return false;
+  }
+  state.mController = controller;
+  return true;
+}
+
 bool
 CompositorParent::RecvAdoptChild(const uint64_t& child)
 {
-  MonitorAutoLock lock(*sIndirectLayerTreesLock);
-  NotifyChildCreated(child);
-  if (sIndirectLayerTrees[child].mLayerTree) {
-    sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
+  RefPtr<GeckoContentController> controller;
+  {
+    MonitorAutoLock lock(*sIndirectLayerTreesLock);
+    NotifyChildCreated(child);
+    if (sIndirectLayerTrees[child].mLayerTree) {
+      sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
+    }
+    if (sIndirectLayerTrees[child].mRoot) {
+      sIndirectLayerTrees[child].mRoot->AsLayerComposite()->SetLayerManager(mLayerManager);
+    }
+    controller = sIndirectLayerTrees[child].mController;
   }
-  if (sIndirectLayerTrees[child].mRoot) {
-    sIndirectLayerTrees[child].mRoot->AsLayerComposite()->SetLayerManager(mLayerManager);
+
+  // Calling ChildAdopted on controller will acquire a lock, to avoid a
+  // potential deadlock between that lock and sIndirectLayerTreesLock we
+  // release sIndirectLayerTreesLock first before calling ChildAdopted.
+  if (mApzcTreeManager && controller) {
+    controller->ChildAdopted();
   }
   return true;
 }
 
 /*static*/ uint64_t
 CompositorParent::AllocateLayerTreeId()
 {
   MOZ_ASSERT(CompositorLoop());
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -20,16 +20,17 @@
 #include "base/basictypes.h"            // for DISALLOW_EVIL_CONSTRUCTORS
 #include "base/platform_thread.h"       // for PlatformThreadId
 #include "base/thread.h"                // for Thread
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/Monitor.h"            // for Monitor
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for TimeStamp
+#include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/LayersMessages.h"  // for TargetConfig
 #include "mozilla/layers/PCompositorParent.h"
 #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
 #include "mozilla/layers/APZTestData.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
@@ -49,16 +50,17 @@ class DrawTarget;
 namespace layers {
 
 class APZCTreeManager;
 class AsyncCompositionManager;
 class Compositor;
 class CompositorParent;
 class LayerManagerComposite;
 class LayerTransactionParent;
+class PAPZParent;
 
 struct ScopedLayerTreeRegistration
 {
   ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
                               uint64_t aLayersId,
                               Layer* aRoot,
                               GeckoContentController* aController);
   ~ScopedLayerTreeRegistration();
@@ -278,17 +280,17 @@ public:
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
    */
   void ForceIsFirstPaint();
   void Destroy();
 
   static void SetShadowProperties(Layer* aLayer);
 
-  void NotifyChildCreated(const uint64_t& aChild);
+  void NotifyChildCreated(uint64_t aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
   void ScheduleRenderOnCompositorThread();
   void SchedulePauseOnCompositorThread();
   void InvalidateOnCompositorThread();
   /**
@@ -442,16 +444,30 @@ public:
    * Returns true if the calling thread is the compositor thread.
    */
   static bool IsInCompositorThread();
 
   nsIWidget* GetWidget() { return mWidget; }
 
   void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
 
+  /**
+   * Creates a new RemoteContentController for aTabId. Should only be called on
+   * the main thread.
+   *
+   * aLayersId The layers id for the browser corresponding to aTabId.
+   * aContentParent The ContentParent for the process that the TabChild for
+   *                aTabId lives in.
+   * aBrowserParent The toplevel TabParent for aTabId.
+   */
+  static bool UpdateRemoteContentController(uint64_t aLayersId,
+                                            dom::ContentParent* aContentParent,
+                                            const dom::TabId& aTabId,
+                                            dom::TabParent* aBrowserParent);
+
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~CompositorParent();
 
   void DeferredDestroy();
 
   virtual PLayerTransactionParent*
     AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "mozilla/GfxMessageUtils.h";
+
+include protocol PContent;
+
+using mozilla::CSSPoint from "Units.h";
+using CSSRect from "Units.h";
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using mozilla::Modifiers from "mozilla/EventForwards.h";
+using class nsRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * If APZ is enabled then one PAPZ will be opened per PBrowser between the
+ * process where the PBrowser child actor lives and the main process (the
+ * PBrowser parent actor doesn't necessarily live in the main process, for
+ * example with nested browsers). This will typically be set up when the layers
+ * id is allocated for the PBrowser.
+ *
+ * Opened through PContent and runs on the main thread in both parent and child.
+ */
+sync protocol PAPZ
+{
+  manager PContent;
+
+parent:
+  async UpdateHitRegion(nsRegion aRegion);
+
+  /**
+   * Instructs the TabParent to forward a request to zoom to a rect given in
+   * CSS pixels. This rect is relative to the document.
+   */
+  async ZoomToRect(uint32_t aPresShellId, ViewID aViewId, CSSRect aRect, uint32_t aFlags);
+
+  /**
+   * We know for sure that content has either preventDefaulted or not
+   * preventDefaulted. This applies to an entire batch of touch events. It is
+   * expected that, if there are any DOM touch listeners, touch events will be
+   * batched and only processed for panning and zooming if content does not
+   * preventDefault.
+   */
+  async ContentReceivedInputBlock(ScrollableLayerGuid aGuid, uint64_t aInputBlockId, bool aPreventDefault);
+
+  /**
+   * Notifies the APZ code of the results of the gecko hit-test for a
+   * particular input block. Each target corresponds to one touch point in the
+   * touch event.
+   */
+  async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+
+  // Start an APZ drag on a scrollbar
+  async StartScrollbarDrag(AsyncDragMetrics aDragMetrics);
+
+  /**
+   * Notifies the APZ code of the allowed touch-behaviours for a particular
+   * input block. Each item in the aFlags array corresponds to one touch point
+   * in the touch event.
+   */
+  async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aFlags);
+
+  /**
+   * Updates the zoom constraints for a scrollable frame in this tab.
+   * The zoom controller code lives on the parent side and so this allows it to
+   * have up-to-date zoom constraints.
+   */
+  async UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId,
+                              MaybeZoomConstraints aConstraints);
+
+child:
+  async UpdateFrame(FrameMetrics frame);
+
+  // The following methods correspond to functions on the GeckoContentController
+  // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
+  // in that file for these functions.
+  async RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination);
+  async AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
+  async HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
+  async HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid, bool aCallTakeFocusForClickFromTap);
+  async HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+  async NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
+  async NotifyFlushComplete();
+
+  async __delete__();
+};
+
+} // layers
+} // mozilla
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -2,26 +2,28 @@
  * vim: sw=2 ts=8 et :
  */
 /* 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 LayersSurfaces;
 include LayersMessages;
+include protocol PBrowser;
 include protocol PLayer;
 include protocol PLayerTransaction;
 include "mozilla/GfxMessageUtils.h";
-include "nsRegion.h";
 
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDeviceIntRegion from "Units.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
 
 namespace mozilla {
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "mozilla/layers/RemoteContentController.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "MainThreadUtils.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/CompositorParent.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/unused.h"
+#include "Units.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+RemoteContentController::RemoteContentController(uint64_t aLayersId,
+                                                 dom::TabParent* aBrowserParent)
+  : mUILoop(MessageLoop::current())
+  , mLayersId(aLayersId)
+  , mBrowserParent(aBrowserParent)
+  , mMutex("RemoteContentController")
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+RemoteContentController::~RemoteContentController()
+{
+  if (mBrowserParent) {
+    Unused << PAPZParent::Send__delete__(this);
+  }
+}
+
+void
+RemoteContentController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (CanSend()) {
+    Unused << SendUpdateFrame(aFrameMetrics);
+  }
+}
+
+void
+RemoteContentController::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
+                                          const mozilla::CSSPoint& aDestination)
+{
+  if (MessageLoop::current() != mUILoop) {
+    // We have to send this message from the "UI thread" (main
+    // thread).
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::RequestFlingSnap,
+                        aScrollId, aDestination));
+    return;
+  }
+  if (CanSend()) {
+    Unused << SendRequestFlingSnap(aScrollId, aDestination);
+  }
+}
+
+void
+RemoteContentController::AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId,
+                                                 const uint32_t& aScrollGeneration)
+{
+  if (MessageLoop::current() != mUILoop) {
+    // We have to send this message from the "UI thread" (main
+    // thread).
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::AcknowledgeScrollUpdate,
+                        aScrollId, aScrollGeneration));
+    return;
+  }
+  if (CanSend()) {
+    Unused << SendAcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
+  }
+}
+
+void
+RemoteContentController::HandleDoubleTap(const CSSPoint& aPoint,
+                                         Modifiers aModifiers,
+                                         const ScrollableLayerGuid& aGuid)
+{
+  if (MessageLoop::current() != mUILoop) {
+    // We have to send this message from the "UI thread" (main
+    // thread).
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::HandleDoubleTap,
+                        aPoint, aModifiers, aGuid));
+    return;
+  }
+  if (CanSend()) {
+    Unused << SendHandleDoubleTap(aPoint, aModifiers, aGuid);
+  }
+}
+
+void
+RemoteContentController::HandleSingleTap(const CSSPoint& aPoint,
+                                         Modifiers aModifiers,
+                                         const ScrollableLayerGuid& aGuid)
+{
+  if (MessageLoop::current() != mUILoop) {
+    // We have to send this message from the "UI thread" (main
+    // thread).
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::HandleSingleTap,
+                        aPoint, aModifiers, aGuid));
+    return;
+  }
+
+  bool callTakeFocusForClickFromTap;
+  layout::RenderFrameParent* frame;
+  if (mBrowserParent && (frame = mBrowserParent->GetRenderFrame()) &&
+      mLayersId == frame->GetLayersId()) {
+    // Avoid going over IPC and back for calling TakeFocusForClickFromTap,
+    // since the right RenderFrameParent is living in this process.
+    frame->TakeFocusForClickFromTap();
+    callTakeFocusForClickFromTap = false;
+  } else {
+    callTakeFocusForClickFromTap = true;
+  }
+
+  if (CanSend()) {
+    Unused << SendHandleSingleTap(aPoint, aModifiers, aGuid,
+                                  callTakeFocusForClickFromTap);
+  }
+}
+
+void
+RemoteContentController::HandleLongTap(const CSSPoint& aPoint,
+                                       Modifiers aModifiers,
+                                       const ScrollableLayerGuid& aGuid,
+                                       uint64_t aInputBlockId)
+{
+  if (MessageLoop::current() != mUILoop) {
+    // We have to send this message from the "UI thread" (main
+    // thread).
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::HandleLongTap,
+                        aPoint, aModifiers, aGuid, aInputBlockId));
+    return;
+  }
+  if (CanSend()) {
+    Unused << SendHandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
+  }
+}
+
+void
+RemoteContentController::PostDelayedTask(Task* aTask, int aDelayMs)
+{
+#ifdef MOZ_ANDROID_APZ
+  AndroidBridge::Bridge()->PostTaskToUiThread(aTask, aDelayMs);
+#else
+  (MessageLoop::current() ? MessageLoop::current() : mUILoop)->
+     PostDelayedTask(FROM_HERE, aTask, aDelayMs);
+#endif
+}
+
+bool
+RemoteContentController::GetTouchSensitiveRegion(CSSRect* aOutRegion)
+{
+  MutexAutoLock lock(mMutex);
+  if (mTouchSensitiveRegion.IsEmpty()) {
+    return false;
+  }
+
+  *aOutRegion = CSSRect::FromAppUnits(mTouchSensitiveRegion.GetBounds());
+  return true;
+}
+
+void
+RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+                                              APZStateChange aChange,
+                                              int aArg)
+{
+  if (MessageLoop::current() != mUILoop) {
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::NotifyAPZStateChange,
+                        aGuid, aChange, aArg));
+    return;
+  }
+  if (CanSend()) {
+    Unused << SendNotifyAPZStateChange(aGuid.mScrollId, aChange, aArg);
+  }
+}
+
+void
+RemoteContentController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+                                                   const nsString& aEvent)
+{
+  if (MessageLoop::current() != mUILoop) {
+    mUILoop->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &RemoteContentController::NotifyMozMouseScrollEvent,
+                        aScrollId, aEvent));
+    return;
+  }
+
+  if (mBrowserParent) {
+    Unused << mBrowserParent->SendMouseScrollTestEvent(mLayersId, aScrollId, aEvent);
+  }
+}
+
+void
+RemoteContentController::NotifyFlushComplete()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (CanSend()) {
+    Unused << SendNotifyFlushComplete();
+  }
+}
+
+bool
+RemoteContentController::RecvUpdateHitRegion(const nsRegion& aRegion)
+{
+  MutexAutoLock lock(mMutex);
+  mTouchSensitiveRegion = aRegion;
+  return true;
+}
+
+bool
+RemoteContentController::RecvZoomToRect(const uint32_t& aPresShellId,
+                                        const ViewID& aViewId,
+                                        const CSSRect& aRect,
+                                        const uint32_t& aFlags)
+{
+  if (RefPtr<APZCTreeManager> apzcTreeManager = GetApzcTreeManager()) {
+    apzcTreeManager->ZoomToRect(ScrollableLayerGuid(mLayersId, aPresShellId, aViewId),
+                                aRect, aFlags);
+  }
+  return true;
+}
+
+bool
+RemoteContentController::RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+                                                       const uint64_t& aInputBlockId,
+                                                       const bool& aPreventDefault)
+{
+  if (aGuid.mLayersId != mLayersId) {
+    // Guard against bad data from hijacked child processes
+    NS_ERROR("Unexpected layers id in RecvContentReceivedInputBlock; dropping message...");
+    return false;
+  }
+  if (RefPtr<APZCTreeManager> apzcTreeManager = GetApzcTreeManager()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
+        apzcTreeManager.get(), &APZCTreeManager::ContentReceivedInputBlock,
+        aInputBlockId, aPreventDefault));
+  }
+  return true;
+}
+
+bool
+RemoteContentController::RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
+{
+  if (RefPtr<APZCTreeManager> apzcTreeManager = GetApzcTreeManager()) {
+    ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId,
+                             aDragMetrics.mViewId);
+
+    APZThreadUtils::RunOnControllerThread(
+      NewRunnableMethod(apzcTreeManager.get(),
+                        &APZCTreeManager::StartScrollbarDrag,
+                        guid, aDragMetrics));
+  }
+  return true;
+}
+
+bool
+RemoteContentController::RecvSetTargetAPZC(const uint64_t& aInputBlockId,
+                                           nsTArray<ScrollableLayerGuid>&& aTargets)
+{
+  for (size_t i = 0; i < aTargets.Length(); i++) {
+    if (aTargets[i].mLayersId != mLayersId) {
+      // Guard against bad data from hijacked child processes
+      NS_ERROR("Unexpected layers id in SetTargetAPZC; dropping message...");
+      return false;
+    }
+  }
+  if (RefPtr<APZCTreeManager> apzcTreeManager = GetApzcTreeManager()) {
+    // need a local var to disambiguate between the SetTargetAPZC overloads.
+    void (APZCTreeManager::*setTargetApzcFunc)(uint64_t, const nsTArray<ScrollableLayerGuid>&)
+        = &APZCTreeManager::SetTargetAPZC;
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
+        apzcTreeManager.get(), setTargetApzcFunc,
+        aInputBlockId, aTargets));
+  }
+  return true;
+}
+
+bool
+RemoteContentController::RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
+                                                     nsTArray<TouchBehaviorFlags>&& aFlags)
+{
+  if (RefPtr<APZCTreeManager> apzcTreeManager = GetApzcTreeManager()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
+        apzcTreeManager.get(), &APZCTreeManager::SetAllowedTouchBehavior,
+        aInputBlockId, Move(aFlags)));
+  }
+  return true;
+}
+
+bool
+RemoteContentController::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
+                                                   const ViewID& aViewId,
+                                                   const MaybeZoomConstraints& aConstraints)
+{
+  if (RefPtr<APZCTreeManager> apzcTreeManager = GetApzcTreeManager()) {
+    apzcTreeManager->UpdateZoomConstraints(ScrollableLayerGuid(mLayersId, aPresShellId, aViewId),
+                                           aConstraints);
+  }
+  return true;
+}
+
+void
+RemoteContentController::ActorDestroy(ActorDestroyReason aWhy)
+{
+  {
+    MutexAutoLock lock(mMutex);
+    mApzcTreeManager = nullptr;
+  }
+  mBrowserParent = nullptr;
+}
+
+// TODO: Remove once upgraded to GCC 4.8+ on linux. Calling a static member
+//       function (like PAPZParent::Send__delete__) in a lambda leads to a bogus
+//       error: "'this' was not captured for this lambda function".
+//
+//       (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51494)
+static void
+DeletePAPZParent(PAPZParent* aPAPZ)
+{
+  Unused << PAPZParent::Send__delete__(aPAPZ);
+}
+
+void
+RemoteContentController::Destroy()
+{
+  RefPtr<RemoteContentController> controller = this;
+  NS_DispatchToMainThread(NS_NewRunnableFunction([controller] {
+    if (controller->CanSend()) {
+      DeletePAPZParent(controller);
+    }
+  }));
+}
+
+void
+RemoteContentController::ChildAdopted()
+{
+  // Clear the cached APZCTreeManager.
+  MutexAutoLock lock(mMutex);
+  mApzcTreeManager = nullptr;
+}
+
+already_AddRefed<APZCTreeManager>
+RemoteContentController::GetApzcTreeManager()
+{
+  // We can't get a ref to the APZCTreeManager until after the child is
+  // created and the static getter knows which CompositorParent is
+  // instantiated with this layers ID. That's why try to fetch it when
+  // we first need it and cache the result.
+  MutexAutoLock lock(mMutex);
+  if (!mApzcTreeManager) {
+    mApzcTreeManager = CompositorParent::GetAPZCTreeManager(mLayersId);
+  }
+  RefPtr<APZCTreeManager> apzcTreeManager(mApzcTreeManager);
+  return apzcTreeManager.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_RemoteContentController_h
+#define mozilla_layers_RemoteContentController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/PAPZParent.h"
+
+namespace mozilla {
+
+namespace dom {
+class TabParent;
+}
+
+namespace layers {
+
+class APZCTreeManager;
+
+/**
+ * RemoteContentController uses the PAPZ protocol to implement a
+ * GeckoContentController for a browser living in a remote process.
+ * Most of the member functions can be called on any thread, exceptions are
+ * annotated in comments. The PAPZ protocol runs on the main thread (so all the
+ * Recv* member functions do too).
+ */
+class RemoteContentController : public GeckoContentController
+                              , public PAPZParent
+{
+  using GeckoContentController::APZStateChange;
+
+public:
+  explicit RemoteContentController(uint64_t aLayersId,
+                                   dom::TabParent* aBrowserParent);
+
+  virtual ~RemoteContentController();
+
+  // Needs to be called on the main thread.
+  virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
+
+  virtual void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
+                                const mozilla::CSSPoint& aDestination) override;
+
+  virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId,
+                                       const uint32_t& aScrollGeneration) override;
+
+  virtual void HandleDoubleTap(const CSSPoint& aPoint,
+                               Modifiers aModifiers,
+                               const ScrollableLayerGuid& aGuid) override;
+
+  virtual void HandleSingleTap(const CSSPoint& aPoint,
+                               Modifiers aModifiers,
+                               const ScrollableLayerGuid& aGuid) override;
+
+  virtual void HandleLongTap(const CSSPoint& aPoint,
+                             Modifiers aModifiers,
+                             const ScrollableLayerGuid& aGuid,
+                             uint64_t aInputBlockId) override;
+
+  virtual void PostDelayedTask(Task* aTask, int aDelayMs) override;
+
+  virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) override;
+
+  virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+                                    APZStateChange aChange,
+                                    int aArg) override;
+
+  virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+                                         const nsString& aEvent) override;
+
+  // Needs to be called on the main thread.
+  virtual void NotifyFlushComplete() override;
+
+  virtual bool RecvUpdateHitRegion(const nsRegion& aRegion) override;
+
+  virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
+                              const ViewID& aViewId,
+                              const CSSRect& aRect,
+                              const uint32_t& aFlags) override;
+
+  virtual bool RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+                                             const uint64_t& aInputBlockId,
+                                             const bool& aPreventDefault) override;
+
+  virtual bool RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
+
+  virtual bool RecvSetTargetAPZC(const uint64_t& aInputBlockId,
+                                 nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+  virtual bool RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
+                                           nsTArray<TouchBehaviorFlags>&& aFlags) override;
+
+  virtual bool RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
+                                         const ViewID& aViewId,
+                                         const MaybeZoomConstraints& aConstraints) override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual void Destroy() override;
+
+  virtual void ChildAdopted() override;
+
+private:
+  bool CanSend()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return !!mBrowserParent;
+  }
+  already_AddRefed<APZCTreeManager> GetApzcTreeManager();
+
+  MessageLoop* mUILoop;
+  uint64_t mLayersId;
+  RefPtr<dom::TabParent> mBrowserParent;
+
+  // Mutex protecting members below accessed from multiple threads.
+  mozilla::Mutex mMutex;
+
+  RefPtr<APZCTreeManager> mApzcTreeManager;
+  nsRegion mTouchSensitiveRegion;
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_RemoteContentController_h
--- a/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h
+++ b/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_
 #define THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_
 
+#include "base/message_loop.h"
 #include "MainThreadUtils.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 inline MessageLoop* GetMainLoopAssertingMainThread()
 {
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -146,32 +146,34 @@ EXPORTS.mozilla.layers += [
     'composite/TextureHost.h',
     'composite/TiledContentHost.h',
     'Compositor.h',
     'CompositorTypes.h',
     'D3D11ShareHandleImage.h',
     'D3D9SurfaceImage.h',
     'Effects.h',
     'ImageDataSerializer.h',
+    'ipc/APZChild.h',
     'ipc/AsyncTransactionTracker.h',
     'ipc/CompositableForwarder.h',
     'ipc/CompositableTransactionParent.h',
     'ipc/CompositorChild.h',
     'ipc/CompositorLRU.h',
     'ipc/CompositorParent.h',
     'ipc/FenceUtils.h',
     'ipc/GonkNativeHandle.h',
     'ipc/GonkNativeHandleUtils.h',
     'ipc/ImageBridgeChild.h',
     'ipc/ImageBridgeParent.h',
     'ipc/ImageContainerParent.h',
     'ipc/ISurfaceAllocator.h',
     'ipc/LayerAnimationUtils.h',
     'ipc/LayerTransactionChild.h',
     'ipc/LayerTransactionParent.h',
+    'ipc/RemoteContentController.h',
     'ipc/ShadowLayerChild.h',
     'ipc/ShadowLayers.h',
     'ipc/ShadowLayersManager.h',
     'ipc/SharedBufferManagerChild.h',
     'ipc/SharedBufferManagerParent.h',
     'ipc/SharedPlanarYCbCrImage.h',
     'ipc/SharedRGBImage.h',
     'LayerMetricsWrapper.h',
@@ -323,30 +325,32 @@ UNIFIED_SOURCES += [
     'composite/TiledContentHost.cpp',
     'Compositor.cpp',
     'CopyableCanvasLayer.cpp',
     'Effects.cpp',
     'FrameMetrics.cpp',
     'GLImages.cpp',
     'ImageDataSerializer.cpp',
     'ImageLayers.cpp',
+    'ipc/APZChild.cpp',
     'ipc/AsyncTransactionTracker.cpp',
     'ipc/CompositableTransactionParent.cpp',
     'ipc/CompositorBench.cpp',
     'ipc/CompositorChild.cpp',
     'ipc/CompositorLRU.cpp',
     'ipc/CompositorParent.cpp',
     'ipc/FenceUtils.cpp',
     'ipc/ImageBridgeChild.cpp',
     'ipc/ImageBridgeParent.cpp',
     'ipc/ImageContainerParent.cpp',
     'ipc/ISurfaceAllocator.cpp',
     'ipc/LayerAnimationUtils.cpp',
     'ipc/LayerTransactionChild.cpp',
     'ipc/LayerTransactionParent.cpp',
+    'ipc/RemoteContentController.cpp',
     'ipc/ShadowLayerChild.cpp',
     'ipc/ShadowLayerParent.cpp',
     'ipc/ShadowLayers.cpp',
     'ipc/SharedBufferManagerChild.cpp',
     'ipc/SharedBufferManagerParent.cpp',
     'ipc/SharedPlanarYCbCrImage.cpp',
     'ipc/SharedRGBImage.cpp',
     'LayerScope.cpp',
@@ -392,16 +396,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
         'basic/MacIOSurfaceTextureHostBasic.cpp',
         'opengl/MacIOSurfaceTextureClientOGL.cpp',
         'opengl/MacIOSurfaceTextureHostOGL.cpp',
     ]
 
 IPDL_SOURCES = [
     'ipc/LayersMessages.ipdlh',
     'ipc/LayersSurfaces.ipdlh',
+    'ipc/PAPZ.ipdl',
     'ipc/PCompositable.ipdl',
     'ipc/PCompositor.ipdl',
     'ipc/PImageBridge.ipdl',
     'ipc/PImageContainer.ipdl',
     'ipc/PLayer.ipdl',
     'ipc/PLayerTransaction.ipdl',
     'ipc/PSharedBufferManager.ipdl',
     'ipc/PTexture.ipdl',
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.h
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h
@@ -72,16 +72,18 @@ public:
     , mCompositor(aCompositor)
     , mGL(aCompositor->gl())
     , mTextureHandle(aTexure)
     , mFBO(aFBO)
   {}
 
   ~CompositingRenderTargetOGL();
 
+  virtual const char* Name() const override { return "CompositingRenderTargetOGL"; }
+
   /**
    * Create a render target around the default FBO, for rendering straight to
    * the window.
    */
   static already_AddRefed<CompositingRenderTargetOGL>
   RenderTargetForWindow(CompositorOGL* aCompositor,
                         const gfx::IntSize& aSize)
   {
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -23,16 +23,18 @@ namespace layers {
 class MacIOSurfaceTextureSourceOGL : public TextureSource
                                    , public TextureSourceOGL
 {
 public:
   MacIOSurfaceTextureSourceOGL(CompositorOGL* aCompositor,
                                MacIOSurface* aSurface);
   virtual ~MacIOSurfaceTextureSourceOGL();
 
+  virtual const char* Name() const override { return "MacIOSurfaceTextureSourceOGL"; }
+
   virtual TextureSourceOGL* AsSourceOGL() override { return this; }
 
   virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) override;
 
   virtual bool IsValid() const override { return !!gl(); }
 
   virtual gfx::IntSize GetSize() const override;
 
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -137,16 +137,17 @@ class TextureImageTextureSourceOGL final
 public:
   explicit TextureImageTextureSourceOGL(CompositorOGL *aCompositor,
                                         TextureFlags aFlags = TextureFlags::DEFAULT)
     : mCompositor(aCompositor)
     , mFlags(aFlags)
     , mIterating(false)
   {}
 
+  virtual const char* Name() const override { return "TextureImageTextureSourceOGL"; }
   // DataTextureSource
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   void EnsureBuffer(const gfx::IntSize& aSize,
                     gfxContentType aContentType);
@@ -232,16 +233,18 @@ public:
                   GLuint aTextureHandle,
                   GLenum aTarget,
                   gfx::IntSize aSize,
                   gfx::SurfaceFormat aFormat,
                   bool aExternallyOwned = false);
 
   ~GLTextureSource();
 
+  virtual const char* Name() const override { return "GLTextureSource"; }
+
   virtual GLTextureSource* AsGLTextureSource() override { return this; }
 
   virtual TextureSourceOGL* AsSourceOGL() override { return this; }
 
   virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) override;
 
   virtual bool IsValid() const override;
 
@@ -339,16 +342,18 @@ class SurfaceTextureSource : public Text
 public:
   SurfaceTextureSource(CompositorOGL* aCompositor,
                        mozilla::gl::AndroidSurfaceTexture* aSurfTex,
                        gfx::SurfaceFormat aFormat,
                        GLenum aTarget,
                        GLenum aWrapMode,
                        gfx::IntSize aSize);
 
+  virtual const char* Name() const override { return "SurfaceTextureSource"; }
+
   virtual TextureSourceOGL* AsSourceOGL() { return this; }
 
   virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) override;
 
   virtual bool IsValid() const override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
@@ -431,16 +436,18 @@ class EGLImageTextureSource : public Tex
 public:
   EGLImageTextureSource(CompositorOGL* aCompositor,
                         EGLImage aImage,
                         gfx::SurfaceFormat aFormat,
                         GLenum aTarget,
                         GLenum aWrapMode,
                         gfx::IntSize aSize);
 
+  virtual const char* Name() const override { return "EGLImageTextureSource"; }
+
   virtual TextureSourceOGL* AsSourceOGL() override { return this; }
 
   virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) override;
 
   virtual bool IsValid() const override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
--- a/gfx/skia/skia/src/ports/SkOSFile_stdio.cpp
+++ b/gfx/skia/skia/src/ports/SkOSFile_stdio.cpp
@@ -18,33 +18,33 @@
 #endif
 
 #ifdef SK_BUILD_FOR_IOS
 #import <CoreFoundation/CoreFoundation.h>
 
 static FILE* ios_open_from_bundle(const char path[], const char* perm) {
     // Get a reference to the main bundle
     CFBundleRef mainBundle = CFBundleGetMainBundle();
-    
+
     // Get a reference to the file's URL
     CFStringRef pathRef = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
     CFURLRef imageURL = CFBundleCopyResourceURL(mainBundle, pathRef, NULL, NULL);
     if (!imageURL) {
         return nullptr;
     }
-    
+
     // Convert the URL reference into a string reference
     CFStringRef imagePath = CFURLCopyFileSystemPath(imageURL, kCFURLPOSIXPathStyle);
-    
+
     // Get the system encoding method
     CFStringEncoding encodingMethod = CFStringGetSystemEncoding();
-    
+
     // Convert the string reference into a C string
     const char *finalPath = CFStringGetCStringPtr(imagePath, encodingMethod);
-   
+
     return fopen(finalPath, perm);
 }
 #endif
 
 
 FILE* sk_fopen(const char path[], SkFILE_Flags flags) {
     char    perm[4];
     char*   p = perm;
@@ -52,131 +52,131 @@ FILE* sk_fopen(const char path[], SkFILE
     if (flags & kRead_SkFILE_Flag) {
         *p++ = 'r';
     }
     if (flags & kWrite_SkFILE_Flag) {
         *p++ = 'w';
     }
     *p++ = 'b';
     *p = 0;
-    
+
     //TODO: on Windows fopen is just ASCII or the current code page,
     //convert to utf16 and use _wfopen
     FILE* file = nullptr;
 #ifdef SK_BUILD_FOR_IOS
     // if read-only, try to open from bundle first
     if (kRead_SkFILE_Flag == flags) {
         file = ios_open_from_bundle(path, perm);
     }
     // otherwise just read from the Documents directory (default)
     if (!file) {
 #endif
-        file = ::fopen(path, perm);
+        file = fopen(path, perm);
 #ifdef SK_BUILD_FOR_IOS
     }
 #endif
     if (nullptr == file && (flags & kWrite_SkFILE_Flag)) {
         SkDEBUGF(("sk_fopen: fopen(\"%s\", \"%s\") returned NULL (errno:%d): %s\n",
                   path, perm, errno, strerror(errno)));
     }
     return file;
 }
 
 char* sk_fgets(char* str, int size, FILE* f) {
-    return ::fgets(str, size, (FILE *)f);
+    return fgets(str, size, (FILE *)f);
 }
 
 int sk_feof(FILE *f) {
     // no :: namespace qualifier because it breaks android
     return feof((FILE *)f);
 }
 
 size_t sk_fgetsize(FILE* f) {
     SkASSERT(f);
 
-    long curr = ::ftell(f); // remember where we are
+    long curr = ftell(f); // remember where we are
     if (curr < 0) {
         return 0;
     }
 
-    ::fseek(f, 0, SEEK_END); // go to the end
-    long size = ::ftell(f); // record the size
+    fseek(f, 0, SEEK_END); // go to the end
+    long size = ftell(f); // record the size
     if (size < 0) {
         size = 0;
     }
 
-    ::fseek(f, curr, SEEK_SET); // go back to our prev location
+    fseek(f, curr, SEEK_SET); // go back to our prev location
     return size;
 }
 
 bool sk_frewind(FILE* f) {
     SkASSERT(f);
     ::rewind(f);
     return true;
 }
 
 size_t sk_fread(void* buffer, size_t byteCount, FILE* f) {
     SkASSERT(f);
     if (buffer == nullptr) {
-        size_t curr = ::ftell(f);
+        size_t curr = ftell(f);
         if ((long)curr == -1) {
             SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof(f), ferror(f)));
             return 0;
         }
-        int err = ::fseek(f, (long)byteCount, SEEK_CUR);
+        int err = fseek(f, (long)byteCount, SEEK_CUR);
         if (err != 0) {
             SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n",
                         byteCount, curr, feof(f), ferror(f), err));
             return 0;
         }
         return byteCount;
     }
     else
-        return ::fread(buffer, 1, byteCount, f);
+        return fread(buffer, 1, byteCount, f);
 }
 
 size_t sk_fwrite(const void* buffer, size_t byteCount, FILE* f) {
     SkASSERT(f);
-    return ::fwrite(buffer, 1, byteCount, f);
+    return fwrite(buffer, 1, byteCount, f);
 }
 
 void sk_fflush(FILE* f) {
     SkASSERT(f);
-    ::fflush(f);
+    fflush(f);
 }
 
 void sk_fsync(FILE* f) {
 #if !defined(_WIN32) && !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) \
         && !defined(_NEWLIB_VERSION)
-    int fd = ::fileno(f);
-    ::fsync(fd);
+    int fd = fileno(f);
+    fsync(fd);
 #endif
 }
 
 bool sk_fseek(FILE* f, size_t byteCount) {
-    int err = ::fseek(f, (long)byteCount, SEEK_SET);
+    int err = fseek(f, (long)byteCount, SEEK_SET);
     return err == 0;
 }
 
 bool sk_fmove(FILE* f, long byteCount) {
-    int err = ::fseek(f, byteCount, SEEK_CUR);
+    int err = fseek(f, byteCount, SEEK_CUR);
     return err == 0;
 }
 
 size_t sk_ftell(FILE* f) {
-    long curr = ::ftell(f);
+    long curr = ftell(f);
     if (curr < 0) {
         return 0;
     }
     return curr;
 }
 
 void sk_fclose(FILE* f) {
     SkASSERT(f);
-    ::fclose(f);
+    fclose(f);
 }
 
 bool sk_isdir(const char *path) {
     struct stat status;
     if (0 != stat(path, &status)) {
         return false;
     }
     return SkToBool(status.st_mode & S_IFDIR);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -191,24 +191,24 @@ public:
 /// this gets called to be large - it is meant for critical errors only.
 
 class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
 {
 public:
   explicit CrashStatsLogForwarder(const char* aKey);
   virtual void Log(const std::string& aString) override;
   virtual void CrashAction(LogReason aReason) override;
+  virtual bool UpdateStringsVector(const std::string& aString) override;
 
   virtual LoggingRecord LoggingRecordCopy() override;
 
   void SetCircularBufferSize(uint32_t aCapacity);
 
 private:
-  // Helpers for the Log()
-  bool UpdateStringsVector(const std::string& aString);
+  // Helper for the Log()
   void UpdateCrashReport();
 
 private:
   LoggingRecord mBuffer;
   nsCString mCrashCriticalKey;
   uint32_t mMaxCapacity;
   int32_t mIndex;
   Mutex mMutex;
@@ -266,39 +266,78 @@ CrashStatsLogForwarder::UpdateStringsVec
     mBuffer[index] = newEntry;
   }
   return true;
 }
 
 void CrashStatsLogForwarder::UpdateCrashReport()
 {
   std::stringstream message;
-  for(LoggingRecord::iterator it = mBuffer.begin(); it != mBuffer.end(); ++it) {
-    message << "|[" << Get<0>(*it) << "]" << Get<1>(*it) << " (t=" << Get<2>(*it) << ") ";
+  if (XRE_IsParentProcess()) {
+    for(LoggingRecord::iterator it = mBuffer.begin(); it != mBuffer.end(); ++it) {
+      message << "|[" << Get<0>(*it) << "]" << Get<1>(*it) << " (t=" << Get<2>(*it) << ") ";
+    }
+  } else {
+    for(LoggingRecord::iterator it = mBuffer.begin(); it != mBuffer.end(); ++it) {
+      message << "|[C" << Get<0>(*it) << "]" << Get<1>(*it) << " (t=" << Get<2>(*it) << ") ";
+    }
   }
 
 #ifdef MOZ_CRASHREPORTER
   nsCString reportString(message.str().c_str());
   nsresult annotated = CrashReporter::AnnotateCrashReport(mCrashCriticalKey, reportString);
 #else
   nsresult annotated = NS_ERROR_NOT_IMPLEMENTED;
 #endif
   if (annotated != NS_OK) {
     printf("Crash Annotation %s: %s",
            mCrashCriticalKey.get(), message.str().c_str());
   }
 }
 
+class LogForwarderEvent : public nsRunnable
+{
+  virtual ~LogForwarderEvent() {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  explicit LogForwarderEvent(const nsCString& aMessage) : mMessage(aMessage) {}
+
+  NS_IMETHOD Run() override {
+    MOZ_ASSERT(NS_IsMainThread() && XRE_IsContentProcess());
+    dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+    cc->SendGraphicsError(mMessage);
+    return NS_OK;
+  }
+
+protected:
+  nsCString mMessage;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(LogForwarderEvent, nsRunnable);
+
 void CrashStatsLogForwarder::Log(const std::string& aString)
 {
   MutexAutoLock lock(mMutex);
 
   if (UpdateStringsVector(aString)) {
     UpdateCrashReport();
   }
+
+  // Add it to the parent strings
+  if (!XRE_IsParentProcess()) {
+    nsCString stringToSend(aString.c_str());
+    if (NS_IsMainThread()) {
+      dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+      cc->SendGraphicsError(stringToSend);
+    } else {
+      nsCOMPtr<nsIRunnable> r1 = new LogForwarderEvent(stringToSend);
+      NS_DispatchToMainThread(r1);
+    }
+  }
 }
 
 class CrashTelemetryEvent : public nsRunnable
 {
   virtual ~CrashTelemetryEvent() {}