Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 14 Mar 2016 17:40:44 -0700
changeset 288566 992db1cffc5cf7af737b705e72ecd5c01a283fd9
parent 288540 b21c3e5856ae8873a4ee618517c738f579bd2d39 (current diff)
parent 288565 422077f61bcb9ae19215de09e635c1082357233e (diff)
child 288567 d6ee82b9a74155b6bfd544166f036fc572ae8c56
push id18167
push userkwierso@gmail.com
push dateTue, 15 Mar 2016 00:40:50 +0000
treeherderfx-team@992db1cffc5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
Merge m-c to fx-team, a=merge MozReview-Commit-ID: COpA4LGoMQN
dom/bluetooth/common/BluetoothHidManager.cpp
dom/bluetooth/common/BluetoothHidManager.h
dom/xslt/tests/mochitest/file_bug1222624.xml
dom/xslt/tests/mochitest/file_bug1222624.xsl
dom/xslt/tests/mochitest/file_bug1222624_data1.xml
dom/xslt/tests/mochitest/file_bug1222624_data2.xml
dom/xslt/tests/mochitest/file_bug1222624_sub.xsl
dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl
dom/xslt/tests/mochitest/test_bug1222624.html
netwerk/base/LoadInfo.cpp
testing/web-platform/meta/html/browsers/windows/browsing-context-names/002.html.ini
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1375,16 +1375,22 @@ pref("browser.newtabpage.directory.sourc
 pref("browser.newtabpage.directory.ping", "https://tiles.services.mozilla.com/v3/links/");
 
 // activates the remote-hosted newtab page
 pref("browser.newtabpage.remote", false);
 
 // Toggles endpoints allowed for remote newtab communications
 pref("browser.newtabpage.remote.mode", "production");
 
+// content-signature tests for remote newtab
+pref("browser.newtabpage.remote.content-signing-test", false);
+
+// verification keys for remote-hosted newtab page
+pref("browser.newtabpage.remote.keys", "");
+
 // Enable the DOM fullscreen API.
 pref("full-screen-api.enabled", true);
 
 // Startup Crash Tracking
 // number of startup crashes that can occur before starting into safe mode automatically
 // (this pref has no effect if more than 6 hours have passed since the last crash)
 pref("toolkit.startup.max_resumed_crashes", 3);
 
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -150,24 +150,36 @@ AboutRedirector::NewChannel(nsIURI* aURI
 
   nsresult rv;
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (int i = 0; i < kRedirTotal; i++) {
     if (!strcmp(path.get(), kRedirMap[i].id)) {
       nsAutoCString url;
+      nsLoadFlags loadFlags = static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL);
 
       if (path.EqualsLiteral("newtab")) {
         // let the aboutNewTabService decide where to redirect
         nsCOMPtr<nsIAboutNewTabService> aboutNewTabService =
           do_GetService("@mozilla.org/browser/aboutnewtab-service;1", &rv);
         NS_ENSURE_SUCCESS(rv, rv);
         rv = aboutNewTabService->GetDefaultURL(url);
         NS_ENSURE_SUCCESS(rv, rv);
+
+        // if about:newtab points to an external resource we have to make sure
+        // the content is signed and trusted
+        bool remoteEnabled = false;
+        rv = aboutNewTabService->GetRemoteEnabled(&remoteEnabled);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (remoteEnabled) {
+          NS_ENSURE_ARG_POINTER(aLoadInfo);
+          aLoadInfo->SetVerifySignedContent(true);
+          loadFlags = static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
+        }
       }
       // fall back to the specified url in the map
       if (url.IsEmpty()) {
         url.AssignASCII(kRedirMap[i].url);
       }
 
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
@@ -178,19 +190,19 @@ AboutRedirector::NewChannel(nsIURI* aURI
       // chrome:// or resource://) then set the LOAD_REPLACE flag on the
       // channel which forces the channel owner to reflect the displayed
       // URL rather then being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsLoadFlags loadFlags =
-        isUIResource ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
-                     : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
+      loadFlags = isUIResource
+                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
+                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
 
       rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
                                  tempURI,
                                  aLoadInfo,
                                  nullptr, // aLoadGroup
                                  nullptr, // aCallbacks
                                  loadFlags);
       NS_ENSURE_SUCCESS(rv, rv);
--- a/browser/components/newtab/aboutNewTabService.js
+++ b/browser/components/newtab/aboutNewTabService.js
@@ -27,16 +27,19 @@ const LOCAL_NEWTAB_URL = "chrome://brows
 
 const REMOTE_NEWTAB_PATH = "/v%VERSION%/%CHANNEL%/%LOCALE%/index.html";
 
 const ABOUT_URL = "about:newtab";
 
 // Pref that tells if remote newtab is enabled
 const PREF_REMOTE_ENABLED = "browser.newtabpage.remote";
 
+// Pref branch necesssary for testing
+const PREF_REMOTE_CS_TEST = "browser.newtabpage.remote.content-signing-test";
+
 // The preference that tells whether to match the OS locale
 const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
 
 // The preference that tells what locale the user selected
 const PREF_SELECTED_LOCALE = "general.useragent.locale";
 
 // The preference that tells what remote mode is enabled.
 const PREF_REMOTE_MODE = "browser.newtabpage.remote.mode";
@@ -121,35 +124,42 @@ AboutNewTabService.prototype = {
    */
   toggleRemote(stateEnabled, forceState) {
 
     if (!forceState && (this._overriden || stateEnabled === this._remoteEnabled)) {
       // exit there is no change of state
       return false;
     }
 
+    let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST);
     if (stateEnabled) {
-      this._remoteURL = this.generateRemoteURL();
+      if (!csTest) {
+        this._remoteURL = this.generateRemoteURL();
+      } else {
+        this._remoteURL = this._newTabURL;
+      }
       NewTabPrefsProvider.prefs.on(
         PREF_SELECTED_LOCALE,
         this._updateRemoteMaybe);
       NewTabPrefsProvider.prefs.on(
         PREF_MATCH_OS_LOCALE,
         this._updateRemoteMaybe);
       NewTabPrefsProvider.prefs.on(
         PREF_REMOTE_MODE,
         this._updateRemoteMaybe);
       this._remoteEnabled = true;
     } else {
       NewTabPrefsProvider.prefs.off(PREF_SELECTED_LOCALE, this._updateRemoteMaybe);
       NewTabPrefsProvider.prefs.off(PREF_MATCH_OS_LOCALE, this._updateRemoteMaybe);
       NewTabPrefsProvider.prefs.off(PREF_REMOTE_MODE, this._updateRemoteMaybe);
       this._remoteEnabled = false;
     }
-    this._newTabURL = ABOUT_URL;
+    if (!csTest) {
+      this._newTabURL = ABOUT_URL;
+    }
     return true;
   },
 
   /*
    * Generate a default url based on remote mode, version, locale and update channel
    */
   generateRemoteURL() {
     let releaseName = this.releaseFromUpdateChannel(UpdateUtils.UpdateChannel);
@@ -165,20 +175,23 @@ AboutNewTabService.prototype = {
   },
 
   /*
    * Returns the default URL.
    *
    * This URL only depends on the browser.newtabpage.remote pref. Overriding
    * the newtab page has no effect on the result of this function.
    *
+   * The result is also the remote URL if this is in a test (PREF_REMOTE_CS_TEST)
+   *
    * @returns {String} the default newtab URL, remote or local depending on browser.newtabpage.remote
    */
   get defaultURL() {
-    if (this._remoteEnabled) {
+    let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST);
+    if (this._remoteEnabled || csTest)  {
       return this._remoteURL;
     }
     return LOCAL_NEWTAB_URL;
   },
 
   /*
    * Updates the remote location when the page is not overriden.
    *
@@ -214,38 +227,44 @@ AboutNewTabService.prototype = {
     return REMOTE_NEWTAB_VERSION;
   },
 
   get remoteReleaseName() {
     return this.releaseFromUpdateChannel(UpdateUtils.UpdateChannel);
   },
 
   set newTabURL(aNewTabURL) {
+    let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST);
     aNewTabURL = aNewTabURL.trim();
     if (aNewTabURL === ABOUT_URL) {
       // avoid infinite redirects in case one sets the URL to about:newtab
       this.resetNewTabURL();
       return;
     } else if (aNewTabURL === "") {
       aNewTabURL = "about:blank";
     }
     let remoteURL = this.generateRemoteURL();
     let prefRemoteEnabled = Services.prefs.getBoolPref(PREF_REMOTE_ENABLED);
     let isResetLocal = !prefRemoteEnabled && aNewTabURL === LOCAL_NEWTAB_URL;
     let isResetRemote = prefRemoteEnabled && aNewTabURL === remoteURL;
 
     if (isResetLocal || isResetRemote) {
-      if (this._overriden) {
-        // only trigger a reset if previously overridden
+      if (this._overriden && !csTest) {
+        // only trigger a reset if previously overridden and this is no test
         this.resetNewTabURL();
       }
       return;
     }
     // turn off remote state if needed
-    this.toggleRemote(false);
+    if (!csTest) {
+      this.toggleRemote(false);
+    } else {
+      // if this is a test, we want the remoteURL to be set
+      this._remoteURL = aNewTabURL;
+    }
     this._newTabURL = aNewTabURL;
     this._overridden = true;
     Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
   },
 
   get overridden() {
     return this._overridden;
   },
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7574,16 +7574,27 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
   //   4. Throw an error dialog box...
   //
   if (url && NS_FAILED(aStatus)) {
     if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
         aStatus == NS_ERROR_CORRUPTED_CONTENT ||
         aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
       DisplayLoadError(aStatus, url, nullptr, aChannel);
       return NS_OK;
+    } else if (aStatus == NS_ERROR_INVALID_SIGNATURE) {
+      // NS_ERROR_INVALID_SIGNATURE indicates a content-signature error.
+      // This currently only happens in case a remote about page fails.
+      // We have to load a fallback in this case.
+      // XXX: We always load about blank here, firefox has to overwrite this if
+      // it wants to display something else.
+      return LoadURI(MOZ_UTF16("about:blank"),  // URI string
+                     nsIChannel::LOAD_NORMAL,   // Load flags
+                     nullptr,                   // Referring URI
+                     nullptr,                   // Post data stream
+                     nullptr);                  // Headers stream
     }
 
     // Handle iframe document not loading error because source was
     // a tracking URL. We make a note of this iframe node by including
     // it in a dedicated array of blocked tracking nodes under its parent
     // document. (document of parent window of blocked document)
     if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
       // frameElement is our nsIContent to be annotated
@@ -9544,16 +9555,37 @@ nsDocShell::CreatePrincipalFromReferrer(
   attrs.InheritFromDocShellToDoc(mOriginAttributes, aReferrer);
   nsCOMPtr<nsIPrincipal> prin =
     BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
   prin.forget(aResult);
 
   return *aResult ? NS_OK : NS_ERROR_FAILURE;
 }
 
+bool
+nsDocShell::IsAboutNewtab(nsIURI* aURI)
+{
+  if (!aURI) {
+    return false;
+  }
+  bool isAbout;
+  if (NS_WARN_IF(NS_FAILED(aURI->SchemeIs("about", &isAbout)))) {
+    return false;
+  }
+  if (!isAbout) {
+    return false;
+  }
+
+  nsAutoCString module;
+  if (NS_WARN_IF(NS_FAILED(NS_GetAboutModuleName(aURI, module)))) {
+    return false;
+  }
+  return module.Equals("newtab");
+}
+
 NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI* aURI,
                          nsIURI* aOriginalURI,
                          bool aLoadReplace,
                          nsIURI* aReferrer,
                          uint32_t aReferrerPolicy,
                          nsISupports* aOwner,
                          uint32_t aFlags,
@@ -10213,18 +10245,26 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       return NS_OK;
     }
   }
 
   // Check if the webbrowser chrome wants the load to proceed; this can be
   // used to cancel attempts to load URIs in the wrong process.
   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
   if (browserChrome3) {
+    // In case this is a remote newtab load, set aURI to aOriginalURI (newtab).
+    // This ensures that the verifySignedContent flag is set on loadInfo in
+    // DoURILoad.
+    nsIURI* uriForShouldLoadCheck = aURI;
+    if (IsAboutNewtab(aOriginalURI)) {
+      uriForShouldLoadCheck = aOriginalURI;
+    }
     bool shouldLoad;
-    rv = browserChrome3->ShouldLoadURI(this, aURI, aReferrer, &shouldLoad);
+    rv = browserChrome3->ShouldLoadURI(this, uriForShouldLoadCheck, aReferrer,
+                                       &shouldLoad);
     if (NS_SUCCEEDED(rv) && !shouldLoad) {
       return NS_OK;
     }
   }
 
   // mContentViewer->PermitUnload can destroy |this| docShell, which
   // causes the next call of CanSavePresentation to crash.
   // Hold onto |this| until we return, to prevent a crash from happening.
@@ -10864,16 +10904,25 @@ nsDocShell::DoURILoad(nsIURI* aURI,
     if (aHeadersData) {
       rv = AddHeadersToChannel(aHeadersData, httpChannel);
     }
     // Set the referrer explicitly
     if (aReferrerURI && aSendReferrer) {
       // Referrer is currenly only set for link clicks here.
       httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
     }
+    // set Content-Signature enforcing bit if aOriginalURI == about:newtab
+    if (aOriginalURI && httpChannel) {
+      if (IsAboutNewtab(aOriginalURI)) {
+        nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo();
+        if (loadInfo) {
+          loadInfo->SetVerifySignedContent(true);
+        }
+      }
+    }
   }
 
   nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
   if (scriptChannel) {
     // Allow execution against our context if the principals match
     scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
   }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -723,16 +723,19 @@ protected:
   already_AddRefed<nsDocShell> GetParentDocshell();
 
   // Check if we have an app redirect registered for the URI and redirect if
   // needed. Returns true if a redirect happened, false otherwise.
   bool DoAppRedirectIfNeeded(nsIURI* aURI,
                              nsIDocShellLoadInfo* aLoadInfo,
                              bool aFirstParty);
 
+  // Check if aURI is about:newtab.
+  bool IsAboutNewtab(nsIURI* aURI);
+
 protected:
   nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);
   nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos,
                              int32_t aCurVerticalPos);
 
   // Override the parent setter from nsDocLoader
   virtual nsresult SetDocLoaderParent(nsDocLoader* aLoader) override;
 
--- a/dom/animation/AnimationPerformanceWarning.cpp
+++ b/dom/animation/AnimationPerformanceWarning.cpp
@@ -47,25 +47,25 @@ AnimationPerformanceWarning::ToLocalized
       key = "AnimationWarningTransformBackfaceVisibilityHidden";
       break;
     case Type::TransformPreserve3D:
       key = "AnimationWarningTransformPreserve3D";
       break;
     case Type::TransformSVG:
       key = "AnimationWarningTransformSVG";
       break;
+    case Type::TransformWithGeometricProperties:
+      key = "AnimationWarningTransformWithGeometricProperties";
+      break;
     case Type::TransformFrameInactive:
       key = "AnimationWarningTransformFrameInactive";
       break;
     case Type::OpacityFrameInactive:
       key = "AnimationWarningOpacityFrameInactive";
       break;
-    case Type::WithGeometricProperties:
-      key = "AnimationWarningWithGeometricProperties";
-      break;
   }
 
   nsresult rv =
     nsContentUtils::GetLocalizedString(nsContentUtils::eLAYOUT_PROPERTIES,
                                        key, aLocalizedString);
   return NS_SUCCEEDED(rv);
 }
 
--- a/dom/animation/AnimationPerformanceWarning.h
+++ b/dom/animation/AnimationPerformanceWarning.h
@@ -16,19 +16,19 @@ namespace mozilla {
 // Represents the reason why we can't run the CSS property on the compositor.
 struct AnimationPerformanceWarning
 {
   enum class Type : uint8_t {
     ContentTooLarge,
     TransformBackfaceVisibilityHidden,
     TransformPreserve3D,
     TransformSVG,
+    TransformWithGeometricProperties,
     TransformFrameInactive,
     OpacityFrameInactive,
-    WithGeometricProperties
   };
 
   explicit AnimationPerformanceWarning(Type aType)
     : mType(aType) { }
 
   AnimationPerformanceWarning(Type aType,
                               std::initializer_list<int32_t> aParams)
     : mType(aType)
--- a/dom/animation/AnimationUtils.cpp
+++ b/dom/animation/AnimationUtils.cpp
@@ -10,18 +10,17 @@
 #include "nsDebug.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsGlobalWindow.h"
 #include "nsString.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
-#include "mozilla/dom/Element.h" // For dom::Element
-#include "xpcpublic.h" // For xpc::CurrentWindowOrNull
+#include "xpcpublic.h" // For xpc::NativeGlobal
 
 namespace mozilla {
 
 /* static */ void
 AnimationUtils::LogAsyncAnimationFailure(nsCString& aMessage,
                                          const nsIContent* aContent)
 {
   if (aContent) {
@@ -36,32 +35,28 @@ AnimationUtils::LogAsyncAnimationFailure
     }
     aMessage.Append(']');
   }
   aMessage.Append('\n');
   printf_stderr("%s", aMessage.get());
 }
 
 /* static */ Maybe<ComputedTimingFunction>
-AnimationUtils::ParseEasing(const dom::Element* aTarget,
-                            const nsAString& aEasing)
+AnimationUtils::ParseEasing(const nsAString& aEasing,
+                            nsIDocument* aDocument)
 {
-  if (!aTarget) {
-    return Nothing();
-  }
-
-  nsIDocument* doc = aTarget->OwnerDoc();
+  MOZ_ASSERT(aDocument);
 
   nsCSSValue value;
   nsCSSParser parser;
   parser.ParseLonghandProperty(eCSSProperty_animation_timing_function,
                                aEasing,
-                               doc->GetDocumentURI(),
-                               doc->GetDocumentURI(),
-                               doc->NodePrincipal(),
+                               aDocument->GetDocumentURI(),
+                               aDocument->GetDocumentURI(),
+                               aDocument->NodePrincipal(),
                                value);
 
   switch (value.GetUnit()) {
     case eCSSUnit_List: {
       const nsCSSValueList* list = value.GetListValue();
       if (list->mNext) {
         // don't support a list of timing functions
         break;
--- a/dom/animation/AnimationUtils.h
+++ b/dom/animation/AnimationUtils.h
@@ -14,20 +14,16 @@
 class nsIContent;
 class nsIDocument;
 struct JSContext;
 
 namespace mozilla {
 
 class ComputedTimingFunction;
 
-namespace dom {
-class Element;
-}
-
 class AnimationUtils
 {
 public:
   static dom::Nullable<double>
   TimeDurationToDouble(const dom::Nullable<TimeDuration>& aTime)
   {
     dom::Nullable<double> result;
 
@@ -54,17 +50,17 @@ public:
                                        const nsIContent* aContent = nullptr);
 
   /**
    * Parses a CSS <single-transition-timing-function> value from
    * aEasing into a ComputedTimingFunction.  If parsing fails, Nothing() will
    * be returned.
    */
   static Maybe<ComputedTimingFunction>
-  ParseEasing(const dom::Element* aTarget, const nsAString& aEasing);
+  ParseEasing(const nsAString& aEasing, nsIDocument* aDocument);
 
   /**
    * Get the document from the JS context to use when parsing CSS properties.
    */
   static nsIDocument*
   GetCurrentRealmDocument(JSContext* aCx);
 };
 
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -106,23 +106,25 @@ FindAnimationsForCompositor(const nsIFra
     MOZ_ASSERT(effect && effect->GetAnimation());
     Animation* animation = effect->GetAnimation();
 
     if (!animation->IsPlaying()) {
       continue;
     }
 
     AnimationPerformanceWarning::Type warningType;
-    if (effect->ShouldBlockCompositorAnimations(aFrame,
-                                                warningType)) {
+    if (aProperty == eCSSProperty_transform &&
+        effect->ShouldBlockAsyncTransformAnimations(aFrame,
+                                                    warningType)) {
       if (aMatches) {
         aMatches->Clear();
       }
-      effect->SetPerformanceWarning(
-        aProperty, AnimationPerformanceWarning(warningType));
+      EffectCompositor::SetPerformanceWarning(
+        aFrame, aProperty,
+        AnimationPerformanceWarning(warningType));
       return false;
     }
 
     if (!effect->HasAnimationOfProperty(aProperty)) {
       continue;
     }
 
     if (aMatches) {
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "mozilla/dom/KeyframeEffect.h"
 
+#include "mozilla/dom/AnimatableBinding.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/dom/PropertyIndexedKeyframesBinding.h"
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/LookAndFeel.h" // For LookAndFeel::GetInt
 #include "mozilla/StyleAnimationValue.h"
 #include "Layers.h" // For Layer
@@ -647,24 +648,37 @@ KeyframeEffectReadOnly::SetIsRunningOnCo
     }
   }
 }
 
 KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
 {
 }
 
-template <class KeyframeEffectType>
+template <class KeyframeEffectType, class OptionsType>
 /* static */ already_AddRefed<KeyframeEffectType>
-KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal,
-                                                const Nullable<ElementOrCSSPseudoElement>& aTarget,
-                                                JS::Handle<JSObject*> aFrames,
-                                                const TimingParams& aTiming,
-                                                ErrorResult& aRv)
+KeyframeEffectReadOnly::ConstructKeyframeEffect(
+    const GlobalObject& aGlobal,
+    const Nullable<ElementOrCSSPseudoElement>& aTarget,
+    JS::Handle<JSObject*> aFrames,
+    const OptionsType& aOptions,
+    ErrorResult& aRv)
 {
+  nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
+  if (!doc) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  TimingParams timingParams =
+    TimingParams::FromOptionsUnion(aOptions, doc, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
   if (aTarget.IsNull()) {
     // We don't support null targets yet.
     aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR);
     return nullptr;
   }
 
   const ElementOrCSSPseudoElement& target = aTarget.Value();
   MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
@@ -689,39 +703,21 @@ KeyframeEffectReadOnly::ConstructKeyfram
                              aFrames, animationProperties, aRv);
 
   if (aRv.Failed()) {
     return nullptr;
   }
 
   RefPtr<KeyframeEffectType> effect =
     new KeyframeEffectType(targetElement->OwnerDoc(), targetElement,
-                           pseudoType, aTiming);
+                           pseudoType, timingParams);
   effect->mProperties = Move(animationProperties);
   return effect.forget();
 }
 
-// Explicit instantiations to avoid linker errors.
-
-template
-already_AddRefed<KeyframeEffectReadOnly>
-KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal,
-                                                  const Nullable<ElementOrCSSPseudoElement>& aTarget,
-                                                  JS::Handle<JSObject*> aFrames,
-                                                  const TimingParams& aTiming,
-                                                  ErrorResult& aRv);
-
-template
-already_AddRefed<KeyframeEffect>
-KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal,
-                                                  const Nullable<ElementOrCSSPseudoElement>& aTarget,
-                                                  JS::Handle<JSObject*> aFrames,
-                                                  const TimingParams& aTiming,
-                                                  ErrorResult& aRv);
-
 void
 KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
 {
   for (AnimationProperty& property : mProperties) {
     property.mIsRunningOnCompositor = false;
   }
 }
 
@@ -1282,20 +1278,19 @@ GenerateValueEntries(Element* aTarget,
                      ErrorResult& aRv)
 {
   nsCSSPropertySet properties;              // All properties encountered.
   nsCSSPropertySet propertiesWithFromValue; // Those with a defined 0% value.
   nsCSSPropertySet propertiesWithToValue;   // Those with a defined 100% value.
 
   for (OffsetIndexedKeyframe& keyframe : aKeyframes) {
     float offset = float(keyframe.mKeyframeDict.mOffset.Value());
-    // ParseEasing uses element's owner doc, so if it is a pseudo element,
-    // we use its parent element's owner doc.
     Maybe<ComputedTimingFunction> easing =
-      AnimationUtils::ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing);
+      AnimationUtils::ParseEasing(keyframe.mKeyframeDict.mEasing,
+                                  aTarget->OwnerDoc());
     // We ignore keyframe.mKeyframeDict.mComposite since we don't support
     // composite modes on keyframes yet.
 
     // keyframe.mPropertyValuePairs is currently sorted by CSS property IDL
     // name, since that was the order we read the properties from the JS
     // object.  Re-sort the list so that longhand properties appear before
     // shorthands, and with shorthands all appearing in increasing order of
     // number of components.  For two longhand properties, or two shorthands
@@ -1549,20 +1544,18 @@ BuildAnimationPropertyListFromPropertyIn
   // get its explicit dictionary members.
   binding_detail::FastPropertyIndexedKeyframes keyframes;
   if (!keyframes.Init(aCx, aValue, "PropertyIndexedKeyframes argument",
                       false)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  // ParseEasing uses element's owner doc, so if it is a pseudo element,
-  // we use its parent element's owner doc.
   Maybe<ComputedTimingFunction> easing =
-    AnimationUtils::ParseEasing(aTarget, keyframes.mEasing);
+    AnimationUtils::ParseEasing(keyframes.mEasing, aTarget->OwnerDoc());
 
   // We ignore easing.mComposite since we don't support composite modes on
   // keyframes yet.
 
   // Get all the property--value-list pairs off the object.
   JS::Rooted<JSObject*> object(aCx, &aValue.toObject());
   nsTArray<PropertyValuesPair> propertyValuesPairs;
   if (!GetPropertyValuesPairs(aCx, object, ListAllowance::eAllow,
@@ -1752,16 +1745,29 @@ KeyframeEffectReadOnly::BuildAnimationPr
   } else {
     BuildAnimationPropertyListFromPropertyIndexedKeyframes(aCx, aTarget,
                                                            aPseudoType,
                                                            objectValue, aResult,
                                                            aRv);
   }
 }
 
+/* static */ already_AddRefed<KeyframeEffectReadOnly>
+KeyframeEffectReadOnly::Constructor(
+    const GlobalObject& aGlobal,
+    const Nullable<ElementOrCSSPseudoElement>& aTarget,
+    JS::Handle<JSObject*> aFrames,
+    const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
+    ErrorResult& aRv)
+{
+  return ConstructKeyframeEffect<KeyframeEffectReadOnly>(aGlobal, aTarget,
+                                                         aFrames, aOptions,
+                                                         aRv);
+}
+
 void
 KeyframeEffectReadOnly::GetTarget(
     Nullable<OwningElementOrCSSPseudoElement>& aRv) const
 {
   if (!mTarget) {
     aRv.SetNull();
     return;
   }
@@ -2130,17 +2136,17 @@ KeyframeEffectReadOnly::CanAnimateTransf
     aPerformanceWarning = AnimationPerformanceWarning::Type::TransformSVG;
     return false;
   }
 
   return true;
 }
 
 bool
-KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(
+KeyframeEffectReadOnly::ShouldBlockAsyncTransformAnimations(
   const nsIFrame* aFrame,
   AnimationPerformanceWarning::Type& aPerformanceWarning) const
 {
   // We currently only expect this method to be called when this effect
   // is attached to a playing Animation. If that ever changes we'll need
   // to update this to only return true when that is the case since paused,
   // filling, cancelled Animations etc. shouldn't stop other Animations from
   // running on the compositor.
@@ -2150,17 +2156,17 @@ KeyframeEffectReadOnly::ShouldBlockCompo
     // If a property is overridden in the CSS cascade, it should not block other
     // animations from running on the compositor.
     if (!property.mWinsInCascade) {
       continue;
     }
     // Check for geometric properties
     if (IsGeometricProperty(property.mProperty)) {
       aPerformanceWarning =
-        AnimationPerformanceWarning::Type::WithGeometricProperties;
+        AnimationPerformanceWarning::Type::TransformWithGeometricProperties;
       return true;
     }
 
     // Check for unsupported transform animations
     if (property.mProperty == eCSSProperty_transform) {
       if (!CanAnimateTransformOnCompositor(aFrame,
                                            aPerformanceWarning)) {
         return true;
@@ -2210,16 +2216,40 @@ KeyframeEffect::KeyframeEffect(nsIDocume
 
 JSObject*
 KeyframeEffect::WrapObject(JSContext* aCx,
                            JS::Handle<JSObject*> aGivenProto)
 {
   return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto);
 }
 
+/* static */ already_AddRefed<KeyframeEffect>
+KeyframeEffect::Constructor(
+    const GlobalObject& aGlobal,
+    const Nullable<ElementOrCSSPseudoElement>& aTarget,
+    JS::Handle<JSObject*> aFrames,
+    const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
+    ErrorResult& aRv)
+{
+  return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
+                                                 aOptions, aRv);
+}
+
+/* static */ already_AddRefed<KeyframeEffect>
+KeyframeEffect::Constructor(
+    const GlobalObject& aGlobal,
+    const Nullable<ElementOrCSSPseudoElement>& aTarget,
+    JS::Handle<JSObject*> aFrames,
+    const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
+    ErrorResult& aRv)
+{
+  return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
+                                                 aOptions, aRv);
+}
+
 void KeyframeEffect::NotifySpecifiedTimingUpdated()
 {
   nsIDocument* doc = nullptr;
   // Bug 1249219:
   // We don't support animation mutation observers on pseudo-elements yet.
   if (mTarget &&
       mPseudoType == CSSPseudoElementType::NotPseudo) {
     doc = mTarget->OwnerDoc();
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -37,16 +37,17 @@ class nsPresContext;
 namespace mozilla {
 
 class AnimValuesStyleRule;
 enum class CSSPseudoElementType : uint8_t;
 
 namespace dom {
 class ElementOrCSSPseudoElement;
 class OwningElementOrCSSPseudoElement;
+class UnrestrictedDoubleOrKeyframeAnimationOptions;
 class UnrestrictedDoubleOrKeyframeEffectOptions;
 enum class IterationCompositeOperation : uint32_t;
 enum class CompositeOperation : uint32_t;
 struct AnimationPropertyState;
 }
 
 /**
  * Stores the results of calculating the timing properties of an animation
@@ -197,26 +198,17 @@ public:
   }
 
   // KeyframeEffectReadOnly interface
   static already_AddRefed<KeyframeEffectReadOnly>
   Constructor(const GlobalObject& aGlobal,
               const Nullable<ElementOrCSSPseudoElement>& aTarget,
               JS::Handle<JSObject*> aFrames,
               const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
-              ErrorResult& aRv)
-  {
-    TimingParams timingParams =
-      TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
-    if (aRv.Failed()) {
-      return nullptr;
-    }
-    return ConstructKeyframeEffect<KeyframeEffectReadOnly>(
-             aGlobal, aTarget, aFrames, timingParams, aRv);
-  }
+              ErrorResult& aRv);
 
   void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
   void GetFrames(JSContext*& aCx,
                  nsTArray<JSObject*>& aResult,
                  ErrorResult& aRv);
 
   // Temporary workaround to return both the target element and pseudo-type
   // until we implement PseudoElement (bug 1174575).
@@ -309,34 +301,26 @@ public:
   void ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
                     nsCSSPropertySet& aSetProperties);
   // Returns true if at least one property is being animated on compositor.
   bool IsRunningOnCompositor() const;
   void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning);
 
   void GetPropertyState(nsTArray<AnimationPropertyState>& aStates) const;
 
-  // Returns true if this effect, applied to |aFrame|, contains
-  // properties that mean we shouldn't run *any* compositor animations on this
-  // element.
+  // Returns true if this effect, applied to |aFrame|, contains properties
+  // that mean we shouldn't run transform compositor animations on this element.
   //
   // For example, if we have an animation of geometric properties like 'left'
-  // and 'top' on an element, we force all 'transform' and 'opacity' animations
-  // running at the same time on the same element to run on the main thread.
-  //
-  // Similarly, some transform animations cannot be run on the compositor and
-  // when that is the case we simply disable all compositor animations
-  // on the same element.
+  // and 'top' on an element, we force all 'transform' animations running at
+  // the same time on the same element to run on the main thread.
   //
-  // Bug 1218620 - It seems like we don't need to be this restrictive. Wouldn't
-  // it be ok to do 'opacity' animations on the compositor in either case?
-  //
-  // When returning true, |aOutPerformanceWarning| stores the reason why
-  // we shouldn't run the compositor animations.
-  bool ShouldBlockCompositorAnimations(
+  // When returning true, |aPerformanceWarning| stores the reason why
+  // we shouldn't run the transform animations.
+  bool ShouldBlockAsyncTransformAnimations(
     const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aPerformanceWarning) const;
 
   nsIDocument* GetRenderedDocument() const;
   nsPresContext* GetPresContext() const;
 
   // Associates a warning with the animated property on the specified frame
   // indicating why, for example, the property could not be animated on the
@@ -349,22 +333,22 @@ public:
 protected:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          Element* aTarget,
                          CSSPseudoElementType aPseudoType,
                          AnimationEffectTimingReadOnly* aTiming);
 
   virtual ~KeyframeEffectReadOnly();
 
-  template<typename KeyframeEffectType>
+  template<class KeyframeEffectType, class OptionsType>
   static already_AddRefed<KeyframeEffectType>
   ConstructKeyframeEffect(const GlobalObject& aGlobal,
                           const Nullable<ElementOrCSSPseudoElement>& aTarget,
                           JS::Handle<JSObject*> aFrames,
-                          const TimingParams& aTiming,
+                          const OptionsType& aOptions,
                           ErrorResult& aRv);
 
   void ResetIsRunningOnCompositor();
 
   // This effect is registered with its target element so long as:
   //
   // (a) It has a target element, and
   // (b) It is "relevant" (i.e. yet to finish but not idle, or finished but
@@ -428,39 +412,27 @@ public:
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<KeyframeEffect>
   Constructor(const GlobalObject& aGlobal,
               const Nullable<ElementOrCSSPseudoElement>& aTarget,
               JS::Handle<JSObject*> aFrames,
               const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
-              ErrorResult& aRv)
-  {
-    TimingParams timingParams =
-      TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
-    if (aRv.Failed()) {
-      return nullptr;
-    }
-    return ConstructKeyframeEffect<KeyframeEffect>(
-      aGlobal, aTarget, aFrames, timingParams, aRv);
-  }
+              ErrorResult& aRv);
 
-  // More generalized version for Animatable.animate.
+  // Variant of Constructor that accepts a KeyframeAnimationOptions object
+  // for use with for Animatable.animate.
   // Not exposed to content.
   static already_AddRefed<KeyframeEffect>
-  inline Constructor(const GlobalObject& aGlobal,
-                     const Nullable<ElementOrCSSPseudoElement>& aTarget,
-                     JS::Handle<JSObject*> aFrames,
-                     const TimingParams& aTiming,
-                     ErrorResult& aRv)
-  {
-    return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
-                                                   aTiming, aRv);
-  }
+  Constructor(const GlobalObject& aGlobal,
+              const Nullable<ElementOrCSSPseudoElement>& aTarget,
+              JS::Handle<JSObject*> aFrames,
+              const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
+              ErrorResult& aRv);
 
   void NotifySpecifiedTimingUpdated();
 
 protected:
   ~KeyframeEffect() override;
 };
 
 } // namespace dom
--- a/dom/animation/TimingParams.cpp
+++ b/dom/animation/TimingParams.cpp
@@ -1,16 +1,18 @@
 /* -*- 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 "mozilla/TimingParams.h"
 
+#include "nsIDocument.h"
+
 namespace mozilla {
 
 template <class OptionsType>
 static const dom::AnimationEffectTimingProperties&
 GetTimingProperties(const OptionsType& aOptions);
 
 template <>
 /* static */ const dom::AnimationEffectTimingProperties&
@@ -27,46 +29,30 @@ GetTimingProperties(
   const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions)
 {
   MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
   return aOptions.GetAsKeyframeAnimationOptions();
 }
 
 template <class OptionsType>
 static TimingParams
-TimingParamsFromOptionsUnion(
-  const OptionsType& aOptions,
-  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
-  ErrorResult& aRv)
+TimingParamsFromOptionsUnion(const OptionsType& aOptions,
+                             nsIDocument* aDocument,
+                             ErrorResult& aRv)
 {
   TimingParams result;
   if (aOptions.IsUnrestrictedDouble()) {
     double durationInMs = aOptions.GetAsUnrestrictedDouble();
     if (durationInMs >= 0) {
       result.mDuration.emplace(
         StickyTimeDuration::FromMilliseconds(durationInMs));
     } else {
       aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     }
   } else {
-    // If aTarget is a pseudo element, we pass its parent element because
-    // TimingParams only needs its owner doc to parse easing and both pseudo
-    // element and its parent element should have the same owner doc.
-    // Bug 1246320: Avoid passing the element for parsing the timing function
-    RefPtr<dom::Element> targetElement;
-    if (!aTarget.IsNull()) {
-      const dom::ElementOrCSSPseudoElement& target = aTarget.Value();
-      MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
-                 "Uninitialized target");
-      if (target.IsElement()) {
-        targetElement = &target.GetAsElement();
-      } else {
-        targetElement = target.GetAsCSSPseudoElement().ParentElement();
-      }
-    }
     const dom::AnimationEffectTimingProperties& timing =
       GetTimingProperties(aOptions);
 
     Maybe<StickyTimeDuration> duration =
       TimingParams::ParseDuration(timing.mDuration, aRv);
     if (aRv.Failed()) {
       return result;
     }
@@ -77,38 +63,37 @@ TimingParamsFromOptionsUnion(
 
     result.mDuration = duration;
     result.mDelay = TimeDuration::FromMilliseconds(timing.mDelay);
     result.mEndDelay = TimeDuration::FromMilliseconds(timing.mEndDelay);
     result.mIterations = timing.mIterations;
     result.mIterationStart = timing.mIterationStart;
     result.mDirection = timing.mDirection;
     result.mFill = timing.mFill;
-    result.mFunction =
-      AnimationUtils::ParseEasing(targetElement, timing.mEasing);
+    result.mFunction = AnimationUtils::ParseEasing(timing.mEasing, aDocument);
   }
   return result;
 }
 
 /* static */ TimingParams
 TimingParams::FromOptionsUnion(
   const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
-  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+  nsIDocument* aDocument,
   ErrorResult& aRv)
 {
-  return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
+  return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv);
 }
 
 /* static */ TimingParams
 TimingParams::FromOptionsUnion(
   const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
-  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+  nsIDocument* aDocument,
   ErrorResult& aRv)
 {
-  return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
+  return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv);
 }
 
 bool
 TimingParams::operator==(const TimingParams& aOther) const
 {
   return mDuration == aOther.mDuration &&
          mDelay == aOther.mDelay &&
          mIterations == aOther.mIterations &&
--- a/dom/animation/TimingParams.h
+++ b/dom/animation/TimingParams.h
@@ -12,41 +12,38 @@
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/TimeStamp.h" // for TimeDuration
 
 // X11 has a #define for None
 #ifdef None
 #undef None
 #endif
-#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"  // for FillMode
-                                                         // and PlaybackDirection
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode
+                                                        // and PlaybackDirection
+
+class nsIDocument;
 
 namespace mozilla {
 
 namespace dom {
-struct AnimationEffectTimingProperties;
-class Element;
 class UnrestrictedDoubleOrKeyframeEffectOptions;
 class UnrestrictedDoubleOrKeyframeAnimationOptions;
-class ElementOrCSSPseudoElement;
 }
 
 struct TimingParams
 {
   TimingParams() = default;
 
   static TimingParams FromOptionsUnion(
     const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
-    const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
-    ErrorResult& aRv);
+    nsIDocument* aDocument, ErrorResult& aRv);
   static TimingParams FromOptionsUnion(
     const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
-    const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
-    ErrorResult& aRv);
+    nsIDocument* aDocument, ErrorResult& aRv);
 
   // Range-checks and validates an UnrestrictedDoubleOrString or
   // OwningUnrestrictedDoubleOrString object and converts to a
   // StickyTimeDuration value or Nothing() if aDuration is "auto".
   // Caller must check aRv.Failed().
   template <class DoubleOrString>
   static Maybe<StickyTimeDuration> ParseDuration(DoubleOrString& aDuration,
                                                  ErrorResult& aRv)
--- a/dom/animation/test/chrome/test_animation_property_state.html
+++ b/dom/animation/test/chrome/test_animation_property_state.html
@@ -74,17 +74,18 @@ function assert_property_state_on_compos
   var sortedActual = actual.sort(compare_property_state);
   var sortedExpected = expected.sort(compare_property_state);
 
   for (var i = 0; i < sortedActual.length; i++) {
     assert_equals(sortedActual[i].property,
                   sortedExpected[i].property,
                   'CSS property name should match');
     assert_true(sortedActual[i].runningOnCompositor,
-                'runningOnCompositor property should be true');
+                'runningOnCompositor property should be true on ' +
+                sortedActual[i].property);
     assert_not_exists(sortedActual[i], 'warning',
                       'warning property should not be set');
   }
 }
 
 var gAnimationsTests = [
   {
     desc: 'animations on compositor',
@@ -140,33 +141,74 @@ var gAnimationsTests = [
       },
       {
         property: 'transform',
         runningOnCompositor: true
       }
     ]
   },
   {
+    desc: 'opacity on compositor with animation of geometric properties',
+    frames: {
+      width: ['100px', '200px'],
+      opacity: [0, 1]
+    },
+    expected: [
+      {
+        property: 'width',
+        runningOnCompositor: false
+      },
+      {
+        property: 'opacity',
+        runningOnCompositor: true
+      }
+    ]
+  },
+  {
     // FIXME: Once we have KeyframeEffect.setFrames, we should rewrite
     // this test case to check that runningOnCompositor is restored to true
     // after 'width' keyframe is removed from the keyframes.
-    desc: 'animation on compositor with animation of geometric properties',
+    desc: 'transform on compositor with animation of geometric properties',
     frames: {
       width: ['100px', '200px'],
       transform: ['translate(0px)', 'translate(100px)']
     },
     expected: [
       {
         property: 'width',
         runningOnCompositor: false
       },
       {
         property: 'transform',
         runningOnCompositor: false,
-        warning: 'AnimationWarningWithGeometricProperties'
+        warning: 'AnimationWarningTransformWithGeometricProperties'
+      }
+    ]
+  },
+  {
+    desc: 'opacity and transform on compositor with animation of geometric ' +
+          'properties',
+    frames: {
+      width: ['100px', '200px'],
+      opacity: [0, 1],
+      transform: ['translate(0px)', 'translate(100px)']
+    },
+    expected: [
+      {
+        property: 'width',
+        runningOnCompositor: false
+      },
+      {
+        property: 'opacity',
+        runningOnCompositor: true
+      },
+      {
+        property: 'transform',
+        runningOnCompositor: false,
+        warning: 'AnimationWarningTransformWithGeometricProperties'
       }
     ]
   },
 ];
 
 gAnimationsTests.forEach(function(subtest) {
   promise_test(function(t) {
     var div = addDiv(t, { class: 'compositable' });
@@ -203,16 +245,253 @@ var gPerformanceWarningTests = [
     expected: [
       {
         property: 'transform',
         runningOnCompositor: false,
         warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
       }
     ]
   },
+  {
+    desc: 'opacity and transform with preserve-3d',
+    frames: {
+      opacity: [0, 1],
+      transform: ['translate(0px)', 'translate(100px)']
+    },
+    style: 'transform-style: preserve-3d',
+    expected: [
+      {
+        property: 'opacity',
+        runningOnCompositor: true
+      },
+      {
+        property: 'transform',
+        runningOnCompositor: false,
+        warning: 'AnimationWarningTransformPreserve3D'
+      }
+    ]
+  },
+  {
+    desc: 'opacity and transform with backface-visibility:hidden',
+    frames: {
+      opacity: [0, 1],
+      transform: ['translate(0px)', 'translate(100px)']
+    },
+    style: 'backface-visibility: hidden;',
+    expected: [
+      {
+        property: 'opacity',
+        runningOnCompositor: true
+      },
+      {
+        property: 'transform',
+        runningOnCompositor: false,
+        warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
+      }
+    ]
+  },
+];
+
+var gMultipleAsyncAnimationsTests = [
+  {
+    desc: 'opacity and transform with preserve-3d',
+    style: 'transform-style: preserve-3d',
+    animations: [
+      {
+        frames: {
+          transform: ['translate(0px)', 'translate(100px)']
+        },
+        expected: [
+          {
+            property: 'transform',
+            runningOnCompositor: false,
+            warning: 'AnimationWarningTransformPreserve3D'
+          }
+        ]
+      },
+      {
+        frames: {
+          opacity: [0, 1]
+        },
+        expected: [
+          {
+            property: 'opacity',
+            runningOnCompositor: true,
+          }
+        ]
+      }
+    ],
+  },
+  {
+    desc: 'opacity and transform with backface-visibility:hidden',
+    style: 'backface-visibility: hidden;',
+    animations: [
+      {
+        frames: {
+          transform: ['translate(0px)', 'translate(100px)']
+        },
+        expected: [
+          {
+            property: 'transform',
+            runningOnCompositor: false,
+            warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
+          }
+        ]
+      },
+      {
+        frames: {
+          opacity: [0, 1]
+        },
+        expected: [
+          {
+            property: 'opacity',
+            runningOnCompositor: true,
+          }
+        ]
+      }
+    ],
+  },
+];
+
+// FIXME: Once we have KeyframeEffect.setFrames, we should rewrite
+// these test cases to check that runningOnCompositor is restored to true
+// after 'width' keyframe is removed from the keyframes.
+var gMultipleAsyncAnimationsWithGeometricKeyframeTests = [
+  {
+    desc: 'transform and opacity with animation of geometric properties',
+    animations: [
+      {
+        frames: {
+          transform: ['translate(0px)', 'translate(100px)']
+        },
+        expected: [
+          {
+            property: 'transform',
+            runningOnCompositor: false,
+            warning: 'AnimationWarningTransformWithGeometricProperties'
+          }
+        ]
+      },
+      {
+        frames: {
+          width: ['100px', '200px'],
+          opacity: [0, 1]
+        },
+        expected: [
+          {
+            property: 'width',
+            runningOnCompositor: false,
+          },
+          {
+            property: 'opacity',
+            runningOnCompositor: true,
+          }
+        ]
+      }
+    ],
+  },
+  {
+    desc: 'opacity and transform with animation of geometric properties',
+    animations: [
+      {
+        frames: {
+          width: ['100px', '200px'],
+          transform: ['translate(0px)', 'translate(100px)']
+        },
+        expected: [
+          {
+            property: 'width',
+            runningOnCompositor: false,
+          },
+          {
+            property: 'transform',
+            runningOnCompositor: false,
+            warning: 'AnimationWarningTransformWithGeometricProperties'
+          }
+        ]
+      },
+      {
+        frames: {
+          opacity: [0, 1]
+        },
+        expected: [
+          {
+            property: 'opacity',
+            runningOnCompositor: true,
+          }
+        ]
+      }
+    ],
+  },
+];
+
+// Test cases that check results of adding/removing 'width' animation on the
+// same element which has async animations.
+var gMultipleAsyncAnimationsWithGeometricAnimationTests = [
+  {
+    desc: 'transform',
+    animations: [
+      {
+        frames: {
+          transform: ['translate(0px)', 'translate(100px)']
+        },
+        expected: [
+          {
+            property: 'transform',
+            runningOnCompositor: false,
+            warning: 'AnimationWarningTransformWithGeometricProperties'
+          }
+        ]
+      },
+    ]
+  },
+  {
+    desc: 'opacity',
+    animations: [
+      {
+        frames: {
+          opacity: [0, 1]
+        },
+        expected: [
+          {
+            property: 'opacity',
+            runningOnCompositor: true
+          }
+        ]
+      },
+    ]
+  },
+  {
+    desc: 'opacity and transform',
+    animations: [
+      {
+        frames: {
+          transform: ['translate(0px)', 'translate(100px)']
+        },
+        expected: [
+          {
+            property: 'transform',
+            runningOnCompositor: false,
+            warning: 'AnimationWarningTransformWithGeometricProperties'
+          }
+        ]
+      },
+      {
+        frames: {
+          opacity: [0, 1]
+        },
+        expected: [
+          {
+            property: 'opacity',
+            runningOnCompositor: true,
+          }
+        ]
+      }
+    ],
+  },
 ];
 
 function start() {
   var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
     .getService(SpecialPowers.Ci.nsIStringBundleService);
   gStringBundle = bundleService
     .createBundle("chrome://global/locale/layout_errors.properties");
 
@@ -224,16 +503,21 @@ function start() {
         assert_animation_property_state_equals(
           animation.effect.getPropertyState(),
           subtest.expected);
       }));
     }, subtest.desc);
   });
 
   gPerformanceWarningTests.forEach(function(subtest) {
+    // FIXME: Bug 1255710: 'preserve-3d' frame breaks other tests,
+    // we should skip all 'preserve-3d' tests here.
+    if (subtest.desc.includes('preserve-3d')) {
+      return;
+    }
     promise_test(function(t) {
       var div = addDiv(t, { class: 'compositable' });
       var animation = div.animate(subtest.frames, 100000);
       return animation.ready.then(t.step_func(function() {
         assert_property_state_on_compositor(
           animation.effect.getPropertyState(),
           subtest.expected);
         div.style = subtest.style;
@@ -247,16 +531,122 @@ function start() {
       })).then(t.step_func(function() {
         assert_property_state_on_compositor(
           animation.effect.getPropertyState(),
           subtest.expected);
       }));
     }, subtest.desc);
   });
 
+  gMultipleAsyncAnimationsTests.forEach(function(subtest) {
+    // FIXME: Bug 1255710: 'preserve-3d' frame breaks other tests,
+    // we should skip all 'preserve-3d' tests here.
+    if (subtest.desc.includes('preserve-3d')) {
+      return;
+    }
+    promise_test(function(t) {
+      var div = addDiv(t, { class: 'compositable' });
+      var animations = subtest.animations.map(function(anim) {
+        var animation = div.animate(anim.frames, 100000);
+
+        // Bind expected values to animation object.
+        animation.expected = anim.expected;
+        return animation;
+      });
+      return waitForAllAnimations(animations).then(t.step_func(function() {
+        animations.forEach(t.step_func(function(anim) {
+          assert_property_state_on_compositor(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+        div.style = subtest.style;
+        return waitForFrame();
+      })).then(t.step_func(function() {
+        animations.forEach(t.step_func(function(anim) {
+          assert_animation_property_state_equals(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+        div.style = '';
+        return waitForFrame();
+      })).then(t.step_func(function() {
+        animations.forEach(t.step_func(function(anim) {
+          assert_property_state_on_compositor(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+      }));
+    }, 'Multiple animations: ' + subtest.desc);
+  });
+
+  gMultipleAsyncAnimationsWithGeometricKeyframeTests.forEach(function(subtest) {
+    promise_test(function(t) {
+      var div = addDiv(t, { class: 'compositable' });
+      var animations = subtest.animations.map(function(anim) {
+        var animation = div.animate(anim.frames, 100000);
+
+        // Bind expected values to animation object.
+        animation.expected = anim.expected;
+        return animation;
+      });
+      return waitForAllAnimations(animations).then(t.step_func(function() {
+        animations.forEach(t.step_func(function(anim) {
+          assert_animation_property_state_equals(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+      }));
+    }, 'Multiple animations with geometric property: ' + subtest.desc);
+  });
+
+  gMultipleAsyncAnimationsWithGeometricAnimationTests.forEach(function(subtest) {
+    promise_test(function(t) {
+      var div = addDiv(t, { class: 'compositable' });
+      var animations = subtest.animations.map(function(anim) {
+        var animation = div.animate(anim.frames, 100000);
+
+        // Bind expected values to animation object.
+        animation.expected = anim.expected;
+        return animation;
+      });
+
+      var widthAnimation;
+
+      return waitForAllAnimations(animations).then(t.step_func(function() {
+        animations.forEach(t.step_func(function(anim) {
+          assert_property_state_on_compositor(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+      })).then(t.step_func(function() {
+        // Append 'width' animation on the same element.
+        widthAnimation = div.animate({ width: ['100px', '200px'] }, 100000);
+        return waitForFrame();
+      })).then(t.step_func(function() {
+        // Now transform animations are not running on compositor because of
+        // the 'width' animation.
+        animations.forEach(t.step_func(function(anim) {
+          assert_animation_property_state_equals(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+        // Remove the 'width' animation.
+        widthAnimation.cancel();
+        return waitForFrame();
+      })).then(t.step_func(function() {
+        // Now all animations are running on compositor.
+        animations.forEach(t.step_func(function(anim) {
+          assert_property_state_on_compositor(
+            anim.effect.getPropertyState(),
+            anim.expected);
+        }));
+      }));
+    }, 'Multiple async animations and geometric animation: ' + subtest.desc);
+  });
+
   promise_test(function(t) {
     var div = addDiv(t, { class: 'compositable' });
     var animation = div.animate(
       { transform: ['translate(0px)', 'translate(100px)'] }, 100000);
     return animation.ready.then(t.step_func(function() {
       assert_animation_property_state_equals(
         animation.effect.getPropertyState(),
         [ { property: 'transform', runningOnCompositor: true } ]);
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3348,24 +3348,18 @@ Element::Animate(const Nullable<ElementO
   if (js::GetContextCompartment(aContext) !=
       js::GetObjectCompartment(ownerGlobal->GetGlobalJSObject())) {
     ac.emplace(aContext, ownerGlobal->GetGlobalJSObject());
     if (!JS_WrapObject(aContext, &frames)) {
       return nullptr;
     }
   }
 
-  TimingParams timingParams =
-    TimingParams::FromOptionsUnion(aOptions, aTarget, aError);
-  if (aError.Failed()) {
-    return nullptr;
-  }
-
   RefPtr<KeyframeEffect> effect =
-    KeyframeEffect::Constructor(global, aTarget, frames, timingParams, aError);
+    KeyframeEffect::Constructor(global, aTarget, frames, aOptions, aError);
   if (aError.Failed()) {
     return nullptr;
   }
 
   RefPtr<Animation> animation =
     Animation::Constructor(global, effect,
                            referenceElement->OwnerDoc()->Timeline(), aError);
   if (aError.Failed()) {
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -793,16 +793,17 @@ GK_ATOM(onfullscreenchange, "onfullscree
 GK_ATOM(onfullscreenerror, "onfullscreenerror")
 GK_ATOM(onspeakerforcedchange, "onspeakerforcedchange")
 GK_ATOM(onget, "onget")
 GK_ATOM(ongroupchange, "ongroupchange")
 GK_ATOM(onhashchange, "onhashchange")
 GK_ATOM(onheadphoneschange, "onheadphoneschange")
 GK_ATOM(onheld, "onheld")
 GK_ATOM(onhfpstatuschanged, "onhfpstatuschanged")
+GK_ATOM(onhidstatuschanged, "onhidstatuschanged")
 GK_ATOM(onholding, "onholding")
 GK_ATOM(oniccchange, "oniccchange")
 GK_ATOM(oniccdetected, "oniccdetected")
 GK_ATOM(oniccinfochange, "oniccinfochange")
 GK_ATOM(oniccundetected, "oniccundetected")
 GK_ATOM(onincoming, "onincoming")
 GK_ATOM(oninput, "oninput")
 GK_ATOM(oninstall, "oninstall")
@@ -918,16 +919,17 @@ GK_ATOM(onstatuschanged, "onstatuschange
 GK_ATOM(onstkcommand, "onstkcommand")
 GK_ATOM(onstksessionend, "onstksessionend")
 GK_ATOM(onstorage, "onstorage")
 GK_ATOM(onstorageareachanged, "onstorageareachanged")
 GK_ATOM(onsubmit, "onsubmit")
 GK_ATOM(onsuccess, "onsuccess")
 GK_ATOM(ontypechange, "ontypechange")
 GK_ATOM(ontext, "ontext")
+GK_ATOM(ontoggle, "ontoggle")
 GK_ATOM(ontouchstart, "ontouchstart")
 GK_ATOM(ontouchend, "ontouchend")
 GK_ATOM(ontouchmove, "ontouchmove")
 GK_ATOM(ontouchcancel, "ontouchcancel")
 GK_ATOM(ontransitionend, "ontransitionend")
 GK_ATOM(onunderflow, "onunderflow")
 GK_ATOM(onunload, "onunload")
 GK_ATOM(onupdatefound, "onupdatefound")
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp
@@ -510,16 +510,88 @@ Convert(int32_t aIn, BluetoothGattStatus
     aOut = GATT_STATUS_UNKNOWN_ERROR;
   } else {
     aOut = sGattStatus[aIn];
   }
   return NS_OK;
 }
 
 nsresult
+Convert(uint8_t aIn, BluetoothHidProtocolMode& aOut)
+{
+  static const BluetoothHidProtocolMode sMode[] = {
+    [0x00] = HID_PROTOCOL_MODE_REPORT,
+    [0x01] = HID_PROTOCOL_MODE_BOOT
+  };
+  if (aIn == 0xff) {
+    /* This case is handled separately to not populate
+     * |sMode| with empty entries. */
+    aOut = HID_PROTOCOL_MODE_UNSUPPORTED;
+    return NS_OK;
+  }
+  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+        aIn >= MOZ_ARRAY_LENGTH(sMode), uint8_t, BluetoothHidProtocolMode)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sMode[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHidConnectionState& aOut)
+{
+  static const BluetoothHidConnectionState sConnectionState[] = {
+    [0x00] = HID_CONNECTION_STATE_CONNECTED,
+    [0x01] = HID_CONNECTION_STATE_CONNECTING,
+    [0x02] = HID_CONNECTION_STATE_DISCONNECTED,
+    [0x03] = HID_CONNECTION_STATE_DISCONNECTING,
+    [0x04] = HID_CONNECTION_STATE_FAILED_MOUSE_FROM_HOST,
+    [0x05] = HID_CONNECTION_STATE_FAILED_KEYBOARD_FROM_HOST,
+    [0x06] = HID_CONNECTION_STATE_FAILED_TOO_MANY_DEVICES,
+    [0x07] = HID_CONNECTION_STATE_FAILED_NO_HID_DRIVER,
+    [0x08] = HID_CONNECTION_STATE_FAILED_GENERIC,
+    [0x09] = HID_CONNECTION_STATE_UNKNOWN
+  };
+  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+        aIn >= MOZ_ARRAY_LENGTH(sConnectionState),
+        uint8_t, BluetoothHidConnectionState)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sConnectionState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHidStatus& aOut)
+{
+  static const BluetoothHidStatus sStatus[] = {
+    [0x00] = HID_STATUS_OK,
+    [0x01] = HID_STATUS_HANDSHAKE_DEVICE_NOT_READY,
+    [0x02] = HID_STATUS_HANDSHAKE_INVALID_REPORT_ID,
+    [0x03] = HID_STATUS_HANDSHAKE_TRANSACTION_NOT_SPT,
+    [0x04] = HID_STATUS_HANDSHAKE_INVALID_PARAMETER,
+    [0x05] = HID_STATUS_HANDSHAKE_GENERIC_ERROR,
+    [0x06] = HID_STATUS_GENERAL_ERROR,
+    [0x07] = HID_STATUS_SDP_ERROR,
+    [0x08] = HID_STATUS_SET_PROTOCOL_ERROR,
+    [0x09] = HID_STATUS_DEVICE_DATABASE_FULL,
+    [0x0a] = HID_STATUS_DEVICE_TYPE_NOT_SUPPORTED,
+    [0x0b] = HID_STATUS_NO_RESOURCES,
+    [0x0c] = HID_STATUS_AUTHENTICATION_FAILED,
+    [0x0d] = HID_STATUS_HDL
+  };
+  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+        aIn >= MOZ_ARRAY_LENGTH(sStatus), uint8_t, BluetoothHidStatus)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sStatus[aIn];
+  return NS_OK;
+}
+
+nsresult
 Convert(nsresult aIn, BluetoothStatus& aOut)
 {
   if (NS_SUCCEEDED(aIn)) {
     aOut = STATUS_SUCCESS;
   } else if (aIn == NS_ERROR_OUT_OF_MEMORY) {
     aOut = STATUS_NOMEM;
   } else {
     aOut = STATUS_FAIL;
@@ -976,16 +1048,57 @@ Convert(BluetoothGattWriteType aIn, int3
         int32_t)) {
     aOut = GATT_WRITE_TYPE_NORMAL; // silences compiler warning
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sGattWriteType[aIn];
   return NS_OK;
 }
 
+nsresult
+Convert(BluetoothHidProtocolMode aIn, uint8_t& aOut)
+{
+  static const uint8_t sMode[] = {
+    [HID_PROTOCOL_MODE_REPORT] = 0x00,
+    [HID_PROTOCOL_MODE_BOOT] = 0x01
+  };
+  if (aIn == HID_PROTOCOL_MODE_UNSUPPORTED) {
+    /* This case is handled separately to not populate
+     * |sValue| with empty entries. */
+    aOut = 0xff;
+    return NS_OK;
+  }
+  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+        aIn >= MOZ_ARRAY_LENGTH(sMode), BluetoothHidProtocolMode, uint8_t)) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sMode[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHidReportType aIn, uint8_t& aOut)
+{
+  static const uint8_t sType[] = {
+    [0x00] = static_cast<BluetoothHidReportType>(0),
+    [HID_REPORT_TYPE_INPUT] = 0x01,
+    [HID_REPORT_TYPE_OUTPUT] = 0x02,
+    [HID_REPORT_TYPE_FEATURE] = 0x03
+  };
+  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+        aIn >= MOZ_ARRAY_LENGTH(sType),
+        BluetoothHidReportType, uint8_t)) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sType[aIn];
+  return NS_OK;
+}
+
 /* |ConvertArray| is a helper for converting arrays. Pass an
  * instance of this structure as the first argument to |Convert|
  * to convert an array. The output type has to support the array
  * subscript operator.
  */
 template <typename T>
 struct ConvertArray
 {
@@ -1401,16 +1514,71 @@ PackPDU(BluetoothGattAuthReq aIn, Daemon
 }
 
 nsresult
 PackPDU(BluetoothGattWriteType aIn, DaemonSocketPDU& aPDU)
 {
   return PackPDU(PackConversion<BluetoothGattWriteType, int32_t>(aIn), aPDU);
 }
 
+nsresult
+PackPDU(const BluetoothHidInfoParam& aIn, DaemonSocketPDU& aPDU)
+{
+  if (MOZ_HAL_IPC_PACK_WARN_IF(
+        aIn.mDescriptorLength > BLUETOOTH_HID_MAX_DESC_LEN,
+        BluetoothHidInfoParam)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  nsresult rv =  PackPDU(aIn.mAttributeMask,
+                         aIn.mSubclass,
+                         aIn.mApplicationId,
+                         aIn.mVendorId,
+                         aIn.mProductId,
+                         aIn.mVersion,
+                         aIn.mCountryCode,
+                         aIn.mDescriptorLength,
+                         aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return PackPDU(
+    PackArray<uint8_t>(aIn.mDescriptorValue, aIn.mDescriptorLength), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHidReport& aIn, DaemonSocketPDU& aPDU)
+{
+  uint8_t* reportData =
+    const_cast<uint8_t*>(aIn.mReportData.Elements());
+
+  nsresult rv = PackPDU(
+    PackConversion<uint32_t, uint16_t>(aIn.mReportData.Length()), aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return PackPDU(
+    PackArray<uint8_t>(reportData, aIn.mReportData.Length()),
+    aPDU);
+}
+
+nsresult
+PackPDU(BluetoothHidProtocolMode aIn, DaemonSocketPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHidProtocolMode, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(BluetoothHidReportType aIn, DaemonSocketPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHidReportType, uint8_t>(aIn), aPDU);
+}
+
 //
 // Unpacking
 //
 
 nsresult
 UnpackPDU(DaemonSocketPDU& aPDU, BluetoothA2dpAudioState& aOut)
 {
   return UnpackPDU(
@@ -1808,9 +1976,90 @@ UnpackPDU(DaemonSocketPDU& aPDU, Bluetoo
   rv = UnpackPDU(aPDU, aOut.mLength);
   if (NS_FAILED(rv)) {
     return rv;
   }
   /* unpack value */
   return aPDU.Read(aOut.mValue, aOut.mLength);
 }
 
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidInfoParam& aOut)
+{
+  /* unpack attribute mask */
+  nsresult rv = UnpackPDU(aPDU, aOut.mAttributeMask);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack subclass */
+  rv = UnpackPDU(aPDU, aOut.mSubclass);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack application id */
+  rv = UnpackPDU(aPDU, aOut.mApplicationId);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack vendor id */
+  rv = UnpackPDU(aPDU, aOut.mVendorId);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack product id */
+  rv = UnpackPDU(aPDU, aOut.mProductId);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack version */
+  rv = UnpackPDU(aPDU, aOut.mVersion);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack country code */
+  rv = UnpackPDU(aPDU, aOut.mCountryCode);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  /* unpack descriptor length */
+  rv = UnpackPDU(aPDU, aOut.mDescriptorLength);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (MOZ_HAL_IPC_PACK_WARN_IF(
+        aOut.mDescriptorLength > BLUETOOTH_HID_MAX_DESC_LEN,
+        BluetoothHidInfoParam)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  /* unpack descriptor value */
+  return UnpackPDU(
+    aPDU,
+    UnpackArray<uint8_t>(aOut.mDescriptorValue, aOut.mDescriptorLength));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidReport& aOut)
+{
+  return UnpackPDU(aPDU, aOut.mReportData);
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidProtocolMode& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHidProtocolMode>(aOut));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidConnectionState& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHidConnectionState>(aOut));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidStatus& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHidStatus>(aOut));
+}
+
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h
@@ -169,16 +169,25 @@ Convert(uint8_t aIn, BluetoothSspVariant
 
 nsresult
 Convert(uint8_t aIn, BluetoothStatus& aOut);
 
 nsresult
 Convert(int32_t aIn, BluetoothAttributeHandle& aOut);
 
 nsresult
+Convert(uint8_t aIn, BluetoothHidProtocolMode& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHidConnectionState& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHidStatus& aOut);
+
+nsresult
 Convert(int32_t aIn, BluetoothGattStatus& aOut);
 
 nsresult
 Convert(const BluetoothAttributeHandle& aIn, int32_t& aOut);
 
 nsresult
 Convert(const BluetoothAttributeHandle& aIn, uint16_t& aOut);
 
@@ -252,16 +261,22 @@ nsresult
 Convert(BluetoothGattAuthReq aIn, uint8_t& aOut);
 
 nsresult
 Convert(BluetoothGattWriteType aIn, int32_t& aOut);
 
 nsresult
 Convert(nsresult aIn, BluetoothStatus& aOut);
 
+nsresult
+Convert(BluetoothHidProtocolMode aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHidReportType aIn, uint8_t& aOut);
+
 //
 // Packing
 //
 
 nsresult
 PackPDU(const BluetoothAddress& aIn, DaemonSocketPDU& aPDU);
 
 nsresult
@@ -366,16 +381,28 @@ nsresult
 PackPDU(BluetoothGattAuthReq aIn, DaemonSocketPDU& aPDU);
 
 nsresult
 PackPDU(BluetoothGattWriteType aIn, DaemonSocketPDU& aPDU);
 
 nsresult
 PackPDU(BluetoothTransport aIn, DaemonSocketPDU& aPDU);
 
+nsresult
+PackPDU(const BluetoothHidInfoParam& aIn, DaemonSocketPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHidReport& aIn, DaemonSocketPDU& aPDU);
+
+nsresult
+PackPDU(BluetoothHidProtocolMode aIn, DaemonSocketPDU& aPDU);
+
+nsresult
+PackPDU(BluetoothHidReportType aIn, DaemonSocketPDU& aPDU);
+
 /* This implementation of |PackPDU| packs |BluetoothUuid| in reversed order.
  * (ex. reversed GATT UUID, see bug 1171866)
  */
 inline nsresult
 PackPDU(const PackReversed<BluetoothUuid>& aIn, DaemonSocketPDU& aPDU)
 {
  return PackPDU(
    PackReversed<PackArray<uint8_t>>(
@@ -508,16 +535,31 @@ nsresult
 UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattReadParam& aOut);
 
 nsresult
 UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattWriteParam& aOut);
 
 nsresult
 UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattNotifyParam& aOut);
 
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidInfoParam& aOut);
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidReport& aOut);
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidProtocolMode& aOut);
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidConnectionState& aOut);
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidStatus& aOut);
+
 /* This implementation of |UnpackPDU| unpacks |BluetoothUuid| in reversed
  * order. (ex. reversed GATT UUID, see bug 1171866)
  */
 inline nsresult
 UnpackPDU(DaemonSocketPDU& aPDU, const UnpackReversed<BluetoothUuid>& aOut)
 {
   return UnpackPDU(
     aPDU,
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp
@@ -0,0 +1,677 @@
+/* -*- 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 "BluetoothDaemonHidInterface.h"
+#include "mozilla/unused.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+using namespace mozilla::ipc;
+
+//
+// Hid module
+//
+
+BluetoothHidNotificationHandler*
+  BluetoothDaemonHidModule::sNotificationHandler;
+
+void
+BluetoothDaemonHidModule::SetNotificationHandler(
+  BluetoothHidNotificationHandler* aNotificationHandler)
+{
+  sNotificationHandler = aNotificationHandler;
+}
+
+void
+BluetoothDaemonHidModule::HandleSvc(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+  DaemonSocketResultHandler* aRes)
+{
+  static void (BluetoothDaemonHidModule::* const HandleOp[])(
+    const DaemonSocketPDUHeader&, DaemonSocketPDU&,
+    DaemonSocketResultHandler*) = {
+    [0] = &BluetoothDaemonHidModule::HandleRsp,
+    [1] = &BluetoothDaemonHidModule::HandleNtf
+  };
+
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  // Negate twice to map bit to 0/1
+  unsigned long isNtf = !!(aHeader.mOpcode & 0x80);
+
+  (this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
+}
+
+// Commands
+//
+
+nsresult
+BluetoothDaemonHidModule::ConnectCmd(
+  const BluetoothAddress& aRemoteAddr, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_CONNECT, 6)); // Address
+
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::DisconnectCmd(
+  const BluetoothAddress& aRemoteAddr, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_DISCONNECT, 6)); // Address
+
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::VirtualUnplugCmd(
+  const BluetoothAddress& aRemoteAddr, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_VIRTUAL_UNPLUG, 6)); // Address
+
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::SetInfoCmd(
+  const BluetoothAddress& aRemoteAddr,
+  const BluetoothHidInfoParam& aHidInfoParam,
+  BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_INFO, 0));
+
+  nsresult rv = PackPDU(aRemoteAddr, aHidInfoParam, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::GetProtocolCmd(
+  const BluetoothAddress& aRemoteAddr,
+  BluetoothHidProtocolMode aHidProtocolMode,
+  BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_PROTOCOL,
+                        6 + // Address
+                        1)); // Protocol Mode
+
+  nsresult rv = PackPDU(aRemoteAddr, aHidProtocolMode, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::SetProtocolCmd(
+  const BluetoothAddress& aRemoteAddr,
+  BluetoothHidProtocolMode aHidProtocolMode,
+  BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_PROTOCOL,
+                        6 + // Remote Address
+                        1)); // Protocol Mode
+
+  nsresult rv = PackPDU(aRemoteAddr, aHidProtocolMode, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::GetReportCmd(
+  const BluetoothAddress& aRemoteAddr, BluetoothHidReportType aType,
+  uint8_t aReportId, uint16_t aBuffSize, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_REPORT,
+                        6 + // Address
+                        1 + // Report Type
+                        1 + // Report ID
+                        2)); // Buffer Size
+
+  nsresult rv = PackPDU(aRemoteAddr, aType, aReportId, aBuffSize, *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::SetReportCmd(
+  const BluetoothAddress& aRemoteAddr, BluetoothHidReportType aType,
+  const BluetoothHidReport& aReport, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_REPORT, 0));
+
+  nsresult rv = PackPDU(aRemoteAddr, aType, aReport, *pdu);
+
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHidModule::SendDataCmd(
+  const BluetoothAddress& aRemoteAddr, uint16_t aDataLen, const uint8_t* aData,
+  BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<DaemonSocketPDU> pdu(
+    new DaemonSocketPDU(SERVICE_ID, OPCODE_SEND_DATA, 0));
+
+  nsresult rv = PackPDU(aRemoteAddr, aDataLen,
+                        PackArray<uint8_t>(aData, aDataLen), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  Unused << pdu.forget();
+  return NS_OK;
+}
+
+// Responses
+//
+
+void
+BluetoothDaemonHidModule::ErrorRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ErrorRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::OnError, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::ConnectRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::Connect, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::DisconnectRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::Disconnect, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::VirtualUnplugRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::VirtualUnplug, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::SetInfoRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::SetInfo, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::GetProtocolRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::GetProtocol, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::SetProtocolRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::SetProtocol, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::GetReportRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::GetReport, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::SetReportRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::SetReport, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::SendDataRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHidResultHandler::SendData, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::HandleRsp(
+  const DaemonSocketPDUHeader& aHeader,
+  DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes)
+{
+  static void (BluetoothDaemonHidModule::* const HandleRsp[])(
+    const DaemonSocketPDUHeader&,
+    DaemonSocketPDU&,
+    BluetoothHidResultHandler*) = {
+    [OPCODE_ERROR] = &BluetoothDaemonHidModule::ErrorRsp,
+    [OPCODE_CONNECT] = &BluetoothDaemonHidModule::ConnectRsp,
+    [OPCODE_DISCONNECT] = &BluetoothDaemonHidModule::DisconnectRsp,
+    [OPCODE_VIRTUAL_UNPLUG] = &BluetoothDaemonHidModule::VirtualUnplugRsp,
+    [OPCODE_SET_INFO] = &BluetoothDaemonHidModule::SetInfoRsp,
+    [OPCODE_GET_PROTOCOL] = &BluetoothDaemonHidModule::GetProtocolRsp,
+    [OPCODE_SET_PROTOCOL] = &BluetoothDaemonHidModule::SetProtocolRsp,
+    [OPCODE_GET_REPORT] = &BluetoothDaemonHidModule::GetReportRsp,
+    [OPCODE_SET_REPORT] = &BluetoothDaemonHidModule::SetReportRsp,
+    [OPCODE_SEND_DATA] = &BluetoothDaemonHidModule::SendDataRsp
+  };
+
+  MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
+
+  if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) ||
+      NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) {
+    return;
+  }
+
+  RefPtr<BluetoothHidResultHandler> res =
+    static_cast<BluetoothHidResultHandler*>(aRes);
+
+  if (!res) {
+    return; // Return early if no result handler has been set for response
+  }
+
+  (this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
+}
+
+// Notifications
+//
+
+// Returns the current notification handler to a notification runnable
+class BluetoothDaemonHidModule::NotificationHandlerWrapper final
+{
+public:
+  typedef BluetoothHidNotificationHandler ObjectType;
+
+  static ObjectType* GetInstance()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return sNotificationHandler;
+  }
+};
+
+void
+BluetoothDaemonHidModule::ConnectionStateNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  ConnectionStateNotification::Dispatch(
+    &BluetoothHidNotificationHandler::ConnectionStateNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::HidInfoNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  HidInfoNotification::Dispatch(
+    &BluetoothHidNotificationHandler::HidInfoNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::ProtocolModeNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  ProtocolModeNotification::Dispatch(
+    &BluetoothHidNotificationHandler::ProtocolModeNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::IdleTimeNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  IdleTimeNotification::Dispatch(
+    &BluetoothHidNotificationHandler::IdleTimeNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::GetReportNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  GetReportNotification::Dispatch(
+    &BluetoothHidNotificationHandler::GetReportNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::VirtualUnplugNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  VirtualUnplugNotification::Dispatch(
+    &BluetoothHidNotificationHandler::VirtualUnplugNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::HandshakeNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+  HandshakeNotification::Dispatch(
+    &BluetoothHidNotificationHandler::HandshakeNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHidModule::HandleNtf(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+  DaemonSocketResultHandler* aRes)
+{
+  static void (BluetoothDaemonHidModule::* const HandleNtf[])(
+    const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {
+      [0] = &BluetoothDaemonHidModule::ConnectionStateNtf,
+      [1] = &BluetoothDaemonHidModule::HidInfoNtf,
+      [2] = &BluetoothDaemonHidModule::ProtocolModeNtf,
+      [3] = &BluetoothDaemonHidModule::IdleTimeNtf,
+      [4] = &BluetoothDaemonHidModule::GetReportNtf,
+      [5] = &BluetoothDaemonHidModule::VirtualUnplugNtf,
+      [6] = &BluetoothDaemonHidModule::HandshakeNtf
+  };
+
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  uint8_t index = aHeader.mOpcode - 0x81;
+
+  if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) ||
+      NS_WARN_IF(!HandleNtf[index])) {
+    return;
+  }
+
+  (this->*(HandleNtf[index]))(aHeader, aPDU);
+}
+
+//
+// Hid Interface
+//
+
+BluetoothDaemonHidInterface::BluetoothDaemonHidInterface(
+  BluetoothDaemonHidModule* aModule)
+  : mModule(aModule)
+{ }
+
+BluetoothDaemonHidInterface::~BluetoothDaemonHidInterface()
+{ }
+
+void
+BluetoothDaemonHidInterface::SetNotificationHandler(
+  BluetoothHidNotificationHandler* aNotificationHandler)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->SetNotificationHandler(aNotificationHandler);
+}
+
+/* Connect / Disconnect */
+
+void
+BluetoothDaemonHidInterface::Connect(
+  const BluetoothAddress& aBdAddr, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+void
+BluetoothDaemonHidInterface::Disconnect(
+  const BluetoothAddress& aBdAddr, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+/* Virtual Unplug */
+
+void
+BluetoothDaemonHidInterface::VirtualUnplug(
+  const BluetoothAddress& aBdAddr, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->VirtualUnplugCmd(aBdAddr, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+/* Set Info */
+
+void
+BluetoothDaemonHidInterface::SetInfo(
+  const BluetoothAddress& aBdAddr,
+  const BluetoothHidInfoParam& aHidInfoParam,
+  BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->SetInfoCmd(aBdAddr, aHidInfoParam, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+/* Protocol */
+
+void
+BluetoothDaemonHidInterface::GetProtocol(
+  const BluetoothAddress& aBdAddr,
+  BluetoothHidProtocolMode aHidProtocolMode, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->GetProtocolCmd(aBdAddr, aHidProtocolMode, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+void
+BluetoothDaemonHidInterface::SetProtocol(
+  const BluetoothAddress& aBdAddr,
+  BluetoothHidProtocolMode aHidProtocolMode, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->SetProtocolCmd(aBdAddr, aHidProtocolMode, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+/* Report */
+
+void
+BluetoothDaemonHidInterface::GetReport(
+  const BluetoothAddress& aBdAddr, BluetoothHidReportType aType,
+  uint8_t aReportId, uint16_t aBuffSize, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->GetReportCmd(
+    aBdAddr, aType, aReportId, aBuffSize, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+void
+BluetoothDaemonHidInterface::SetReport(
+  const BluetoothAddress& aBdAddr, BluetoothHidReportType aType,
+  const BluetoothHidReport& aReport, BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->SetReportCmd(
+    aBdAddr, aType, aReport, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+/* Send Data */
+
+void
+BluetoothDaemonHidInterface::SendData(
+  const BluetoothAddress& aBdAddr, uint16_t aDataLen, const uint8_t* aData,
+  BluetoothHidResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  nsresult rv = mModule->SendDataCmd(aBdAddr, aDataLen, aData, aRes);
+
+  if (NS_FAILED(rv)) {
+    DispatchError(aRes, rv);
+  }
+}
+
+void
+BluetoothDaemonHidInterface::DispatchError(
+  BluetoothHidResultHandler* aRes, BluetoothStatus aStatus)
+{
+  DaemonResultRunnable1<BluetoothHidResultHandler, void,
+                        BluetoothStatus, BluetoothStatus>::Dispatch(
+    aRes, &BluetoothHidResultHandler::OnError,
+    ConstantInitOp1<BluetoothStatus>(aStatus));
+}
+
+void
+BluetoothDaemonHidInterface::DispatchError(
+  BluetoothHidResultHandler* aRes, nsresult aRv)
+{
+  BluetoothStatus status;
+
+  if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
+    status = STATUS_FAIL;
+  }
+  DispatchError(aRes, status);
+}
+
+END_BLUETOOTH_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h
@@ -0,0 +1,298 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothDaemonHidInterface_h
+#define mozilla_dom_bluetooth_bluedroid_BluetoothDaemonHidInterface_h
+
+#include "BluetoothDaemonHelpers.h"
+#include "BluetoothInterface.h"
+#include "mozilla/ipc/DaemonRunnables.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+using mozilla::ipc::DaemonSocketPDU;
+using mozilla::ipc::DaemonSocketPDUHeader;
+using mozilla::ipc::DaemonSocketResultHandler;
+
+class BluetoothDaemonHidModule
+{
+public:
+  enum {
+    SERVICE_ID = 0x03
+  };
+
+  enum {
+    OPCODE_ERROR = 0x00,
+    OPCODE_CONNECT = 0x01,
+    OPCODE_DISCONNECT = 0x02,
+    OPCODE_VIRTUAL_UNPLUG = 0x03,
+    OPCODE_SET_INFO = 0x04,
+    OPCODE_GET_PROTOCOL = 0x05,
+    OPCODE_SET_PROTOCOL = 0x06,
+    OPCODE_GET_REPORT = 0x07,
+    OPCODE_SET_REPORT = 0x08,
+    OPCODE_SEND_DATA = 0x09
+  };
+
+  virtual nsresult Send(DaemonSocketPDU* aPDU,
+                        DaemonSocketResultHandler* aRes) = 0;
+
+  void SetNotificationHandler(
+    BluetoothHidNotificationHandler* aNotificationHandler);
+
+  //
+  // Commands
+  //
+
+  nsresult ConnectCmd(const BluetoothAddress& aBdAddr,
+                      BluetoothHidResultHandler* aRes);
+  nsresult DisconnectCmd(const BluetoothAddress& aBdAddr,
+                         BluetoothHidResultHandler* aRes);
+
+  /* Virtual Unplug */
+
+  nsresult VirtualUnplugCmd(const BluetoothAddress& aBdAddr,
+                            BluetoothHidResultHandler* aRes);
+
+  /* Set Info */
+
+  nsresult SetInfoCmd(
+    const BluetoothAddress& aBdAddr,
+    const BluetoothHidInfoParam& aHidInfoParam,
+    BluetoothHidResultHandler* aRes);
+
+  /* Protocol */
+
+  nsresult GetProtocolCmd(const BluetoothAddress& aBdAddr,
+                          BluetoothHidProtocolMode aHidProtocolMode,
+                          BluetoothHidResultHandler* aRes);
+  nsresult SetProtocolCmd(const BluetoothAddress& aBdAddr,
+                          BluetoothHidProtocolMode aHidProtocolMode,
+                          BluetoothHidResultHandler* aRes);
+
+  /* Report */
+
+  nsresult GetReportCmd(const BluetoothAddress& aBdAddr,
+                        BluetoothHidReportType aType,
+                        uint8_t aReportId,
+                        uint16_t aBuffSize,
+                        BluetoothHidResultHandler* aRes);
+  nsresult SetReportCmd(const BluetoothAddress& aBdAddr,
+                        BluetoothHidReportType aType,
+                        const BluetoothHidReport& aReport,
+                        BluetoothHidResultHandler* aRes);
+
+  /* Send Data */
+
+  nsresult SendDataCmd(const BluetoothAddress& aBdAddr,
+                       uint16_t aDataLen, const uint8_t* aData,
+                       BluetoothHidResultHandler* aRes);
+
+protected:
+  void HandleSvc(const DaemonSocketPDUHeader& aHeader,
+                 DaemonSocketPDU& aPDU,
+                 DaemonSocketResultHandler* aRes);
+
+  //
+  // Responses
+  //
+
+  typedef mozilla::ipc::DaemonResultRunnable0<
+    BluetoothHidResultHandler, void>
+    ResultRunnable;
+
+  typedef mozilla::ipc::DaemonResultRunnable1<
+    BluetoothHidResultHandler, void, BluetoothStatus, BluetoothStatus>
+    ErrorRunnable;
+
+  void ErrorRsp(const DaemonSocketPDUHeader& aHeader,
+                DaemonSocketPDU& aPDU,
+                BluetoothHidResultHandler* aRes);
+
+  void ConnectRsp(const DaemonSocketPDUHeader& aHeader,
+                  DaemonSocketPDU& aPDU,
+                  BluetoothHidResultHandler* aRes);
+
+  void DisconnectRsp(const DaemonSocketPDUHeader& aHeader,
+                     DaemonSocketPDU& aPDU,
+                     BluetoothHidResultHandler* aRes);
+
+  void VirtualUnplugRsp(const DaemonSocketPDUHeader& aHeader,
+                        DaemonSocketPDU& aPDU,
+                        BluetoothHidResultHandler* aRes);
+
+  void SetInfoRsp(const DaemonSocketPDUHeader& aHeader,
+                  DaemonSocketPDU& aPDU,
+                  BluetoothHidResultHandler* aRes);
+
+  void GetProtocolRsp(const DaemonSocketPDUHeader& aHeader,
+                      DaemonSocketPDU& aPDU,
+                      BluetoothHidResultHandler* aRes);
+
+  void SetProtocolRsp(const DaemonSocketPDUHeader& aHeader,
+                      DaemonSocketPDU& aPDU,
+                      BluetoothHidResultHandler* aRes);
+
+  void GetReportRsp(const DaemonSocketPDUHeader& aHeader,
+                    DaemonSocketPDU& aPDU,
+                    BluetoothHidResultHandler* aRes);
+
+  void SetReportRsp(const DaemonSocketPDUHeader& aHeader,
+                    DaemonSocketPDU& aPDU,
+                    BluetoothHidResultHandler* aRes);
+
+  void SendDataRsp(const DaemonSocketPDUHeader& aHeader,
+                   DaemonSocketPDU& aPDU,
+                   BluetoothHidResultHandler* aRes);
+
+  void HandleRsp(const DaemonSocketPDUHeader& aHeader,
+                 DaemonSocketPDU& aPDU,
+                 DaemonSocketResultHandler* aRes);
+
+  //
+  // Notifications
+  //
+
+  class NotificationHandlerWrapper;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable2<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidConnectionState,
+    const BluetoothAddress&, BluetoothHidConnectionState>
+    ConnectionStateNotification;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable2<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidInfoParam,
+    const BluetoothAddress&, const BluetoothHidInfoParam&>
+    HidInfoNotification;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable3<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidStatus, BluetoothHidProtocolMode,
+    const BluetoothAddress&, BluetoothHidStatus, BluetoothHidProtocolMode>
+    ProtocolModeNotification;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable3<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidStatus, uint16_t,
+    const BluetoothAddress&, BluetoothHidStatus, uint16_t>
+    IdleTimeNotification;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable3<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidStatus, BluetoothHidReport,
+    const BluetoothAddress&, BluetoothHidStatus,
+    const BluetoothHidReport&>
+    GetReportNotification;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable2<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidStatus,
+    const BluetoothAddress&, BluetoothHidStatus>
+    VirtualUnplugNotification;
+
+  typedef mozilla::ipc::DaemonNotificationRunnable2<
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, BluetoothHidStatus,
+    const BluetoothAddress&, BluetoothHidStatus>
+    HandshakeNotification;
+
+  void ConnectionStateNtf(const DaemonSocketPDUHeader& aHeader,
+                          DaemonSocketPDU& aPDU);
+
+  void HidInfoNtf(const DaemonSocketPDUHeader& aHeader,
+                  DaemonSocketPDU& aPDU);
+
+  void ProtocolModeNtf(const DaemonSocketPDUHeader& aHeader,
+                       DaemonSocketPDU& aPDU);
+
+  void IdleTimeNtf(const DaemonSocketPDUHeader& aHeader,
+                   DaemonSocketPDU& aPDU);
+
+  void GetReportNtf(const DaemonSocketPDUHeader& aHeader,
+                    DaemonSocketPDU& aPDU);
+
+  void VirtualUnplugNtf(const DaemonSocketPDUHeader& aHeader,
+                        DaemonSocketPDU& aPDU);
+
+  void HandshakeNtf(const DaemonSocketPDUHeader& aHeader,
+                    DaemonSocketPDU& aPDU);
+
+  void HandleNtf(const DaemonSocketPDUHeader& aHeader,
+                 DaemonSocketPDU& aPDU,
+                 DaemonSocketResultHandler* aRes);
+
+  static BluetoothHidNotificationHandler* sNotificationHandler;
+};
+
+class BluetoothDaemonHidInterface final
+  : public BluetoothHidInterface
+{
+public:
+  BluetoothDaemonHidInterface(BluetoothDaemonHidModule* aModule);
+  ~BluetoothDaemonHidInterface();
+
+  void SetNotificationHandler(
+    BluetoothHidNotificationHandler* aNotificationHandler) override;
+
+  /* Connect / Disconnect */
+
+  void Connect(const BluetoothAddress& aBdAddr,
+               BluetoothHidResultHandler* aRes) override;
+  void Disconnect(const BluetoothAddress& aBdAddr,
+                  BluetoothHidResultHandler* aRes) override;
+
+  /* Virtual Unplug */
+
+  void VirtualUnplug(const BluetoothAddress& aBdAddr,
+                     BluetoothHidResultHandler* aRes) override;
+
+  /* Set Info */
+
+  void SetInfo(
+    const BluetoothAddress& aBdAddr,
+    const BluetoothHidInfoParam& aHidInfoParam,
+    BluetoothHidResultHandler* aRes) override;
+
+  /* Protocol */
+
+  void GetProtocol(const BluetoothAddress& aBdAddr,
+                   BluetoothHidProtocolMode aHidProtoclMode,
+                   BluetoothHidResultHandler* aRes) override;
+
+  void SetProtocol(const BluetoothAddress& aBdAddr,
+                   BluetoothHidProtocolMode aHidProtocolMode,
+                   BluetoothHidResultHandler* aRes) override;
+
+  /* Report */
+
+  void GetReport(const BluetoothAddress& aBdAddr,
+                 BluetoothHidReportType aType,
+                 uint8_t aReportId, uint16_t aBuffSize,
+                 BluetoothHidResultHandler* aRes) override;
+  void SetReport(const BluetoothAddress& aBdAddr,
+                 BluetoothHidReportType aType,
+                 const BluetoothHidReport& aReport,
+                 BluetoothHidResultHandler* aRes) override;
+
+  /* Send Data */
+
+  void SendData(const BluetoothAddress& aBdAddr,
+                uint16_t aDataLen, const uint8_t* aData,
+                BluetoothHidResultHandler* aRes) override;
+
+private:
+  void DispatchError(BluetoothHidResultHandler* aRes,
+                     BluetoothStatus aStatus);
+  void DispatchError(BluetoothHidResultHandler* aRes, nsresult aRv);
+
+  BluetoothDaemonHidModule* mModule;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif // mozilla_dom_bluetooth_bluedroid_BluetoothDaemonHidInterface_h
--- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
@@ -9,16 +9,17 @@
 #include <fcntl.h>
 #include <stdlib.h>
 #include "BluetoothDaemonA2dpInterface.h"
 #include "BluetoothDaemonAvrcpInterface.h"
 #include "BluetoothDaemonCoreInterface.h"
 #include "BluetoothDaemonGattInterface.h"
 #include "BluetoothDaemonHandsfreeInterface.h"
 #include "BluetoothDaemonHelpers.h"
+#include "BluetoothDaemonHidInterface.h"
 #include "BluetoothDaemonSetupInterface.h"
 #include "BluetoothDaemonSocketInterface.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/DaemonRunnables.h"
 #include "mozilla/ipc/DaemonSocket.h"
 #include "mozilla/ipc/DaemonSocketConnector.h"
 #include "mozilla/ipc/ListenSocket.h"
 
@@ -75,16 +76,17 @@ class BluetoothDaemonProtocol final
   : public DaemonSocketIOConsumer
   , public BluetoothDaemonSetupModule
   , public BluetoothDaemonCoreModule
   , public BluetoothDaemonSocketModule
   , public BluetoothDaemonHandsfreeModule
   , public BluetoothDaemonA2dpModule
   , public BluetoothDaemonAvrcpModule
   , public BluetoothDaemonGattModule
+  , public BluetoothDaemonHidModule
 {
 public:
   BluetoothDaemonProtocol();
 
   void SetConnection(DaemonSocket* aConnection);
 
   // Outgoing PDUs
   //
@@ -119,16 +121,19 @@ private:
                      DaemonSocketPDU& aPDU,
                      DaemonSocketResultHandler* aUserData);
   void HandleAvrcpSvc(const DaemonSocketPDUHeader& aHeader,
                       DaemonSocketPDU& aPDU,
                       DaemonSocketResultHandler* aRes);
   void HandleGattSvc(const DaemonSocketPDUHeader& aHeader,
                      DaemonSocketPDU& aPDU,
                      DaemonSocketResultHandler* aRes);
+  void HandleHidSvc(const DaemonSocketPDUHeader& aHeader,
+                    DaemonSocketPDU& aPDU,
+                    DaemonSocketResultHandler* aRes);
 
   DaemonSocket* mConnection;
   nsTArray<RefPtr<DaemonSocketResultHandler>> mResQ;
 };
 
 BluetoothDaemonProtocol::BluetoothDaemonProtocol()
 { }
 
@@ -211,28 +216,37 @@ void
 BluetoothDaemonProtocol::HandleGattSvc(
   const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
   DaemonSocketResultHandler* aRes)
 {
   BluetoothDaemonGattModule::HandleSvc(aHeader, aPDU, aRes);
 }
 
 void
+BluetoothDaemonProtocol::HandleHidSvc(
+  const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+  DaemonSocketResultHandler* aRes)
+{
+  BluetoothDaemonHidModule::HandleSvc(aHeader, aPDU, aRes);
+}
+
+void
 BluetoothDaemonProtocol::Handle(DaemonSocketPDU& aPDU)
 {
   static void (BluetoothDaemonProtocol::* const HandleSvc[])(
     const DaemonSocketPDUHeader&, DaemonSocketPDU&,
     DaemonSocketResultHandler*) = {
     [BluetoothDaemonSetupModule::SERVICE_ID] =
       &BluetoothDaemonProtocol::HandleSetupSvc,
     [BluetoothDaemonCoreModule::SERVICE_ID] =
       &BluetoothDaemonProtocol::HandleCoreSvc,
     [BluetoothDaemonSocketModule::SERVICE_ID] =
       &BluetoothDaemonProtocol::HandleSocketSvc,
-    [0x03] = nullptr, // HID host
+    [BluetoothDaemonHidModule::SERVICE_ID] =
+      &BluetoothDaemonProtocol::HandleHidSvc,
     [0x04] = nullptr, // PAN
     [BluetoothDaemonHandsfreeModule::SERVICE_ID] =
       &BluetoothDaemonProtocol::HandleHandsfreeSvc,
     [BluetoothDaemonA2dpModule::SERVICE_ID] =
       &BluetoothDaemonProtocol::HandleA2dpSvc,
     [0x07] = nullptr, // Health
     [BluetoothDaemonAvrcpModule::SERVICE_ID] =
       &BluetoothDaemonProtocol::HandleAvrcpSvc,
@@ -629,16 +643,28 @@ BluetoothDaemonInterface::GetBluetoothGa
     return mGattInterface;
   }
 
   mGattInterface = new BluetoothDaemonGattInterface(mProtocol);
 
   return mGattInterface;
 }
 
+BluetoothHidInterface*
+BluetoothDaemonInterface::GetBluetoothHidInterface()
+{
+  if (mHidInterface) {
+    return mHidInterface;
+  }
+
+  mHidInterface = new BluetoothDaemonHidInterface(mProtocol);
+
+  return mHidInterface;
+}
+
 // |DaemonSocketConsumer|, |ListenSocketConsumer|
 
 void
 BluetoothDaemonInterface::OnConnectSuccess(int aIndex)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
 
--- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
@@ -25,16 +25,17 @@ BEGIN_BLUETOOTH_NAMESPACE
 class BluetoothDaemonA2dpInterface;
 class BluetoothDaemonAvrcpInterface;
 class BluetoothDaemonCoreInterface;
 class BluetoothDaemonGattInterface;
 class BluetoothDaemonHandsfreeInterface;
 class BluetoothDaemonProtocol;
 class BluetoothDaemonSetupInterface;
 class BluetoothDaemonSocketInterface;
+class BluetoothDaemonHidInterface;
 
 class BluetoothDaemonInterface final
   : public BluetoothInterface
   , public mozilla::ipc::DaemonSocketConsumer
   , public mozilla::ipc::ListenSocketConsumer
 {
 public:
   class CleanupResultHandler;
@@ -53,16 +54,17 @@ public:
 
   BluetoothSetupInterface* GetBluetoothSetupInterface() override;
   BluetoothCoreInterface* GetBluetoothCoreInterface() override;
   BluetoothSocketInterface* GetBluetoothSocketInterface() override;
   BluetoothHandsfreeInterface* GetBluetoothHandsfreeInterface() override;
   BluetoothA2dpInterface* GetBluetoothA2dpInterface() override;
   BluetoothAvrcpInterface* GetBluetoothAvrcpInterface() override;
   BluetoothGattInterface* GetBluetoothGattInterface() override;
+  BluetoothHidInterface* GetBluetoothHidInterface() override;
 
 protected:
   enum Channel {
     LISTEN_SOCKET,
     CMD_CHANNEL,
     NTF_CHANNEL
   };
 
@@ -92,13 +94,14 @@ private:
 
   nsAutoPtr<BluetoothDaemonSetupInterface> mSetupInterface;
   nsAutoPtr<BluetoothDaemonCoreInterface> mCoreInterface;
   nsAutoPtr<BluetoothDaemonSocketInterface> mSocketInterface;
   nsAutoPtr<BluetoothDaemonHandsfreeInterface> mHandsfreeInterface;
   nsAutoPtr<BluetoothDaemonA2dpInterface> mA2dpInterface;
   nsAutoPtr<BluetoothDaemonAvrcpInterface> mAvrcpInterface;
   nsAutoPtr<BluetoothDaemonGattInterface> mGattInterface;
+  nsAutoPtr<BluetoothDaemonHidInterface> mHidInterface;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif // mozilla_dom_bluetooth_bluedroid_BluetoothDaemonInterface_h
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothHidManager.cpp
@@ -0,0 +1,610 @@
+/* -*- 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 "BluetoothHidManager.h"
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsIObserverService.h"
+
+using namespace mozilla;
+USING_BLUETOOTH_NAMESPACE
+
+namespace {
+  StaticRefPtr<BluetoothHidManager> sBluetoothHidManager;
+  static BluetoothHidInterface* sBluetoothHidInterface = nullptr;
+  bool sInShutdown = false;
+} // namesapce
+
+const int BluetoothHidManager::MAX_NUM_CLIENTS = 1;
+
+BluetoothHidManager::BluetoothHidManager()
+  : mHidConnected(false)
+{
+}
+
+void
+BluetoothHidManager::Reset()
+{
+  mDeviceAddress.Clear();
+  mController = nullptr;
+  mHidConnected = false;
+}
+
+class BluetoothHidManager::RegisterModuleResultHandler final
+  : public BluetoothSetupResultHandler
+{
+public:
+  RegisterModuleResultHandler(BluetoothHidInterface* aInterface,
+                              BluetoothProfileResultHandler* aRes)
+    : mInterface(aInterface)
+    , mRes(aRes)
+  {
+    MOZ_ASSERT(mInterface);
+  }
+
+  void OnError(BluetoothStatus aStatus) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    BT_WARNING("BluetoothSetupInterface::RegisterModule failed for HID: %d",
+               (int)aStatus);
+
+    mInterface->SetNotificationHandler(nullptr);
+
+    if (mRes) {
+      mRes->OnError(NS_ERROR_FAILURE);
+    }
+  }
+
+  void RegisterModule() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    sBluetoothHidInterface = mInterface;
+
+    if (mRes) {
+      mRes->Init();
+    }
+  }
+
+private:
+  BluetoothHidInterface* mInterface;
+  RefPtr<BluetoothProfileResultHandler> mRes;
+};
+
+class BluetoothHidManager::InitProfileResultHandlerRunnable final
+  : public nsRunnable
+{
+public:
+  InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
+                                   nsresult aRv)
+    : mRes(aRes)
+    , mRv(aRv)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (NS_SUCCEEDED(mRv)) {
+      mRes->Init();
+    } else {
+      mRes->OnError(mRv);
+    }
+    return NS_OK;
+  }
+
+private:
+  RefPtr<BluetoothProfileResultHandler> mRes;
+  nsresult mRv;
+};
+
+// static
+void
+BluetoothHidManager::InitHidInterface(BluetoothProfileResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sBluetoothHidInterface) {
+    BT_LOGR("Bluetooth HID interface is already initialized.");
+    RefPtr<nsRunnable> r =
+      new InitProfileResultHandlerRunnable(aRes, NS_OK);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID Init runnable");
+    }
+    return;
+  }
+
+  auto btInf = BluetoothInterface::GetInstance();
+
+  if (NS_WARN_IF(!btInf)) {
+    // If there's no backend interface, we dispatch a runnable
+    // that calls the profile result handler.
+    RefPtr<nsRunnable> r =
+      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID OnError runnable");
+    }
+    return;
+  }
+
+  auto setupInterface = btInf->GetBluetoothSetupInterface();
+
+  if (NS_WARN_IF(!setupInterface)) {
+    // If there's no Setup interface, we dispatch a runnable
+    // that calls the profile result handler.
+    RefPtr<nsRunnable> r =
+      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID OnError runnable");
+    }
+    return;
+  }
+
+  auto hidinterface = btInf->GetBluetoothHidInterface();
+
+  if (NS_WARN_IF(!hidinterface)) {
+    // If there's no HID interface, we dispatch a runnable
+    // that calls the profile result handler.
+    RefPtr<nsRunnable> r =
+      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID OnError runnable");
+    }
+    return;
+  }
+
+  // Set notification handler _before_ registering the module. It could
+  // happen that we receive notifications, before the result handler runs.
+  hidinterface->SetNotificationHandler(BluetoothHidManager::Get());
+
+  setupInterface->RegisterModule(
+    SETUP_SERVICE_ID_HID, 0, MAX_NUM_CLIENTS,
+    new RegisterModuleResultHandler(hidinterface, aRes));
+}
+
+BluetoothHidManager::~BluetoothHidManager()
+{ }
+
+void
+BluetoothHidManager::Uninit()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE_VOID(obs);
+
+  if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
+    BT_WARNING("Failed to remove shutdown observer!");
+  }
+}
+
+class BluetoothHidManager::UnregisterModuleResultHandler final
+  : public BluetoothSetupResultHandler
+{
+public:
+  UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
+    : mRes(aRes)
+  { }
+
+  void OnError(BluetoothStatus aStatus) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for HID: %d",
+               (int)aStatus);
+
+    sBluetoothHidInterface->SetNotificationHandler(nullptr);
+    sBluetoothHidInterface = nullptr;
+
+    sBluetoothHidManager->Uninit();
+    sBluetoothHidManager = nullptr;
+
+    if (mRes) {
+      mRes->OnError(NS_ERROR_FAILURE);
+    }
+  }
+
+  void UnregisterModule() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    sBluetoothHidInterface->SetNotificationHandler(nullptr);
+    sBluetoothHidInterface = nullptr;
+
+    sBluetoothHidManager->Uninit();
+    sBluetoothHidManager = nullptr;
+
+    if (mRes) {
+      mRes->Deinit();
+    }
+  }
+
+private:
+  RefPtr<BluetoothProfileResultHandler> mRes;
+};
+
+class BluetoothHidManager::DeinitProfileResultHandlerRunnable final
+  : public nsRunnable
+{
+public:
+  DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
+                                     nsresult aRv)
+    : mRes(aRes)
+    , mRv(aRv)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (NS_SUCCEEDED(mRv)) {
+      mRes->Deinit();
+    } else {
+      mRes->OnError(mRv);
+    }
+    return NS_OK;
+  }
+
+private:
+  RefPtr<BluetoothProfileResultHandler> mRes;
+  nsresult mRv;
+};
+
+// static
+void
+BluetoothHidManager::DeinitHidInterface(BluetoothProfileResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sBluetoothHidInterface) {
+    BT_LOGR("Bluetooth Hid interface has not been initialized.");
+    RefPtr<nsRunnable> r =
+      new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID Deinit runnable");
+    }
+    return;
+  }
+
+  auto btInf = BluetoothInterface::GetInstance();
+
+  if (NS_WARN_IF(!btInf)) {
+    // If there's no backend interface, we dispatch a runnable
+    // that calls the profile result handler.
+    RefPtr<nsRunnable> r =
+      new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID OnError runnable");
+    }
+    return;
+  }
+
+  auto setupInterface = btInf->GetBluetoothSetupInterface();
+
+  if (NS_WARN_IF(!setupInterface)) {
+    // If there's no Setup interface, we dispatch a runnable
+    // that calls the profile result handler.
+    RefPtr<nsRunnable> r =
+      new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HID OnError runnable");
+    }
+    return;
+  }
+
+  setupInterface->UnregisterModule(
+    SETUP_SERVICE_ID_HID,
+    new UnregisterModuleResultHandler(aRes));
+}
+
+NS_IMETHODIMP
+BluetoothHidManager::Observe(nsISupports* aSubject,
+                            const char* aTopic,
+                            const char16_t* aData)
+{
+  MOZ_ASSERT(sBluetoothHidManager);
+
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    HandleShutdown();
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "BluetoothHidManager got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+// static
+BluetoothHidManager*
+BluetoothHidManager::Get()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // If we already exist, exit early
+  if (sBluetoothHidManager) {
+    return sBluetoothHidManager;
+  }
+
+  // If we're in shutdown, don't create a new instance
+  NS_ENSURE_FALSE(sInShutdown, nullptr);
+
+  // Create a new instance, register, and return
+  sBluetoothHidManager = new BluetoothHidManager();
+
+  return sBluetoothHidManager;
+}
+
+void
+BluetoothHidManager::NotifyConnectionStateChanged(const nsAString& aType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Notify Gecko observers
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE_VOID(obs);
+
+  nsAutoString deviceAddressStr;
+  AddressToString(mDeviceAddress, deviceAddressStr);
+
+  if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(),
+                                     deviceAddressStr.get()))) {
+    BT_WARNING("Failed to notify observsers!");
+  }
+
+  // Dispatch an event of status change
+  DispatchStatusChangedEvent(
+    NS_LITERAL_STRING(HID_STATUS_CHANGED_ID), mDeviceAddress, IsConnected());
+}
+
+bool
+BluetoothHidManager::IsConnected()
+{
+  return mHidConnected;
+}
+
+void
+BluetoothHidManager::OnConnectError()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
+  Reset();
+}
+
+class BluetoothHidManager::ConnectResultHandler final
+  : public BluetoothHidResultHandler
+{
+public:
+  ConnectResultHandler(BluetoothHidManager* aHidManager)
+    : mHidManager(aHidManager)
+  {
+    MOZ_ASSERT(mHidManager);
+  }
+
+  void OnError(BluetoothStatus aStatus) override
+  {
+    BT_WARNING("BluetoothHidInterface::Connect failed: %d",
+               (int)aStatus);
+    mHidManager->OnConnectError();
+  }
+
+private:
+  BluetoothHidManager* mHidManager;
+};
+
+void
+BluetoothHidManager::Connect(const BluetoothAddress& aDeviceAddress,
+                             BluetoothProfileController* aController)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!aDeviceAddress.IsCleared());
+  MOZ_ASSERT(aController && !mController);
+
+  if(sInShutdown) {
+    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
+    return;
+  }
+
+  if(IsConnected()) {
+    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
+    return;
+  }
+
+  if(!sBluetoothHidInterface) {
+    BT_LOGR("sBluetoothHidInterface is null");
+    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
+    return;
+  }
+
+  mDeviceAddress = aDeviceAddress;
+  mController = aController;
+
+  sBluetoothHidInterface->Connect(mDeviceAddress,
+                                  new ConnectResultHandler(this));
+}
+
+void
+BluetoothHidManager::OnDisconnectError()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE_VOID(mController);
+
+  mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
+
+  mController = nullptr;
+}
+
+class BluetoothHidManager::DisconnectResultHandler final
+  : public BluetoothHidResultHandler
+{
+public:
+  DisconnectResultHandler(BluetoothHidManager* aHidManager)
+    : mHidManager(aHidManager)
+  {
+    MOZ_ASSERT(mHidManager);
+  }
+
+  void OnError(BluetoothStatus aStatus) override
+  {
+    BT_WARNING("BluetoothHidInterface::Disconnect failed: %d",
+               (int)aStatus);
+    mHidManager->OnDisconnectError();
+  }
+
+private:
+  BluetoothHidManager* mHidManager;
+};
+
+void
+BluetoothHidManager::Disconnect(BluetoothProfileController* aController)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mController);
+
+  if (!IsConnected()) {
+    if (aController) {
+      aController->NotifyCompletion(
+        NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
+    }
+    return;
+  }
+
+  MOZ_ASSERT(!mDeviceAddress.IsCleared());
+
+  if (!sBluetoothHidInterface) {
+    BT_LOGR("sBluetoothHidInterface is null");
+    if (aController) {
+      aController->NotifyCompletion(
+        NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
+    }
+    return;
+  }
+
+  mController = aController;
+
+  sBluetoothHidInterface->Disconnect(mDeviceAddress,
+                                     new DisconnectResultHandler(this));
+}
+
+void
+BluetoothHidManager::OnConnect(const nsAString& aErrorStr)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  /**
+   * On the one hand, notify the controller that we've done for outbound
+   * connections. On the other hand, we do nothing for inbound connections.
+   */
+  NS_ENSURE_TRUE_VOID(mController);
+
+  mController->NotifyCompletion(aErrorStr);
+  mController = nullptr;
+}
+
+void
+BluetoothHidManager::OnDisconnect(const nsAString& aErrorStr)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  /**
+   * On the one hand, notify the controller that we've done for outbound
+   * connections. On the other hand, we do nothing for inbound connections.
+   */
+  NS_ENSURE_TRUE_VOID(mController);
+
+  mController->NotifyCompletion(aErrorStr);
+  Reset();
+}
+
+void
+BluetoothHidManager::HandleShutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  sInShutdown = true;
+  Disconnect(nullptr);
+  sBluetoothHidManager = nullptr;
+}
+
+void
+BluetoothHidManager::HandleBackendError()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mHidConnected) {
+    ConnectionStateNotification(mDeviceAddress,
+                                HID_CONNECTION_STATE_DISCONNECTED);
+  }
+}
+
+void
+BluetoothHidManager::GetAddress(BluetoothAddress& aDeviceAddress)
+{
+  aDeviceAddress = mDeviceAddress;
+}
+
+void
+BluetoothHidManager::OnUpdateSdpRecords(
+  const BluetoothAddress& aDeviceAddress)
+{
+  // Bluedroid handles this part
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothHidManager::OnGetServiceChannel(
+  const BluetoothAddress& aDeviceAddress,
+  const BluetoothUuid& aServiceUuid,
+  int aChannel)
+{
+  // Bluedroid handles this part
+  MOZ_ASSERT(false);
+}
+
+//
+// Bluetooth notifications
+//
+
+/**
+ * There are totally 10 connection states, and will receive 4 possible
+ * states: "connected", "connecting", "disconnected", "disconnecting".
+ * Here we only handle connected and disconnected states. We do nothing
+ * for remaining states.
+ *
+ * Possible cases are listed below:
+ * CONNECTED:
+ *   1. Successful inbound or outbound connection
+ * DISCONNECTED:
+ *   2. Attempt disconnection succeeded
+ *   3. Attempt connection failed
+ *   4. Either unpair from the remote device or the remote device is
+ *      out of range in connected state
+ */
+void
+BluetoothHidManager::ConnectionStateNotification(
+  const BluetoothAddress& aBdAddr, BluetoothHidConnectionState aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  BT_LOGR("state %d", aState);
+
+  if (aState == HID_CONNECTION_STATE_CONNECTED) {
+    mHidConnected = true;
+    mDeviceAddress = aBdAddr;
+    NotifyConnectionStateChanged(
+      NS_LITERAL_STRING(BLUETOOTH_HID_STATUS_CHANGED_ID));
+    OnConnect(EmptyString());
+  } else if (aState == HID_CONNECTION_STATE_DISCONNECTED) {
+    mHidConnected = false;
+    NotifyConnectionStateChanged(
+      NS_LITERAL_STRING(BLUETOOTH_HID_STATUS_CHANGED_ID));
+    OnDisconnect(EmptyString());
+  }
+}
+
+NS_IMPL_ISUPPORTS(BluetoothHidManager, nsIObserver)
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothHidManager.h
@@ -0,0 +1,71 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothHidManager_h
+#define mozilla_dom_bluetooth_bluedroid_BluetoothHidMnagaer_h
+
+#include "BluetoothCommon.h"
+#include "BluetoothInterface.h"
+#include "BluetoothProfileController.h"
+#include "BluetoothProfileManagerBase.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothHidManager : public BluetoothProfileManagerBase
+                          , public BluetoothHidNotificationHandler
+{
+public:
+  BT_DECL_PROFILE_MGR_BASE
+
+  static const int MAX_NUM_CLIENTS;
+
+  void OnConnectError();
+  void OnDisconnectError();
+
+  virtual void GetName(nsACString& aName)
+  {
+    aName.AssignLiteral("HID");
+  }
+
+  static BluetoothHidManager* Get();
+  static void InitHidInterface(BluetoothProfileResultHandler* aRes);
+  static void DeinitHidInterface(BluetoothProfileResultHandler* aRes);
+
+  void HandleBackendError();
+
+protected:
+  virtual ~BluetoothHidManager();
+
+private:
+  class DeinitProfileResultHandlerRunnable;
+  class InitProfileResultHandlerRunnable;
+  class RegisterModuleResultHandler;
+  class UnregisterModuleResultHandler;
+
+  class ConnectResultHandler;
+  class DisconnectResultHandler;
+
+  BluetoothHidManager();
+  void Uninit();
+  void HandleShutdown();
+  void NotifyConnectionStateChanged(const nsAString& aType);
+
+  //
+  // Bluetooth notifications
+  //
+
+  void ConnectionStateNotification(
+    const BluetoothAddress& aBdAddr,
+    BluetoothHidConnectionState aState) override;
+
+  bool mHidConnected;
+  BluetoothAddress mDeviceAddress;
+  RefPtr<BluetoothProfileController> mController;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif // mozilla_dom_bluetooth_bluedroid_BluetoothHidManager_h
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -2627,23 +2627,27 @@ BluetoothServiceBluedroid::BackendErrorN
   if (!aCrashed) {
     return;
   }
 
   /*
    * Reset following profile manager states for unexpected backend crash.
    * - HFP: connection state and audio state
    * - A2DP: connection state
+   * - HID: connection state
    */
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   NS_ENSURE_TRUE_VOID(hfp);
   hfp->HandleBackendError();
   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   NS_ENSURE_TRUE_VOID(a2dp);
   a2dp->HandleBackendError();
+  BluetoothHidManager* hid = BluetoothHidManager::Get();
+  NS_ENSURE_TRUE_VOID(hid);
+  hid->HandleBackendError();
 
   mIsRestart = true;
   BT_LOGR("Recovery step2: stop bluetooth");
   StopBluetooth(false, nullptr);
 }
 
 void
 BluetoothServiceBluedroid::CompleteToggleBt(bool aEnabled)
rename from dom/bluetooth/common/BluetoothHidManager.cpp
rename to dom/bluetooth/bluez/BluetoothHidManager.cpp
rename from dom/bluetooth/common/BluetoothHidManager.h
rename to dom/bluetooth/bluez/BluetoothHidManager.h
--- a/dom/bluetooth/common/BluetoothCommon.h
+++ b/dom/bluetooth/common/BluetoothCommon.h
@@ -202,16 +202,17 @@ extern bool gBluetoothDebugFlag;
 #define BLUETOOTH_SCO_STATUS_CHANGED_ID  "bluetooth-sco-status-changed"
 
 /**
  * When the connection status of a Bluetooth profile is changed, we'll
  * dispatch one of the following events.
  */
 #define A2DP_STATUS_CHANGED_ID               "a2dpstatuschanged"
 #define HFP_STATUS_CHANGED_ID                "hfpstatuschanged"
+#define HID_STATUS_CHANGED_ID                "hidstatuschanged"
 #define SCO_STATUS_CHANGED_ID                "scostatuschanged"
 
 /**
  * Types of pairing requests for constructing BluetoothPairingEvent and
  * BluetoothPairingHandle.
  */
 #define PAIRING_REQ_TYPE_DISPLAYPASSKEY       "displaypasskeyreq"
 #define PAIRING_REQ_TYPE_ENTERPINCODE         "enterpincodereq"
@@ -302,16 +303,23 @@ extern bool gBluetoothDebugFlag;
 #define ERR_INTERNAL_ERROR "InternalError"
 
 /**
  * BT specification v4.1 defines the maximum attribute length as 512 octets.
  * Currently use 600 here to conform to bluedroid's BTGATT_MAX_ATTR_LEN.
  */
 #define BLUETOOTH_GATT_MAX_ATTR_LEN 600
 
+/**
+ * The maximum descriptor length defined in BlueZ ipc spec.
+ * Please refer to http://git.kernel.org/cgit/bluetooth/bluez.git/tree/\
+ * android/hal-ipc-api.txt#n532
+ */
+#define BLUETOOTH_HID_MAX_DESC_LEN 884
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 enum BluetoothStatus {
   STATUS_SUCCESS,
   STATUS_FAIL,
   STATUS_NOT_READY,
   STATUS_NOMEM,
   STATUS_BUSY,
@@ -871,16 +879,74 @@ struct BluetoothProperty {
 
 enum BluetoothSocketType {
   RFCOMM = 1,
   SCO    = 2,
   L2CAP  = 3,
   EL2CAP = 4
 };
 
+struct BluetoothHidInfoParam {
+  uint16_t mAttributeMask;
+  uint8_t mSubclass;
+  uint8_t mApplicationId;
+  uint16_t mVendorId;
+  uint16_t mProductId;
+  uint16_t mVersion;
+  uint8_t mCountryCode;
+  uint16_t mDescriptorLength;
+  uint8_t mDescriptorValue[BLUETOOTH_HID_MAX_DESC_LEN];
+};
+
+struct BluetoothHidReport {
+  nsTArray<uint8_t> mReportData;
+};
+
+enum BluetoothHidProtocolMode {
+  HID_PROTOCOL_MODE_REPORT = 0x00,
+  HID_PROTOCOL_MODE_BOOT = 0x01,
+  HID_PROTOCOL_MODE_UNSUPPORTED = 0xff
+};
+
+enum BluetoothHidReportType {
+  HID_REPORT_TYPE_INPUT = 0x01,
+  HID_REPORT_TYPE_OUTPUT = 0x02,
+  HID_REPORT_TYPE_FEATURE = 0x03
+};
+
+enum BluetoothHidConnectionState {
+  HID_CONNECTION_STATE_CONNECTED,
+  HID_CONNECTION_STATE_CONNECTING,
+  HID_CONNECTION_STATE_DISCONNECTED,
+  HID_CONNECTION_STATE_DISCONNECTING,
+  HID_CONNECTION_STATE_FAILED_MOUSE_FROM_HOST,
+  HID_CONNECTION_STATE_FAILED_KEYBOARD_FROM_HOST,
+  HID_CONNECTION_STATE_FAILED_TOO_MANY_DEVICES,
+  HID_CONNECTION_STATE_FAILED_NO_HID_DRIVER,
+  HID_CONNECTION_STATE_FAILED_GENERIC,
+  HID_CONNECTION_STATE_UNKNOWN
+};
+
+enum BluetoothHidStatus {
+  HID_STATUS_OK,
+  HID_STATUS_HANDSHAKE_DEVICE_NOT_READY,
+  HID_STATUS_HANDSHAKE_INVALID_REPORT_ID,
+  HID_STATUS_HANDSHAKE_TRANSACTION_NOT_SPT,
+  HID_STATUS_HANDSHAKE_INVALID_PARAMETER,
+  HID_STATUS_HANDSHAKE_GENERIC_ERROR,
+  HID_STATUS_GENERAL_ERROR,
+  HID_STATUS_SDP_ERROR,
+  HID_STATUS_SET_PROTOCOL_ERROR,
+  HID_STATUS_DEVICE_DATABASE_FULL,
+  HID_STATUS_DEVICE_TYPE_NOT_SUPPORTED,
+  HID_STATUS_NO_RESOURCES,
+  HID_STATUS_AUTHENTICATION_FAILED,
+  HID_STATUS_HDL
+};
+
 enum BluetoothHandsfreeAtResponse {
   HFP_AT_RESPONSE_ERROR,
   HFP_AT_RESPONSE_OK
 };
 
 enum BluetoothHandsfreeAudioState {
   HFP_AUDIO_STATE_DISCONNECTED,
   HFP_AUDIO_STATE_CONNECTING,
--- a/dom/bluetooth/common/BluetoothInterface.cpp
+++ b/dom/bluetooth/common/BluetoothInterface.cpp
@@ -263,16 +263,121 @@ BluetoothSocketResultHandler::Accept(int
 
 // Interface
 //
 
 BluetoothSocketInterface::~BluetoothSocketInterface()
 { }
 
 //
+//Hid Interface
+//
+
+//Notification handling
+//
+
+BluetoothHidNotificationHandler::BluetoothHidNotificationHandler()
+{ }
+
+BluetoothHidNotificationHandler::~BluetoothHidNotificationHandler()
+{ }
+
+void
+BluetoothHidNotificationHandler::ConnectionStateNotification(
+  const BluetoothAddress& aBdAddr, BluetoothHidConnectionState aState)
+{ }
+
+void
+BluetoothHidNotificationHandler::HidInfoNotification(
+  const BluetoothAddress& aBdAddr,
+  const BluetoothHidInfoParam& aHidInfoParam)
+{ }
+
+void
+BluetoothHidNotificationHandler::ProtocolModeNotification(
+  const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus,
+  BluetoothHidProtocolMode aProtocolMode)
+{ }
+
+void
+BluetoothHidNotificationHandler::IdleTimeNotification(
+  const BluetoothAddress& aBdAddr,
+  BluetoothHidStatus aStatus, uint16_t aIdleTime)
+{ }
+
+void
+BluetoothHidNotificationHandler::GetReportNotification(
+  const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus,
+  const BluetoothHidReport& aReport)
+{ }
+
+void
+BluetoothHidNotificationHandler::VirtualUnplugNotification(
+  const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus)
+{ }
+
+void
+BluetoothHidNotificationHandler::HandshakeNotification(
+  const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus)
+{ }
+
+// Result handling
+//
+
+void BluetoothHidResultHandler::OnError(BluetoothStatus aStatus)
+{
+  BT_WARNING("Received error code %d", (int)aStatus);
+}
+
+void
+BluetoothHidResultHandler::Connect()
+{ }
+
+void
+BluetoothHidResultHandler::Disconnect()
+{ }
+
+void
+BluetoothHidResultHandler::VirtualUnplug()
+{ }
+
+void
+BluetoothHidResultHandler::SetInfo()
+{ }
+
+void
+BluetoothHidResultHandler::GetProtocol()
+{ }
+
+void
+BluetoothHidResultHandler::SetProtocol()
+{ }
+
+void
+BluetoothHidResultHandler::GetReport()
+{ }
+
+void
+BluetoothHidResultHandler::SetReport()
+{ }
+
+void
+BluetoothHidResultHandler::SendData()
+{ }
+
+// Interface
+//
+
+BluetoothHidInterface::BluetoothHidInterface()
+{ }
+
+BluetoothHidInterface::~BluetoothHidInterface()
+{ }
+
+//
 // Handsfree Interface
 //
 
 // Notification handling
 //
 
 BluetoothHandsfreeNotificationHandler::BluetoothHandsfreeNotificationHandler()
 { }
--- a/dom/bluetooth/common/BluetoothInterface.h
+++ b/dom/bluetooth/common/BluetoothInterface.h
@@ -272,16 +272,140 @@ public:
 
   virtual void Close(BluetoothSocketResultHandler* aRes) = 0;
 
 protected:
   virtual ~BluetoothSocketInterface();
 };
 
 //
+// HID Interface
+//
+
+class BluetoothHidNotificationHandler
+{
+public:
+  virtual void
+  ConnectionStateNotification(const BluetoothAddress& aBdAddr,
+                              BluetoothHidConnectionState aState);
+
+  virtual void
+  HidInfoNotification(
+    const BluetoothAddress& aBdAddr,
+    const BluetoothHidInfoParam& aHidInfoParam);
+
+  virtual void
+  ProtocolModeNotification(const BluetoothAddress& aBdAddr,
+                           BluetoothHidStatus aStatus,
+                           BluetoothHidProtocolMode aProtocolMode);
+
+  virtual void
+  IdleTimeNotification(const BluetoothAddress& aBdAddr,
+                       BluetoothHidStatus aStatus,
+                       uint16_t aIdleTime);
+
+  virtual void
+  GetReportNotification(const BluetoothAddress& aBdAddr,
+                        BluetoothHidStatus aStatus,
+                        const BluetoothHidReport& aReport);
+
+  virtual void
+  VirtualUnplugNotification(const BluetoothAddress& aBdAddr,
+                            BluetoothHidStatus aStatus);
+
+  virtual void
+  HandshakeNotification(const BluetoothAddress& aBdAddr,
+                        BluetoothHidStatus aStatus);
+
+protected:
+  BluetoothHidNotificationHandler();
+  virtual ~BluetoothHidNotificationHandler();
+};
+
+class BluetoothHidResultHandler
+  : public mozilla::ipc::DaemonSocketResultHandler
+{
+public:
+  virtual void OnError(BluetoothStatus aStatus);
+
+  virtual void Connect();
+  virtual void Disconnect();
+
+  virtual void VirtualUnplug();
+
+  virtual void SetInfo();
+
+  virtual void GetProtocol();
+  virtual void SetProtocol();
+
+  virtual void GetReport();
+  virtual void SetReport();
+
+  virtual void SendData();
+
+protected:
+  virtual ~BluetoothHidResultHandler() { }
+};
+
+class BluetoothHidInterface
+{
+public:
+  virtual void SetNotificationHandler(
+    BluetoothHidNotificationHandler* aNotificationHandler) = 0;
+
+  /* Connect / Disconnect */
+
+  virtual void Connect(const BluetoothAddress& aBdAddr,
+                       BluetoothHidResultHandler* aRes) = 0;
+  virtual void Disconnect(const BluetoothAddress& aBdAddr,
+                          BluetoothHidResultHandler* aRes) = 0;
+
+  /* Virtual Unplug */
+  virtual void VirtualUnplug(const BluetoothAddress& aBdAddr,
+                             BluetoothHidResultHandler* aRes) = 0;
+
+  /* Set Info */
+
+  virtual void SetInfo(const BluetoothAddress& aBdAddr,
+                       const BluetoothHidInfoParam& aHidInfoParam,
+                       BluetoothHidResultHandler* aRes) = 0;
+
+  /* Protocol */
+
+  virtual void GetProtocol(const BluetoothAddress& aBdAddr,
+                           BluetoothHidProtocolMode aHidProtoclMode,
+                           BluetoothHidResultHandler* aRes) = 0;
+  virtual void SetProtocol(const BluetoothAddress& aBdAddr,
+                           BluetoothHidProtocolMode aHidProtocolMode,
+                           BluetoothHidResultHandler* aRes) = 0;
+
+  /* Report */
+
+  virtual void GetReport(const BluetoothAddress& aBdAddr,
+                         BluetoothHidReportType aType,
+                         uint8_t aReportId,
+                         uint16_t aBuffSize,
+                         BluetoothHidResultHandler* aRes) = 0;
+  virtual void SetReport(const BluetoothAddress& aBdAddr,
+                         BluetoothHidReportType aType,
+                         const BluetoothHidReport& aReport,
+                         BluetoothHidResultHandler* aRes) = 0;
+
+  /* Send Data */
+
+  virtual void SendData(const BluetoothAddress& aBdAddr,
+                        uint16_t aDataLen, const uint8_t* aData,
+                        BluetoothHidResultHandler* aRes) = 0;
+
+protected:
+  BluetoothHidInterface();
+  virtual ~BluetoothHidInterface();
+};
+
+//
 // Handsfree Interface
 //
 
 class BluetoothHandsfreeNotificationHandler
 {
 public:
   virtual void
   ConnectionStateNotification(BluetoothHandsfreeConnectionState aState,
@@ -1149,16 +1273,17 @@ public:
 
   virtual BluetoothSetupInterface* GetBluetoothSetupInterface() = 0;
   virtual BluetoothCoreInterface* GetBluetoothCoreInterface() = 0;
   virtual BluetoothSocketInterface* GetBluetoothSocketInterface() = 0;
   virtual BluetoothHandsfreeInterface* GetBluetoothHandsfreeInterface() = 0;
   virtual BluetoothA2dpInterface* GetBluetoothA2dpInterface() = 0;
   virtual BluetoothAvrcpInterface* GetBluetoothAvrcpInterface() = 0;
   virtual BluetoothGattInterface* GetBluetoothGattInterface() = 0;
+  virtual BluetoothHidInterface* GetBluetoothHidInterface() = 0;
 
 protected:
   BluetoothInterface();
   virtual ~BluetoothInterface();
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth/common/BluetoothProfileController.cpp
+++ b/dom/bluetooth/common/BluetoothProfileController.cpp
@@ -183,16 +183,17 @@ BluetoothProfileController::SetupProfile
   // The value of CoD is invalid. Since the device didn't declare its class of
   // device properly, we assume the device may support all of these profiles.
   // Note the invalid CoD from bluedroid callback usually results from
   // NFC-triggered direct pairing for no EIR query records.
   if (isInvalid) {
     AddProfile(BluetoothHfpManager::Get());
     AddProfile(BluetoothA2dpManager::Get());
     AddProfile(BluetoothAvrcpManager::Get()); // register after A2DP
+    AddProfile(BluetoothHidManager::Get());
     return;
   }
 
   NS_ENSURE_TRUE_VOID(hasAudio || hasRendering || isPeripheral);
 
   // Audio bit should be set if remote device supports HFP/HSP.
   if (hasAudio) {
     AddProfile(BluetoothHfpManager::Get());
--- a/dom/bluetooth/common/webapi/BluetoothAdapter.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothAdapter.cpp
@@ -534,16 +534,17 @@ BluetoothAdapter::Notify(const Bluetooth
       HandleLeDeviceFound(v);
     }
   } else if (aData.name().EqualsLiteral(DEVICE_PAIRED_ID)) {
     HandleDevicePaired(aData.value());
   } else if (aData.name().EqualsLiteral(DEVICE_UNPAIRED_ID)) {
     HandleDeviceUnpaired(aData.value());
   } else if (aData.name().EqualsLiteral(HFP_STATUS_CHANGED_ID) ||
              aData.name().EqualsLiteral(SCO_STATUS_CHANGED_ID) ||
+             aData.name().EqualsLiteral(HID_STATUS_CHANGED_ID) ||
              aData.name().EqualsLiteral(A2DP_STATUS_CHANGED_ID)) {
     MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
     const InfallibleTArray<BluetoothNamedValue>& arr =
       v.get_ArrayOfBluetoothNamedValue();
 
     MOZ_ASSERT(arr.Length() == 2 &&
                arr[0].value().type() == BluetoothValue::TBluetoothAddress &&
                arr[1].value().type() == BluetoothValue::Tbool);
--- a/dom/bluetooth/common/webapi/BluetoothAdapter.h
+++ b/dom/bluetooth/common/webapi/BluetoothAdapter.h
@@ -85,19 +85,20 @@ public:
   /****************************************************************************
    * Event Handlers
    ***************************************************************************/
   IMPL_EVENT_HANDLER(attributechanged);
   // PAIRING
   IMPL_EVENT_HANDLER(devicepaired);
   IMPL_EVENT_HANDLER(deviceunpaired);
   IMPL_EVENT_HANDLER(pairingaborted);
-  // HFP/A2DP/AVRCP
+  // HFP/A2DP/AVRCP/HID
   IMPL_EVENT_HANDLER(a2dpstatuschanged);
   IMPL_EVENT_HANDLER(hfpstatuschanged);
+  IMPL_EVENT_HANDLER(hidstatuschanged);
   IMPL_EVENT_HANDLER(scostatuschanged);
   IMPL_EVENT_HANDLER(requestmediaplaystatus);
   // PBAP
   IMPL_EVENT_HANDLER(obexpasswordreq);
   IMPL_EVENT_HANDLER(pullphonebookreq);
   IMPL_EVENT_HANDLER(pullvcardentryreq);
   IMPL_EVENT_HANDLER(pullvcardlistingreq);
   // MAP
--- a/dom/bluetooth/moz.build
+++ b/dom/bluetooth/moz.build
@@ -12,17 +12,16 @@ if CONFIG['MOZ_B2G_BT']:
 
     if CONFIG['MOZ_B2G_RIL']:
         SOURCES += [
             'common/BluetoothRilListener.cpp'
         ]
 
     SOURCES += [
         'common/BluetoothGattReplyRunnable.cpp',
-        'common/BluetoothHidManager.cpp',
         'common/BluetoothInterface.cpp',
         'common/BluetoothProfileController.cpp',
         'common/BluetoothReplyRunnable.cpp',
         'common/BluetoothService.cpp',
         'common/BluetoothUtils.cpp',
         'common/BluetoothUuidHelper.cpp',
         'common/ObexBase.cpp',
         'common/webapi/BluetoothAdapter.cpp',
@@ -60,16 +59,17 @@ if CONFIG['MOZ_B2G_BT']:
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         if CONFIG['MOZ_B2G_BT_BLUEZ']:
             CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
             SOURCES += [
                 'bluez/BluetoothA2dpManager.cpp',
                 'bluez/BluetoothAvrcpManager.cpp',
                 'bluez/BluetoothDBusService.cpp',
                 'bluez/BluetoothHfpManager.cpp',
+                'bluez/BluetoothHidManager.cpp',
                 'bluez/BluetoothOppManager.cpp',
                 'bluez/BluetoothSocket.cpp',
                 'bluez/BluetoothUnixSocketConnector.cpp'
             ]
             LOCAL_INCLUDES += [
                 'bluez',
             ]
             DEFINES['MOZ_B2G_BT_BLUEZ'] = True
@@ -78,20 +78,22 @@ if CONFIG['MOZ_B2G_BT']:
                 'bluedroid/BluetoothA2dpManager.cpp',
                 'bluedroid/BluetoothAvrcpManager.cpp',
                 'bluedroid/BluetoothDaemonA2dpInterface.cpp',
                 'bluedroid/BluetoothDaemonAvrcpInterface.cpp',
                 'bluedroid/BluetoothDaemonCoreInterface.cpp',
                 'bluedroid/BluetoothDaemonGattInterface.cpp',
                 'bluedroid/BluetoothDaemonHandsfreeInterface.cpp',
                 'bluedroid/BluetoothDaemonHelpers.cpp',
+                'bluedroid/BluetoothDaemonHidInterface.cpp',
                 'bluedroid/BluetoothDaemonInterface.cpp',
                 'bluedroid/BluetoothDaemonSetupInterface.cpp',
                 'bluedroid/BluetoothDaemonSocketInterface.cpp',
                 'bluedroid/BluetoothGattManager.cpp',
+                'bluedroid/BluetoothHidManager.cpp',
                 'bluedroid/BluetoothMapBMessage.cpp',
                 'bluedroid/BluetoothMapFolder.cpp',
                 'bluedroid/BluetoothMapSmsManager.cpp',
                 'bluedroid/BluetoothOppManager.cpp',
                 'bluedroid/BluetoothPbapManager.cpp',
                 'bluedroid/BluetoothServiceBluedroid.cpp',
                 'bluedroid/BluetoothSocket.cpp',
                 'bluedroid/BluetoothSocketMessageWatcher.cpp'
--- a/dom/events/AsyncEventDispatcher.cpp
+++ b/dom/events/AsyncEventDispatcher.cpp
@@ -18,45 +18,54 @@ using namespace dom;
 
 /******************************************************************************
  * mozilla::AsyncEventDispatcher
  ******************************************************************************/
 
 AsyncEventDispatcher::AsyncEventDispatcher(EventTarget* aTarget,
                                            WidgetEvent& aEvent)
   : mTarget(aTarget)
-  , mOnlyChromeDispatch(false)
 {
   MOZ_ASSERT(mTarget);
   RefPtr<Event> event =
     EventDispatcher::CreateEvent(aTarget, nullptr, &aEvent, EmptyString());
   mEvent = do_QueryInterface(event);
   NS_ASSERTION(mEvent, "Should never fail to create an event");
   mEvent->DuplicatePrivateData();
   mEvent->SetTrusted(aEvent.mFlags.mIsTrusted);
 }
 
 NS_IMETHODIMP
 AsyncEventDispatcher::Run()
 {
+  if (mCanceled) {
+    return NS_OK;
+  }
   RefPtr<Event> event = mEvent ? mEvent->InternalDOMEvent() : nullptr;
   if (!event) {
     event = NS_NewDOMEvent(mTarget, nullptr, nullptr);
     event->InitEvent(mEventType, mBubbles, false);
     event->SetTrusted(true);
   }
   if (mOnlyChromeDispatch) {
     MOZ_ASSERT(event->IsTrusted());
     event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   }
   bool dummy;
   mTarget->DispatchEvent(event, &dummy);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+AsyncEventDispatcher::Cancel()
+{
+  mCanceled = true;
+  return NS_OK;
+}
+
 nsresult
 AsyncEventDispatcher::PostDOMEvent()
 {
   RefPtr<AsyncEventDispatcher> ensureDeletionWhenFailing = this;
   return NS_DispatchToCurrentThread(this);
 }
 
 void
--- a/dom/events/AsyncEventDispatcher.h
+++ b/dom/events/AsyncEventDispatcher.h
@@ -14,23 +14,23 @@
 #include "nsString.h"
 #include "nsThreadUtils.h"
 
 class nsINode;
 
 namespace mozilla {
 
 /**
- * Use nsAsyncDOMEvent to fire a DOM event that requires safe a stable DOM.
+ * Use AsyncEventDispatcher to fire a DOM event that requires safe a stable DOM.
  * For example, you may need to fire an event from within layout, but
  * want to ensure that the event handler doesn't mutate the DOM at
  * the wrong time, in order to avoid resulting instability.
  */
- 
-class AsyncEventDispatcher : public nsRunnable
+
+class AsyncEventDispatcher : public nsCancelableRunnable
 {
 public:
   /**
    * If aOnlyChromeDispatch is true, the event is dispatched to only
    * chrome node. In that case, if aTarget is already a chrome node,
    * the event is dispatched to it, otherwise the dispatch path starts
    * at the first chrome ancestor of that target.
    */
@@ -43,39 +43,38 @@ public:
   {
   }
 
   AsyncEventDispatcher(dom::EventTarget* aTarget, const nsAString& aEventType,
                        bool aBubbles)
     : mTarget(aTarget)
     , mEventType(aEventType)
     , mBubbles(aBubbles)
-    , mOnlyChromeDispatch(false)
   {
   }
 
   AsyncEventDispatcher(dom::EventTarget* aTarget, nsIDOMEvent* aEvent)
     : mTarget(aTarget)
     , mEvent(aEvent)
-    , mBubbles(false)
-    , mOnlyChromeDispatch(false)
   {
   }
 
   AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent);
 
   NS_IMETHOD Run() override;
+  NS_IMETHOD Cancel() override;
   nsresult PostDOMEvent();
   void RunDOMEventWhenSafe();
 
   nsCOMPtr<dom::EventTarget> mTarget;
   nsCOMPtr<nsIDOMEvent> mEvent;
   nsString              mEventType;
-  bool                  mBubbles;
-  bool                  mOnlyChromeDispatch;
+  bool                  mBubbles = false;
+  bool                  mOnlyChromeDispatch = false;
+  bool                  mCanceled = false;
 };
 
 class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher
 {
 public:
   LoadBlockingAsyncEventDispatcher(nsINode* aEventNode,
                                    const nsAString& aEventType,
                                    bool aBubbles, bool aDispatchChromeOnly)
@@ -91,17 +90,17 @@ public:
   LoadBlockingAsyncEventDispatcher(nsINode* aEventNode, nsIDOMEvent* aEvent)
     : AsyncEventDispatcher(aEventNode, aEvent)
     , mBlockedDoc(aEventNode->OwnerDoc())
   {
     if (mBlockedDoc) {
       mBlockedDoc->BlockOnload();
     }
   }
-  
+
   ~LoadBlockingAsyncEventDispatcher();
 
 private:
   nsCOMPtr<nsIDocument> mBlockedDoc;
 };
 
 } // namespace mozilla
 
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -416,16 +416,20 @@ EVENT(submit,
 EVENT(suspend,
       eSuspend,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(timeupdate,
       eTimeUpdate,
       EventNameType_HTML,
       eBasicEventClass)
+EVENT(toggle,
+      eToggle,
+      EventNameType_HTML,
+      eBasicEventClass)
 EVENT(volumechange,
       eVolumeChange,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(waiting,
       eWaiting,
       EventNameType_HTML,
       eBasicEventClass)
--- a/dom/html/HTMLDetailsElement.cpp
+++ b/dom/html/HTMLDetailsElement.cpp
@@ -66,16 +66,35 @@ HTMLDetailsElement::GetAttributeChangeHi
   nsChangeHint hint =
     nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
   if (aAttribute == nsGkAtoms::open) {
     NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
   }
   return hint;
 }
 
+nsresult
+HTMLDetailsElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                  nsAttrValueOrString* aValue, bool aNotify)
+{
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::open) {
+    bool setOpen = aValue != nullptr;
+    if (Open() != setOpen) {
+      if (mToggleEventDispatcher) {
+        mToggleEventDispatcher->Cancel();
+      }
+      mToggleEventDispatcher = new ToggleEventDispatcher(this);
+      mToggleEventDispatcher->PostDOMEvent();
+    }
+  }
+
+  return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
+                                             aNotify);
+}
+
 JSObject*
 HTMLDetailsElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLDetailsElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLDetailsElement.h
+++ b/dom/html/HTMLDetailsElement.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; 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/. */
 
 #ifndef mozilla_dom_HTMLDetailsElement_h
 #define mozilla_dom_HTMLDetailsElement_h
 
+#include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Attributes.h"
 #include "nsGenericHTMLElement.h"
 
 namespace mozilla {
 namespace dom {
 
 // HTMLDetailsElement implements the <details> tag, which is used as a
 // disclosure widget from which the user can obtain additional information or
@@ -33,35 +34,57 @@ public:
 
   nsIContent* GetFirstSummary() const;
 
   nsresult Clone(NodeInfo* aNodeInfo, nsINode** aResult) const override;
 
   nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                       int32_t aModType) const override;
 
+  nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                         nsAttrValueOrString* aValue, bool aNotify) override;
+
   // HTMLDetailsElement WebIDL
   bool Open() const { return GetBoolAttr(nsGkAtoms::open); }
 
   void SetOpen(bool aOpen, ErrorResult& aError)
   {
-    // TODO: Bug 1225412: Need to follow the spec to fire "toggle" event.
     SetHTMLBoolAttr(nsGkAtoms::open, aOpen, aError);
   }
 
   void ToggleOpen()
   {
     ErrorResult rv;
     SetOpen(!Open(), rv);
     rv.SuppressException();
   }
 
 protected:
   virtual ~HTMLDetailsElement();
 
   JSObject* WrapNode(JSContext* aCx,
                      JS::Handle<JSObject*> aGivenProto) override;
+
+  class ToggleEventDispatcher final : public AsyncEventDispatcher
+  {
+  public:
+    // According to the html spec, a 'toggle' event is a simple event which does
+    // not bubble.
+    explicit ToggleEventDispatcher(nsINode* aTarget)
+      : AsyncEventDispatcher(aTarget, NS_LITERAL_STRING("toggle"), false)
+    {
+    }
+
+    NS_IMETHOD Run() override
+    {
+      auto* details = static_cast<HTMLDetailsElement*>(mTarget.get());
+      details->mToggleEventDispatcher = nullptr;
+      return AsyncEventDispatcher::Run();
+    }
+  };
+
+  RefPtr<ToggleEventDispatcher> mToggleEventDispatcher;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_HTMLDetailsElement_h */
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -19,16 +19,16 @@ ScrollLinkedEffectFound2=This site appea
 AnimationWarningContentTooLarge=Async animation disabled because frame size (%1$S, %2$S) is bigger than the viewport (%3$S, %4$S) or the visual rectangle (%5$S, %6$S) is larger than the max allowed value (%7$S)
 ## LOCALIZATION NOTE(AnimationWarningTransformBackfaceVisibilityHidde):
 ## 'backface-visibility: hidden' is a CSS property, don't translate it.
 AnimationWarningTransformBackfaceVisibilityHidden=Async animation of 'backface-visibility: hidden' transforms is not supported
 ## LOCALIZATION NOTE(AnimationWarningTransformPreserve3D):
 ## 'transform-style: preserve-3d' is a CSS property, don't translate it.
 AnimationWarningTransformPreserve3D=Async animation of 'transform-style: preserve-3d' transforms is not supported
 ## LOCALIZATION NOTE(AnimationWarningTransformSVG,
+##                   AnimationWarningTransformWithGeometricProperties,
 ##                   AnimationWarningTransformFrameInactive,
-##                   AnimationWarningOpacityFrameInactive,
-##                   AnimationWarningWithGeometricProperties):
+##                   AnimationWarningOpacityFrameInactive):
 ## 'transform' and 'opacity' mean CSS property names, don't translate it.
 AnimationWarningTransformSVG=Async 'transform' animations of aFrames with SVG transforms is not supported
+AnimationWarningTransformWithGeometricProperties=Async animation of 'transform' not possible due to animation of geometric properties on the same element
 AnimationWarningTransformFrameInactive=Async animation disabled because frame was not marked active for 'transform' animation
 AnimationWarningOpacityFrameInactive=Async animation disabled because frame was not marked active for 'opacity' animation
-AnimationWarningWithGeometricProperties=Async animation of 'transform' or 'opacity' not possible due to animation of geometric properties on the same element
new file mode 100644
--- /dev/null
+++ b/dom/security/ContentVerifier.cpp
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; 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/. */
+
+#include "ContentVerifier.h"
+
+#include "mozilla/fallible.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsIInputStream.h"
+#include "nsIRequest.h"
+#include "nssb64.h"
+#include "nsSecurityHeaderParser.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+static LazyLogModule gContentVerifierPRLog("ContentVerifier");
+#define CSV_LOG(args) MOZ_LOG(gContentVerifierPRLog, LogLevel::Debug, args)
+
+// Content-Signature prefix
+const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
+
+NS_IMPL_ISUPPORTS(ContentVerifier, nsIStreamListener, nsISupports);
+
+nsresult
+ContentVerifier::Init(const nsAString& aContentSignatureHeader)
+{
+  mVks = Preferences::GetString("browser.newtabpage.remote.keys");
+
+  if (aContentSignatureHeader.IsEmpty() || mVks.IsEmpty()) {
+    CSV_LOG(
+      ("Content-Signature header and verification keys must not be empty!\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  nsresult rv = ParseContentSignatureHeader(aContentSignatureHeader);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
+  return CreateContext();
+}
+
+/**
+ * Implement nsIStreamListener
+ * We buffer the entire content here and kick off verification
+ */
+NS_METHOD
+AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
+                  const char* aRawSegment, uint32_t aToOffset, uint32_t aCount,
+                  uint32_t* outWrittenCount)
+{
+  FallibleTArray<nsCString>* decodedData =
+    static_cast<FallibleTArray<nsCString>*>(aClosure);
+  nsAutoCString segment(aRawSegment, aCount);
+  if (!decodedData->AppendElement(segment, fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  *outWrittenCount = aCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+                               nsresult aStatus)
+{
+  // Verify the content:
+  // If this fails, we return an invalid signature error to load a fallback page.
+  // If everthing is good, we return a new stream to the next listener and kick
+  // that one of.
+  CSV_LOG(("VerifySignedContent, b64signature: %s\n", mSignature.get()));
+  CSV_LOG(("VerifySignedContent, key: \n[\n%s\n]\n", mKey.get()));
+  bool verified = false;
+  nsresult rv = End(&verified);
+  if (NS_FAILED(rv) || !verified || NS_FAILED(aStatus)) {
+    // cancel the request and return error
+    if (NS_FAILED(aStatus)) {
+      rv = aStatus;
+    } else {
+      rv = NS_ERROR_INVALID_SIGNATURE;
+    }
+    CSV_LOG(("failed to verify content\n"));
+    mNextListener->OnStartRequest(aRequest, aContext);
+    mNextListener->OnStopRequest(aRequest, aContext, rv);
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  CSV_LOG(("Successfully verified content signature.\n"));
+
+  // start the next listener
+  rv = mNextListener->OnStartRequest(aRequest, aContext);
+  if (NS_SUCCEEDED(rv)) {
+    // We emptied aInStr so we have to create a new one from buf to hand it
+    // to the consuming listener.
+    for (uint32_t i = 0; i < mContent.Length(); ++i) {
+      nsCOMPtr<nsIInputStream> oInStr;
+      rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
+      if (NS_FAILED(rv)) {
+        break;
+      }
+      // let the next listener know that there is data in oInStr
+      rv = mNextListener->OnDataAvailable(aRequest, aContext, oInStr, 0,
+                                          mContent[i].Length());
+      if (NS_FAILED(rv)) {
+        break;
+      }
+    }
+  }
+
+  // propagate OnStopRequest and return
+  return mNextListener->OnStopRequest(aRequest, aContext, rv);
+}
+
+NS_IMETHODIMP
+ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+                                 nsIInputStream* aInputStream, uint64_t aOffset,
+                                 uint32_t aCount)
+{
+  // buffer the entire stream
+  uint32_t read;
+  nsresult rv = aInputStream->ReadSegments(AppendNextSegment, &mContent, aCount,
+                                           &read);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // update the signature verifier
+  return Update(mContent[mContent.Length()-1]);
+}
+
+/**
+ * ContentVerifier logic and utils
+ */
+
+nsresult
+ContentVerifier::GetVerificationKey(const nsAString& aKeyId)
+{
+  // get verification keys from the pref and see if we have |aKeyId|
+  nsCharSeparatedTokenizer tokenizerVK(mVks, ';');
+  while (tokenizerVK.hasMoreTokens()) {
+    nsDependentSubstring token = tokenizerVK.nextToken();
+    nsCharSeparatedTokenizer tokenizerKey(token, '=');
+    nsString prefKeyId;
+    if (tokenizerKey.hasMoreTokens()) {
+      prefKeyId = tokenizerKey.nextToken();
+    }
+    nsString key;
+    if (tokenizerKey.hasMoreTokens()) {
+      key = tokenizerKey.nextToken();
+    }
+    if (prefKeyId.Equals(aKeyId)) {
+      mKey.Assign(NS_ConvertUTF16toUTF8(key));
+      return NS_OK;
+    }
+  }
+
+  // we didn't find the appropriate key
+  return NS_ERROR_INVALID_SIGNATURE;
+}
+
+nsresult
+ContentVerifier::ParseContentSignatureHeader(
+  const nsAString& aContentSignatureHeader)
+{
+  // We only support p384 ecdsa according to spec
+  NS_NAMED_LITERAL_CSTRING(keyid_var, "keyid");
+  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
+
+  nsAutoString contentSignature;
+  nsAutoString keyId;
+  nsAutoCString header = NS_ConvertUTF16toUTF8(aContentSignatureHeader);
+  nsSecurityHeaderParser parser(header.get());
+  nsresult rv = parser.Parse();
+  if (NS_FAILED(rv)) {
+    CSV_LOG(("ContentVerifier: could not parse ContentSignature header\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
+
+  for (nsSecurityHeaderDirective* directive = directives->getFirst();
+       directive != nullptr; directive = directive->getNext()) {
+    CSV_LOG(("ContentVerifier: found directive %s\n", directive->mName.get()));
+    if (directive->mName.Length() == keyid_var.Length() &&
+        directive->mName.EqualsIgnoreCase(keyid_var.get(),
+                                          keyid_var.Length())) {
+      if (!keyId.IsEmpty()) {
+        CSV_LOG(("ContentVerifier: found two keyIds\n"));
+        return NS_ERROR_INVALID_SIGNATURE;
+      }
+
+      CSV_LOG(("ContentVerifier: found a keyid directive\n"));
+      keyId = NS_ConvertUTF8toUTF16(directive->mValue);
+      rv = GetVerificationKey(keyId);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
+    }
+    if (directive->mName.Length() == signature_var.Length() &&
+        directive->mName.EqualsIgnoreCase(signature_var.get(),
+                                          signature_var.Length())) {
+      if (!contentSignature.IsEmpty()) {
+        CSV_LOG(("ContentVerifier: found two ContentSignatures\n"));
+        return NS_ERROR_INVALID_SIGNATURE;
+      }
+
+      CSV_LOG(("ContentVerifier: found a ContentSignature directive\n"));
+      contentSignature = NS_ConvertUTF8toUTF16(directive->mValue);
+      mSignature = directive->mValue;
+    }
+  }
+
+  // we have to ensure that we found a key and a signature at this point
+  if (mKey.IsEmpty()) {
+    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
+             "an appropriate key.\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  if (mSignature.IsEmpty()) {
+    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
+             "a signature.\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  return NS_OK;
+}
+
+/**
+ * Parse signature, public key, and algorithm data for input to verification
+ * functions in VerifyData and CreateContext.
+ *
+ * https://datatracker.ietf.org/doc/draft-thomson-http-content-signature/
+ * If aSignature is a content signature, the function returns
+ * NS_ERROR_INVALID_SIGNATURE if anything goes wrong. Only p384 with sha384
+ * is supported and aSignature is a raw signature (r||s).
+ */
+nsresult
+ContentVerifier::ParseInput(ScopedSECKEYPublicKey& aPublicKeyOut,
+                            ScopedSECItem& aSignatureItemOut,
+                            SECOidTag& aOidOut,
+                            const nsNSSShutDownPreventionLock&)
+{
+  // Base 64 decode the key
+  ScopedSECItem keyItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  if (!keyItem ||
+      !NSSBase64_DecodeBuffer(nullptr, keyItem,
+                              mKey.get(),
+                              mKey.Length())) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Extract the public key from the keyItem
+  ScopedCERTSubjectPublicKeyInfo pki(
+    SECKEY_DecodeDERSubjectPublicKeyInfo(keyItem));
+  if (!pki) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  aPublicKeyOut = SECKEY_ExtractPublicKey(pki.get());
+
+  // in case we were not able to extract a key
+  if (!aPublicKeyOut) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Base 64 decode the signature
+  ScopedSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  if (!rawSignatureItem ||
+      !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem,
+                              mSignature.get(),
+                              mSignature.Length())) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // get signature object and oid
+  if (!aSignatureItemOut) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  // We have a raw ecdsa signature r||s so we have to DER-encode it first
+  // Note that we have to check rawSignatureItem->len % 2 here as
+  // DSAU_EncodeDerSigWithLen asserts this
+  if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  if (DSAU_EncodeDerSigWithLen(aSignatureItemOut, rawSignatureItem,
+                               rawSignatureItem->len) != SECSuccess) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  aOidOut = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
+
+  return NS_OK;
+}
+
+/**
+ * Create a context for a signature verification.
+ * It sets signature, public key, and algorithms that should be used to verify
+ * the data. It also updates the verification buffer with the content-signature
+ * prefix.
+ */
+nsresult
+ContentVerifier::CreateContext()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Bug 769521: We have to change b64 url to regular encoding as long as we
+  // don't have a b64 url decoder. This should change soon, but in the meantime
+  // we have to live with this.
+  mSignature.ReplaceChar('-', '+');
+  mSignature.ReplaceChar('_', '/');
+
+  ScopedSECKEYPublicKey publicKey;
+  ScopedSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  SECOidTag oid;
+  nsresult rv = ParseInput(publicKey, signatureItem, oid, locker);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  mCx = UniqueVFYContext(VFY_CreateContext(publicKey, signatureItem, oid, NULL));
+  if (!mCx) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  if (VFY_Begin(mCx.get()) != SECSuccess) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // add the prefix to the verification buffer
+  return Update(kPREFIX);
+}
+
+/**
+ * Add data to the context that should be verified.
+ */
+nsresult
+ContentVerifier::Update(const nsACString& aData)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  if (!aData.IsEmpty()) {
+    if (VFY_Update(mCx.get(),
+                   (const unsigned char*)nsPromiseFlatCString(aData).get(),
+                   aData.Length()) != SECSuccess) {
+      return NS_ERROR_INVALID_SIGNATURE;
+    }
+  }
+
+  return NS_OK;
+}
+
+/**
+ * Finish signature verification and return the result in _retval.
+ */
+nsresult
+ContentVerifier::End(bool* _retval)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  *_retval = (VFY_End(mCx.get()) == SECSuccess);
+
+  return NS_OK;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/ContentVerifier.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef mozilla_dom_ContentVerifier_h
+#define mozilla_dom_ContentVerifier_h
+
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsIStreamListener.h"
+#include "nsNSSShutDown.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "ScopedNSSTypes.h"
+
+/**
+ * Mediator intercepting OnStartRequest in nsHttpChannel, blocks until all
+ * data is read from the input stream, verifies the content signature and
+ * releases the request to the next listener if the verification is successful.
+ * If the verification fails or anything else goes wrong, a
+ * NS_ERROR_INVALID_SIGNATURE is thrown.
+ */
+class ContentVerifier : public nsIStreamListener
+                      , public nsNSSShutDownObject
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+
+  explicit ContentVerifier(nsIStreamListener* aMediatedListener,
+                           nsISupports* aMediatedContext)
+    : mNextListener(aMediatedListener)
+    , mContext(aMediatedContext)
+    , mCx(nullptr) {}
+
+  nsresult Init(const nsAString& aContentSignatureHeader);
+
+  // nsNSSShutDownObject
+  virtual void virtualDestroyNSSReference() override
+  {
+    destructorSafeDestroyNSSReference();
+  }
+
+protected:
+  virtual ~ContentVerifier()
+  {
+    nsNSSShutDownPreventionLock locker;
+    if (isAlreadyShutDown()) {
+      return;
+    }
+    destructorSafeDestroyNSSReference();
+    shutdown(calledFromObject);
+  }
+
+  void destructorSafeDestroyNSSReference()
+  {
+    mCx = nullptr;
+  }
+
+private:
+  nsresult ParseContentSignatureHeader(const nsAString& aContentSignatureHeader);
+  nsresult GetVerificationKey(const nsAString& aKeyId);
+
+  // utility function to parse input before put into verification functions
+  nsresult ParseInput(mozilla::ScopedSECKEYPublicKey& aPublicKeyOut,
+                      mozilla::ScopedSECItem& aSignatureItemOut,
+                      SECOidTag& aOidOut,
+                      const nsNSSShutDownPreventionLock&);
+
+  // create a verifier context and store it in mCx
+  nsresult CreateContext();
+
+  // Adds data to the context that was used to generate the signature.
+  nsresult Update(const nsACString& aData);
+
+  // Finalises the signature and returns the result of the signature
+  // verification.
+  nsresult End(bool* _retval);
+
+  // content and next listener for nsIStreamListener
+  nsCOMPtr<nsIStreamListener> mNextListener;
+  nsCOMPtr<nsISupports> mContext;
+
+  // verifier context for incrementel verifications
+  mozilla::UniqueVFYContext mCx;
+  // buffered content to verify
+  FallibleTArray<nsCString> mContent;
+  // signature to verify
+  nsCString mSignature;
+  // verification key
+  nsCString mKey;
+  // verification key preference
+  nsString mVks;
+};
+
+#endif /* mozilla_dom_ContentVerifier_h */
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -2,30 +2,32 @@
 # 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/.
 
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
+    'ContentVerifier.h',
     'nsContentSecurityManager.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
     'SRICheck.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ContentVerifier.cpp',
     'nsContentSecurityManager.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
     'SRICheck.cpp',
     'SRIMetadata.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files =
+  file_contentserver.sjs
+  file_about_newtab.html
+  file_about_newtab_bad.html
+  file_about_newtab_good_signature
+  file_about_newtab_bad_signature
+  file_about_newtab_broken_signature
+
+[browser_verify_content_about_newtab.js]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
@@ -0,0 +1,188 @@
+/*
+ * Test Content-Signature for remote about:newtab
+ *  - Bug 1226928 - allow about:newtab to load remote content
+ *
+ * This tests content-signature verification on remote about:newtab in the
+ * following cases (see TESTS, all failed loads display about:blank fallback):
+ * - good case (signature should verify and correct page is displayed)
+ * - reload of newtab when the siganture was invalidated after the last correct
+ *   load
+ * - malformed content-signature header
+ * - malformed keyid directive
+ * - malformed p384ecdsa directive
+ * - wrong signature (this is not a siganture for the delivered document)
+ * - invalid signature (this is not even a signature)
+ * - loading a file that doesn't fit the key or signature
+ * - cache poisoning (load a malicious remote page not in newtab, subsequent
+ *   newtab load has to load the fallback)
+ */
+
+const ABOUT_NEWTAB_URI = "about:newtab";
+
+const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
+const URI_GOOD = BASE + "sig=good&key=good&file=good&header=good";
+
+const INVALIDATE_FILE = BASE + "invalidateFile=yep";
+const VALIDATE_FILE = BASE + "validateFile=yep";
+
+const URI_HEADER_BASE = BASE + "sig=good&key=good&file=good&header=";
+const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
+const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInKeyid";
+const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
+const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
+
+const URI_BAD_SIG = BASE + "sig=bad&key=good&file=good&header=good";
+const URI_BROKEN_SIG = BASE + "sig=broken&key=good&file=good&header=good";
+const URI_BAD_KEY = BASE + "sig=good&key=bad&file=good&header=good";
+const URI_BAD_FILE = BASE + "sig=good&key=good&file=bad&header=good";
+const URI_BAD_ALL = BASE + "sig=bad&key=bad&file=bad&header=bad";
+
+const URI_BAD_FILE_CACHED = BASE + "sig=good&key=good&file=bad&header=good&cached=true";
+
+const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
+const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
+const ABOUT_BLANK = "<head></head><body></body>";
+
+const TESTS = [
+  // { newtab (aboutURI) or regular load (url) : url,
+  //   testString : expected string in the loaded page }
+  { "aboutURI" : URI_GOOD, "testString" : GOOD_ABOUT_STRING },
+  { "aboutURI" : URI_ERROR_HEADER, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_KEYERROR_HEADER, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_SIGERROR_HEADER, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_NO_HEADER, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_BAD_SIG, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_BROKEN_SIG, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_BAD_KEY, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_BAD_FILE, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_BAD_ALL, "testString" : ABOUT_BLANK },
+  { "url" : URI_BAD_FILE_CACHED, "testString" : BAD_ABOUT_STRING },
+  { "aboutURI" : URI_BAD_FILE_CACHED, "testString" : ABOUT_BLANK },
+  { "aboutURI" : URI_GOOD, "testString" : GOOD_ABOUT_STRING }
+];
+
+var browser = null;
+var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
+                           .getService(Ci.nsIAboutNewTabService);
+
+function pushPrefs(...aPrefs) {
+  return new Promise((resolve) => {
+    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
+  });
+}
+
+/*
+ * run tests with input from TESTS
+ */
+function doTest(aExpectedString, reload, aUrl, aNewTabPref) {
+  // set about:newtab location for this test if it's a newtab test
+  if (aNewTabPref) {
+    aboutNewTabService.newTabURL = aNewTabPref;
+  }
+
+  // set prefs
+  yield pushPrefs(
+      ["browser.newtabpage.remote.content-signing-test", true],
+      ["browser.newtabpage.remote", true], [
+        "browser.newtabpage.remote.keys",
+        "RemoteNewTabNightlyv0=BO9QHuP6E2eLKybql8iuD4o4Np9YFDfW3D+k" +
+        "a70EcXXTqZcikc7Am1CwyP1xBDTpEoe6gb9SWzJmaDW3dNh1av2u90VkUM" +
+        "B7aHIrImjTjLNg/1oC8GRcTKM4+WzbKF00iA==;OtherKey=eKQJ2fNSId" +
+        "CFzL6N326EzZ/5LCeFU5eyq3enwZ5MLmvOw+3gycr4ZVRc36/EiSPsQYHE" +
+        "3JvJs1EKs0QCaguHFOZsHwqXMPicwp/gLdeYbuOmN2s1SEf/cxw8GtcxSA" +
+        "kG;RemoteNewTab=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4k3FmG7dFo" +
+        "Ot3Tuzl76abTRtK8sb/r/ibCSeVKa96RbrOX2ciscz/TT8wfqBYS/8cN4z" +
+        "Me1+f7wRmkNrCUojZR1ZKmYM2BeiUOMlMoqk2O7+uwsn1DwNQSYP58TkvZt6"
+      ]);
+
+  // start the test
+  yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: aUrl,
+    },
+    function * (browser) {
+      // check if everything's set correct for testing
+      ok(Services.prefs.getBoolPref(
+          "browser.newtabpage.remote.content-signing-test"),
+          "sanity check: remote newtab signing test should be used");
+      ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
+          "sanity check: remote newtab should be used");
+      // we only check this if we really do a newtab test
+      if (aNewTabPref) {
+        ok(aboutNewTabService.overridden,
+            "sanity check: default URL for about:newtab should be overriden");
+        is(aboutNewTabService.newTabURL, aNewTabPref,
+            "sanity check: default URL for about:newtab should return the new URL");
+      }
+      yield ContentTask.spawn(
+          browser, aExpectedString, function * (aExpectedString) {
+            ok(content.document.documentElement.innerHTML.includes(aExpectedString),
+               "Expect the following value in the result\n" + aExpectedString +
+               "\nand got " + content.document.documentElement.innerHTML);
+          });
+
+      // for good test cases we check if a reload fails if the remote page
+      // changed from valid to invalid in the meantime
+      if (reload) {
+        yield BrowserTestUtils.withNewTab({
+            gBrowser,
+            url: INVALIDATE_FILE,
+          },
+          function * (browser2) {
+            yield ContentTask.spawn(browser2, null, function * () {
+              ok(content.document.documentElement.innerHTML.includes("Done"),
+                 "Expect the following value in the result\n" + "Done" +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            });
+          }
+        );
+
+        browser.reload();
+        yield BrowserTestUtils.browserLoaded(browser);
+
+        aExpectedString = ABOUT_BLANK;
+        yield ContentTask.spawn(browser, aExpectedString,
+          function * (aExpectedString) {
+            ok(content.document.documentElement.innerHTML.includes(aExpectedString),
+               "Expect the following value in the result\n" + aExpectedString +
+               "\nand got " + content.document.documentElement.innerHTML);
+          }
+        );
+
+        yield BrowserTestUtils.withNewTab({
+            gBrowser,
+            url: VALIDATE_FILE,
+          },
+          function * (browser2) {
+            yield ContentTask.spawn(browser2, null, function * () {
+              ok(content.document.documentElement.innerHTML.includes("Done"),
+                 "Expect the following value in the result\n" + "Done" +
+                 "\nand got " + content.document.documentElement.innerHTML);
+              });
+          }
+        );
+      }
+    }
+  );
+}
+
+add_task(function * test() {
+  // run tests from TESTS
+  for (let i = 0; i < TESTS.length; i++) {
+    let testCase = TESTS[i];
+    let url = "", aNewTabPref = "";
+    let reload = false;
+    let aExpectedString = testCase.testString;
+    if (testCase.aboutURI) {
+      url = ABOUT_NEWTAB_URI;
+      aNewTabPref = testCase.aboutURI;
+      if (aExpectedString == GOOD_ABOUT_STRING) {
+        reload = true;
+      }
+    } else {
+      url = testCase.url;
+    }
+
+    yield doTest(aExpectedString, reload, url, aNewTabPref);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
+<head>
+  <meta charset="utf-8">
+  <title>Testpage for bug 1226928</title>
+</head>
+<body>
+  Just a fully good testpage for Bug 1226928<br/>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_bad.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
+<head>
+  <meta charset="utf-8">
+  <title>Testpage for bug 1226928</title>
+</head>
+<body>
+  Just a bad testpage for Bug 1226928<br/>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_bad_signature
@@ -0,0 +1,1 @@
+KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_broken_signature
@@ -0,0 +1,1 @@
+MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_good_signature
@@ -0,0 +1,1 @@
+XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_contentserver.sjs
@@ -0,0 +1,179 @@
+// sjs for remote about:newtab (bug 1226928)
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+const path = "browser/dom/security/test/contentverifier/";
+
+const goodFileName = "file_about_newtab.html";
+const goodFileBase = path + goodFileName;
+const goodFile = FileUtils.getDir("TmpD", [], true);
+goodFile.append(goodFileName);
+const goodSignature = path + "file_about_newtab_good_signature";
+const goodKeyId = "RemoteNewTab";
+
+const badFile = path + "file_about_newtab_bad.html";
+const brokenSignature = path + "file_about_newtab_broken_signature";
+const badSignature = path + "file_about_newtab_bad_signature";
+const badKeyId = "OldRemoteNewTabKey";
+
+// we copy the file to serve as newtab to a temp directory because
+// we modify it during tests.
+setupTestFile();
+
+function setupTestFile() {
+  let tempFile = FileUtils.getDir("TmpD", [], true);
+  tempFile.append(goodFileName);
+  if (!tempFile.exists()) {
+    let fileIn = getFileName(goodFileBase, "CurWorkD");
+    fileIn.copyTo(FileUtils.getDir("TmpD", [], true), "");
+  }
+}
+
+function getFileName(filePath, dir) {
+  // Since it's relative to the cwd of the test runner, we start there and
+  // append to get to the actual path of the file.
+  let testFile =
+    Cc["@mozilla.org/file/directory_service;1"].
+      getService(Components.interfaces.nsIProperties).
+      get(dir, Components.interfaces.nsILocalFile);
+  let dirs = filePath.split("/");
+  for (let i = 0; i < dirs.length; i++) {
+    testFile.append(dirs[i]);
+  }
+  return testFile;
+}
+
+function loadFile(file) {
+  // Load a file to return it.
+  let testFileStream =
+    Cc["@mozilla.org/network/file-input-stream;1"]
+      .createInstance(Components.interfaces.nsIFileInputStream);
+  testFileStream.init(file, -1, 0, 0);
+  return NetUtil.readInputStreamToString(testFileStream,
+                                         testFileStream.available());
+}
+
+function appendToFile(aFile, content) {
+  try {
+    let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_APPEND |
+                                                     FileUtils.MODE_WRONLY);
+    file.write(content, content.length);
+    file.close();
+  } catch (e) {
+    dump(">>> Error in appendToFile "+e);
+    return "Error";
+  }
+  return "Done";
+}
+
+function truncateFile(aFile, length) {
+  let fileIn = loadFile(aFile);
+  fileIn = fileIn.slice(0, -length);
+
+  try {
+    let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_WRONLY |
+                                                     FileUtils.MODE_TRUNCATE);
+    file.write(fileIn, fileIn.length);
+    file.close();
+  } catch (e) {
+    dump(">>> Error in truncateFile "+e);
+    return "Error";
+  }
+  return "Done";
+}
+
+/*
+ * handle requests of the following form:
+ * sig=good&key=good&file=good&header=good&cached=no to serve pages with
+ * content signatures
+ *
+ * it further handles invalidateFile=yep and validateFile=yep to change the
+ * served file
+ */
+function handleRequest(request, response) {
+  let params = new URLSearchParams(request.queryString);
+  let keyType = params.get("key");
+  let signatureType = params.get("sig");
+  let fileType = params.get("file");
+  let headerType = params.get("header");
+  let cached = params.get("cached");
+  let invalidateFile = params.get("invalidateFile");
+  let validateFile = params.get("validateFile");
+
+  // if invalidateFile is set, this doesn't actually return a newtab page
+  // but changes the served file to invalidate the signature
+  // NOTE: make sure to make the file valid again afterwards!
+  if (invalidateFile) {
+    response.setHeader("Content-Type", "text/html", false);
+    let r = appendToFile(goodFile, "!");
+    response.write(r);
+    return;
+  }
+
+  // if validateFile is set, this doesn't actually return a newtab page
+  // but changes the served file to make the signature valid again
+  if (validateFile) {
+    response.setHeader("Content-Type", "text/html", false);
+    let r = truncateFile(goodFile, 1);
+    response.write(r);
+    return;
+  }
+
+  // avoid confusing cache behaviours
+  if (!cached) {
+    response.setHeader("Cache-Control", "no-cache", false);
+  } else {
+    response.setHeader("Cache-Control", "max-age=3600", false);
+  }
+
+  // send HTML to test allowed/blocked behaviours
+  response.setHeader("Content-Type", "text/html", false);
+
+  // set signature header and key for Content-Signature header
+  /* By default a good content-signature header is returned. Any broken return
+   * value has to be indicated in the url.
+   */
+  let csHeader = "";
+  let keyId = goodKeyId;
+  let signature = goodSignature;
+  let file = goodFile;
+  if (keyType == "bad") {
+    keyId = badKeyId;
+  }
+  if (signatureType == "bad") {
+    signature = badSignature;
+  } else if (signatureType == "broken") {
+    signature = brokenSignature;
+  }
+  if (fileType == "bad") {
+    file = getFileName(badFile, "CurWorkD");
+  }
+
+  if (headerType == "good") {
+    // a valid content-signature header
+    csHeader = "keyid=" + keyId + ";p384ecdsa=" +
+               loadFile(getFileName(signature, "CurWorkD"));
+  } else if (headerType == "error") {
+    // this content-signature header is missing ; before p384ecdsa
+    csHeader = "keyid=" + keyId + "p384ecdsa=" +
+               loadFile(getFileName(signature, "CurWorkD"));
+  } else if (headerType == "errorInKeyid") {
+    // this content-signature header is missing the keyid directive
+    csHeader = "keid=" + keyId + ";p384ecdsa=" +
+               loadFile(getFileName(signature, "CurWorkD"));
+  } else if (headerType == "errorInSignature") {
+    // this content-signature header is missing the p384ecdsa directive
+    csHeader = "keyid=" + keyId + ";p385ecdsa=" +
+               loadFile(getFileName(signature, "CurWorkD"));
+  }
+
+  if (csHeader) {
+    response.setHeader("Content-Signature", csHeader, false);
+  }
+  let result = loadFile(file);
+
+  response.write(result);
+}
new file mode 100644
index 0000000000000000000000000000000000000000..011b94142c3d79406bbd812a154ade1531a5a002
GIT binary patch
literal 103
zc$@)e0GR(UWdbl<60>?e;^{C2WzE%Q%Yu4I{^8aZBoR3Iz0Nw)t2tkZBQh@na3P%K
zb!tDP5^XyIF#wVug>oV>SLNi{1rU`KGaT;dTd_Eqc?>E>+X2hV&>?Fg)Xr`f;VN)v
JqyasuT~785E71S|
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/sk.pem
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAzX2TrGOr0WE92AbAl+nqnpqh25pKCLYNMTV2hJHztrkVPWOp8w0mh
+scIodK8RMpagBwYFK4EEACKhZANiAATiTcWYbt0Wg63dO7OXvpptNG0ryxv+v+Js
+JJ5Upr3pFus5fZyKxzP9NPzB+oFhL/xw3jMx7X5/vBGaQ2sJSiNlHVkqZgzYF6JQ
+4yUyiqTY7v67CyfUPA1BJg/nxOS9m3o=
+-----END EC PRIVATE KEY-----
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -19,10 +19,11 @@ MOCHITEST_MANIFESTS += [
     'sri/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'csp/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
+    'contentverifier/browser.ini',
     'csp/browser.ini',
 ]
--- a/dom/webidl/BluetoothAdapter.webidl
+++ b/dom/webidl/BluetoothAdapter.webidl
@@ -58,16 +58,19 @@ interface BluetoothAdapter : EventTarget
            attribute EventHandler   onpairingaborted;
 
   // Fired when a2dp connection status changed
            attribute EventHandler   ona2dpstatuschanged;
 
   // Fired when handsfree connection status changed
            attribute EventHandler   onhfpstatuschanged;
 
+  // Fired when handsfree connection status changed
+           attribute EventHandler   onhidstatuschanged;
+
   // Fired when sco connection status changed
            attribute EventHandler   onscostatuschanged;
 
   // Fired when remote devices query current media play status
            attribute EventHandler   onrequestmediaplaystatus;
 
   // Fired when remote devices request password for OBEX authentication
            attribute EventHandler   onobexpasswordreq;
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -87,16 +87,19 @@ interface GlobalEventHandlers {
            attribute EventHandler onsuspend;
            attribute EventHandler ontimeupdate;
            attribute EventHandler onvolumechange;
            attribute EventHandler onwaiting;
 
            [Pref="dom.select_events.enabled"]
            attribute EventHandler onselectstart;
 
+           [Pref="dom.details_element.enabled"]
+           attribute EventHandler ontoggle;
+
            // Pointer events handlers
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onpointercancel;
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onpointerdown;
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onpointerup;
            [Pref="dom.w3c_pointer_events.enabled"]
--- a/dom/xslt/base/txURIUtils.cpp
+++ b/dom/xslt/base/txURIUtils.cpp
@@ -12,16 +12,40 @@
 
 using mozilla::LoadInfo;
 
 /**
  * URIUtils
  * A set of utilities for handling URIs
 **/
 
+/**
+ * Resolves the given href argument, using the given documentBase
+ * if necessary.
+ * The new resolved href will be appended to the given dest String
+**/
+void URIUtils::resolveHref(const nsAString& href, const nsAString& base,
+                           nsAString& dest) {
+    if (base.IsEmpty()) {
+        dest.Append(href);
+        return;
+    }
+    if (href.IsEmpty()) {
+        dest.Append(base);
+        return;
+    }
+    nsCOMPtr<nsIURI> pURL;
+    nsAutoString resultHref;
+    nsresult result = NS_NewURI(getter_AddRefs(pURL), base);
+    if (NS_SUCCEEDED(result)) {
+        NS_MakeAbsoluteURI(resultHref, href, pURL);
+        dest.Append(resultHref);
+    }
+} //-- resolveHref
+
 // static
 void
 URIUtils::ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode)
 {
     nsCOMPtr<nsINode> node = do_QueryInterface(aSourceNode);
     if (!node) {
         // XXXbz passing nullptr as the first arg to Reset is illegal
         aNewDoc->Reset(nullptr, nullptr);
--- a/dom/xslt/base/txURIUtils.h
+++ b/dom/xslt/base/txURIUtils.h
@@ -18,12 +18,20 @@ class nsIDOMNode;
 
 class URIUtils {
 public:
 
     /**
      * Reset the given document with the document of the source node
      */
     static void ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode);
+
+    /**
+     * Resolves the given href argument, using the given documentBase
+     * if necessary.
+     * The new resolved href will be appended to the given dest String
+    **/
+    static void resolveHref(const nsAString& href, const nsAString& base,
+                            nsAString& dest);
 }; //-- URIUtils
 
 /* */
 #endif
--- a/dom/xslt/nsIXSLTProcessorPrivate.idl
+++ b/dom/xslt/nsIXSLTProcessorPrivate.idl
@@ -1,26 +1,19 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "nsISupports.idl"
 
-[scriptable, uuid(75d14f5d-293d-4872-8a26-e79268de592f)]
+[scriptable, uuid(b8d727f7-67f4-4dc1-a318-ec0c87280816)]
 interface nsIXSLTProcessorPrivate : nsISupports
 {
   /**
-   * This needs to be called if the XSLTProcessor is instantiated
-   * through the XPCOM registry (i.e. using do_createInstance) and the
-   * stylesheet uses xsl:import/xsl:include or the document() xpath function.
-   */
-  void init(in nsISupports global);
-
-  /**
    * Disables all loading of external documents, such as from
    * <xsl:import> and document()
    * Defaults to off and is *not* reset by calls to reset()
    */
   const unsigned long DISABLE_ALL_LOADS = 1;
 
   /**
    * Flags for this processor. Defaults to 0. See individual flags above
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/file_bug1222624.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml-stylesheet type="text/xsl" href="file_bug1222624.xsl"?>
-<root>
-  <load>file_bug1222624_data2.xml</load>
-</root>
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/file_bug1222624.xsl
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-  <xsl:import href="file_bug1222624_sub.xsl"/>
-  <xsl:template match="/root">
-    <result>
-      <xsl:call-template name="external"/>
-      <xsl:value-of select="document('file_bug1222624_data1.xml')"/>
-      <xsl:text>!</xsl:text>
-      <xsl:value-of select="document(load)"/>
-    </result>
-  </xsl:template>
-</xsl:stylesheet>
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/file_bug1222624_data1.xml
+++ /dev/null
@@ -1,1 +0,0 @@
-<data>doc1</data>
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/file_bug1222624_data2.xml
+++ /dev/null
@@ -1,1 +0,0 @@
-<data>doc2</data>
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-  <xsl:include href="file_bug1222624_sub_sub.xsl"/>
-</xsl:stylesheet>
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-  <xsl:template name="external">
-    <external/>
-  </xsl:template>
-</xsl:stylesheet>
--- a/dom/xslt/tests/mochitest/mochitest.ini
+++ b/dom/xslt/tests/mochitest/mochitest.ini
@@ -11,12 +11,10 @@
 [test_bug551654.html]
 [test_bug566629.html]
 [test_bug566629.xhtml]
 [test_bug603159.html]
 [test_bug616774.html]
 [test_bug667315.html]
 [test_bug1135764.html]
 support-files = file_bug1135764.xml file_bug1135764.xsl
-[test_bug1222624.html]
-support-files = file_bug1222624.xml file_bug1222624.xsl file_bug1222624_sub.xsl file_bug1222624_sub_sub.xsl file_bug1222624_data1.xml file_bug1222624_data2.xml
 [test_exslt_regex.html]
 [test_parameter.html]
deleted file mode 100644
--- a/dom/xslt/tests/mochitest/test_bug1222624.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1222624
--->
-<head>
-  <title>Test for Bug 1222624</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=1222624">Mozilla Bug 1222624</a>
-<p id="display"></p>
-<iframe id="frame"></iframe>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-/** Test for Bug 1222624 **/
-
-const nl = (navigator.platform == 'Win32' ||
-            navigator.platform == 'Win64') ? '\r\n' : '\n';
-
-const transformRes = '<?xml version="1.0" encoding="UTF-8"?>' + nl + '<result><external/>doc1!doc2</result>';
-
-xhr = new XMLHttpRequest();
-xhr.open("GET", "file_bug1222624.xml", false);
-xhr.send();
-var xmlDoc = xhr.responseXML;
-
-xhr.open("GET", "file_bug1222624.xsl", false);
-xhr.send();
-var xslDoc = xhr.responseXML;
-
-var processor = new XSLTProcessor;
-processor.importStylesheet(xslDoc);
-var apiRes = new XMLSerializer().serializeToString(processor.transformToDocument(xmlDoc));
-
-is(apiRes, transformRes, "API transformation correct");
-
-SimpleTest.waitForExplicitFinish();
-
-var frame = $("frame");
-frame.src = "file_bug1222624.xml";
-frame.onload = function () {
-  var piRes = new XMLSerializer().serializeToString(frame.contentDocument);
-  is(piRes, transformRes, "processing-instruction transformation correct");
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/xslt/xml/txXMLParser.cpp
+++ b/dom/xslt/xml/txXMLParser.cpp
@@ -10,49 +10,51 @@
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsSyncLoadService.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 
 nsresult
-txParseDocumentFromURI(nsIURI* aUri,
-                       nsIDocument* aLoadingDocument,
+txParseDocumentFromURI(const nsAString& aHref,
+                       const txXPathNode& aLoader,
                        nsAString& aErrMsg,
                        txXPathNode** aResult)
 {
+    NS_ENSURE_ARG_POINTER(aResult);
     *aResult = nullptr;
+    nsCOMPtr<nsIURI> documentURI;
+    nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
+    nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader);
+
+    nsCOMPtr<nsILoadGroup> loadGroup = loaderDocument->GetDocumentLoadGroup();
 
     // For the system principal loaderUri will be null here, which is good
     // since that means that chrome documents can load any uri.
 
     // Raw pointer, we want the resulting txXPathNode to hold a reference to
     // the document.
     nsIDOMDocument* theDocument = nullptr;
-    nsAutoSyncOperation sync(aLoadingDocument);
-    nsresult rv =
-        nsSyncLoadService::LoadDocument(aUri,
-                                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
-                                        aLoadingDocument->NodePrincipal(),
-                                        nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
-                                        loadGroup,
-                                        true,
-                                        aLoadingDocument->GetReferrerPolicy(),
-                                        &theDocument);
+    nsAutoSyncOperation sync(loaderDocument);
+    rv = nsSyncLoadService::LoadDocument(documentURI,
+                                         nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
+                                         loaderDocument->NodePrincipal(),
+                                         nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+                                         loadGroup, true,
+                                         loaderDocument->GetReferrerPolicy(),
+                                         &theDocument);
 
     if (NS_FAILED(rv)) {
         aErrMsg.AppendLiteral("Document load of ");
-        nsAutoCString spec;
-        aUri->GetSpec(spec);
-        aErrMsg.Append(NS_ConvertUTF8toUTF16(spec));
+        aErrMsg.Append(aHref);
         aErrMsg.AppendLiteral(" failed.");
-        return rv;
+        return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
     }
 
     *aResult = txXPathNativeNode::createXPathNode(theDocument);
     if (!*aResult) {
         NS_RELEASE(theDocument);
         return NS_ERROR_FAILURE;
     }
 
--- a/dom/xslt/xml/txXMLParser.h
+++ b/dom/xslt/xml/txXMLParser.h
@@ -4,27 +4,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MITRE_XMLPARSER_H
 #define MITRE_XMLPARSER_H
 
 #include "txCore.h"
 
 class txXPathNode;
-class nsIURI;
-class nsIDocument;
 
 /**
  * API to load XML files into DOM datastructures.
  * Parsing is either done by expat, or by expat via the syncloaderservice
  */
 
 /**
  * Parse a document from the aHref location, with referrer URI on behalf
  * of the document aLoader.
  */
 extern "C" nsresult
-txParseDocumentFromURI(nsIURI* aUri,
-                       nsIDocument* aLoadingDocument,
-                       nsAString& aErrMsg,
-                       txXPathNode** aResult);
+txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader,
+                       nsAString& aErrMsg, txXPathNode** aResult);
 
 #endif
--- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
+++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
@@ -558,19 +558,19 @@ txXPathNodeUtils::getXSLTId(const txXPat
                                          nodeid, aNode.mIndex), aResult);
     }
 
     return NS_OK;
 }
 
 /* static */
 void
-txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsIURI** aUri)
+txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
 {
-    *aUri = aNode.mNode->GetBaseURI().take();
+    aNode.mNode->GetBaseURI(aURI);
 }
 
 /* static */
 int
 txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
                                   const txXPathNode& aOtherNode)
 {
     // First check for equal nodes or attribute-nodes on the same element.
--- a/dom/xslt/xpath/txXPathTreeWalker.h
+++ b/dom/xslt/xpath/txXPathTreeWalker.h
@@ -88,17 +88,17 @@ public:
     static uint16_t getNodeType(const txXPathNode& aNode);
     static void appendNodeValue(const txXPathNode& aNode, nsAString& aResult);
     static bool isWhitespace(const txXPathNode& aNode);
     static txXPathNode* getOwnerDocument(const txXPathNode& aNode);
     static int32_t getUniqueIdentifier(const txXPathNode& aNode);
     static nsresult getXSLTId(const txXPathNode& aNode,
                               const txXPathNode& aBase, nsAString& aResult);
     static void release(txXPathNode* aNode);
-    static void getBaseURI(const txXPathNode& aNode, nsIURI** aURI);
+    static void getBaseURI(const txXPathNode& aNode, nsAString& aURI);
     static int comparePosition(const txXPathNode& aNode,
                                const txXPathNode& aOtherNode);
     static bool localNameEquals(const txXPathNode& aNode,
                                   nsIAtom* aLocalName);
     static bool isRoot(const txXPathNode& aNode);
     static bool isElement(const txXPathNode& aNode);
     static bool isAttribute(const txXPathNode& aNode);
     static bool isProcessingInstruction(const txXPathNode& aNode);
--- a/dom/xslt/xslt/txDocumentFunctionCall.cpp
+++ b/dom/xslt/xslt/txDocumentFunctionCall.cpp
@@ -8,43 +8,56 @@
  * A representation of the XSLT additional function: document()
  */
 
 #include "nsGkAtoms.h"
 #include "txIXPathContext.h"
 #include "txXSLTFunctions.h"
 #include "txExecutionState.h"
 #include "txURIUtils.h"
-#include "nsIURI.h"
-#include "nsNetUtil.h"
+
+/*
+ * Creates a new DocumentFunctionCall.
+ */
+DocumentFunctionCall::DocumentFunctionCall(const nsAString& aBaseURI)
+    : mBaseURI(aBaseURI)
+{
+}
 
 static void
-retrieveNode(txExecutionState* aExecutionState,
-             const nsAString& aUri,
-             nsIURI* aBaseUri,
-             txNodeSet* aNodeSet)
+retrieveNode(txExecutionState* aExecutionState, const nsAString& aUri,
+             const nsAString& aBaseUri, txNodeSet* aNodeSet)
 {
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri, nullptr, aBaseUri);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
+    nsAutoString absUrl;
+    URIUtils::resolveHref(aUri, aBaseUri, absUrl);
+
+    int32_t hash = absUrl.RFindChar(char16_t('#'));
+    uint32_t urlEnd, fragStart, fragEnd;
+    if (hash == kNotFound) {
+        urlEnd = absUrl.Length();
+        fragStart = 0;
+        fragEnd = 0;
+    }
+    else {
+        urlEnd = hash;
+        fragStart = hash + 1;
+        fragEnd = absUrl.Length();
     }
 
-    nsAutoCString frag;
-    uri->GetRef(frag);
-    uri->SetRef(EmptyCString());
+    nsDependentSubstring docUrl(absUrl, 0, urlEnd);
+    nsDependentSubstring frag(absUrl, fragStart, fragEnd);
 
-    const txXPathNode* loadNode = aExecutionState->retrieveDocument(uri);
+    const txXPathNode* loadNode = aExecutionState->retrieveDocument(docUrl);
     if (loadNode) {
         if (frag.IsEmpty()) {
             aNodeSet->add(*loadNode);
         }
         else {
             txXPathTreeWalker walker(*loadNode);
-            if (walker.moveToElementById(NS_ConvertUTF8toUTF16(frag))) {
+            if (walker.moveToElementById(frag)) {
                 aNodeSet->add(walker.getCurrentPosition());
             }
         }
     }
 }
 
 /*
  * Evaluates this Expr based on the given context node and processor state
@@ -69,66 +82,65 @@ DocumentFunctionCall::evaluate(txIEvalCo
     if (!requireParams(1, 2, aContext)) {
         return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
     }
 
     RefPtr<txAExprResult> exprResult1;
     rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult1));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIURI> baseURI;
+    nsAutoString baseURI;
     bool baseURISet = false;
 
     if (mParams.Length() == 2) {
         // We have 2 arguments, get baseURI from the first node
         // in the resulting nodeset
         RefPtr<txNodeSet> nodeSet2;
         rv = evaluateToNodeSet(mParams[1],
                                aContext, getter_AddRefs(nodeSet2));
         NS_ENSURE_SUCCESS(rv, rv);
 
         // Make this true, even if nodeSet2 is empty. For relative URLs,
         // we'll fail to load the document with an empty base URI, and for
         // absolute URLs, the base URI doesn't matter
         baseURISet = true;
 
         if (!nodeSet2->isEmpty()) {
-            txXPathNodeUtils::getBaseURI(nodeSet2->get(0),
-                                         getter_AddRefs(baseURI));
+            txXPathNodeUtils::getBaseURI(nodeSet2->get(0), baseURI);
         }
     }
 
     if (exprResult1->getResultType() == txAExprResult::NODESET) {
         // The first argument is a NodeSet, iterate on its nodes
         txNodeSet* nodeSet1 = static_cast<txNodeSet*>
                                          (static_cast<txAExprResult*>
                                                      (exprResult1));
         int32_t i;
         for (i = 0; i < nodeSet1->size(); ++i) {
             const txXPathNode& node = nodeSet1->get(i);
             nsAutoString uriStr;
             txXPathNodeUtils::appendNodeValue(node, uriStr);
             if (!baseURISet) {
                 // if the second argument wasn't specified, use
                 // the baseUri of node itself
-                txXPathNodeUtils::getBaseURI(node, getter_AddRefs(baseURI));
+                txXPathNodeUtils::getBaseURI(node, baseURI);
             }
             retrieveNode(es, uriStr, baseURI, nodeSet);
         }
         
         NS_ADDREF(*aResult = nodeSet);
         
         return NS_OK;
     }
 
     // The first argument is not a NodeSet
     nsAutoString uriStr;
     exprResult1->stringValue(uriStr);
-    nsIURI* base = baseURISet ? baseURI.get() : mBaseURI.get();
-    retrieveNode(es, uriStr, base, nodeSet);
+    const nsAString* base = baseURISet ? &baseURI : &mBaseURI;
+    retrieveNode(es, uriStr, *base, nodeSet);
 
     NS_ADDREF(*aResult = nodeSet);
 
     return NS_OK;
 }
 
 Expr::ResultType
 DocumentFunctionCall::getReturnType()
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -16,48 +16,46 @@
 
 const int32_t txExecutionState::kMaxRecursionDepth = 20000;
 
 void
 txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
 {
     mSourceDocument = aSourceDocument;
 
-    nsCOMPtr<nsIURI> baseURI;
-    txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI));
+    nsAutoString baseURI;
+    txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
 
-    LookupOrAdd(baseURI)->mDocument = mSourceDocument;
+    PutEntry(baseURI)->mDocument = mSourceDocument;
 }
 
 txLoadedDocumentsHash::~txLoadedDocumentsHash()
 {
     if (mSourceDocument) {
-        nsCOMPtr<nsIURI> baseURI;
-        txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI));
+        nsAutoString baseURI;
+        txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
 
-        txLoadedDocumentInfo* info = Get(baseURI);
-        if (info) {
-            delete info->mDocument.forget();
+        txLoadedDocumentEntry* entry = GetEntry(baseURI);
+        if (entry) {
+            delete entry->mDocument.forget();
         }
     }
 }
 
 txExecutionState::txExecutionState(txStylesheet* aStylesheet,
-                                   bool aDisableLoads,
-                                   nsIDocument* aLoadingDocument)
+                                   bool aDisableLoads)
     : mOutputHandler(nullptr),
       mResultHandler(nullptr),
       mStylesheet(aStylesheet),
       mNextInstruction(nullptr),
       mLocalVariables(nullptr),
       mRecursionDepth(0),
       mEvalContext(nullptr),
       mInitialEvalContext(nullptr),
       mGlobalParams(nullptr),
-      mLoadingDocument(aLoadingDocument),
       mKeyHash(aStylesheet->getKeyMap()),
       mDisableLoads(aDisableLoads)
 {
     MOZ_COUNT_CTOR(txExecutionState);
 }
 
 txExecutionState::~txExecutionState()
 {
@@ -369,58 +367,51 @@ txExecutionState::popTemplateRule()
 
 txIEvalContext*
 txExecutionState::getEvalContext()
 {
     return mEvalContext;
 }
 
 const txXPathNode*
-txExecutionState::retrieveDocument(nsIURI* aUri)
+txExecutionState::retrieveDocument(const nsAString& aUri)
 {
-#ifdef DEBUG
-    {
-        bool hasFrag;
-        aUri->GetHasRef(&hasFrag);
-        MOZ_ASSERT(!hasFrag, "Remove the fragment");
+    NS_ASSERTION(!aUri.Contains(char16_t('#')),
+                 "Remove the fragment.");
+
+    if (mDisableLoads) {
+        return nullptr;
     }
-#endif
+
+    MOZ_LOG(txLog::xslt, LogLevel::Debug,
+           ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
 
-    if (mDisableLoads || !mLoadingDocument) {
+    // try to get already loaded document
+    txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
+    if (!entry) {
         return nullptr;
     }
 
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) {
-        nsAutoCString spec;
-        aUri->GetSpec(spec);
-        MOZ_LOG(txLog::xslt, LogLevel::Debug,
-                ("Retrieve Document %s", spec.get()));
-    }
-
-    // try to get already loaded document
-    txLoadedDocumentInfo* info = mLoadedDocuments.LookupOrAdd(aUri);
-
-    if (!info->mDocument && !info->LoadingFailed()) {
+    if (!entry->mDocument && !entry->LoadingFailed()) {
         // open URI
         nsAutoString errMsg;
-        info->mLoadResult =
-            txParseDocumentFromURI(aUri, mLoadingDocument,
-                                   errMsg, getter_Transfers(info->mDocument));
+        // XXX we should get the loader from the actual node
+        // triggering the load, but this will do for the time being
+        entry->mLoadResult =
+            txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
+                                   errMsg, getter_Transfers(entry->mDocument));
 
-        if (info->LoadingFailed()) {
-            nsAutoCString spec;
-            aUri->GetSpec(spec);
+        if (entry->LoadingFailed()) {
             receiveError(NS_LITERAL_STRING("Couldn't load document '") +
-                         NS_ConvertUTF8toUTF16(spec) +
-                         NS_LITERAL_STRING("': ") + errMsg,
-                         info->mLoadResult);
+                         aUri + NS_LITERAL_STRING("': ") + errMsg,
+                         entry->mLoadResult);
         }
     }
 
-    return info->mDocument;
+    return entry->mDocument;
 }
 
 nsresult
 txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
                               const txXPathNode& aRoot,
                               const nsAString& aKeyValue,
                               bool aIndexIfNotFound,
                               txNodeSet** aResult)
--- a/dom/xslt/xslt/txExecutionState.h
+++ b/dom/xslt/xslt/txExecutionState.h
@@ -12,29 +12,34 @@
 #include "txIXPathContext.h"
 #include "txVariableMap.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "txKey.h"
 #include "txStylesheet.h"
 #include "txXPathTreeWalker.h"
 #include "nsTArray.h"
-#include "nsURIHashKey.h"
 
 class txAOutputHandlerFactory;
 class txAXMLEventHandler;
 class txInstruction;
 
-class txLoadedDocumentInfo
+class txLoadedDocumentEntry : public nsStringHashKey
 {
 public:
-    explicit txLoadedDocumentInfo() : mLoadResult(NS_OK)
+    explicit txLoadedDocumentEntry(KeyTypePointer aStr) : nsStringHashKey(aStr),
+                                                          mLoadResult(NS_OK)
     {
     }
-    ~txLoadedDocumentInfo()
+    txLoadedDocumentEntry(const txLoadedDocumentEntry& aToCopy)
+        : nsStringHashKey(aToCopy)
+    {
+        NS_ERROR("We're horked.");
+    }
+    ~txLoadedDocumentEntry()
     {
         if (mDocument) {
             txXPathNodeUtils::release(mDocument);
         }
     }
     bool LoadingFailed()
     {
         NS_ASSERTION(NS_SUCCEEDED(mLoadResult) || !mDocument,
@@ -42,38 +47,37 @@ public:
 
         return NS_FAILED(mLoadResult);
     }
 
     nsAutoPtr<txXPathNode> mDocument;
     nsresult mLoadResult;
 };
 
-class txLoadedDocumentsHash : public nsClassHashtable<nsURIHashKey, txLoadedDocumentInfo>
+class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
 {
 public:
     txLoadedDocumentsHash()
-        : nsClassHashtable<nsURIHashKey, txLoadedDocumentInfo>(4),
+        : nsTHashtable<txLoadedDocumentEntry>(4),
           mSourceDocument(nullptr)
     {
     }
     ~txLoadedDocumentsHash();
     void init(txXPathNode* aSourceDocument);
 
 private:
     friend class txExecutionState;
     txXPathNode* mSourceDocument;
 };
 
 
 class txExecutionState : public txIMatchContext
 {
 public:
-    txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads,
-                     nsIDocument* aLoaderDocument);
+    txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads);
     ~txExecutionState();
     nsresult init(const txXPathNode& aNode,
                   txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams);
     nsresult end(nsresult aResult);
 
     TX_DECL_MATCH_CONTEXT;
 
     /**
@@ -98,17 +102,17 @@ public:
                           const txExpandedName& aMode,
                           txVariableMap* aParams);
     void popTemplateRule();
     nsresult pushParamMap(txVariableMap* aParams);
     txVariableMap* popParamMap();
 
     // state-getting functions
     txIEvalContext* getEvalContext();
-    const txXPathNode* retrieveDocument(nsIURI* aUri);
+    const txXPathNode* retrieveDocument(const nsAString& aUri);
     nsresult getKeyNodes(const txExpandedName& aKeyName,
                          const txXPathNode& aRoot,
                          const nsAString& aKeyValue, bool aIndexIfNotFound,
                          txNodeSet** aResult);
     TemplateRule* getCurrentTemplateRule();
     const txXPathNode& getSourceDocument()
     {
         NS_ASSERTION(mLoadedDocuments.mSourceDocument,
@@ -152,17 +156,16 @@ private:
 
     AutoTArray<TemplateRule, 10> mTemplateRules;
 
     txIEvalContext* mEvalContext;
     txIEvalContext* mInitialEvalContext;
     //Document* mRTFDocument;
     txOwningExpandedNameMap<txIGlobalParameter>* mGlobalParams;
 
-    nsCOMPtr<nsIDocument> mLoadingDocument;
     txLoadedDocumentsHash mLoadedDocuments;
     txKeyHash mKeyHash;
     RefPtr<txResultRecycler> mRecycler;
     bool mDisableLoads;
 
     static const int32_t kMaxRecursionDepth;
 };
 
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -252,26 +252,16 @@ txStylesheetSink::OnDataAvailable(nsIReq
 
 NS_IMETHODIMP
 txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
     int32_t charsetSource = kCharsetFromDocTypeDefault;
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
 
-    nsCOMPtr<nsIPrincipal> channelPrincipal;
-    nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
-        channel, getter_AddRefs(channelPrincipal));
-    mCompiler->setPrincipal(channelPrincipal);
-
-    nsCOMPtr<nsIURI> baseURI;
-    nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(baseURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-    mCompiler->setBaseURI(baseURI);
-
     // check channel's charset...
     nsAutoCString charsetVal;
     nsAutoCString charset;
     if (NS_SUCCEEDED(channel->GetContentCharset(charsetVal))) {
         if (EncodingUtils::FindEncodingForLabel(charsetVal, charset)) {
             charsetSource = kCharsetFromChannel;
         }
     }
@@ -380,18 +370,19 @@ class txCompileObserver final : public t
 {
 public:
     txCompileObserver(txMozillaXSLTProcessor* aProcessor,
                       nsIDocument* aLoaderDocument);
 
     TX_DECL_ACOMPILEOBSERVER
     NS_INLINE_DECL_REFCOUNTING(txCompileObserver)
 
-    nsresult startLoad(nsIURI* aUri, nsIPrincipal* aSourcePrincipal,
-                       txStylesheetCompiler* aCompiler);
+    nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
+                       nsIPrincipal* aSourcePrincipal,
+                       ReferrerPolicy aReferrerPolicy);
 
 private:
     RefPtr<txMozillaXSLTProcessor> mProcessor;
     nsCOMPtr<nsIDocument> mLoaderDocument;
 
     // This exists solely to suppress a warning from nsDerivedSafe
     txCompileObserver();
 
@@ -404,25 +395,40 @@ private:
 txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor,
                                      nsIDocument* aLoaderDocument)
     : mProcessor(aProcessor),
       mLoaderDocument(aLoaderDocument)
 {
 }
 
 nsresult
-txCompileObserver::loadURI(nsIURI* aUri,
-                           nsIPrincipal* aReferrerPrincipal,
+txCompileObserver::loadURI(const nsAString& aUri,
+                           const nsAString& aReferrerUri,
+                           ReferrerPolicy aReferrerPolicy,
                            txStylesheetCompiler* aCompiler)
 {
-    if (mProcessor->IsLoadDisabled() || !mLoaderDocument) {
+    if (mProcessor->IsLoadDisabled()) {
         return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
     }
 
-    return startLoad(aUri, aReferrerPrincipal, aCompiler);
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIURI> referrerUri;
+    rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIPrincipal> referrerPrincipal;
+    rv = nsContentUtils::GetSecurityManager()->
+      GetSimpleCodebasePrincipal(referrerUri,
+                                 getter_AddRefs(referrerPrincipal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy);
 }
 
 void
 txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
                                    nsresult aResult,
                                    const char16_t *aErrorText,
                                    const char16_t *aParam)
 {
@@ -430,21 +436,20 @@ txCompileObserver::onDoneCompiling(txSty
         mProcessor->setStylesheet(aCompiler->getStylesheet());
     }
     else {
         mProcessor->reportError(aResult, aErrorText, aParam);
     }
 }
 
 nsresult
-txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal,
-                             txStylesheetCompiler* aCompiler)
+txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
+                             nsIPrincipal* aReferrerPrincipal,
+                             ReferrerPolicy aReferrerPolicy)
 {
-    MOZ_ASSERT(aReferrerPrincipal);
-
     nsCOMPtr<nsILoadGroup> loadGroup = mLoaderDocument->GetDocumentLoadGroup();
     if (!loadGroup) {
         return NS_ERROR_FAILURE;
     }
 
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = NS_NewChannelWithTriggeringPrincipal(
                     getter_AddRefs(channel),
@@ -463,57 +468,55 @@ txCompileObserver::startLoad(nsIURI* aUr
     if (httpChannel) {
         httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                       NS_LITERAL_CSTRING("*/*"),
                                       false);
 
         nsCOMPtr<nsIURI> referrerURI;
         aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI));
         if (referrerURI) {
-            httpChannel->SetReferrerWithPolicy(referrerURI,
-                mLoaderDocument->GetReferrerPolicy());
+            httpChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy);
         }
     }
 
     nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     RefPtr<txStylesheetSink> sink = new txStylesheetSink(aCompiler, parser);
+    NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
 
     channel->SetNotificationCallbacks(sink);
 
     parser->SetCommand(kLoadAsData);
     parser->SetContentSink(sink);
     parser->Parse(aUri);
 
     return channel->AsyncOpen2(sink);
 }
 
 nsresult
 TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
-             nsIDocument* aLoaderDocument)
+             nsIDocument* aLoaderDocument, ReferrerPolicy aReferrerPolicy)
 {
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
-        nsAutoCString spec;
-        aUri->GetSpec(spec);
-        MOZ_LOG(txLog::xslt, LogLevel::Info,
-                ("TX_LoadSheet: %s\n", spec.get()));
-    }
+    nsIPrincipal* principal = aLoaderDocument->NodePrincipal();
+
+    nsAutoCString spec;
+    aUri->GetSpec(spec);
+    MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get()));
 
     RefPtr<txCompileObserver> observer =
         new txCompileObserver(aProcessor, aLoaderDocument);
-
-    nsAutoCString fragment;
-    aUri->GetRef(fragment);
+    NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY);
 
     RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), observer);
+        new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), aReferrerPolicy,
+                                 observer);
+    NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
 
-    return observer->startLoad(aUri, aLoaderDocument->NodePrincipal(),
-                               compiler);
+    return observer->startLoad(aUri, compiler, principal, aReferrerPolicy);
 }
 
 /**
  * handling DOM->txStylesheet
  * Observer needs to do synchronous loads.
  */
 static nsresult
 handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler)
@@ -577,99 +580,138 @@ handleNode(nsINode* aNode, txStylesheetC
     }
 
     return NS_OK;
 }
 
 class txSyncCompileObserver final : public txACompileObserver
 {
 public:
-    explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor,
-                                   nsIDocument* aLoaderDocument)
-        : mProcessor(aProcessor),
-          mLoaderDocument(aLoaderDocument)
-    {}
+    explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor);
 
     TX_DECL_ACOMPILEOBSERVER
     NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver)
 
 private:
     // Private destructor, to discourage deletion outside of Release():
     ~txSyncCompileObserver()
     {
     }
 
     RefPtr<txMozillaXSLTProcessor> mProcessor;
-    nsCOMPtr<nsIDocument> mLoaderDocument;
 };
 
+txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor)
+  : mProcessor(aProcessor)
+{
+}
+
 nsresult
-txSyncCompileObserver::loadURI(nsIURI* aUri,
-                               nsIPrincipal* aReferrerPrincipal,
+txSyncCompileObserver::loadURI(const nsAString& aUri,
+                               const nsAString& aReferrerUri,
+                               ReferrerPolicy aReferrerPolicy,
                                txStylesheetCompiler* aCompiler)
 {
-    MOZ_ASSERT(aReferrerPrincipal);
-
-    if (mProcessor->IsLoadDisabled() || !mLoaderDocument) {
+    if (mProcessor->IsLoadDisabled()) {
         return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
     }
 
-    nsAutoSyncOperation sync(mLoaderDocument);
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIURI> referrerUri;
+    rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIPrincipal> referrerPrincipal;
+    rv = nsContentUtils::GetSecurityManager()->
+      GetSimpleCodebasePrincipal(referrerUri,
+                                 getter_AddRefs(referrerPrincipal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // This is probably called by js, a loadGroup for the channel doesn't
+    // make sense.
+    nsCOMPtr<nsINode> source;
+    if (mProcessor) {
+      source =
+        do_QueryInterface(mProcessor->GetSourceContentModel());
+    }
+    nsAutoSyncOperation sync(source ? source->OwnerDoc() : nullptr);
     nsCOMPtr<nsIDOMDocument> document;
 
-    nsCOMPtr<nsILoadGroup> loadGroup = mLoaderDocument->GetDocumentLoadGroup();
-    nsresult rv =
-        nsSyncLoadService::LoadDocument(aUri,
-                                        nsIContentPolicy::TYPE_XSLT,
-                                        aReferrerPrincipal,
-                                        nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
-                                        loadGroup,
-                                        false,
-                                        mLoaderDocument->GetReferrerPolicy(),
-                                        getter_AddRefs(document));
+    rv = nsSyncLoadService::LoadDocument(uri, nsIContentPolicy::TYPE_XSLT,
+                                         referrerPrincipal,
+                                         nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+                                         nullptr, false,
+                                         aReferrerPolicy,
+                                         getter_AddRefs(document));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
-    nsCOMPtr<nsIURI> baseURI = doc->GetBaseURI();
-    aCompiler->setBaseURI(baseURI);
-    aCompiler->setPrincipal(doc->NodePrincipal());
     rv = handleNode(doc, aCompiler);
     if (NS_FAILED(rv)) {
         nsAutoCString spec;
-        aUri->GetSpec(spec);
+        uri->GetSpec(spec);
         aCompiler->cancel(rv, nullptr, NS_ConvertUTF8toUTF16(spec).get());
         return rv;
     }
 
     rv = aCompiler->doneLoading();
     return rv;
 }
 
 void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
                                             nsresult aResult,
                                             const char16_t *aErrorText,
                                             const char16_t *aParam)
 {
 }
 
 nsresult
-TX_CompileStylesheet(nsINode* aNode,
-                     nsIDocument* aLoaderDocument,
-                     txMozillaXSLTProcessor* aProcessor,
+TX_CompileStylesheet(nsINode* aNode, txMozillaXSLTProcessor* aProcessor,
                      txStylesheet** aStylesheet)
 {
+    // If we move GetBaseURI to nsINode this can be simplified.
+    nsCOMPtr<nsIDocument> doc = aNode->OwnerDoc();
+
+    nsCOMPtr<nsIURI> uri;
+    if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
+      uri = static_cast<nsIContent*>(aNode)->GetBaseURI();
+    }
+    else { 
+      NS_ASSERTION(aNode->IsNodeOfType(nsINode::eDOCUMENT), "not a doc");
+      uri = static_cast<nsIDocument*>(aNode)->GetBaseURI();
+    }
+    NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+    
+    nsAutoCString spec;
+    uri->GetSpec(spec);
+    NS_ConvertUTF8toUTF16 baseURI(spec);
+
+    nsIURI* docUri = doc->GetDocumentURI();
+    NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE);
+
+    // We need to remove the ref, a URI with a ref would mean that we have an
+    // embedded stylesheet.
+    docUri->CloneIgnoringRef(getter_AddRefs(uri));
+    NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+    uri->GetSpec(spec);
+    NS_ConvertUTF8toUTF16 stylesheetURI(spec);
+
     RefPtr<txSyncCompileObserver> obs =
-        new txSyncCompileObserver(aProcessor, aLoaderDocument);
+        new txSyncCompileObserver(aProcessor);
+    NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY);
 
     RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(EmptyString(), obs);
+        new txStylesheetCompiler(stylesheetURI, doc->GetReferrerPolicy(), obs);
+    NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
 
-    nsCOMPtr<nsIURI> baseURI = aNode->GetBaseURI();
     compiler->setBaseURI(baseURI);
-    compiler->setPrincipal(aNode->NodePrincipal());
 
     nsresult rv = handleNode(aNode, compiler);
     if (NS_FAILED(rv)) {
         compiler->cancel(rv);
         return rv;
     }
 
     rv = compiler->doneLoading();
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -356,23 +356,16 @@ txMozillaXSLTProcessor::txMozillaXSLTPro
   : mOwner(aOwner),
     mStylesheetDocument(nullptr),
     mTransformResult(NS_OK),
     mCompileResult(NS_OK),
     mFlags(0)
 {
 }
 
-NS_IMETHODIMP
-txMozillaXSLTProcessor::Init(nsISupports* aOwner)
-{
-    mOwner = aOwner;
-    return NS_OK;
-}
-
 txMozillaXSLTProcessor::~txMozillaXSLTProcessor()
 {
     if (mStylesheetDocument) {
         mStylesheetDocument->RemoveMutationObserver(this);
     }
 }
 
 NS_IMETHODIMP
@@ -606,17 +599,17 @@ txMozillaXSLTProcessor::ImportStylesheet
     }
     
     nsCOMPtr<nsINode> styleNode = do_QueryInterface(aStyle);
     NS_ENSURE_TRUE(styleNode &&
                    (styleNode->IsElement() ||
                     styleNode->IsNodeOfType(nsINode::eDOCUMENT)),
                    NS_ERROR_INVALID_ARG);
 
-    nsresult rv = TX_CompileStylesheet(styleNode, getLoaderDoc(), this,
+    nsresult rv = TX_CompileStylesheet(styleNode, this,
                                        getter_AddRefs(mStylesheet));
     // XXX set up exception context, bug 204658
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (styleNode->IsElement()) {
         mStylesheetDocument = styleNode->OwnerDoc();
         NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_UNEXPECTED);
 
@@ -661,17 +654,17 @@ txMozillaXSLTProcessor::TransformToDoc(n
     }
 
     nsCOMPtr<nsIDOMDocument> sourceDOMDocument;
     mSource->GetOwnerDocument(getter_AddRefs(sourceDOMDocument));
     if (!sourceDOMDocument) {
         sourceDOMDocument = do_QueryInterface(mSource);
     }
 
-    txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc());
+    txExecutionState es(mStylesheet, IsLoadDisabled());
 
     // XXX Need to add error observers
 
     // If aResult is non-null, we're a data document
     txToDocHandlerFactory handlerFactory(&es, sourceDOMDocument, mObserver,
                                          aCreateDataDocument);
     es.mOutputHandlerFactory = &handlerFactory;
 
@@ -729,17 +722,17 @@ txMozillaXSLTProcessor::TransformToFragm
     nsresult rv = ensureStylesheet();
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoPtr<txXPathNode> sourceNode(txXPathNativeNode::createXPathNode(aSource));
     if (!sourceNode) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc());
+    txExecutionState es(mStylesheet, IsLoadDisabled());
 
     // XXX Need to add error observers
 
     rv = aOutput->CreateDocumentFragment(aResult);
     NS_ENSURE_SUCCESS(rv, rv);
     txToFragmentHandlerFactory handlerFactory(*aResult);
     es.mOutputHandlerFactory = &handlerFactory;
 
@@ -1038,17 +1031,22 @@ txMozillaXSLTProcessor::GetFlags(uint32_
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri,
                                        nsIDocument* aLoaderDocument)
 {
-    nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument);
+    mozilla::net::ReferrerPolicy refpol = mozilla::net::RP_Default;
+    if (mStylesheetDocument) {
+        refpol = mStylesheetDocument->GetReferrerPolicy();
+    }
+
+    nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol);
     if (NS_FAILED(rv) && mObserver) {
         // This is most likely a network or security error, just
         // use the uri as context.
         nsAutoCString spec;
         aUri->GetSpec(spec);
         CopyUTF8toUTF16(spec, mSourceText);
         nsresult status = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_XSLT ? rv :
                           NS_ERROR_XSLT_NETWORK_ERROR;
@@ -1195,50 +1193,31 @@ txMozillaXSLTProcessor::notifyError()
     MOZ_ASSERT(document->GetReadyStateEnum() ==
                  nsIDocument::READYSTATE_LOADING,
                "Bad readyState.");
     document->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
 
     mObserver->OnTransformDone(mTransformResult, document);
 }
 
-nsIDocument*
-txMozillaXSLTProcessor::getLoaderDoc()
-{
-    if (mOwner) {
-        nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(mOwner);
-        if (win) {
-            return win->GetExtantDoc();
-        }
-    }
-
-    if (mSource) {
-        nsCOMPtr<nsINode> node = do_QueryInterface(mSource);
-        return node->OwnerDoc();
-    }
-
-    return nullptr;
-}
-
 nsresult
 txMozillaXSLTProcessor::ensureStylesheet()
 {
     if (mStylesheet) {
         return NS_OK;
     }
 
     NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_NOT_INITIALIZED);
 
     nsINode* style = mEmbeddedStylesheetRoot;
     if (!style) {
         style = mStylesheetDocument;
     }
 
-    return TX_CompileStylesheet(style, getLoaderDoc(), this,
-                                getter_AddRefs(mStylesheet));
+    return TX_CompileStylesheet(style, this, getter_AddRefs(mStylesheet));
 }
 
 void
 txMozillaXSLTProcessor::NodeWillBeDestroyed(const nsINode* aNode)
 {
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     if (NS_FAILED(mCompileResult)) {
         return;
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.h
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h
@@ -161,18 +161,16 @@ private:
      * Default destructor for txMozillaXSLTProcessor
      */
     ~txMozillaXSLTProcessor();
 
     nsresult DoTransform();
     void notifyError();
     nsresult ensureStylesheet();
 
-    nsIDocument* getLoaderDoc();
-
     nsCOMPtr<nsISupports> mOwner;
 
     RefPtr<txStylesheet> mStylesheet;
     nsIDocument* mStylesheetDocument; // weak
     nsCOMPtr<nsIContent> mEmbeddedStylesheetRoot;
 
     nsCOMPtr<nsIDOMNode> mSource;
     nsresult mTransformResult;
@@ -182,16 +180,16 @@ private:
     txOwningExpandedNameMap<txIGlobalParameter> mVariables;
     txNamespaceMap mParamNamespaceMap;
     RefPtr<txResultRecycler> mRecycler;
 
     uint32_t mFlags;
 };
 
 extern nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
-                             nsIDocument* aLoaderDocument);
+                             nsIDocument* aLoaderDocument,
+                             mozilla::net::ReferrerPolicy aReferrerPolicy);
 
 extern nsresult TX_CompileStylesheet(nsINode* aNode,
-                                     nsIDocument* aLoaderDocument,
                                      txMozillaXSLTProcessor* aProcessor,
                                      txStylesheet** aStylesheet);
 
 #endif
--- a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp
+++ b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp
@@ -15,17 +15,16 @@
 #include "txCore.h"
 #include "txStringUtils.h"
 #include "txStylesheet.h"
 #include "txToplevelItems.h"
 #include "txPatternParser.h"
 #include "txNamespaceMap.h"
 #include "txURIUtils.h"
 #include "txXSLTFunctions.h"
-#include "nsNetUtil.h"
 
 using namespace mozilla;
 
 txHandlerTable* gTxIgnoreHandler = 0;
 txHandlerTable* gTxRootHandler = 0;
 txHandlerTable* gTxEmbedHandler = 0;
 txHandlerTable* gTxTopHandler = 0;
 txHandlerTable* gTxTemplateHandler = 0;
@@ -752,22 +751,20 @@ txFnStartImport(int32_t aNamespaceID,
     
     txImportItem* importPtr = import.forget();
     
     txStylesheetAttr* attr = nullptr;
     rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
                       nsGkAtoms::href, true, &attr);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIURI> uri;
-    rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, nullptr,
-                   aState.mElementContext->mBaseURI);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aState.loadImportedStylesheet(uri, importPtr->mFrame);
+    nsAutoString absUri;
+    URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI,
+                          absUri);
+    rv = aState.loadImportedStylesheet(absUri, importPtr->mFrame);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return aState.pushHandlerTable(gTxIgnoreHandler);
 }
 
 static nsresult
 txFnEndImport(txStylesheetCompilerState& aState)
 {
@@ -785,22 +782,20 @@ txFnStartInclude(int32_t aNamespaceID,
                  int32_t aAttrCount,
                  txStylesheetCompilerState& aState)
 {
     txStylesheetAttr* attr = nullptr;
     nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
                                nsGkAtoms::href, true, &attr);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIURI> uri;
-    rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, nullptr,
-                   aState.mElementContext->mBaseURI);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aState.loadIncludedStylesheet(uri);
+    nsAutoString absUri;
+    URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI,
+                          absUri);
+    rv = aState.loadIncludedStylesheet(absUri);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return aState.pushHandlerTable(gTxIgnoreHandler);
 }
 
 static nsresult
 txFnEndInclude(txStylesheetCompilerState& aState)
 {
--- a/dom/xslt/xslt/txStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txStylesheetCompiler.cpp
@@ -18,60 +18,52 @@
 #include "txExprParser.h"
 #include "txLog.h"
 #include "txPatternParser.h"
 #include "txStringUtils.h"
 #include "txXSLTFunctions.h"
 #include "nsICategoryManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTArray.h"
-#include "nsIURI.h"
 
 using namespace mozilla;
 using mozilla::net::ReferrerPolicy;
 
-txStylesheetCompiler::txStylesheetCompiler(const nsString& aFragment,
+txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI,
+                                           ReferrerPolicy aReferrerPolicy,
                                            txACompileObserver* aObserver)
     : txStylesheetCompilerState(aObserver)
 {
-    mStatus = init(aFragment, nullptr, nullptr);
+    mStatus = init(aStylesheetURI, aReferrerPolicy, nullptr, nullptr);
 }
 
-txStylesheetCompiler::txStylesheetCompiler(const nsString& aFragment,
+txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI,
                                            txStylesheet* aStylesheet,
                                            txListIterator* aInsertPosition,
+                                           ReferrerPolicy aReferrerPolicy,
                                            txACompileObserver* aObserver)
     : txStylesheetCompilerState(aObserver)
 {
-    mStatus = init(aFragment, aStylesheet, aInsertPosition);
+    mStatus = init(aStylesheetURI, aReferrerPolicy, aStylesheet,
+                   aInsertPosition);
 }
 
 void
-txStylesheetCompiler::setBaseURI(nsIURI* aBaseURI)
+txStylesheetCompiler::setBaseURI(const nsString& aBaseURI)
 {
     NS_ASSERTION(mObjectStack.size() == 1 && !mObjectStack.peek(),
                  "Execution already started");
 
     if (NS_FAILED(mStatus)) {
         return;
     }
 
     mElementContext->mBaseURI = aBaseURI;
 }
 
-void
-txStylesheetCompiler::setPrincipal(nsIPrincipal* aPrincipal)
-{
-    if (NS_FAILED(mStatus)) {
-        return;
-    }
-
-    mStylesheetPrincipal = aPrincipal;
-}
-
 nsresult
 txStylesheetCompiler::startElement(int32_t aNamespaceID, nsIAtom* aLocalName,
                                    nsIAtom* aPrefix,
                                    txStylesheetAttr* aAttributes,
                                    int32_t aAttrCount)
 {
     if (NS_FAILED(mStatus)) {
         // ignore content after failure
@@ -220,21 +212,19 @@ txStylesheetCompiler::startElementIntern
 
         // xml:base
         if (attr->mNamespaceID == kNameSpaceID_XML &&
             attr->mLocalName == nsGkAtoms::base &&
             !attr->mValue.IsEmpty()) {
             rv = ensureNewElementContext();
             NS_ENSURE_SUCCESS(rv, rv);
             
-            nsCOMPtr<nsIURI> uri;
-            rv = NS_NewURI(getter_AddRefs(uri), attr->mValue,
-                           nullptr, mElementContext->mBaseURI);
-            NS_ENSURE_SUCCESS(rv, rv);
-            mElementContext->mBaseURI = uri.forget();
+            nsAutoString uri;
+            URIUtils::resolveHref(attr->mValue, mElementContext->mBaseURI, uri);
+            mElementContext->mBaseURI = uri;
         }
 
         // extension-element-prefixes
         if ((attr->mNamespaceID == kNameSpaceID_XSLT &&
              attr->mLocalName == nsGkAtoms::extensionElementPrefixes &&
              aNamespaceID != kNameSpaceID_XSLT) ||
             (attr->mNamespaceID == kNameSpaceID_None &&
              attr->mLocalName == nsGkAtoms::extensionElementPrefixes &&
@@ -376,49 +366,37 @@ txStylesheetCompiler::characters(const n
     mCharacters.Append(aStr);
 
     return NS_OK;
 }
 
 nsresult
 txStylesheetCompiler::doneLoading()
 {
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
-        nsCOMPtr<nsIURI> uri;
-        mStylesheetPrincipal->GetURI(getter_AddRefs(uri));
-        nsAutoCString spec;
-        uri->GetSpec(spec);
-        MOZ_LOG(txLog::xslt, LogLevel::Info,
-               ("Compiler::doneLoading: %s\n",
-                spec.get()));
-    }
+    MOZ_LOG(txLog::xslt, LogLevel::Info,
+           ("Compiler::doneLoading: %s\n",
+            NS_LossyConvertUTF16toASCII(mStylesheetURI).get()));
     if (NS_FAILED(mStatus)) {
         return mStatus;
     }
 
     mDoneWithThisStylesheet = true;
 
     return maybeDoneCompiling();
 }
 
 void
 txStylesheetCompiler::cancel(nsresult aError, const char16_t *aErrorText,
                              const char16_t *aParam)
 {
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
-        nsCOMPtr<nsIURI> uri;
-        mStylesheetPrincipal->GetURI(getter_AddRefs(uri));
-        nsAutoCString spec;
-        uri->GetSpec(spec);
-        MOZ_LOG(txLog::xslt, LogLevel::Info,
-               ("Compiler::cancel: %s, module: %d, code %d\n",
-                spec.get(),
-                NS_ERROR_GET_MODULE(aError),
-                NS_ERROR_GET_CODE(aError)));
-    }
+    MOZ_LOG(txLog::xslt, LogLevel::Info,
+           ("Compiler::cancel: %s, module: %d, code %d\n",
+            NS_LossyConvertUTF16toASCII(mStylesheetURI).get(),
+            NS_ERROR_GET_MODULE(aError),
+            NS_ERROR_GET_CODE(aError)));
     if (NS_SUCCEEDED(mStatus)) {
         mStatus = aError;
     }
 
     if (mObserver) {
         mObserver->onDoneCompiling(this, mStatus, aErrorText, aParam);
         // This will ensure that we don't call onDoneCompiling twice. Also
         // ensures that we don't keep the observer alive longer then necessary.
@@ -428,40 +406,30 @@ txStylesheetCompiler::cancel(nsresult aE
 
 txStylesheet*
 txStylesheetCompiler::getStylesheet()
 {
     return mStylesheet;
 }
 
 nsresult
-txStylesheetCompiler::loadURI(nsIURI* aUri,
-                              nsIPrincipal* aReferrerPrincipal,
+txStylesheetCompiler::loadURI(const nsAString& aUri,
+                              const nsAString& aReferrerUri,
+                              ReferrerPolicy aReferrerPolicy,
                               txStylesheetCompiler* aCompiler)
 {
-    nsCOMPtr<nsIURI> stylesheetURI;
-    mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI));
-
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
-        nsAutoCString stylesheetSpec;
-        stylesheetURI->GetSpec(stylesheetSpec);
-        nsAutoCString uriSpec;
-        aUri->GetSpec(uriSpec);
-        MOZ_LOG(txLog::xslt, LogLevel::Info,
-               ("Compiler::loadURI forwards %s thru %s\n",
-                uriSpec.get(),
-                stylesheetSpec.get()));
-    }
-
-    bool equals;
-    if (NS_FAILED(stylesheetURI->Equals(aUri, &equals)) || equals) {
+    MOZ_LOG(txLog::xslt, LogLevel::Info,
+           ("Compiler::loadURI forwards %s thru %s\n",
+            NS_LossyConvertUTF16toASCII(aUri).get(),
+            NS_LossyConvertUTF16toASCII(mStylesheetURI).get()));
+    if (mStylesheetURI.Equals(aUri)) {
         return NS_ERROR_XSLT_LOAD_RECURSION;
     }
     return mObserver ?
-        mObserver->loadURI(aUri, aReferrerPrincipal, aCompiler) :
+        mObserver->loadURI(aUri, aReferrerUri, aReferrerPolicy, aCompiler) :
         NS_ERROR_FAILURE;
 }
 
 void
 txStylesheetCompiler::onDoneCompiling(txStylesheetCompiler* aCompiler,
                                       nsresult aResult,
                                       const char16_t *aErrorText,
                                       const char16_t *aParam)
@@ -561,30 +529,37 @@ txStylesheetCompilerState::txStylesheetC
 {
     // Embedded stylesheets have another handler, which is set in
     // txStylesheetCompiler::init if the baseURI has a fragment identifier.
     mHandlerTable = gTxRootHandler;
 
 }
 
 nsresult
-txStylesheetCompilerState::init(const nsString& aFragment,
+txStylesheetCompilerState::init(const nsAString& aStylesheetURI,
+                                ReferrerPolicy aReferrerPolicy,
                                 txStylesheet* aStylesheet,
                                 txListIterator* aInsertPosition)
 {
     NS_ASSERTION(!aStylesheet || aInsertPosition,
                  "must provide insertposition if loading subsheet");
-
+    mStylesheetURI = aStylesheetURI;
+    mReferrerPolicy = aReferrerPolicy;
     // Check for fragment identifier of an embedded stylesheet.
-    if (!aFragment.IsEmpty()) {
-        // This is really an embedded stylesheet, not just a
-        // "url#". We may want to unescape the fragment.
-        mTarget = aFragment;
-        mEmbedStatus = eNeedEmbed;
-        mHandlerTable = gTxEmbedHandler;
+    int32_t fragment = aStylesheetURI.FindChar('#') + 1;
+    if (fragment > 0) {
+        int32_t fragmentLength = aStylesheetURI.Length() - fragment;
+        if (fragmentLength > 0) {
+            // This is really an embedded stylesheet, not just a
+            // "url#". We may want to unescape the fragment.
+            mTarget = Substring(aStylesheetURI, (uint32_t)fragment,
+                                fragmentLength);
+            mEmbedStatus = eNeedEmbed;
+            mHandlerTable = gTxEmbedHandler;
+        }
     }
     nsresult rv = NS_OK;
     if (aStylesheet) {
         mStylesheet = aStylesheet;
         mToplevelIterator = *aInsertPosition;
         mIsTopCompiler = false;
     }
     else {
@@ -593,17 +568,18 @@ txStylesheetCompilerState::init(const ns
         NS_ENSURE_SUCCESS(rv, rv);
         
         mToplevelIterator =
             txListIterator(&mStylesheet->mRootFrame->mToplevelItems);
         mToplevelIterator.next(); // go to the end of the list
         mIsTopCompiler = true;
     }
    
-    mElementContext = new txElementContext();
+    mElementContext = new txElementContext(aStylesheetURI);
+    NS_ENSURE_TRUE(mElementContext->mMappings, NS_ERROR_OUT_OF_MEMORY);
 
     // Push the "old" txElementContext
     rv = pushObject(0);
     NS_ENSURE_SUCCESS(rv, rv);
     
     return NS_OK;
 }
 
@@ -756,105 +732,87 @@ txStylesheetCompilerState::addInstructio
         *mGotoTargetPointers[i] = newInstr;
     }
     mGotoTargetPointers.Clear();
 
     return NS_OK;
 }
 
 nsresult
-txStylesheetCompilerState::loadIncludedStylesheet(nsIURI* aURI)
+txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI)
 {
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
-        nsAutoCString spec;
-        aURI->GetSpec(spec);
-        MOZ_LOG(txLog::xslt, LogLevel::Info,
-               ("CompilerState::loadIncludedStylesheet: %s\n",
-                spec.get()));
-    }
-
-    nsCOMPtr<nsIURI> stylesheetURI;
-    mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI));
-    bool equals;
-    if (NS_FAILED(stylesheetURI->Equals(aURI, &equals)) || equals) {
+    MOZ_LOG(txLog::xslt, LogLevel::Info,
+           ("CompilerState::loadIncludedStylesheet: %s\n",
+            NS_LossyConvertUTF16toASCII(aURI).get()));
+    if (mStylesheetURI.Equals(aURI)) {
         return NS_ERROR_XSLT_LOAD_RECURSION;
     }
     NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED);
 
     nsAutoPtr<txToplevelItem> item(new txDummyItem);
+    NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
 
     nsresult rv = mToplevelIterator.addBefore(item);
     NS_ENSURE_SUCCESS(rv, rv);
     
     item.forget();
 
     // step back to the dummy-item
     mToplevelIterator.previous();
     
     txACompileObserver* observer = static_cast<txStylesheetCompiler*>(this);
 
-    nsAutoCString fragment;
-    aURI->GetRef(fragment);
-
     RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment),
-                                 mStylesheet,
-                                 &mToplevelIterator,
-                                 observer);
+        new txStylesheetCompiler(aURI, mStylesheet, &mToplevelIterator,
+                                 mReferrerPolicy, observer);
+    NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
 
     // step forward before calling the observer in case of syncronous loading
     mToplevelIterator.next();
 
-    mChildCompilerList.AppendElement(compiler);
+    if (mChildCompilerList.AppendElement(compiler) == nullptr) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-    rv = mObserver->loadURI(aURI, mStylesheetPrincipal, compiler);
+    rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, compiler);
     if (NS_FAILED(rv)) {
         mChildCompilerList.RemoveElement(compiler);
     }
 
     return rv;
 }
 
 nsresult
-txStylesheetCompilerState::loadImportedStylesheet(nsIURI* aURI,
+txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI,
                                                   txStylesheet::ImportFrame* aFrame)
 {
-    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
-        nsAutoCString spec;
-        aURI->GetSpec(spec);
-        MOZ_LOG(txLog::xslt, LogLevel::Info,
-               ("CompilerState::loadImportedStylesheet: %s\n",
-                spec.get()));
-    }
-
-    nsCOMPtr<nsIURI> stylesheetURI;
-    mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI));
-    bool equals;
-    if (NS_FAILED(stylesheetURI->Equals(aURI, &equals)) || equals) {
+    MOZ_LOG(txLog::xslt, LogLevel::Info,
+           ("CompilerState::loadImportedStylesheet: %s\n",
+            NS_LossyConvertUTF16toASCII(aURI).get()));
+    if (mStylesheetURI.Equals(aURI)) {
         return NS_ERROR_XSLT_LOAD_RECURSION;
     }
     NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED);
 
     txListIterator iter(&aFrame->mToplevelItems);
     iter.next(); // go to the end of the list
 
     txACompileObserver* observer = static_cast<txStylesheetCompiler*>(this);
 
-    nsAutoCString fragment;
-    aURI->GetRef(fragment);
+    RefPtr<txStylesheetCompiler> compiler =
+        new txStylesheetCompiler(aURI, mStylesheet, &iter, mReferrerPolicy,
+                                 observer);
+    NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
 
-    RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment),
-                                 mStylesheet,
-                                 &iter,
-                                 observer);
+    if (mChildCompilerList.AppendElement(compiler) == nullptr) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-    mChildCompilerList.AppendElement(compiler);
-
-    nsresult rv = mObserver->loadURI(aURI, mStylesheetPrincipal, compiler);
+    nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy,
+                                     compiler);
     if (NS_FAILED(rv)) {
         mChildCompilerList.RemoveElement(compiler);
     }
 
     return rv;  
 }
 
 nsresult
@@ -1105,17 +1063,19 @@ findFunction(nsIAtom* aName, int32_t aNa
     return TX_ResolveFunctionCallXPCOM(map->mContractID, aNamespaceID, aName,
                                        nullptr, aResult);
 }
 
 extern bool
 TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID)
 {
     RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(EmptyString(), nullptr);
+        new txStylesheetCompiler(EmptyString(),
+                                 mozilla::net::RP_Default, nullptr);
+    NS_ENSURE_TRUE(compiler, false);
 
     nsAutoPtr<FunctionCall> fnCall;
 
     return NS_SUCCEEDED(findFunction(aName, aNameSpaceID, compiler,
                                      getter_Transfers(fnCall)));
 }
 
 nsresult
@@ -1149,19 +1109,20 @@ txStylesheetCompilerState::SetErrorOffse
 /* static */
 void
 txStylesheetCompilerState::shutdown()
 {
     delete sXPCOMFunctionMappings;
     sXPCOMFunctionMappings = nullptr;
 }
 
-txElementContext::txElementContext()
+txElementContext::txElementContext(const nsAString& aBaseURI)
     : mPreserveWhitespace(false),
       mForwardsCompatibleParsing(true),
+      mBaseURI(aBaseURI),
       mMappings(new txNamespaceMap),
       mDepth(0)
 {
     mInstructionNamespaces.AppendElement(kNameSpaceID_XSLT);
 }
 
 txElementContext::txElementContext(const txElementContext& aOther)
     : mPreserveWhitespace(aOther.mPreserveWhitespace),
--- a/dom/xslt/xslt/txStylesheetCompiler.h
+++ b/dom/xslt/xslt/txStylesheetCompiler.h
@@ -27,58 +27,60 @@ class txNamespaceMap;
 class txToplevelItem;
 class txPushNewContext;
 class txStylesheetCompiler;
 class txInScopeVariable;
 
 class txElementContext : public txObject
 {
 public:
-    explicit txElementContext();
+    explicit txElementContext(const nsAString& aBaseURI);
     txElementContext(const txElementContext& aOther);
 
     bool mPreserveWhitespace;
     bool mForwardsCompatibleParsing;
-    nsCOMPtr<nsIURI> mBaseURI;
+    nsString mBaseURI;
     RefPtr<txNamespaceMap> mMappings;
     nsTArray<int32_t> mInstructionNamespaces;
     int32_t mDepth;
 };
 
 class txACompileObserver
 {
 public:
     NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
     NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
 
-    virtual nsresult loadURI(nsIURI* aUri,
-                             nsIPrincipal* aReferrerPrincipal,
+    virtual nsresult loadURI(const nsAString& aUri,
+                             const nsAString& aReferrerUri,
+                             mozilla::net::ReferrerPolicy aReferrerPolicy,
                              txStylesheetCompiler* aCompiler) = 0;
     virtual void onDoneCompiling(txStylesheetCompiler* aCompiler,
                                  nsresult aResult,
                                  const char16_t *aErrorText = nullptr,
                                  const char16_t *aParam = nullptr) = 0;
 };
 
 #define TX_DECL_ACOMPILEOBSERVER \
-  nsresult loadURI(nsIURI* aUri, \
-                   nsIPrincipal* aReferrerPrincipal, \
+  nsresult loadURI(const nsAString& aUri, const nsAString& aReferrerUri, \
+                   mozilla::net::ReferrerPolicy aReferrerPolicy, \
                    txStylesheetCompiler* aCompiler); \
   void onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, \
                        const char16_t *aErrorText = nullptr, \
                        const char16_t *aParam = nullptr);
 
 class txStylesheetCompilerState : public txIParseContext
 {
 public:
     explicit txStylesheetCompilerState(txACompileObserver* aObserver);
     ~txStylesheetCompilerState();
     
-    nsresult init(const nsString& aFragment, txStylesheet* aStylesheet,
-                  txListIterator* aInsertPosition);
+    nsresult init(const nsAString& aStylesheetURI,
+                  mozilla::net::ReferrerPolicy aReferrerPolicy,
+                  txStylesheet* aStylesheet, txListIterator* aInsertPosition);
 
     // Embedded stylesheets state
     bool handleEmbeddedSheet()
     {
         return mEmbedStatus == eInEmbed;
     }
     void doneEmbedding()
     {
@@ -109,18 +111,18 @@ public:
     nsresult pushPtr(void* aPtr, enumStackType aType);
     void* popPtr(enumStackType aType);
     
     // stylesheet functions
     nsresult addToplevelItem(txToplevelItem* aItem);
     nsresult openInstructionContainer(txInstructionContainer* aContainer);
     void closeInstructionContainer();
     nsresult addInstruction(nsAutoPtr<txInstruction>&& aInstruction);
-    nsresult loadIncludedStylesheet(nsIURI* aURI);
-    nsresult loadImportedStylesheet(nsIURI* aURI,
+    nsresult loadIncludedStylesheet(const nsAString& aURI);
+    nsresult loadImportedStylesheet(const nsAString& aURI,
                                     txStylesheet::ImportFrame* aFrame);
     
     // misc
     nsresult addGotoTarget(txInstruction** aTargetPointer);
     nsresult addVariable(const txExpandedName& aName);
 
     // txIParseContext
     nsresult resolveNamespacePrefix(nsIAtom* aPrefix, int32_t& aID) override;
@@ -159,39 +161,40 @@ public:
     nsAutoPtr<txElementContext> mElementContext;
     txPushNewContext* mSorter;
     nsAutoPtr<txList> mChooseGotoList;
     bool mDOE;
     bool mSearchingForFallback;
     uint16_t mDisAllowed;
 
 protected:
-    nsCOMPtr<nsIPrincipal> mStylesheetPrincipal;
     RefPtr<txACompileObserver> mObserver;
     nsTArray<txInScopeVariable*> mInScopeVariables;
     nsTArray<txStylesheetCompiler*> mChildCompilerList;
     // embed info, target information is the ID
     nsString mTarget;
     enum 
     {
         eNoEmbed,
         eNeedEmbed,
         eInEmbed,
         eHasEmbed
     } mEmbedStatus;
+    nsString mStylesheetURI;
     bool mIsTopCompiler;
     bool mDoneWithThisStylesheet;
     txStack mObjectStack;
     txStack mOtherStack;
     nsTArray<enumStackType> mTypeStack;
 
 private:
     txInstruction** mNextInstrPtr;
     txListIterator mToplevelIterator;
     nsTArray<txInstruction**> mGotoTargetPointers;
+    mozilla::net::ReferrerPolicy mReferrerPolicy;
 };
 
 struct txStylesheetAttr
 {
     int32_t mNamespaceID;
     nsCOMPtr<nsIAtom> mLocalName;
     nsCOMPtr<nsIAtom> mPrefix;
     nsString mValue;
@@ -199,25 +202,26 @@ struct txStylesheetAttr
 
 class txStylesheetCompiler final : private txStylesheetCompilerState,
                                    public txACompileObserver
 {
 public:
     friend class txStylesheetCompilerState;
     friend bool TX_XSLTFunctionAvailable(nsIAtom* aName,
                                            int32_t aNameSpaceID);
-    txStylesheetCompiler(const nsString& aFragment,
+    txStylesheetCompiler(const nsAString& aStylesheetURI,
+                         mozilla::net::ReferrerPolicy  aReferrerPolicy,
                          txACompileObserver* aObserver);
-    txStylesheetCompiler(const nsString& aFragment,
+    txStylesheetCompiler(const nsAString& aStylesheetURI,
                          txStylesheet* aStylesheet,
                          txListIterator* aInsertPosition,
+                         mozilla::net::ReferrerPolicy aReferrerPolicy,
                          txACompileObserver* aObserver);
 
-    void setBaseURI(nsIURI* aBaseURI);
-    void setPrincipal(nsIPrincipal* aPrincipal);
+    void setBaseURI(const nsString& aBaseURI);
 
     nsresult startElement(int32_t aNamespaceID, nsIAtom* aLocalName,
                           nsIAtom* aPrefix, txStylesheetAttr* aAttributes,
                           int32_t aAttrCount);
     nsresult startElement(const char16_t *aName,
                           const char16_t **aAtts,
                           int32_t aAttrCount);
     nsresult endElement();
--- a/dom/xslt/xslt/txXSLTFunctions.h
+++ b/dom/xslt/xslt/txXSLTFunctions.h
@@ -18,24 +18,22 @@ class txStylesheet;
 **/
 class DocumentFunctionCall : public FunctionCall {
 
 public:
 
     /**
      * Creates a new document() function call
     **/
-    explicit DocumentFunctionCall(nsIURI* aBaseURI)
-        : mBaseURI(aBaseURI)
-    {}
+    explicit DocumentFunctionCall(const nsAString& aBaseURI);
 
     TX_DECL_FUNCTION
 
 private:
-    nsCOMPtr<nsIURI> mBaseURI;
+    nsString mBaseURI;
 };
 
 /*
  * The definition for the XSLT key() function
  */
 class txKeyFunctionCall : public FunctionCall {
 
 public:
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1744,19 +1744,28 @@ DrawTargetCairo::CreateSimilarDrawTarget
 {
   if (cairo_surface_status(cairo_get_group_target(mContext))) {
     RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
     if (target->Init(aSize, aFormat)) {
       return target.forget();
     }
   }
 
-  cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
-                                                          GfxFormatToCairoContent(aFormat),
-                                                          aSize.width, aSize.height);
+  cairo_surface_t* similar;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+  if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_WIN32) {
+    similar = cairo_win32_surface_create_with_dib(GfxFormatToCairoFormat(aFormat),
+                                                  aSize.width, aSize.height);
+  } else
+#endif
+  {
+    similar = cairo_surface_create_similar(mSurface,
+                                           GfxFormatToCairoContent(aFormat),
+                                           aSize.width, aSize.height);
+  }
 
   if (!cairo_surface_status(similar)) {
     RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
     if (target->InitAlreadyReferenced(similar, aSize)) {
       return target.forget();
     }
   }
 
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -172,16 +172,17 @@ nsBMPDecoder::nsBMPDecoder(RasterImage* 
   , mLexer(Transition::To(aState, aLength))
   , mIsWithinICO(false)
   , mMayHaveTransparency(false)
   , mDoesHaveTransparency(false)
   , mNumColors(0)
   , mColors(nullptr)
   , mBytesPerColor(0)
   , mPreGapLength(0)
+  , mPixelRowSize(0)
   , mCurrentRow(0)
   , mCurrentPos(0)
   , mAbsoluteModeNumPixels(0)
 {
 }
 
 // Constructor for normal BMP files.
 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -239,16 +239,17 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
   *aOptionalLoadInfoArgs =
     LoadInfoArgs(
       requestingPrincipalInfo,
       triggeringPrincipalInfo,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
+      aLoadInfo->GetVerifySignedContent(),
       aLoadInfo->GetInnerWindowID(),
       aLoadInfo->GetOuterWindowID(),
       aLoadInfo->GetParentOuterWindowID(),
       aLoadInfo->GetEnforceSecurity(),
       aLoadInfo->GetInitialSecurityCheckDone(),
       aLoadInfo->GetIsInThirdPartyContext(),
       aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects,
@@ -298,16 +299,17 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(requestingPrincipal,
                           triggeringPrincipal,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
                           static_cast<LoadTainting>(loadInfoArgs.tainting()),
                           loadInfoArgs.upgradeInsecureRequests(),
+                          loadInfoArgs.verifySignedContent(),
                           loadInfoArgs.innerWindowID(),
                           loadInfoArgs.outerWindowID(),
                           loadInfoArgs.parentOuterWindowID(),
                           loadInfoArgs.enforceSecurity(),
                           loadInfoArgs.initialSecurityCheckDone(),
                           loadInfoArgs.isInThirdPartyContext(),
                           loadInfoArgs.originAttributes(),
                           redirectChainIncludingInternalRedirects,
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -180,16 +180,23 @@ DecodeExpr(FunctionDecoder& f, ExprType*
 static bool
 DecodeNop(FunctionDecoder& f, ExprType* type)
 {
     *type = ExprType::Void;
     return true;
 }
 
 static bool
+DecodeUnreachable(FunctionDecoder& f, ExprType* type)
+{
+    *type = AnyType;
+    return true;
+}
+
+static bool
 DecodeCallWithSig(FunctionDecoder& f, const Sig& sig, ExprType* type)
 {
     for (ValType argType : sig.args()) {
         ExprType exprType;
         if (!DecodeExpr(f, &exprType))
             return false;
 
         if (!CheckType(f, exprType, argType))
@@ -838,16 +845,18 @@ DecodeExpr(FunctionDecoder& f, ExprType*
       case Expr::Br:
         return DecodeBranch(f, expr, type);
       case Expr::BrIf:
         return DecodeBranch(f, expr, type);
       case Expr::BrTable:
         return DecodeBrTable(f, type);
       case Expr::Return:
         return DecodeReturn(f, type);
+      case Expr::Unreachable:
+        return DecodeUnreachable(f, type);
       default:
         // Note: it's important not to remove this default since readExpr()
         // can return Expr values for which there is no enumerator.
         break;
     }
 
     return f.fail("bad expression code");
 }
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -16,16 +16,17 @@
  * limitations under the License.
  */
 
 #include "asmjs/WasmBinaryToText.h"
 
 #include "mozilla/CheckedInt.h"
 
 #include "jsnum.h"
+#include "jsprf.h"
 
 #include "asmjs/Wasm.h"
 #include "asmjs/WasmTypes.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::wasm;
@@ -154,16 +155,22 @@ RenderFullLine(WasmRenderContext& c)
 
 static bool
 RenderNop(WasmRenderContext& c)
 {
     return c.buffer.append("(nop)");
 }
 
 static bool
+RenderUnreachable(WasmRenderContext& c)
+{
+    return c.buffer.append("(trap)");
+}
+
+static bool
 RenderCallWithSig(WasmRenderContext& c, uint32_t sigIndex)
 {
     const DeclaredSig& sig = c.signatures[sigIndex];
     for (uint32_t i = 0; i < sig.args().length(); i++) {
         if (!c.buffer.append(" "))
             return false;
         if (!RenderExpr(c))
             return false;
@@ -872,16 +879,18 @@ RenderExpr(WasmRenderContext& c)
 {
     Expr expr;
     if (!c.d.readExpr(&expr))
         return RenderFail(c, "unable to read expression");
 
     switch (expr) {
       case Expr::Nop:
         return RenderNop(c);
+      case Expr::Unreachable:
+        return RenderUnreachable(c);
       case Expr::Call:
         return RenderCall(c);
       case Expr::CallImport:
         return RenderCallImport(c);
       case Expr::CallIndirect:
         return RenderCallIndirect(c);
       case Expr::I32Const:
         return RenderConstI32(c);
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -860,16 +860,27 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return;
         MAsmJSVoidReturn* ins = MAsmJSVoidReturn::New(alloc());
         curBlock_->end(ins);
         curBlock_ = nullptr;
     }
 
+    bool unreachableTrap()
+    {
+        if (inDeadCode())
+            return true;
+
+        auto* ins = MAsmThrowUnreachable::New(alloc());
+        curBlock_->end(ins);
+        curBlock_ = nullptr;
+        return true;
+    }
+
     bool branchAndStartThen(MDefinition* cond, MBasicBlock** thenBlock, MBasicBlock** elseBlock)
     {
         if (inDeadCode())
             return true;
 
         bool hasThenBlock = *thenBlock != nullptr;
         bool hasElseBlock = *elseBlock != nullptr;
 
@@ -2613,16 +2624,23 @@ EmitReturn(FunctionCompiler& f, MDefinit
 
     f.returnExpr(retVal);
 
     *def = nullptr;
     return true;
 }
 
 static bool
+EmitUnreachable(FunctionCompiler& f, MDefinition** def)
+{
+    *def = nullptr;
+    return f.unreachableTrap();
+}
+
+static bool
 EmitBlock(FunctionCompiler& f, MDefinition** def)
 {
     if (!f.startBlock())
         return false;
     if (uint32_t numStmts = f.readVarU32()) {
         for (uint32_t i = 0; i < numStmts - 1; i++) {
             MDefinition* _;
             if (!EmitExpr(f, &_))
@@ -2685,26 +2703,27 @@ EmitExpr(FunctionCompiler& f, MDefinitio
         return EmitLoop(f, def);
       case Expr::Br:
       case Expr::BrIf:
         return EmitBranch(f, op, def);
       case Expr::BrTable:
         return EmitBrTable(f, def);
       case Expr::Return:
         return EmitReturn(f, def);
+      case Expr::Unreachable:
+        return EmitUnreachable(f, def);
 
       // Calls
       case Expr::Call:
         return EmitCall(f, exprOffset, def);
       case Expr::CallIndirect:
         return EmitCallIndirect(f, exprOffset, def);
       case Expr::CallImport:
         return EmitCallImport(f, exprOffset, def);
 
-
       // Locals and globals
       case Expr::GetLocal:
         return EmitGetLocal(f, def);
       case Expr::SetLocal:
         return EmitSetLocal(f, def);
       case Expr::LoadGlobal:
         return EmitLoadGlobal(f, def);
       case Expr::StoreGlobal:
@@ -3028,17 +3047,16 @@ EmitExpr(FunctionCompiler& f, MDefinitio
       case Expr::I64Popcnt:
       case Expr::I64Eqz:
       case Expr::I32Rotr:
       case Expr::I32Rotl:
       case Expr::I64Rotr:
       case Expr::I64Rotl:
       case Expr::MemorySize:
       case Expr::GrowMemory:
-      case Expr::Unreachable:
         MOZ_CRASH("NYI");
         break;
       case Expr::Limit:;
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -771,17 +771,18 @@ static Offsets
 GenerateErrorStub(MacroAssembler& masm, SymbolicAddress address)
 {
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
     // sp can be anything at this point, so ensure it is aligned when calling
-    // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
+    // into C++.  We unconditionally jump to throw so don't worry about
+    // restoring sp.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(address);
     masm.jump(JumpTarget::Throw);
 
     offsets.end = masm.currentOffset();
     return offsets;
@@ -827,16 +828,18 @@ wasm::GenerateJumpTarget(MacroAssembler&
       case JumpTarget::StackOverflow:
         return GenerateStackOverflow(masm);
       case JumpTarget::ConversionError:
         return GenerateErrorStub(masm, SymbolicAddress::OnImpreciseConversion);
       case JumpTarget::OutOfBounds:
         return GenerateErrorStub(masm, SymbolicAddress::OnOutOfBounds);
       case JumpTarget::BadIndirectCall:
         return GenerateErrorStub(masm, SymbolicAddress::BadIndirectCall);
+      case JumpTarget::UnreachableTrap:
+        return GenerateErrorStub(masm, SymbolicAddress::UnreachableTrap);
       case JumpTarget::Throw:
         return GenerateThrow(masm);
       case JumpTarget::Limit:
         break;
     }
     MOZ_CRASH("bad JumpTarget");
 }
 
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/Maybe.h"
 
 #include "jsdtoa.h"
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jsstr.h"
 
 #include "asmjs/WasmBinary.h"
+#include "asmjs/WasmTypes.h"
 #include "ds/LifoAlloc.h"
 #include "js/CharacterEncoding.h"
 #include "js/HashTable.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::BitwiseCast;
@@ -204,16 +205,17 @@ enum class WasmAstExprKind
     ConversionOperator,
     GetLocal,
     If,
     Load,
     Nop,
     Return,
     SetLocal,
     Store,
+    Trap,
     UnaryOperator,
 };
 
 class WasmAstExpr : public WasmAstNode
 {
     const WasmAstExprKind kind_;
 
   protected:
@@ -233,16 +235,23 @@ class WasmAstExpr : public WasmAstNode
 
 struct WasmAstNop : WasmAstExpr
 {
     WasmAstNop()
       : WasmAstExpr(WasmAstExprKind::Nop)
     {}
 };
 
+struct WasmAstTrap : WasmAstExpr
+{
+    WasmAstTrap()
+      : WasmAstExpr(WasmAstExprKind::Trap)
+    {}
+};
+
 class WasmAstConst : public WasmAstExpr
 {
     const Val val_;
 
   public:
     static const WasmAstExprKind Kind = WasmAstExprKind::Const;
     explicit WasmAstConst(Val val)
       : WasmAstExpr(Kind),
@@ -791,16 +800,17 @@ class WasmToken
         Param,
         Result,
         Return,
         Segment,
         SetLocal,
         Store,
         Table,
         Text,
+        Trap,
         Type,
         UnaryOpcode,
         ValueType
     };
   private:
     Kind kind_;
     const char16_t* begin_;
     const char16_t* end_;
@@ -1926,16 +1936,18 @@ WasmTokenStream::next()
             return WasmToken(WasmToken::Segment, begin, cur_);
         break;
 
       case 't':
         if (consume(MOZ_UTF16("table")))
             return WasmToken(WasmToken::Table, begin, cur_);
         if (consume(MOZ_UTF16("type")))
             return WasmToken(WasmToken::Type, begin, cur_);
+        if (consume(MOZ_UTF16("trap")))
+            return WasmToken(WasmToken::Trap, begin, cur_);
         break;
 
       default:
         break;
     }
 
     return fail(begin);
 }
@@ -2664,16 +2676,18 @@ ParseBranchTable(WasmParseContext& c, Wa
 static WasmAstExpr*
 ParseExprInsideParens(WasmParseContext& c)
 {
     WasmToken token = c.ts.get();
 
     switch (token.kind()) {
       case WasmToken::Nop:
         return new(c.lifo) WasmAstNop;
+      case WasmToken::Trap:
+        return new(c.lifo) WasmAstTrap;
       case WasmToken::BinaryOpcode:
         return ParseBinaryOperator(c, token.expr());
       case WasmToken::Block:
         return ParseBlock(c, Expr::Block);
       case WasmToken::Br:
         return ParseBranch(c, Expr::Br);
       case WasmToken::BrIf:
         return ParseBranch(c, Expr::BrIf);
@@ -3368,16 +3382,17 @@ ResolveBranchTable(Resolver& r, WasmAstB
     return ResolveExpr(r, bt.index());
 }
 
 static bool
 ResolveExpr(Resolver& r, WasmAstExpr& expr)
 {
     switch (expr.kind()) {
       case WasmAstExprKind::Nop:
+      case WasmAstExprKind::Trap:
         return true;
       case WasmAstExprKind::BinaryOperator:
         return ResolveBinaryOperator(r, expr.as<WasmAstBinaryOperator>());
       case WasmAstExprKind::Block:
         return ResolveBlock(r, expr.as<WasmAstBlock>());
       case WasmAstExprKind::Branch:
         return ResolveBranch(r, expr.as<WasmAstBranch>());
       case WasmAstExprKind::Call:
@@ -3698,16 +3713,18 @@ EncodeBranchTable(Encoder& e, WasmAstBra
 }
 
 static bool
 EncodeExpr(Encoder& e, WasmAstExpr& expr)
 {
     switch (expr.kind()) {
       case WasmAstExprKind::Nop:
         return e.writeExpr(Expr::Nop);
+      case WasmAstExprKind::Trap:
+        return e.writeExpr(Expr::Unreachable);
       case WasmAstExprKind::BinaryOperator:
         return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
       case WasmAstExprKind::Block:
         return EncodeBlock(e, expr.as<WasmAstBlock>());
       case WasmAstExprKind::Branch:
         return EncodeBranch(e, expr.as<WasmAstBranch>());
       case WasmAstExprKind::Call:
         return EncodeCall(e, expr.as<WasmAstCall>());
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -74,16 +74,23 @@ OnImpreciseConversion()
 
 static void
 BadIndirectCall()
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IND_CALL);
 }
 
+static void
+UnreachableTrap()
+{
+    JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNREACHABLE);
+}
+
 static int32_t
 CoerceInPlace_ToInt32(MutableHandleValue val)
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
 
     int32_t i32;
     if (!ToInt32(cx, val, &i32))
         return false;
@@ -181,16 +188,18 @@ wasm::AddressOf(SymbolicAddress imm, Exc
       case SymbolicAddress::ReportOverRecursed:
         return FuncCast(WasmReportOverRecursed, Args_General0);
       case SymbolicAddress::OnOutOfBounds:
         return FuncCast(OnOutOfBounds, Args_General0);
       case SymbolicAddress::OnImpreciseConversion:
         return FuncCast(OnImpreciseConversion, Args_General0);
       case SymbolicAddress::BadIndirectCall:
         return FuncCast(BadIndirectCall, Args_General0);
+      case SymbolicAddress::UnreachableTrap:
+        return FuncCast(UnreachableTrap, Args_General0);
       case SymbolicAddress::HandleExecutionInterrupt:
         return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
       case SymbolicAddress::InvokeImport_Void:
         return FuncCast(InvokeImport_Void, Args_General3);
       case SymbolicAddress::InvokeImport_I32:
         return FuncCast(InvokeImport_I32, Args_General3);
       case SymbolicAddress::InvokeImport_F64:
         return FuncCast(InvokeImport_F64, Args_General3);
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -530,16 +530,17 @@ enum class SymbolicAddress
     ATan2D,
     Runtime,
     RuntimeInterruptUint32,
     StackLimit,
     ReportOverRecursed,
     OnOutOfBounds,
     OnImpreciseConversion,
     BadIndirectCall,
+    UnreachableTrap,
     HandleExecutionInterrupt,
     InvokeImport_Void,
     InvokeImport_I32,
     InvokeImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
     Limit
 };
@@ -553,16 +554,17 @@ AddressOf(SymbolicAddress imm, Exclusive
 // and patched specially by the MacroAssembler and ModuleGenerator.
 
 enum class JumpTarget
 {
     StackOverflow,
     OutOfBounds,
     ConversionError,
     BadIndirectCall,
+    UnreachableTrap,
     Throw,
     Limit
 };
 
 typedef EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray;
 
 // The CompileArgs struct captures global parameters that affect all wasm code
 // generation. It also currently is the single source of truth for whether or
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jsgc.h"
 #include "jsprf.h"
 
+#include "asmjs/WasmModule.h"
 #include "builtin/ModuleObject.h"
 #include "gc/GCInternals.h"
 #include "gc/Policy.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/ScopeObject.h"
--- a/js/src/jit-test/tests/wasm/basic-control-flow.js
+++ b/js/src/jit-test/tests/wasm/basic-control-flow.js
@@ -401,8 +401,18 @@ var f = wasmEvalText(`(module (func (res
 
 assertEq(f(-2), -1);
 assertEq(f(-1), -1);
 assertEq(f(0), 0);
 assertEq(f(1), 0);
 assertEq(f(2), 2);
 assertEq(f(3), -1);
 
+// ----------------------------------------------------------------------------
+// unreachable
+
+const UNREACHABLE = /unreachable/;
+assertErrorMessage(wasmEvalText(`(module (func (trap)) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (if (trap) (i32.const 0))) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (trap)))) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (trap)))) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (trap))) (export "" 0))`), Error, UNREACHABLE);
+
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10622,16 +10622,23 @@ CodeGenerator::visitAsmJSInterruptCheck(
 
     MOZ_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % ABIStackAlignment == 0);
     masm.call(wasm::SymbolicAddress::HandleExecutionInterrupt);
     masm.branchIfFalseBool(ReturnReg, wasm::JumpTarget::Throw);
 
     masm.bind(&rejoin);
 }
 
+void
+CodeGenerator::visitAsmThrowUnreachable(LAsmThrowUnreachable* lir)
+{
+    MOZ_ASSERT(gen->compilingAsmJS());
+    masm.jump(wasm::JumpTarget::UnreachableTrap);
+}
+
 typedef bool (*RecompileFn)(JSContext*);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
 
 typedef bool (*ForcedRecompileFn)(JSContext*);
 static const VMFunction ForcedRecompileFnInfo = FunctionInfo<ForcedRecompileFn>(ForcedRecompile);
 
 void
 CodeGenerator::visitRecompileCheck(LRecompileCheck* ins)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -390,16 +390,17 @@ class CodeGenerator : public CodeGenerat
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
+    void visitAsmThrowUnreachable(LAsmThrowUnreachable* lir);
     void visitRecompileCheck(LRecompileCheck* ins);
 
     void visitRandom(LRandom* ins);
 
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2462,16 +2462,22 @@ LIRGenerator::visitInterruptCheck(MInter
 void
 LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins)
 {
     gen->setPerformsCall();
     add(new(alloc()) LAsmJSInterruptCheck, ins);
 }
 
 void
+LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins)
+{
+    add(new(alloc()) LAsmThrowUnreachable, ins);
+}
+
+void
 LIRGenerator::visitStoreSlot(MStoreSlot* ins)
 {
     LInstruction* lir;
 
     switch (ins->value()->type()) {
       case MIRType_Value:
         lir = new(alloc()) LStoreSlotV(useRegister(ins->slots()), useBox(ins->value()));
         add(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -178,16 +178,17 @@ class LIRGenerator : public LIRGenerator
     void visitConvertElementsToDoubles(MConvertElementsToDoubles* ins);
     void visitMaybeToDoubleElement(MMaybeToDoubleElement* ins);
     void visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins);
     void visitLoadSlot(MLoadSlot* ins);
     void visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins);
     void visitFunctionEnvironment(MFunctionEnvironment* ins);
     void visitInterruptCheck(MInterruptCheck* ins);
     void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins);
+    void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins);
     void visitStoreSlot(MStoreSlot* ins);
     void visitFilterTypeSet(MFilterTypeSet* ins);
     void visitTypeBarrier(MTypeBarrier* ins);
     void visitMonitorTypes(MMonitorTypes* ins);
     void visitPostWriteBarrier(MPostWriteBarrier* ins);
     void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins);
     void visitArrayLength(MArrayLength* ins);
     void visitSetArrayLength(MSetArrayLength* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4719,16 +4719,39 @@ MLoadElement::foldsTo(TempAllocator& all
         return this;
 
     if (store->index() != index())
         return this;
 
     return foldsToStoredValue(alloc, store->value());
 }
 
+MDefinition*
+MLoadUnboxedObjectOrNull::foldsTo(TempAllocator& alloc)
+{
+    if (!dependency() || !dependency()->isStoreUnboxedObjectOrNull())
+        return this;
+
+    MStoreUnboxedObjectOrNull* store = dependency()->toStoreUnboxedObjectOrNull();
+    if (!store->block()->dominates(block()))
+        return this;
+
+    if (store->elements() != elements())
+        return this;
+
+    if (store->index() != index())
+        return this;
+
+    if (store->value()->type() == MIRType_ObjectOrNull)
+        return this;
+
+    MOZ_ASSERT(offsetAdjustment() == store->offsetAdjustment());
+    return foldsToStoredValue(alloc, store->value());
+}
+
 // Gets the MDefinition* representing the source/target object's storage.
 // Usually this is just an MElements*, but sometimes there are layers
 // of indirection or inlining, which are handled elsewhere.
 static inline const MElements*
 MaybeUnwrapElements(const MDefinition* elementsOrObj)
 {
     // Sometimes there is a level of indirection for conversion.
     if (elementsOrObj->isConvertElementsToDoubles())
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7587,16 +7587,32 @@ class MAsmJSInterruptCheck
 {
   public:
     INSTRUCTION_HEADER(AsmJSInterruptCheck)
     static MAsmJSInterruptCheck* New(TempAllocator& alloc) {
         return new(alloc) MAsmJSInterruptCheck;
     }
 };
 
+// Directly jumps to the unreachable trap handler.
+class MAsmThrowUnreachable
+  : public MAryControlInstruction<0, 0>,
+    public NoTypePolicy::Data
+{
+  public:
+    INSTRUCTION_HEADER(AsmThrowUnreachable)
+    static MAsmThrowUnreachable* New(TempAllocator& alloc) {
+        return new(alloc) MAsmThrowUnreachable;
+    }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+};
+
 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
 // it to baseline to throw at the correct pc.
 class MLexicalCheck
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
 {
     BailoutKind kind_;
     explicit MLexicalCheck(MDefinition* input, BailoutKind kind)
@@ -9277,16 +9293,17 @@ class MLoadUnboxedObjectOrNull
             return false;
         if (offsetAdjustment() != other->offsetAdjustment())
             return false;
         return congruentIfOperandsEqual(other);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::UnboxedElement);
     }
+    MDefinition* foldsTo(TempAllocator& alloc) override;
     bool mightAlias(const MDefinition* store) const override;
 
     ALLOW_CLONE(MLoadUnboxedObjectOrNull)
 };
 
 class MLoadUnboxedString
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -248,16 +248,17 @@ namespace jit {
     _(Floor)                                                                \
     _(Ceil)                                                                 \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(AsmJSInterruptCheck)                                                  \
+    _(AsmThrowUnreachable)                                                  \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsCallable)                                                           \
     _(IsObject)                                                             \
     _(HasClass)                                                             \
     _(AsmJSNeg)                                                             \
     _(AsmJSUnsignedToDouble)                                                \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1242,16 +1242,25 @@ class LAsmJSInterruptCheck : public LIns
     LAsmJSInterruptCheck()
     { }
 
     bool isCall() const {
         return true;
     }
 };
 
+class LAsmThrowUnreachable : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(AsmThrowUnreachable);
+
+    LAsmThrowUnreachable()
+    { }
+};
+
 class LInterruptCheck : public LInstructionHelper<0, 0, 0>
 {
     Label* oolEntry_;
 
     // Whether this is an implicit interrupt check. Implicit interrupt checks
     // use a patchable backedge and signal handlers instead of an explicit
     // rt->interrupt check.
     bool implicit_;
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -339,16 +339,17 @@
     _(RoundF)                       \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(AsmJSInterruptCheck)          \
+    _(AsmThrowUnreachable)          \
     _(GetDOMProperty)               \
     _(GetDOMMemberV)                \
     _(GetDOMMemberT)                \
     _(SetDOMProperty)               \
     _(CallDOMNative)                \
     _(IsCallable)                   \
     _(IsObject)                     \
     _(IsObjectAndBranch)            \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -344,16 +344,17 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1
 
 // wasm
 MSG_DEF(JSMSG_WASM_FAIL,               1, JSEXN_TYPEERR, "wasm error: {0}")
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR, "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,     "wasm indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR, "first argument must be a typed array")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR, "second argument, if present, must be an object")
+MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,     "reached unreachable trap")
 
 // Proxy
 MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE,   2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
 MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
 MSG_DEF(JSMSG_CANT_DEFINE_INVALID,     0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
 MSG_DEF(JSMSG_CANT_DEFINE_NEW,         0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
 MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC,    0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
 MSG_DEF(JSMSG_PROXY_DEFINE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy defineProperty handler returned false for property '{0}'")
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -547,58 +547,61 @@ TraceLoggerThread::logTimestamp(uint32_t
 
 void
 TraceLoggerThread::log(uint32_t id)
 {
     if (enabled == 0)
         return;
 
     MOZ_ASSERT(traceLoggerState);
-    if (!events.ensureSpaceBeforeAdd()) {
+    if (!events.hasSpaceForAdd()) {
         uint64_t start = rdtsc() - traceLoggerState->startupTime;
 
-        if (graph.get())
-            graph->log(events);
+        if (!events.ensureSpaceBeforeAdd()) {
+            if (graph.get())
+                graph->log(events);
+
+            iteration_++;
+            events.clear();
+
+            // Remove the item in the pointerMap for which the payloads
+            // have no uses anymore
+            for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) {
+                if (e.front().value()->uses() != 0)
+                    continue;
 
-        iteration_++;
-        events.clear();
+                TextIdHashMap::Ptr p = textIdPayloads.lookup(e.front().value()->textId());
+                MOZ_ASSERT(p);
+                textIdPayloads.remove(p);
+
+                e.removeFront();
+            }
+
+            // Free all payloads that have no uses anymore.
+            for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) {
+                if (e.front().value()->uses() == 0) {
+                    js_delete(e.front().value());
+                    e.removeFront();
+                }
+            }
+        }
 
         // Log the time it took to flush the events as being from the
         // Tracelogger.
         if (graph.get()) {
             MOZ_ASSERT(events.capacity() > 2);
             EventEntry& entryStart = events.pushUninitialized();
             entryStart.time = start;
             entryStart.textId = TraceLogger_Internal;
 
             EventEntry& entryStop = events.pushUninitialized();
             entryStop.time = rdtsc() - traceLoggerState->startupTime;
             entryStop.textId = TraceLogger_Stop;
         }
 
-        // Remove the item in the pointerMap for which the payloads
-        // have no uses anymore
-        for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) {
-            if (e.front().value()->uses() != 0)
-                continue;
-
-            TextIdHashMap::Ptr p = textIdPayloads.lookup(e.front().value()->textId());
-            MOZ_ASSERT(p);
-            textIdPayloads.remove(p);
-
-            e.removeFront();
-        }
-
-        // Free all payloads that have no uses anymore.
-        for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) {
-            if (e.front().value()->uses() == 0) {
-                js_delete(e.front().value());
-                e.removeFront();
-            }
-        }
     }
 
     uint64_t time = rdtsc() - traceLoggerState->startupTime;
 
     EventEntry& entry = events.pushUninitialized();
     entry.time = time;
     entry.textId = id;
 }
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -26,21 +26,21 @@ namespace js {
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  *
  * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 351;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 352;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 418,
+static_assert(JSErr_Limit == 419,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -41,16 +41,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                         aLoadingContext->NodePrincipal() : aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal ?
                            aTriggeringPrincipal : mLoadingPrincipal.get())
   , mLoadingContext(do_GetWeakReference(aLoadingContext))
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(LoadTainting::Basic)
   , mUpgradeInsecureRequests(false)
+  , mVerifySignedContent(false)
   , mInnerWindowID(0)
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(true)
   , mForcePreflight(false)
   , mIsPreflight(false)
@@ -122,16 +123,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
                    nsIPrincipal* aTriggeringPrincipal,
                    nsSecurityFlags aSecurityFlags)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(nsIContentPolicy::TYPE_DOCUMENT)
   , mTainting(LoadTainting::Basic)
   , mUpgradeInsecureRequests(false)
+  , mVerifySignedContent(false)
   , mInnerWindowID(0)
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
   , mForcePreflight(false)
   , mIsPreflight(false)
@@ -159,16 +161,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
 LoadInfo::LoadInfo(const LoadInfo& rhs)
   : mLoadingPrincipal(rhs.mLoadingPrincipal)
   , mTriggeringPrincipal(rhs.mTriggeringPrincipal)
   , mLoadingContext(rhs.mLoadingContext)
   , mSecurityFlags(rhs.mSecurityFlags)
   , mInternalContentPolicyType(rhs.mInternalContentPolicyType)
   , mTainting(rhs.mTainting)
   , mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
+  , mVerifySignedContent(rhs.mVerifySignedContent)
   , mInnerWindowID(rhs.mInnerWindowID)
   , mOuterWindowID(rhs.mOuterWindowID)
   , mParentOuterWindowID(rhs.mParentOuterWindowID)
   , mEnforceSecurity(rhs.mEnforceSecurity)
   , mInitialSecurityCheckDone(rhs.mInitialSecurityCheckDone)
   , mIsThirdPartyContext(rhs.mIsThirdPartyContext)
   , mOriginAttributes(rhs.mOriginAttributes)
   , mRedirectChainIncludingInternalRedirects(
@@ -181,16 +184,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
                    LoadTainting aTainting,
                    bool aUpgradeInsecureRequests,
+                   bool aVerifySignedContent,
                    uint64_t aInnerWindowID,
                    uint64_t aOuterWindowID,
                    uint64_t aParentOuterWindowID,
                    bool aEnforceSecurity,
                    bool aInitialSecurityCheckDone,
                    bool aIsThirdPartyContext,
                    const NeckoOriginAttributes& aOriginAttributes,
                    nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
@@ -199,16 +203,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    bool aForcePreflight,
                    bool aIsPreflight)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(aTainting)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
+  , mVerifySignedContent(aVerifySignedContent)
   , mInnerWindowID(aInnerWindowID)
   , mOuterWindowID(aOuterWindowID)
   , mParentOuterWindowID(aParentOuterWindowID)
   , mEnforceSecurity(aEnforceSecurity)
   , mInitialSecurityCheckDone(aInitialSecurityCheckDone)
   , mIsThirdPartyContext(aIsThirdPartyContext)
   , mOriginAttributes(aOriginAttributes)
   , mCorsUnsafeHeaders(aCorsUnsafeHeaders)
@@ -422,16 +427,32 @@ LoadInfo::InternalContentPolicyType()
 NS_IMETHODIMP
 LoadInfo::GetUpgradeInsecureRequests(bool* aResult)
 {
   *aResult = mUpgradeInsecureRequests;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::SetVerifySignedContent(bool aVerifySignedContent)
+{
+  MOZ_ASSERT(mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
+            "can only verify content for TYPE_DOCUMENT");
+  mVerifySignedContent = aVerifySignedContent;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::GetVerifySignedContent(bool* aResult)
+{
+  *aResult = mVerifySignedContent;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetInnerWindowID(uint64_t* aResult)
 {
   *aResult = mInnerWindowID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LoadInfo::GetOuterWindowID(uint64_t* aResult)
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -75,16 +75,17 @@ private:
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   // Please note that aRedirectChain uses swapElements.
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            LoadTainting aTainting,
            bool aUpgradeInsecureRequests,
+           bool aVerifySignedContent,
            uint64_t aInnerWindowID,
            uint64_t aOuterWindowID,
            uint64_t aParentOuterWindowID,
            bool aEnforceSecurity,
            bool aInitialSecurityCheckDone,
            bool aIsThirdPartyRequest,
            const NeckoOriginAttributes& aOriginAttributes,
            nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
@@ -112,16 +113,17 @@ private:
   // if you add a member, please also update the copy constructor
   nsCOMPtr<nsIPrincipal>           mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal>           mTriggeringPrincipal;
   nsWeakPtr                        mLoadingContext;
   nsSecurityFlags                  mSecurityFlags;
   nsContentPolicyType              mInternalContentPolicyType;
   LoadTainting                     mTainting;
   bool                             mUpgradeInsecureRequests;
+  bool                             mVerifySignedContent;
   uint64_t                         mInnerWindowID;
   uint64_t                         mOuterWindowID;
   uint64_t                         mParentOuterWindowID;
   bool                             mEnforceSecurity;
   bool                             mInitialSecurityCheckDone;
   bool                             mIsThirdPartyContext;
   NeckoOriginAttributes            mOriginAttributes;
   nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChainIncludingInternalRedirects;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -332,16 +332,27 @@ interface nsILoadInfo : nsISupports
    * requests in e10s where the loadingDocument is not available.
    *
    * Warning: If the loadingDocument is null, then the
    * upgradeInsecureRequests is false.
    */
   [infallible] readonly attribute boolean upgradeInsecureRequests;
 
   /**
+   * If true, the content of the channel is queued up and checked
+   * if it matches a content signature. Note, setting this flag
+   * to true will negatively impact performance since the preloader
+   * can not start until all of the content is fetched from the
+   * netwerk.
+   *
+   * Only use that in combination with TYPE_DOCUMENT.
+   */
+  [infallible] attribute boolean verifySignedContent;
+
+  /**
    * Typically these are the window IDs of the window in which the element being
    * loaded lives. However, if the element being loaded is <frame
    * src="foo.html"> (or, more generally, if the element QIs to
    * nsIFrameLoaderOwner) then the window IDs are for the window containing the
    * foo.html document. In this case, parentOuterWindowID is the window ID of
    * the window containing the <frame> element.
    *
    * Note that these window IDs can be 0 if the window is not
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -28,16 +28,17 @@ namespace net {
 struct LoadInfoArgs
 {
   PrincipalInfo    requestingPrincipalInfo;
   PrincipalInfo    triggeringPrincipalInfo;
   uint32_t         securityFlags;
   uint32_t         contentPolicyType;
   uint32_t         tainting;
   bool             upgradeInsecureRequests;
+  bool             verifySignedContent;
   uint64_t         innerWindowID;
   uint64_t         outerWindowID;
   uint64_t         parentOuterWindowID;
   bool             enforceSecurity;
   bool             initialSecurityCheckDone;
   bool             isInThirdPartyContext;
   NeckoOriginAttributes originAttributes;
   PrincipalInfo[]  redirectChainIncludingInternalRedirects;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -35,16 +35,17 @@
 #include "prprf.h"
 #include "prnetdb.h"
 #include "nsEscape.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsDNSPrefetch.h"
 #include "nsChannelClassifier.h"
 #include "nsIRedirectResultListener.h"
+#include "mozilla/dom/ContentVerifier.h"
 #include "mozilla/TimeStamp.h"
 #include "nsError.h"
 #include "nsPrintfCString.h"
 #include "nsAlgorithm.h"
 #include "nsQueryObject.h"
 #include "GeckoProfiler.h"
 #include "nsIConsoleService.h"
 #include "mozilla/Attributes.h"
@@ -981,16 +982,30 @@ nsHttpChannel::CallOnStartRequest()
         if (NS_ERROR_FILE_TOO_BIG == rv) {
           // Don't throw the entry away, we will need it later.
           LOG(("  entry too big"));
         } else {
           NS_ENSURE_SUCCESS(rv, rv);
         }
     }
 
+    // Check for a Content-Signature header and inject mediator if the header is
+    // requested and available.
+    // If requested (mLoadInfo->GetVerifySignedContent), but not present, or
+    // present but not valid, fail this channel and return
+    // NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a
+    // fallback load in nsDocShell.
+    if (!mCanceled) {
+        rv = ProcessContentSignatureHeader(mResponseHead);
+        if (NS_FAILED(rv)) {
+            LOG(("Content-signature verification failed.\n"));
+            return rv;
+        }
+    }
+
     LOG(("  calling mListener->OnStartRequest\n"));
     if (mListener) {
         MOZ_ASSERT(!mOnStartRequestCalled,
                    "We should not call OsStartRequest twice");
         rv = mListener->OnStartRequest(this, mListenerContext);
         mOnStartRequestCalled = true;
         if (NS_FAILED(rv))
             return rv;
@@ -1320,16 +1335,61 @@ nsHttpChannel::ProcessSecurityHeaders()
 
     rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP,
                                      sslStatus, flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
+nsresult
+nsHttpChannel::ProcessContentSignatureHeader(nsHttpResponseHead *aResponseHead)
+{
+    nsresult rv = NS_OK;
+
+    // we only do this if we require it in loadInfo
+    if (!mLoadInfo || !mLoadInfo->GetVerifySignedContent()) {
+        return NS_OK;
+    }
+    NS_ENSURE_TRUE(aResponseHead, NS_ERROR_ABORT);
+    nsAutoCString contentSignatureHeader;
+    nsHttpAtom atom = nsHttp::ResolveAtom("Content-Signature");
+    rv = aResponseHead->GetHeader(atom, contentSignatureHeader);
+    if (NS_FAILED(rv)) {
+        LOG(("Content-Signature header is missing but expected."));
+        DoInvalidateCacheEntry(mURI);
+        return NS_ERROR_INVALID_SIGNATURE;
+    }
+
+    // if we require a signature but it is empty, fail
+    if (contentSignatureHeader.IsEmpty()) {
+      DoInvalidateCacheEntry(mURI);
+      LOG(("An expected content-signature header is missing.\n"));
+      return NS_ERROR_INVALID_SIGNATURE;
+    }
+
+    // we ensure a content type here to avoid running into problems with
+    // content sniffing, which might sniff parts of the content before we can
+    // verify the signature
+    if (aResponseHead->ContentType().IsEmpty()) {
+        NS_WARNING("Empty content type can get us in trouble when verifying "
+                   "content signatures");
+        return NS_ERROR_INVALID_SIGNATURE;
+    }
+    // create a new listener that meadiates the content
+    RefPtr<ContentVerifier> contentVerifyingMediator =
+      new ContentVerifier(mListener, mListenerContext);
+    rv = contentVerifyingMediator->Init(
+      NS_ConvertUTF8toUTF16(contentSignatureHeader));
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
+    mListener = contentVerifyingMediator;
+
+    return NS_OK;
+}
+
 /**
  * Decide whether or not to send a security report and, if so, give the
  * SecurityReporter the information required to send such a report.
  */
 void
 nsHttpChannel::ProcessSecurityReport(nsresult status) {
     uint32_t errorClass;
     nsCOMPtr<nsINSSErrorsService> errSvc =
@@ -3350,16 +3410,24 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
                 doValidation = fromPreviousSession;
         }
         else
             doValidation = true;
 
         LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v"));
     }
 
+
+    // If a content signature is expected to be valid in this load,
+    // set doValidation to force a signature check.
+    if (!doValidation &&
+        mLoadInfo && mLoadInfo->GetVerifySignedContent()) {
+        doValidation = true;
+    }
+
     if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) &&
         (methodWasGet || methodWasHead)) {
         const char *requestedETag, *cachedETag;
         cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
         requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match);
         if (cachedETag && (!strncmp(cachedETag, "W/", 2) ||
             strcmp(requestedETag, cachedETag))) {
             // User has defined If-Match header, if the cached entry is not
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -362,16 +362,27 @@ private:
      * A function that takes care of reading STS and PKP headers and enforcing
      * STS and PKP load rules. After a secure channel is erected, STS and PKP
      * requires the channel to be trusted or any STS or PKP header data on
      * the channel is ignored. This is called from ProcessResponse.
      */
     nsresult ProcessSecurityHeaders();
 
     /**
+     * Taking care of the Content-Signature header and fail the channel if
+     * the signature verification fails or is required but the header is not
+     * present.
+     * This sets mListener to ContentVerifier, which buffers the entire response
+     * before verifying the Content-Signature header. If the verification is
+     * successful, the load proceeds as usual. If the verification fails, a
+     * NS_ERROR_INVALID_SIGNATURE is thrown and a fallback loaded in nsDocShell
+     */
+    nsresult ProcessContentSignatureHeader(nsHttpResponseHead *aResponseHead);
+
+    /**
      * A function that will, if the feature is enabled, send security reports.
      */
     void ProcessSecurityReport(nsresult status);
 
     /**
      * A function to process a single security header (STS or PKP), assumes
      * some basic sanity checks have been applied to the channel. Called
      * from ProcessSecurityHeaders.
--- a/security/manager/ssl/ScopedNSSTypes.h
+++ b/security/manager/ssl/ScopedNSSTypes.h
@@ -144,73 +144,73 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLAT
  *   rv = digest.End(SEC_OID_SHA1, digestContext);
  *   NS_ENSURE_SUCCESS(rv, rv)
  */
 class Digest
 {
 public:
   Digest()
   {
-    item.type = siBuffer;
-    item.data = buf;
-    item.len = 0;
+    mItem.type = siBuffer;
+    mItem.data = mItemBuf;
+    mItem.len = 0;
   }
 
   nsresult DigestBuf(SECOidTag hashAlg, const uint8_t * buf, uint32_t len)
   {
     if (len > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
       return NS_ERROR_INVALID_ARG;
     }
     nsresult rv = SetLength(hashAlg);
     NS_ENSURE_SUCCESS(rv, rv);
-    return MapSECStatus(PK11_HashBuf(hashAlg, item.data, buf,
+    return MapSECStatus(PK11_HashBuf(hashAlg, mItem.data, buf,
                                      static_cast<int32_t>(len)));
   }
 
   nsresult End(SECOidTag hashAlg, ScopedPK11Context & context)
   {
     nsresult rv = SetLength(hashAlg);
     NS_ENSURE_SUCCESS(rv, rv);
     uint32_t len;
-    rv = MapSECStatus(PK11_DigestFinal(context, item.data, &len, item.len));
+    rv = MapSECStatus(PK11_DigestFinal(context, mItem.data, &len, mItem.len));
     NS_ENSURE_SUCCESS(rv, rv);
     context = nullptr;
-    NS_ENSURE_TRUE(len == item.len, NS_ERROR_UNEXPECTED);
+    NS_ENSURE_TRUE(len == mItem.len, NS_ERROR_UNEXPECTED);
     return NS_OK;
   }
 
-  const SECItem & get() const { return item; }
+  const SECItem & get() const { return mItem; }
 
 private:
   nsresult SetLength(SECOidTag hashType)
   {
 #ifdef _MSC_VER
 #pragma warning(push)
     // C4061: enumerator 'symbol' in switch of enum 'symbol' is not
     // explicitly handled.
 #pragma warning(disable:4061)
 #endif
     switch (hashType)
     {
-      case SEC_OID_SHA1:   item.len = SHA1_LENGTH;   break;
-      case SEC_OID_SHA256: item.len = SHA256_LENGTH; break;
-      case SEC_OID_SHA384: item.len = SHA384_LENGTH; break;
-      case SEC_OID_SHA512: item.len = SHA512_LENGTH; break;
+      case SEC_OID_SHA1:   mItem.len = SHA1_LENGTH;   break;
+      case SEC_OID_SHA256: mItem.len = SHA256_LENGTH; break;
+      case SEC_OID_SHA384: mItem.len = SHA384_LENGTH; break;
+      case SEC_OID_SHA512: mItem.len = SHA512_LENGTH; break;
       default:
         return NS_ERROR_INVALID_ARG;
     }
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
     return NS_OK;
   }
 
-  uint8_t buf[HASH_LENGTH_MAX];
-  SECItem item;
+  uint8_t mItemBuf[HASH_LENGTH_MAX];
+  SECItem mItem;
 };
 
 // Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SlotInfo,
                                           PK11SlotInfo,
                                           PK11_FreeSlot)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SymKey,
                                           PK11SymKey,
@@ -289,16 +289,21 @@ inline void SECOID_DestroyAlgorithmID_tr
   return SECOID_DestroyAlgorithmID(a, true);
 }
 
 inline void SECKEYEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo * epki)
 {
   return SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
 }
 
+inline void VFY_DestroyContext_true(VFYContext * ctx)
+{
+  VFY_DestroyContext(ctx, true);
+}
+
 } // namespace internal
 
 // Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECItem,
                                           SECItem,
                                           internal::SECITEM_FreeItem_true)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPrivateKey,
                                           SECKEYPrivateKey,
@@ -356,11 +361,14 @@ MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(Un
                                       SECItem,
                                       internal::SECITEM_FreeItem_true)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey,
                                       SECKEYPublicKey,
                                       SECKEY_DestroyPublicKey)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule,
                                       SECMODModule,
                                       SECMOD_DestroyModule)
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext,
+                                      VFYContext,
+                                      internal::VFY_DestroyContext_true)
 } // namespace mozilla
 
 #endif // mozilla_ScopedNSSTypes_h
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -56,16 +56,17 @@ EXPORTS += [
     'nsClientAuthRemember.h',
     'nsCrypto.h',
     'nsNSSCallbacks.h',
     'nsNSSCertificate.h',
     'nsNSSComponent.h',
     'nsNSSHelper.h',
     'nsNSSShutDown.h',
     'nsRandomGenerator.h',
+    'nsSecurityHeaderParser.h',
     'NSSErrorsService.h',
     'ScopedNSSTypes.h',
     'SharedCertVerifier.h',
 ]
 
 EXPORTS.mozilla += [
     'DataStorage.h',
     'PublicSSL.h',
deleted file mode 100644
--- a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/002.html.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[002.html]
-  type: testharness
-  disabled:
-    if debug or (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1215461
-  [Link with target=_blank, no rel]
-    expected:
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-
--- a/testing/web-platform/meta/html/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.html.ini
@@ -131,19 +131,16 @@
     expected: FAIL
 
   [Document interface: attribute onmousewheel]
     expected: FAIL
 
   [Document interface: attribute onsort]
     expected: FAIL
 
-  [Document interface: attribute ontoggle]
-    expected: FAIL
-
   [Stringification of iframe.contentDocument]
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "origin" with the proper type (3)]
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "styleSheetSets" with the proper type (31)]
     expected: FAIL
@@ -200,19 +197,16 @@
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "onmousewheel" with the proper type (135)]
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (148)]
     expected: FAIL
 
-  [Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (153)]
-    expected: FAIL
-
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "origin" with the proper type (3)]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "styleSheetSets" with the proper type (31)]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "domain" with the proper type (34)]
     expected: FAIL
@@ -404,19 +398,16 @@
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onmousewheel" with the proper type (135)]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (148)]
     expected: FAIL
 
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (153)]
-    expected: FAIL
-
   [Touch interface: attribute region]
     expected: FAIL
 
   [HTMLAllCollection must be primary interface of document.all]
     expected: FAIL
 
   [Stringification of document.all]
     expected: FAIL
@@ -587,19 +578,16 @@
     expected: FAIL
 
   [HTMLElement interface: attribute onmousewheel]
     expected: FAIL
 
   [HTMLElement interface: attribute onsort]
     expected: FAIL
 
-  [HTMLElement interface: attribute ontoggle]
-    expected: FAIL
-
   [HTMLElement interface: document.createElement("noscript") must inherit property "translate" with the proper type (2)]
     expected: FAIL
 
   [HTMLElement interface: document.createElement("noscript") must inherit property "dropzone" with the proper type (20)]
     expected: FAIL
 
   [HTMLElement interface: document.createElement("noscript") must inherit property "forceSpellCheck" with the proper type (25)]
     expected: FAIL
@@ -641,19 +629,16 @@
     expected: FAIL
 
   [HTMLElement interface: document.createElement("noscript") must inherit property "onmousewheel" with the proper type (74)]
     expected: FAIL
 
   [HTMLElement interface: document.createElement("noscript") must inherit property "onsort" with the proper type (87)]
     expected: FAIL
 
-  [HTMLElement interface: document.createElement("noscript") must inherit property "ontoggle" with the proper type (92)]
-    expected: FAIL
-
   [Element interface: document.createElement("noscript") must inherit property "prepend" with the proper type (31)]
     expected: FAIL
 
   [Element interface: calling prepend([object Object\],[object Object\]) on document.createElement("noscript") with too few arguments must throw TypeError]
     expected: FAIL
 
   [Element interface: document.createElement("noscript") must inherit property "append" with the proper type (32)]
     expected: FAIL
@@ -1960,19 +1945,16 @@
     expected: FAIL
 
   [Window interface: attribute onmousewheel]
     expected: FAIL
 
   [Window interface: attribute onsort]
     expected: FAIL
 
-  [Window interface: attribute ontoggle]
-    expected: FAIL
-
   [Window interface: operation createImageBitmap(ImageBitmapSource,long,long,long,long)]
     expected: FAIL
 
   [Window interface: window must inherit property "showModalDialog" with the proper type (34)]
     expected:
       if not e10s: PASS
       FAIL
 
@@ -1995,19 +1977,16 @@
     expected: FAIL
 
   [Window interface: window must inherit property "onmousewheel" with the proper type (80)]
     expected: FAIL
 
   [Window interface: window must inherit property "onsort" with the proper type (93)]
     expected: FAIL
 
-  [Window interface: window must inherit property "ontoggle" with the proper type (98)]
-    expected: FAIL
-
   [Window interface: calling showModalDialog(DOMString,any) on window with too few arguments must throw TypeError]
     expected:
       if not e10s: PASS
       FAIL
 
   [Window interface: calling createImageBitmap(ImageBitmapSource,long,long,long,long) on window with too few arguments must throw TypeError]
     expected: FAIL
 
@@ -2510,19 +2489,16 @@
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "onmousewheel" with the proper type (136)]
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (149)]
     expected: FAIL
 
-  [Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (154)]
-    expected: FAIL
-
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "styleSheetSets" with the proper type (32)]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "domain" with the proper type (35)]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "cookie" with the proper type (37)]
     expected: FAIL
@@ -2660,19 +2636,16 @@
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onmousewheel" with the proper type (136)]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (149)]
     expected: FAIL
 
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (154)]
-    expected: FAIL
-
   [Location interface: window.location must have own property "ancestorOrigins"]
     expected: FAIL
 
   [WorkerLocation interface: attribute href]
     expected: FAIL
 
   [WorkerLocation interface: attribute origin]
     expected: FAIL
--- a/testing/web-platform/meta/html/semantics/interactive-elements/the-details-element/toggleEvent.html.ini
+++ b/testing/web-platform/meta/html/semantics/interactive-elements/the-details-element/toggleEvent.html.ini
@@ -1,21 +1,3 @@
 [toggleEvent.html]
   type: testharness
-  expected: TIMEOUT
-  [Adding open to 'details' should fire a toggle event at the 'details' element]
-    expected: NOTRUN
-
-  [Removing open from 'details' should fire a toggle event at the 'details' element]
-    expected: NOTRUN
-
-  [Adding open to 'details' (display:none) should fire a toggle event at the 'details' element]
-    expected: NOTRUN
-
-  [Adding open from 'details' (no children) should fire a toggle event at the 'details' element]
-    expected: NOTRUN
-
-  [Calling open twice on 'details' fires only one toggle event]
-    expected: FAIL
-
-  [Adding open to 'details' (not in the document) should fire a toggle event at the 'details' element]
-    expected: TIMEOUT
-
+  prefs: [dom.details_element.enabled:true]
--- a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/002.html
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/002.html
@@ -1,26 +1,25 @@
 <!doctype html>
 <title>Link with target=_blank, no rel</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <div id="log"></div>
 <a href="002-1.html" target="_blank">Link</a>
 <script>
-var t = async_test();
 var a;
-t.step(function() {
+async_test(function(t) {
   a = document.getElementsByTagName("a")[0];
   a.click();
-  //This is a bit hacky; if the test fails there isn't a link back to the parent
-  //window so we have to pass on a timeout. But opening the link could be slow in
-  //some cases, so there is some possibility of false fails
-  setTimeout(t.step_func(function() {
-               assert_unreached("Failed to get callback from opened window");
-             }), 1000);
+
+  // This is a bit hacky; if the test fails there isn't a link back to the parent
+  // window so we have to pass on a timeout. But opening the link could be slow in
+  // some cases, so there is some possibility of false fails
+  step_timeout(t.step_func(function() {
+                 assert_unreached("Failed to get callback from opened window");
+               }), 5000);
+
+  onmessage = t.step_func(function(e) {
+    assert_equals(e.data, "PASS");
+    t.done()
+  });
 });
-
-onmessage = t.step_func(function(e) {
-  assert_equals(e.data, "PASS");
-  t.done()
-}
-);
 </script>
--- a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
@@ -19,27 +19,52 @@
   <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
 </details>
 <details id=details4>
 </details>
 <details id=details6>
   <summary>Lorem ipsum</summary>
   <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
 </details>
+<details id=details7>
+  <summary>Lorem ipsum</summary>
+  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details8 open>
+  <summary>Lorem ipsum</summary>
+  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details9 open>
+  <summary>Lorem ipsum</summary>
+  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details10>
+  <summary>Lorem ipsum</summary>
+  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
 <script>
   var t1 = async_test("Adding open to 'details' should fire a toggle event at the 'details' element"),
   t2 = async_test("Removing open from 'details' should fire a toggle event at the 'details' element"),
   t3 = async_test("Adding open to 'details' (display:none) should fire a toggle event at the 'details' element"),
   t4 = async_test("Adding open from 'details' (no children) should fire a toggle event at the 'details' element"),
   t6 = async_test("Calling open twice on 'details' fires only one toggle event"),
+  t7 = async_test("Calling setAttribute('open', '') to 'details' should fire a toggle event at the 'details' element"),
+  t8 = async_test("Calling removeAttribute('open') to 'details' should fire a toggle event at the 'details' element"),
+  t9 = async_test("Setting open=true to opened 'details' element should not fire a toggle event at the 'details' element"),
+  t10 = async_test("Setting open=false to closed 'details' element should not fire a toggle event at the 'details' element"),
+
   details1 = document.getElementById('details1'),
   details2 = document.getElementById('details2'),
   details3 = document.getElementById('details3'),
   details4 = document.getElementById('details4'),
   details6 = document.getElementById('details6'),
+  details7 = document.getElementById('details7'),
+  details8 = document.getElementById('details8'),
+  details9 = document.getElementById('details9'),
+  details10 = document.getElementById('details10'),
   loop=false;
 
   function testEvent(evt) {
     assert_true(evt.isTrusted, "event is trusted");
     assert_false(evt.bubbles, "event doesn't bubble");
     assert_false(evt.cancelable, "event is not cancelable");
     assert_equals(Object.getPrototypeOf(evt), Event.prototype, "Prototype of toggle event is Event.prototype");
   }
@@ -85,9 +110,48 @@
     } else {
       loop = true;
     }
   });
   setTimeout(t6.step_func(function() {
     assert_true(loop);
     t6.done();
   }), 0);
+
+  details7.ontoggle = t7.step_func_done(function(evt) {
+    assert_true(details7.open);
+    testEvent(evt)
+  });
+  details7.setAttribute('open', ''); // opens details7
+
+  details8.ontoggle = t8.step_func_done(function(evt) {
+    assert_false(details8.open);
+    testEvent(evt)
+  });
+  details8.removeAttribute('open'); // closes details8
+
+  var toggleFiredOnDetails9 = false;
+  details9.ontoggle = t9.step_func_done(function(evt) {
+    if (toggleFiredOnDetails9) {
+      assert_unreached("toggle event fired twice on opened details element");
+    } else {
+      toggleFiredOnDetails9 = true;
+    }
+  });
+  // The toggle event should be fired once when declaring details9 with open
+  // attribute.
+  details9.open = true; // opens details9
+  setTimeout(t9.step_func(function() {
+    assert_true(details9.open);
+    assert_true(toggleFiredOnDetails9);
+    t9.done();
+  }), 0);
+
+  details10.ontoggle = t10.step_func_done(function(evt) {
+    assert_unreached("toggle event fired on closed details element");
+  });
+  details10.open = false; // closes details10
+  setTimeout(t10.step_func(function() {
+    assert_false(details10.open);
+    t10.done();
+  }), 0);
+
 </script>
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -410,12 +410,15 @@ NS_EVENT_MESSAGE_FIRST_LAST(eGamepadEven
 
 // input and beforeinput events.
 NS_EVENT_MESSAGE(eEditorInput)
 
 // selection events
 NS_EVENT_MESSAGE(eSelectStart)
 NS_EVENT_MESSAGE(eSelectionChange)
 
+// Details element events.
+NS_EVENT_MESSAGE(eToggle)
+
 #ifdef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST
 #undef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST
 #undef NS_EVENT_MESSAGE_FIRST_LAST
 #endif
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -174,16 +174,20 @@
   ERROR(NS_ERROR_UNKNOWN_PROTOCOL,                    FAILURE(18)),
   /* The content encoding of the source document was incorrect, for example
    * returning a plain HTML document advertised as Content-Encoding: gzip */
   ERROR(NS_ERROR_INVALID_CONTENT_ENCODING,            FAILURE(27)),
   /* A transport level corruption was found in the source document. for example
    * a document with a calculated checksum that does not match the Content-MD5
    * http header. */
   ERROR(NS_ERROR_CORRUPTED_CONTENT,                   FAILURE(29)),
+  /* A content signature verification failed for some reason. This can be either
+   * an actual verification error, or any other error that led to the fact that
+   * a content signature that was expected couldn't be verified. */
+  ERROR(NS_ERROR_INVALID_SIGNATURE,                   FAILURE(58)),
   /* While parsing for the first component of a header field using syntax as in
    * Content-Disposition or Content-Type, the first component was found to be
    * empty, such as in: Content-Disposition: ; filename=foo */
   ERROR(NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY,  FAILURE(34)),
   /* Returned from nsIChannel::asyncOpen when trying to open the channel again
    * (reopening is not supported). */
   ERROR(NS_ERROR_ALREADY_OPENED,                      FAILURE(73)),