Merge inbound to m-c. a=merge FIREFOX_AURORA_37_BASE
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 12 Jan 2015 15:26:50 -0500
changeset 223386 2c951493eef5b50b8085ef78ffe0d7902ff3d593
parent 223313 34c1ede59bebf0bf01cb614718939c135ddf0bbf (current diff)
parent 223385 332fa08016f81abeceb85cd1265099e891e16b6c (diff)
child 223387 c8b4096b0934f8e8a864878a83b16e4c949a7aa9
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone37.0a1
Merge inbound to m-c. a=merge
js/src/jit-test/tests/gc/nursery-finalize.js
--- a/build/upload.py
+++ b/build/upload.py
@@ -19,17 +19,18 @@
 #
 # All files to be uploaded should be passed as commandline arguments to this
 # script. The script takes one other parameter, --base-path, which you can use
 # to indicate that files should be uploaded including their paths relative
 # to the base path.
 
 import sys, os
 from optparse import OptionParser
-from subprocess import PIPE, Popen, check_call
+from subprocess import check_call, check_output
+import redo
 
 def RequireEnvironmentVariable(v):
     """Return the value of the environment variable named v, or print
     an error and exit if it's unset (or empty)."""
     if not v in os.environ or os.environ[v] == "":
         print "Error: required environment variable %s not set" % v
         sys.exit(1)
     return os.environ[v]
@@ -77,31 +78,35 @@ def AppendOptionalArgsToSSHCommandline(c
         cmdline.extend(["-o", "IdentityFile=%s" % ssh_key])
 
 def DoSSHCommand(command, user, host, port=None, ssh_key=None):
     """Execute command on user@host using ssh. Optionally use
     port and ssh_key, if provided."""
     cmdline = ["ssh"]
     AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key)
     cmdline.extend(["%s@%s" % (user, host), command])
-    cmd = Popen(cmdline, stdout=PIPE)
-    retcode = cmd.wait()
-    if retcode != 0:
-        raise Exception("Command %s returned non-zero exit code: %i" % \
-          (cmdline, retcode))
-    return cmd.stdout.read().strip()
+
+    with redo.retrying(check_output, sleeptime=10) as f:
+        output = f(cmdline).strip()
+        return output
+
+    raise Exception("Command %s returned non-zero exit code" % cmdline)
 
 def DoSCPFile(file, remote_path, user, host, port=None, ssh_key=None):
     """Upload file to user@host:remote_path using scp. Optionally use
     port and ssh_key, if provided."""
     cmdline = ["scp"]
     AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key)
     cmdline.extend([WindowsPathToMsysPath(file),
                     "%s@%s:%s" % (user, host, remote_path)])
-    check_call(cmdline)
+    with redo.retrying(check_call, sleeptime=10) as f:
+        f(cmdline)
+        return
+
+    raise Exception("Command %s returned non-zero exit code" % cmdline)
 
 def GetRemotePath(path, local_file, base_path):
     """Given a remote path to upload to, a full path to a local file, and an
     optional full path that is a base path of the local file, construct the
     full remote path to place the file in. If base_path is not None, include
     the relative path from base_path to file."""
     if base_path is None or not local_file.startswith(base_path):
         return path
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -19,8 +19,9 @@ mozilla.pth:dom/bindings/parser
 mozilla.pth:layout/tools/reftest
 moztreedocs.pth:tools/docs
 copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
 objdir:build
 gyp.pth:media/webrtc/trunk/tools/gyp/pylib
 pyasn1.pth:python/pyasn1
 bitstring.pth:python/bitstring
+redo.pth:python/redo
--- a/dom/base/test/chrome/file_bug990812-2.xul
+++ b/dom/base/test/chrome/file_bug990812-2.xul
@@ -43,17 +43,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       var window = promiseMessage("window", messageManager);
       var group = promiseMessage("group", getGroupMessageManager("test"));
 
       var browser = document.querySelector("browser");
       browser.messageManager.loadFrameScript(FRAME_SCRIPT, true);
 
       Promise.all([global, window, group]).then(function () {
         opener.setTimeout("next()");
-        window.close();
+        self.close();
       });
     }
 
   ]]></script>
 
   <browser messagemanagergroup="test" type="content-targetable" src="about:mozilla" />
 
 </window>
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -280,16 +280,28 @@ class DescriptorProvider:
     def getDescriptor(self, interfaceName):
         """
         Gets the appropriate descriptor for the given interface name given the
         context of the current descriptor. This selects the appropriate
         implementation for cases like workers.
         """
         return self.config.getDescriptor(interfaceName, self.workers)
 
+def methodReturnsJSObject(method):
+    assert method.isMethod()
+    if method.returnsPromise():
+        return True
+
+    for signature in method.signatures():
+        returnType = signature[0]
+        if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+            return True
+
+    return False
+
 class Descriptor(DescriptorProvider):
     """
     Represents a single descriptor for an interface. See Bindings.conf.
     """
     def __init__(self, config, interface, desc):
         DescriptorProvider.__init__(self, config, desc.get('workers', False))
         self.interface = interface
 
@@ -617,16 +629,21 @@ class Descriptor(DescriptorProvider):
                 (throws is not True and
                  ('Workers' not in throws or not self.workers) and
                  ('MainThread' not in throws or self.workers))):
                 attrs.append("infallible")
 
         name = member.identifier.name
         throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
         if member.isMethod():
+            # JSObject-returning [NewObject] methods must be fallible,
+            # since they have to (fallibly) allocate the new JSObject.
+            if (member.getExtendedAttribute("NewObject") and
+                methodReturnsJSObject(member)):
+                throws = True
             attrs = self.extendedAttributes['all'].get(name, [])
             maybeAppendInfallibleToAttrs(attrs, throws)
             return attrs
 
         assert member.isAttr()
         assert bool(getter) != bool(setter)
         key = 'getterOnly' if getter else 'setterOnly'
         attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
--- a/dom/devicestorage/test/test_app_permissions.html
+++ b/dom/devicestorage/test/test_app_permissions.html
@@ -123,17 +123,17 @@ function TestEnumerate(iframe, data) {
 
   request.onerror = function(e) {
     isnot(data.shouldPass, true, "onfailure was called for type " + data.type + " Error: " + e.target.error.name);
     is(e.target.error.name, "SecurityError", "onerror should fire a SecurityError");
     testComplete(iframe, data);
   };
 }
 
-var gTestUri = "https://example.com/tests/dom/devicestorage/test/test_app_permissions.html"
+var gTestUri = "https://example.com/chrome/dom/devicestorage/test/test_app_permissions.html"
 
 var gData = [
 
   // Get
   // Web applications with no permissions
   {
     type: 'pictures',
     shouldPass: false,
--- a/dom/gamepad/Gamepad.cpp
+++ b/dom/gamepad/Gamepad.cpp
@@ -1,14 +1,15 @@
 /* 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 "Gamepad.h"
 #include "nsAutoPtr.h"
+#include "nsPIDOMWindow.h"
 #include "nsTArray.h"
 #include "nsVariant.h"
 #include "mozilla/dom/GamepadBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Gamepad)
@@ -16,32 +17,45 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Gamepad
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Gamepad)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Gamepad, mParent, mButtons)
 
+void
+Gamepad::UpdateTimestamp()
+{
+  nsCOMPtr<nsPIDOMWindow> newWindow(do_QueryInterface(mParent));
+  if(newWindow) {
+    nsPerformance* perf = newWindow->GetPerformance();
+    if (perf) {
+      mTimestamp =  perf->GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now());
+    }
+  }
+}
+
 Gamepad::Gamepad(nsISupports* aParent,
                  const nsAString& aID, uint32_t aIndex,
                  GamepadMappingType aMapping,
                  uint32_t aNumButtons, uint32_t aNumAxes)
   : mParent(aParent),
     mID(aID),
     mIndex(aIndex),
     mMapping(aMapping),
     mConnected(true),
     mButtons(aNumButtons),
     mAxes(aNumAxes)
 {
   for (unsigned i = 0; i < aNumButtons; i++) {
     mButtons.InsertElementAt(i, new GamepadButton(mParent));
   }
   mAxes.InsertElementsAt(0, aNumAxes, 0.0f);
+  UpdateTimestamp();
 }
 
 void
 Gamepad::SetIndex(uint32_t aIndex)
 {
   mIndex = aIndex;
 }
 
@@ -52,26 +66,28 @@ Gamepad::SetConnected(bool aConnected)
 }
 
 void
 Gamepad::SetButton(uint32_t aButton, bool aPressed, double aValue)
 {
   MOZ_ASSERT(aButton < mButtons.Length());
   mButtons[aButton]->SetPressed(aPressed);
   mButtons[aButton]->SetValue(aValue);
+  UpdateTimestamp();
 }
 
 void
 Gamepad::SetAxis(uint32_t aAxis, double aValue)
 {
   MOZ_ASSERT(aAxis < mAxes.Length());
   if (mAxes[aAxis] != aValue) {
     mAxes[aAxis] = aValue;
     GamepadBinding::ClearCachedAxesValue(this);
   }
+  UpdateTimestamp();
 }
 
 void
 Gamepad::SyncState(Gamepad* aOther)
 {
   if (mButtons.Length() != aOther->mButtons.Length() ||
       mAxes.Length() != aOther->mAxes.Length()) {
     return;
@@ -85,16 +101,17 @@ Gamepad::SyncState(Gamepad* aOther)
   bool changed = false;
   for (uint32_t i = 0; i < mAxes.Length(); ++i) {
     changed = changed || (mAxes[i] != aOther->mAxes[i]);
     mAxes[i] = aOther->mAxes[i];
   }
   if (changed) {
     GamepadBinding::ClearCachedAxesValue(this);
   }
+  UpdateTimestamp();
 }
 
 already_AddRefed<Gamepad>
 Gamepad::Clone(nsISupports* aParent)
 {
   nsRefPtr<Gamepad> out =
     new Gamepad(aParent, mID, mIndex, mMapping,
                 mButtons.Length(), mAxes.Length());
--- a/dom/gamepad/Gamepad.h
+++ b/dom/gamepad/Gamepad.h
@@ -8,16 +8,17 @@
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/GamepadBinding.h"
 #include "mozilla/dom/GamepadButton.h"
 #include <stdint.h>
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
+#include "nsPerformance.h"
 
 namespace mozilla {
 namespace dom {
 
 // Per spec:
 // https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#remapping
 const int kStandardGamepadButtons = 17;
 const int kStandardGamepadAxes = 4;
@@ -60,16 +61,21 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   void GetId(nsAString& aID) const
   {
     aID = mID;
   }
 
+  DOMHighResTimeStamp Timestamp() const
+  {
+     return mTimestamp;
+  }
+
   GamepadMappingType Mapping()
   {
     return mMapping;
   }
 
   bool Connected() const
   {
     return mConnected;
@@ -87,29 +93,31 @@ public:
 
   void GetAxes(nsTArray<double>& aAxes) const
   {
     aAxes = mAxes;
   }
 
 private:
   virtual ~Gamepad() {}
+  void UpdateTimestamp();
 
 protected:
   nsCOMPtr<nsISupports> mParent;
   nsString mID;
   uint32_t mIndex;
 
   // The mapping in use.
   GamepadMappingType mMapping;
 
   // true if this gamepad is currently connected.
   bool mConnected;
 
   // Current state of buttons, axes.
   nsTArray<nsRefPtr<GamepadButton>> mButtons;
   nsTArray<double> mAxes;
+  DOMHighResTimeStamp mTimestamp;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_gamepad_Gamepad_h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4267,21 +4267,21 @@ HTMLMediaElement::VideoTracks()
 {
   if (!mVideoTrackList) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
     mVideoTrackList = new VideoTrackList(window, this);
   }
   return mVideoTrackList;
 }
 
-/* readonly attribute TextTrackList textTracks; */
+/* readonly attribute TextTrackList? textTracks; */
 TextTrackList*
-HTMLMediaElement::TextTracks()
+HTMLMediaElement::GetTextTracks()
 {
-  return GetOrCreateTextTrackManager()->TextTracks();
+  return GetOrCreateTextTrackManager()->GetTextTracks();
 }
 
 already_AddRefed<TextTrack>
 HTMLMediaElement::AddTextTrack(TextTrackKind aKind,
                                const nsAString& aLabel,
                                const nsAString& aLanguage)
 {
   return
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -574,17 +574,17 @@ public:
   }
 
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   AudioTrackList* AudioTracks();
 
   VideoTrackList* VideoTracks();
 
-  TextTrackList* TextTracks();
+  TextTrackList* GetTextTracks();
 
   already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage);
 
   void AddTextTrack(TextTrack* aTextTrack) {
     GetOrCreateTextTrackManager()->AddTextTrack(aTextTrack);
   }
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -148,23 +148,22 @@ HTMLTrackElement::CreateTextTrack()
 
   TextTrackKind kind;
   if (const nsAttrValue* value = GetParsedAttr(nsGkAtoms::kind)) {
     kind = static_cast<TextTrackKind>(value->GetEnumValue());
   } else {
     kind = TextTrackKind::Subtitles;
   }
 
-  bool hasHadScriptObject = true;
-  nsIScriptGlobalObject* scriptObject =
-    OwnerDoc()->GetScriptHandlingObject(hasHadScriptObject);
+  nsISupports* parentObject =
+    OwnerDoc()->GetParentObject();
 
-  NS_ENSURE_TRUE_VOID(scriptObject || !hasHadScriptObject);
+  NS_ENSURE_TRUE_VOID(parentObject);
 
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(scriptObject);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(parentObject);
   mTrack = new TextTrack(window, kind, label, srcLang,
                          TextTrackMode::Disabled,
                          TextTrackReadyState::NotLoaded,
                          TextTrackSource::Track);
   mTrack->SetTrackElement(this);
 
   if (mMediaParent) {
     mMediaParent->AddTextTrack(mTrack);
--- a/dom/html/TextTrackManager.cpp
+++ b/dom/html/TextTrackManager.cpp
@@ -86,23 +86,22 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(TextTrac
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextTrackManager)
 
 StaticRefPtr<nsIWebVTTParserWrapper> TextTrackManager::sParserWrapper;
 
 TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
   : mMediaElement(aMediaElement)
   , performedTrackSelection(false)
 {
-  bool hasHadScriptObject = true;
-  nsIScriptGlobalObject* scriptObject =
-    mMediaElement->OwnerDoc()->GetScriptHandlingObject(hasHadScriptObject);
+  nsISupports* parentObject =
+    mMediaElement->OwnerDoc()->GetParentObject();
 
-  NS_ENSURE_TRUE_VOID(scriptObject || !hasHadScriptObject);
+  NS_ENSURE_TRUE_VOID(parentObject);
 
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(scriptObject);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(parentObject);
   mNewCues = new TextTrackCueList(window);
   mTextTracks = new TextTrackList(window, this);
   mPendingTextTracks = new TextTrackList(window, this);
 
   if (!sParserWrapper) {
     nsCOMPtr<nsIWebVTTParserWrapper> parserWrapper =
       do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID);
     sParserWrapper = parserWrapper;
@@ -110,17 +109,17 @@ TextTrackManager::TextTrackManager(HTMLM
   }
 }
 
 TextTrackManager::~TextTrackManager()
 {
 }
 
 TextTrackList*
-TextTrackManager::TextTracks() const
+TextTrackManager::GetTextTracks() const
 {
   return mTextTracks;
 }
 
 already_AddRefed<TextTrack>
 TextTrackManager::AddTextTrack(TextTrackKind aKind, const nsAString& aLabel,
                                const nsAString& aLanguage,
                                TextTrackMode aMode,
--- a/dom/html/TextTrackManager.h
+++ b/dom/html/TextTrackManager.h
@@ -40,17 +40,17 @@ class TextTrackManager MOZ_FINAL : publi
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(TextTrackManager)
 
   NS_DECL_NSIDOMEVENTLISTENER
 
   explicit TextTrackManager(HTMLMediaElement* aMediaElement);
 
-  TextTrackList* TextTracks() const;
+  TextTrackList* GetTextTracks() const;
   already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage,
                                            TextTrackMode aMode,
                                            TextTrackReadyState aReadyState,
                                            TextTrackSource aTextTrackSource);
   void AddTextTrack(TextTrack* aTextTrack);
   void RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly);
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -526,16 +526,23 @@ bool MediaDecoderStateMachine::HaveEnoug
       GetDecodedAudioDuration() < aAmpleAudioUSecs) {
     return false;
   }
   if (!mAudioCaptured) {
     return true;
   }
 
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
+
+  // Since stream is initialized in SendStreamData() which is called when
+  // audio/video samples are received, we need to keep decoding to ensure
+  // the stream is initialized.
+  if (stream && !stream->mStreamInitialized) {
+    return false;
+  }
   if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) {
     if (!stream->mStream->HaveEnoughBuffered(kAudioTrack)) {
       return false;
     }
     stream->mStream->DispatchWhenNotEnoughBuffered(kAudioTrack,
         GetStateMachineThread(), GetWakeDecoderRunnable());
   }
 
@@ -546,16 +553,21 @@ bool MediaDecoderStateMachine::HaveEnoug
 {
   AssertCurrentThreadInMonitor();
 
   if (static_cast<uint32_t>(VideoQueue().GetSize()) < mAmpleVideoFrames * mPlaybackRate) {
     return false;
   }
 
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
+
+  if (stream && !stream->mStreamInitialized) {
+    return false;
+  }
+
   if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) {
     if (!stream->mStream->HaveEnoughBuffered(kVideoTrack)) {
       return false;
     }
     stream->mStream->DispatchWhenNotEnoughBuffered(kVideoTrack,
         GetStateMachineThread(), GetWakeDecoderRunnable());
   }
 
--- a/dom/tests/mochitest/gamepad/mochitest.ini
+++ b/dom/tests/mochitest/gamepad/mochitest.ini
@@ -1,15 +1,17 @@
 [DEFAULT]
 skip-if = e10s
 support-files =
   gamepad_frame.html
   gamepad_frame_state.html
   mock_gamepad.js
 
+[test_check_timestamp.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad_connect_events.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad_frame_state_sync.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad_hidden_frame.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/gamepad/test_check_timestamp.html
@@ -0,0 +1,51 @@
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Gamepad.timestamp</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript" src="mock_gamepad.js"></script>
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+var index = GamepadService.addGamepad("test gamepad", // id
+                                      SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
+                                      4, // buttons
+                                      2);// axes
+
+var timea=0;
+window.addEventListener("gamepadbuttondown", buttonpresshandler);
+
+var firstPress = true;
+GamepadService.newButtonEvent(index, 0, true);
+GamepadService.newButtonEvent(index, 0, true);
+
+function cleanup(){
+  SpecialPowers.executeSoon(function() {
+    GamepadService.removeGamepad(index);
+    SimpleTest.finish();
+  });
+}
+
+function buttonpresshandler(e) {
+  if(timea == 0){
+    timea = e.gamepad.timestamp;
+  }
+  else{
+    ok(timea <= e.gamepad.timestamp);
+  }
+  GamepadService.newButtonEvent(index, 0, false);
+  if (!firstPress) {
+    SpecialPowers.executeSoon(cleanup);
+  }
+  else {
+    firstPress = false;
+  }
+}
+
+</script>
+</body>
+</html>
--- a/dom/tests/mochitest/gamepad/test_gamepad.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad.html
@@ -15,16 +15,17 @@ window.addEventListener("gamepadconnecte
 // Add a gamepad
 var index = GamepadService.addGamepad("test gamepad", // id
                                       SpecialPowers.Ci.nsIGamepadServiceTest.STANDARD_MAPPING,
                                       4, // buttons
                                       2);// axes
 // Press a button
 GamepadService.newButtonEvent(index, 0, true);
 function connecthandler(e) {
+  ok(e.gamepad.timestamp <= performance.now());
   is(e.gamepad.id, "test gamepad", "correct gamepad name");
   is(e.gamepad.mapping, "standard", "standard mapping");
   is(e.gamepad.buttons.length, 4, "correct number of buttons");
   is(e.gamepad.axes.length, 2, "correct number of axes");
   SimpleTest.executeSoon(function() {
     GamepadService.removeGamepad(index);
     SimpleTest.finish();
   });
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -335,19 +335,19 @@ var interfaceNamesInGlobalScope =
     "DeviceLightEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DeviceMotionEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DeviceOrientationEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DeviceProximityEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "DeviceStorage",
+    { name: "DeviceStorage", pref: "device.storage.enabled" },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "DeviceStorageChangeEvent",
+    { name: "DeviceStorageChangeEvent", pref: "device.storage.enabled" },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Document",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DocumentFragment",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DocumentType",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DOMConstructor", xbl: true},
--- a/dom/webidl/BluetoothAdapter2.webidl
+++ b/dom/webidl/BluetoothAdapter2.webidl
@@ -67,34 +67,34 @@ interface BluetoothAdapter : EventTarget
 
   /**
    * Enable/Disable a local bluetooth adapter by asynchronus methods and return
    * its result through a Promise.
    *
    * Several onattributechanged events would be triggered during processing the
    * request, and the last one indicates adapter.state becomes enabled/disabled.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> enable();
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> disable();
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> setName(DOMString aName);
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> setDiscoverable(boolean aDiscoverable);
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<BluetoothDiscoveryHandle> startDiscovery();
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> stopDiscovery();
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> pair(DOMString deviceAddress);
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> unpair(DOMString deviceAddress);
 
   sequence<BluetoothDevice> getPairedDevices();
 
   [NewObject, Throws, AvailableIn=CertifiedApps]
   DOMRequest getConnectedDevices(unsigned short serviceUuid);
 
   /**
--- a/dom/webidl/BluetoothDevice2.webidl
+++ b/dom/webidl/BluetoothDevice2.webidl
@@ -21,17 +21,17 @@ interface BluetoothDevice : EventTarget
 
   /**
    * Fetch the up-to-date UUID list of each bluetooth service that the device
    * provides and refresh the cache value of attribute uuids if it is updated.
    *
    * If the operation succeeds, the promise will be resolved with up-to-date
    * UUID list which is identical to attribute uuids.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<sequence<DOMString>>              fetchUuids();
 };
 
 enum BluetoothDeviceType
 {
   "unknown",
   "classic",
   "le",
--- a/dom/webidl/BluetoothPairingHandle.webidl
+++ b/dom/webidl/BluetoothPairingHandle.webidl
@@ -8,13 +8,13 @@ interface BluetoothPairingHandle
 {
   /**
    * A 6-digit string ranging from decimal 000000 to 999999.
    * This attribute is an empty string for enterpincodereq and
    * pairingconsentreq.
    */
   readonly attribute DOMString passkey;
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> setPinCode(DOMString aPinCode);
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> setPairingConfirmation(boolean aConfirm);
 };
--- a/dom/webidl/DOMRequest.webidl
+++ b/dom/webidl/DOMRequest.webidl
@@ -14,14 +14,14 @@ interface DOMRequestShared {
 
   attribute EventHandler onsuccess;
   attribute EventHandler onerror;
 };
 
 interface DOMRequest : EventTarget {
   // The [TreatNonCallableAsNull] annotation is required since then() should do
   // nothing instead of throwing errors when non-callable arguments are passed.
-  [NewObject, Throws]
+  [NewObject]
   Promise<any> then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
                     [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
 };
 
 DOMRequest implements DOMRequestShared;
--- a/dom/webidl/DeviceStorage.webidl
+++ b/dom/webidl/DeviceStorage.webidl
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 dictionary DeviceStorageEnumerationParameters {
   Date since;
 };
 
+[Pref="device.storage.enabled"]
 interface DeviceStorage : EventTarget {
   attribute EventHandler onchange;
 
   [Throws]
   DOMRequest? add(Blob? aBlob);
   [Throws]
   DOMRequest? addNamed(Blob? aBlob, DOMString aName);
 
@@ -80,12 +81,12 @@ interface DeviceStorage : EventTarget {
 
   // Determines if this storage area is the one which will be used by default
   // for storing new files.
   readonly attribute boolean default;
 
   // Indicates if the storage area denoted by storageName is removable
   readonly attribute boolean isRemovable;
 
-  [NewObject, Throws]
+  [NewObject]
   // XXXbz what type does this really return?
   Promise<any> getRoot();
 };
--- a/dom/webidl/DeviceStorageChangeEvent.webidl
+++ b/dom/webidl/DeviceStorageChangeEvent.webidl
@@ -1,15 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional DeviceStorageChangeEventInit eventInitDict)]
+[Constructor(DOMString type, optional DeviceStorageChangeEventInit eventInitDict),
+ Pref="device.storage.enabled"]
 interface DeviceStorageChangeEvent : Event
 {
   readonly attribute DOMString? path;
   readonly attribute DOMString? reason;
 };
 
 dictionary DeviceStorageChangeEventInit : EventInit
 {
--- a/dom/webidl/Directory.webidl
+++ b/dom/webidl/Directory.webidl
@@ -30,68 +30,68 @@ interface Directory {
    * a new file to replace the existing one;
    * If 'ifExists' is 'replace', the path already exists, but is a directory,
    * createFile must fail.
    * Otherwise, if no other error occurs, createFile will create a new file.
    * The 'data' property contains the new file's content.
    * @return If succeeds, the promise is resolved with the new created
    * File object. Otherwise, rejected with a DOM error.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<File> createFile(DOMString path, optional CreateFileOptions options);
 
   /*
    * Creates a descendent directory. This method will create any intermediate
    * directories specified by the path segments.
    *
    * @param path The relative path of the new directory to current directory.
    * If path exists, createDirectory must fail.
    * @return If succeeds, the promise is resolved with the new created
    * Directory object. Otherwise, rejected with a DOM error.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<Directory> createDirectory(DOMString path);
 
   /*
    * Gets a descendent file or directory with the given path.
    *
    * @param path The descendent entry's relative path to current directory.
    * @return If the path exists and no error occurs, the promise is resolved
    * with a File or Directory object, depending on the entry's type. Otherwise,
    * rejected with a DOM error.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<(File or Directory)> get(DOMString path);
 
   /*
    * Deletes a file or an empty directory. The target must be a descendent of
    * current directory.
    * @param path If a DOM string is passed, it is the relative path of the
    * target. Otherwise, the File or Directory object of the target should be
    * passed.
    * @return If the target is a non-empty directory, or if deleting the target
    * fails, the promise is rejected with a DOM error. If the target did not
    * exist, the promise is resolved with boolean false. If the target did exist
    * and was successfully deleted, the promise is resolved with boolean true.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<boolean> remove((DOMString or File or Directory) path);
 
   /*
    * Deletes a file or a directory recursively. The target should be a
    * descendent of current directory.
    * @param path If a DOM string is passed, it is the relative path of the
    * target. Otherwise, the File or Directory object of the target should be
    * passed.
    * @return If the target exists, but deleting the target fails, the promise is
    * rejected with a DOM error. If the target did not exist, the promise is
    * resolved with boolean false. If the target did exist and was successfully
    * deleted, the promise is resolved with boolean true.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<boolean> removeDeep((DOMString or File or Directory) path);
 };
 
 enum CreateIfExistsMode { "replace", "fail" };
 
 dictionary CreateFileOptions {
   CreateIfExistsMode ifExists = "fail";
   (DOMString or Blob or ArrayBuffer or ArrayBufferView) data;
--- a/dom/webidl/Gamepad.webidl
+++ b/dom/webidl/Gamepad.webidl
@@ -46,9 +46,14 @@ interface Gamepad {
   readonly attribute sequence<GamepadButton> buttons;
 
   /**
    * The current position of all axes on the device, an
    * array of doubles.
    */
   [Pure, Cached, Frozen]
   readonly attribute sequence<double> axes;
+
+  /**
+   * Timestamp from when the data of this device was last updated.
+   */
+  readonly attribute DOMHighResTimeStamp timestamp;
 };
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -86,17 +86,17 @@ interface HTMLMediaElement : HTMLElement
 
   // TODO: Bug 847379
   // tracks
   [Pref="media.track.enabled"]
   readonly attribute AudioTrackList audioTracks;
   [Pref="media.track.enabled"]
   readonly attribute VideoTrackList videoTracks;
   [Pref="media.webvtt.enabled"]
-  readonly attribute TextTrackList textTracks;
+  readonly attribute TextTrackList? textTracks;
   [Pref="media.webvtt.enabled"]
   TextTrack addTextTrack(TextTrackKind kind,
                          optional DOMString label = "",
                          optional DOMString language = "");
 };
 
 // Mozilla extensions:
 partial interface HTMLMediaElement {
@@ -146,17 +146,17 @@ enum MediaWaitingFor {
 
 #ifdef MOZ_EME
 // Encrypted Media Extensions
 partial interface HTMLMediaElement {
   [Pref="media.eme.enabled"]
   readonly attribute MediaKeys? mediaKeys;
 
   // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
-  [Pref="media.eme.enabled", Throws, NewObject]
+  [Pref="media.eme.enabled", NewObject]
   Promise<void> setMediaKeys(MediaKeys? mediaKeys);
 
   [Pref="media.eme.enabled"]
   attribute EventHandler onencrypted;
 
   [Pref="media.eme.enabled"]
   readonly attribute MediaWaitingFor waitingFor;
 };
--- a/dom/webidl/MediaKeySession.webidl
+++ b/dom/webidl/MediaKeySession.webidl
@@ -18,27 +18,27 @@ interface MediaKeySession : EventTarget 
   // session properties
   readonly attribute DOMString keySystem;
   readonly attribute DOMString sessionId;
 
   readonly attribute unrestricted double expiration;
 
   readonly attribute Promise<void> closed;
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> generateRequest(DOMString initDataType, (ArrayBufferView or ArrayBuffer) initData);
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<boolean> load(DOMString sessionId);
 
   // session operations
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> update((ArrayBufferView or ArrayBuffer) response);
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> close();
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> remove();
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<sequence<ArrayBuffer>> getUsableKeyIds();
 };
--- a/dom/webidl/MediaKeySystemAccess.webidl
+++ b/dom/webidl/MediaKeySystemAccess.webidl
@@ -24,11 +24,11 @@ dictionary MediaKeySystemOptions {
   DOMString            videoCapability = "";
   MediaKeysRequirement uniqueidentifier = "optional";
   MediaKeysRequirement stateful = "optional";
 };
 
 [Pref="media.eme.enabled"]
 interface MediaKeySystemAccess {
   readonly    attribute DOMString keySystem;
-  [NewObject, Throws]
+  [NewObject]
   Promise<MediaKeys> createMediaKeys();
 };
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -15,11 +15,11 @@ enum SessionType { "temporary", "persist
 
 [Pref="media.eme.enabled"]
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional SessionType sessionType = "temporary");
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
 };
--- a/dom/webidl/MozIcc.webidl
+++ b/dom/webidl/MozIcc.webidl
@@ -346,11 +346,11 @@ interface MozIcc : EventTarget
    *
    * @param service
    *        Identifies the service type.
    *
    * @return a Promise
    *         If succeeds, the promise is resolved with boolean indicating the
    *         availability of the service. Otherwise, rejected with a DOMError.
    */
-  [NewObject, Throws]
+  [NewObject]
   Promise<boolean> getServiceState(IccService service);
 };
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -127,17 +127,17 @@ interface NavigatorBattery {
     readonly attribute BatteryManager? battery;
 };
 Navigator implements NavigatorBattery;
 
 // https://wiki.mozilla.org/WebAPI/DataStore
 [NoInterfaceObject,
  Exposed=(Window,Worker)]
 interface NavigatorDataStore {
-    [Throws, NewObject, Func="Navigator::HasDataStoreSupport"]
+    [NewObject, Func="Navigator::HasDataStoreSupport"]
     Promise<sequence<DataStore>> getDataStores(DOMString name,
                                                optional DOMString? owner = null);
 };
 Navigator implements NavigatorDataStore;
 
 // http://www.w3.org/TR/vibration/#vibration-interface
 partial interface Navigator {
     // We don't support sequences in unions yet
@@ -168,17 +168,17 @@ dictionary MobileIdOptions {
 };
 
 [NoInterfaceObject]
 interface NavigatorMobileId {
     // Ideally we would use [CheckPermissions] here, but the "mobileid"
     // permission is set to PROMPT_ACTION and [CheckPermissions] only checks
     // for ALLOW_ACTION.
     // XXXbz what is this promise resolved with?
-    [Throws, NewObject, Func="Navigator::HasMobileIdSupport"]
+    [NewObject, Func="Navigator::HasMobileIdSupport"]
     Promise<any> getMobileIdAssertion(optional MobileIdOptions options);
 };
 Navigator implements NavigatorMobileId;
 #endif // MOZ_B2G
 
 // nsIDOMNavigator
 partial interface Navigator {
   [Throws]
@@ -400,14 +400,14 @@ partial interface Navigator {
 
 partial interface Navigator {
   [Pref="dom.tv.enabled", CheckPermissions="tv", Func="Navigator::HasTVSupport"]
   readonly attribute TVManager? tv;
 };
 
 #ifdef MOZ_EME
 partial interface Navigator {
-  [Pref="media.eme.enabled", Throws, NewObject]
+  [Pref="media.eme.enabled", NewObject]
   Promise<MediaKeySystemAccess>
   requestMediaKeySystemAccess(DOMString keySystem,
                               optional sequence<MediaKeySystemOptions> supportedConfigurations);
 };
 #endif
--- a/dom/webidl/Promise.webidl
+++ b/dom/webidl/Promise.webidl
@@ -22,28 +22,28 @@ callback AnyCallback = any (any value);
 interface _Promise {
   // TODO bug 875289 - static Promise fulfill(any value);
 
   // Disable the static methods when the interface object is supposed to be
   // disabled, just in case some code decides to walk over to .constructor from
   // the proto of a promise object or someone screws up and manages to create a
   // Promise object in this scope without having resolved the interface object
   // first.
-  [NewObject, Throws]
+  [NewObject]
   static Promise<any> resolve(optional any value);
-  [NewObject, Throws]
+  [NewObject]
   static Promise<void> reject(optional any value);
 
   // The [TreatNonCallableAsNull] annotation is required since then() should do
   // nothing instead of throwing errors when non-callable arguments are passed.
-  [NewObject, Throws]
+  [NewObject]
   Promise<any> then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
                     [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<any> catch([TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
 
-  [NewObject, Throws]
+  [NewObject]
   static Promise<any> all(sequence<any> iterable);
 
-  [NewObject, Throws]
+  [NewObject]
   static Promise<any> race(sequence<any> iterable);
 };
--- a/dom/webidl/TelephonyCall.webidl
+++ b/dom/webidl/TelephonyCall.webidl
@@ -28,23 +28,23 @@ interface TelephonyCall : EventTarget {
 
   // Indicate whether the call can be added into TelephonyCallGroup.
   readonly attribute boolean mergeable;
 
   readonly attribute DOMError? error;
 
   readonly attribute TelephonyCallGroup? group;
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> answer();
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> hangUp();
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> hold();
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> resume();
 
   attribute EventHandler onstatechange;
   attribute EventHandler ondialing;
   attribute EventHandler onalerting;
   attribute EventHandler onconnecting;
   attribute EventHandler onconnected;
   attribute EventHandler ondisconnecting;
--- a/dom/webidl/TelephonyCallGroup.webidl
+++ b/dom/webidl/TelephonyCallGroup.webidl
@@ -12,17 +12,17 @@ interface TelephonyCallGroup : EventTarg
   void add(TelephonyCall call);
 
   [Throws]
   void add(TelephonyCall call, TelephonyCall secondCall);
 
   [Throws]
   void remove(TelephonyCall call);
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> hangUp();
 
   [Throws]
   void hold();
 
   [Throws]
   void resume();
 
--- a/dom/webidl/TextEncoder.webidl
+++ b/dom/webidl/TextEncoder.webidl
@@ -10,11 +10,11 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 [Constructor(optional DOMString utfLabel = "utf-8"),
  Exposed=(Window,Worker,System)]
 interface TextEncoder {
   [Constant]
   readonly attribute DOMString encoding;
-  [Throws, NewObject]
+  [NewObject]
   Uint8Array encode(optional USVString input = "");
 };
--- a/dom/webidl/USSDSession.webidl
+++ b/dom/webidl/USSDSession.webidl
@@ -4,14 +4,14 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [Pref="dom.telephony.enabled",
  CheckPermissions="telephony",
  AvailableIn="CertifiedApps",
  Constructor(unsigned long serviceId)]
 interface USSDSession {
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> send(DOMString ussd);
 
-  [NewObject, Throws]
+  [NewObject]
   Promise<void> cancel();
 };
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -15,16 +15,18 @@ using namespace mozilla::gfx;
 gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
                                gfxFontEntry *aFontEntry,
                                const gfxFontStyle *aFontStyle)
     : gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
       mSpaceGlyph(0),
       mHasMetrics(false)
 {
     cairo_scaled_font_reference(mScaledFont);
+    gfxFT2LockedFace face(this);
+    mFUnitsConvFactor = face.XScale();
 }
 
 gfxFT2FontBase::~gfxFT2FontBase()
 {
     cairo_scaled_font_destroy(mScaledFont);
 }
 
 uint32_t
@@ -112,17 +114,16 @@ gfxFT2FontBase::GetHorizontalMetrics()
     if (mHasMetrics)
         return mMetrics;
 
     if (MOZ_UNLIKELY(GetStyle()->size <= 0.0)) {
         new(&mMetrics) gfxFont::Metrics(); // zero initialize
         mSpaceGlyph = 0;
     } else {
         gfxFT2LockedFace face(this);
-        mFUnitsConvFactor = face.XScale();
         face.GetMetrics(&mMetrics, &mSpaceGlyph);
     }
 
     SanitizeMetrics(&mMetrics, false);
 
 #if 0
     //    printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
     //    printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3156,17 +3156,17 @@ gfxFont::InitMetricsFromSfntTables(Metri
 
     if (mFUnitsConvFactor == 0.0) {
         // If the conversion factor from FUnits is not yet set,
         // get the unitsPerEm from the 'head' table via the font entry
         uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
         if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
             return false;
         }
-        mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
+        mFUnitsConvFactor = GetAdjustedSize() / unitsPerEm;
     }
 
     // 'hhea' table is required to get vertical extents
     gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
     if (!hheaTable) {
         return false; // no 'hhea' table -> not an sfnt
     }
     const MetricsHeader* hhea =
@@ -3449,17 +3449,17 @@ gfxFont::CreateVerticalMetrics()
         const MetricsHeader* vhea =
             reinterpret_cast<const MetricsHeader*>
                 (hb_blob_get_data(vheaTable, &len));
         if (len >= sizeof(MetricsHeader)) {
             SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
             // Redistribute space between ascent/descent because we want a
             // centered vertical baseline by default.
             gfxFloat halfExtent = 0.5 * gfxFloat(mFUnitsConvFactor) *
-                (int16_t(vhea->ascender) - int16_t(vhea->descender));
+                (int16_t(vhea->ascender) + std::abs(int16_t(vhea->descender)));
             metrics->maxAscent = halfExtent;
             metrics->maxDescent = halfExtent;
             SET_SIGNED(externalLeading, vhea->lineGap);
         }
     }
 
     // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
     // font of some kind (Type1, bitmap, vector, ...), so fall back to using
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -337,16 +337,17 @@ nsICODecoder::WriteInternal(const char* 
     aCount -= toCopy;
     aBuffer += toCopy;
 
     mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes,
                      PNGSIGNATURESIZE);
     if (mIsPNG) {
       mContainedDecoder = new nsPNGDecoder(mImage);
       mContainedDecoder->SetSizeDecode(IsSizeDecode());
+      mContainedDecoder->SetSendPartialInvalidations(mSendPartialInvalidations);
       mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
                                            mColormap, mColormapSize,
                                            Move(mRefForContainedDecoder));
       if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE)) {
         return;
       }
     }
   }
@@ -415,16 +416,17 @@ nsICODecoder::WriteInternal(const char* 
 
     // Init the bitmap decoder which will do most of the work for us
     // It will do everything except the AND mask which isn't present in bitmaps
     // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder
     nsBMPDecoder* bmpDecoder = new nsBMPDecoder(mImage);
     mContainedDecoder = bmpDecoder;
     bmpDecoder->SetUseAlphaData(true);
     mContainedDecoder->SetSizeDecode(IsSizeDecode());
+    mContainedDecoder->SetSendPartialInvalidations(mSendPartialInvalidations);
     mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
                                          mColormap, mColormapSize,
                                          Move(mRefForContainedDecoder));
 
     // The ICO format when containing a BMP does not include the 14 byte
     // bitmap file header. To use the code of the BMP decoder we need to
     // generate this header ourselves and feed it to the BMP decoder.
     int8_t bfhBuffer[BMPFILEHEADERSIZE];
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -23,16 +23,17 @@ namespace image {
 Decoder::Decoder(RasterImage &aImage)
   : mImage(aImage)
   , mProgress(NoProgress)
   , mImageData(nullptr)
   , mColormap(nullptr)
   , mChunkCount(0)
   , mDecodeFlags(0)
   , mBytesDecoded(0)
+  , mSendPartialInvalidations(false)
   , mDecodeDone(false)
   , mDataError(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mNeedsNewFrame(false)
   , mNeedsToFlushData(false)
   , mInitialized(false)
   , mSizeDecode(false)
@@ -362,23 +363,23 @@ Decoder::InternalAddFrame(uint32_t aFram
   }
 
   RawAccessFrameRef ref = frame->RawAccessRef();
   if (!ref) {
     frame->Abort();
     return RawAccessFrameRef();
   }
 
-  bool succeeded =
+  InsertOutcome outcome =
     SurfaceCache::Insert(frame, ImageKey(&mImage),
                          RasterSurfaceKey(imageSize.ToIntSize(),
                                           aDecodeFlags,
                                           aFrameNum),
                          Lifetime::Persistent);
-  if (!succeeded) {
+  if (outcome != InsertOutcome::SUCCESS) {
     ref->Abort();
     return RawAccessFrameRef();
   }
 
   nsIntRect refreshArea;
 
   if (aFrameNum == 1) {
     MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
@@ -482,28 +483,37 @@ Decoder::PostFrameStop(Opacity aFrameOpa
   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
 
   // Update our state
   mInFrame = false;
 
   mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
 
   mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
+
+  // If we're not sending partial invalidations, then we send an invalidation
+  // here when the first frame is complete.
+  if (!mSendPartialInvalidations && !mIsAnimated) {
+    mInvalidRect.UnionRect(mInvalidRect, mCurrentFrame->GetRect());
+  }
 }
 
 void
 Decoder::PostInvalidation(nsIntRect& aRect)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
   NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
 
-  // Account for the new region
-  mInvalidRect.UnionRect(mInvalidRect, aRect);
-  mCurrentFrame->ImageUpdated(aRect);
+  // Record this invalidation, unless we're not sending partial invalidations
+  // or we're past the first frame.
+  if (mSendPartialInvalidations && !mIsAnimated) {
+    mInvalidRect.UnionRect(mInvalidRect, aRect);
+    mCurrentFrame->ImageUpdated(aRect);
+  }
 }
 
 void
 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
 {
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
   NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -110,16 +110,34 @@ public:
   // must be enabled by SetSizeDecode() _before_calling Init().
   bool IsSizeDecode() { return mSizeDecode; }
   void SetSizeDecode(bool aSizeDecode)
   {
     NS_ABORT_IF_FALSE(!mInitialized, "Can't set size decode after Init()!");
     mSizeDecode = aSizeDecode;
   }
 
+  /**
+   * Set whether should send partial invalidations.
+   *
+   * If @aSend is true, we'll send partial invalidations when decoding the first
+   * frame of the image, so image notifications observers will be able to
+   * gradually draw in the image as it downloads.
+   *
+   * If @aSend is false (the default), we'll only send an invalidation when we
+   * complete the first frame.
+   *
+   * This must be called before Init() is called.
+   */
+  void SetSendPartialInvalidations(bool aSend)
+  {
+    MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
+    mSendPartialInvalidations = aSend;
+  }
+
   size_t BytesDecoded() const { return mBytesDecoded; }
 
   // The amount of time we've spent inside Write() so far for this decoder.
   TimeDuration DecodeTime() const { return mDecodeTime; }
 
   // The number of times Write() has been called so far for this decoder.
   uint32_t ChunkCount() const { return mChunkCount; }
 
@@ -306,16 +324,17 @@ protected:
   uint32_t mColormapSize;
 
   // Telemetry data for this decoder.
   TimeDuration mDecodeTime;
   uint32_t mChunkCount;
 
   uint32_t mDecodeFlags;
   size_t mBytesDecoded;
+  bool mSendPartialInvalidations;
   bool mDecodeDone;
   bool mDataError;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
   nsresult mFailCode;
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1493,16 +1493,17 @@ RasterImage::InitDecoder(bool aDoSizeDec
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
     mDecoder->SetSize(mSize, mOrientation);
     mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
                            SurfaceFormat::B8G8R8A8);
     mDecoder->AllocateFrame();
   }
+  mDecoder->SetSendPartialInvalidations(!mHasBeenDecoded);
   mDecoder->Init();
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
   if (!aDoSizeDecode) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
 
@@ -2395,26 +2396,16 @@ RasterImage::FinishedSomeDecoding(Shutdo
       }
 
       // If there were any final changes, grab them.
       invalidRect.Union(decoder->TakeInvalidRect());
       progress |= decoder->TakeProgress();
     }
   }
 
-  if (GetCurrentFrameIndex() > 0) {
-    // Don't send invalidations for animated frames after the first; let
-    // RequestRefresh take care of that.
-    invalidRect = nsIntRect();
-  }
-  if (mHasBeenDecoded && !invalidRect.IsEmpty()) {
-    // Don't send partial invalidations if we've been decoded before.
-    invalidRect = mDecoded ? GetFirstFrameRect()
-                           : nsIntRect();
-  }
   if (!invalidRect.IsEmpty() && wasDefaultFlags) {
     // Update our image container since we're invalidating.
     UpdateImageContainer();
   }
 
   if (mNotifying) {
     // Accumulate the progress changes. We don't permit recursive notifications
     // because they cause subtle concurrency bugs, so we'll delay sending out
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -299,29 +299,32 @@ private:
     UnregisterWeakMemoryReporter(this);
   }
 
 public:
   void InitMemoryReporter() { RegisterWeakMemoryReporter(this); }
 
   Mutex& GetMutex() { return mMutex; }
 
-  bool Insert(imgFrame*         aSurface,
-              const Cost        aCost,
-              const ImageKey    aImageKey,
-              const SurfaceKey& aSurfaceKey,
-              Lifetime          aLifetime)
+  InsertOutcome Insert(imgFrame*         aSurface,
+                       const Cost        aCost,
+                       const ImageKey    aImageKey,
+                       const SurfaceKey& aSurfaceKey,
+                       Lifetime          aLifetime)
   {
-    MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey),
-               "Inserting a duplicate surface into the SurfaceCache");
+    // If this is a duplicate surface, refuse to replace the original.
+    if (MOZ_UNLIKELY(Lookup(aImageKey, aSurfaceKey))) {
+      return InsertOutcome::FAILURE_ALREADY_PRESENT;
+    }
 
     // If this is bigger than we can hold after discarding everything we can,
     // refuse to cache it.
-    if (!CanHoldAfterDiscarding(aCost))
-      return false;
+    if (MOZ_UNLIKELY(!CanHoldAfterDiscarding(aCost))) {
+      return InsertOutcome::FAILURE;
+    }
 
     // Remove elements in order of cost until we can fit this in the cache. Note
     // that locked surfaces aren't in mCosts, so we never remove them here.
     while (aCost > mAvailableCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and it still won't fit");
       Remove(mCosts.LastElement().GetSurface());
     }
 
@@ -336,26 +339,26 @@ public:
     nsRefPtr<CachedSurface> surface =
       new CachedSurface(aSurface, aCost, aImageKey, aSurfaceKey, aLifetime);
 
     // We require that locking succeed if the image is locked and the surface is
     // persistent; the caller may need to know this to handle errors correctly.
     if (cache->IsLocked() && aLifetime == Lifetime::Persistent) {
       surface->SetLocked(true);
       if (!surface->IsLocked()) {
-        return false;
+        return InsertOutcome::FAILURE;
       }
     }
 
     // Insert.
     MOZ_ASSERT(aCost <= mAvailableCost, "Inserting despite too large a cost");
     cache->Insert(aSurfaceKey, surface);
     StartTracking(surface);
 
-    return true;
+    return InsertOutcome::SUCCESS;
   }
 
   void Remove(CachedSurface* aSurface)
   {
     MOZ_ASSERT(aSurface, "Should have a surface");
     ImageKey imageKey = aSurface->GetImageKey();
 
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
@@ -790,24 +793,24 @@ SurfaceCache::Lookup(const ImageKey    a
   if (!sInstance) {
     return DrawableFrameRef();
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
   return sInstance->Lookup(aImageKey, aSurfaceKey);
 }
 
-/* static */ bool
+/* static */ InsertOutcome
 SurfaceCache::Insert(imgFrame*         aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey,
                      Lifetime          aLifetime)
 {
   if (!sInstance) {
-    return false;
+    return InsertOutcome::FAILURE;
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
   Cost cost = ComputeCost(aSurfaceKey.Size());
   return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey, aLifetime);
 }
 
 /* static */ bool
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -109,16 +109,22 @@ VectorSurfaceKey(const gfx::IntSize& aSi
   return SurfaceKey(aSize, aSVGContext, aAnimationTime, 0);
 }
 
 MOZ_BEGIN_ENUM_CLASS(Lifetime, uint8_t)
   Transient,
   Persistent
 MOZ_END_ENUM_CLASS(Lifetime)
 
+MOZ_BEGIN_ENUM_CLASS(InsertOutcome, uint8_t)
+  SUCCESS,                 // Success (but see Insert documentation).
+  FAILURE,                 // Couldn't insert (e.g., for capacity reasons).
+  FAILURE_ALREADY_PRESENT  // A surface with the same key is already present.
+MOZ_END_ENUM_CLASS(InsertOutcome)
+
 /**
  * SurfaceCache is an imagelib-global service that allows caching of temporary
  * surfaces. Surfaces normally expire from the cache automatically if they go
  * too long without being accessed.
  *
  * SurfaceCache does not hold surfaces directly; instead, it holds imgFrame
  * objects, which hold surfaces but also layer on additional features specific
  * to imagelib's needs like animation, padding support, and transparent support
@@ -168,53 +174,57 @@ struct SurfaceCache
    *
    * @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
    *         or an empty DrawableFrameRef if not found.
    */
   static DrawableFrameRef Lookup(const ImageKey    aImageKey,
                                  const SurfaceKey& aSurfaceKey);
 
   /**
-   * Insert a surface into the cache. It is an error to call this function
-   * without first calling Lookup to verify that the surface is not already in
-   * the cache.
+   * Insert a surface into the cache. If a surface with the same ImageKey and
+   * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT.
    *
    * Each surface in the cache has a lifetime, either Transient or Persistent.
    * Transient surfaces can expire from the cache at any time. Persistent
    * surfaces can ordinarily also expire from the cache at any time, but if the
    * image they're associated with is locked, then these surfaces will never
    * expire. This means that surfaces which cannot be rematerialized should be
    * inserted with a persistent lifetime *after* the image is locked with
    * LockImage(); if you use the other order, the surfaces might expire before
    * LockImage() gets called.
    *
    * If a surface cannot be rematerialized, it may be important to know whether
-   * it was inserted into the cache successfully. Insert() returns false if it
+   * it was inserted into the cache successfully. Insert() returns FAILURE if it
    * failed to insert the surface, which could happen because of capacity
    * reasons, or because it was already freed by the OS. If you aren't inserting
    * a surface with persistent lifetime, or if the surface isn't associated with
-   * a locked image, the return value is useless: the surface might expire
-   * immediately after being inserted, even though Insert() returned true. Thus,
-   * most callers do not need to check the return value.
+   * a locked image, checking for SUCCESS or FAILURE is useless: the surface
+   * might expire immediately after being inserted, even though Insert()
+   * returned SUCCESS. Thus, many callers do not need to check the result of
+   * Insert() at all.
    *
    * @param aTarget      The new surface (wrapped in an imgFrame) to insert into
    *                     the cache.
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
    * @param aLifetime    Whether this is a transient surface that can always be
    *                     allowed to expire, or a persistent surface that
    *                     shouldn't expire if the image is locked.
-   * @return false if the surface could not be inserted. Only check this if
-   *         inserting a persistent surface associated with a locked image (see
-   *         above for more information).
+   * @return SUCCESS if the surface was inserted successfully. (But see above
+   *           for more information about when you should check this.)
+   *         FAILURE if the surface could not be inserted, e.g. for capacity
+   *           reasons. (But see above for more information about when you
+   *           should check this.)
+   *         FAILURE_ALREADY_PRESENT if a surface with the same ImageKey and
+   *           SurfaceKey already exists in the cache.
    */
-  static bool Insert(imgFrame*         aSurface,
-                     const ImageKey    aImageKey,
-                     const SurfaceKey& aSurfaceKey,
-                     Lifetime          aLifetime);
+  static InsertOutcome Insert(imgFrame*         aSurface,
+                              const ImageKey    aImageKey,
+                              const SurfaceKey& aSurfaceKey,
+                              Lifetime          aLifetime);
 
   /**
    * Checks if a surface of a given size could possibly be stored in the cache.
    * If CanHold() returns false, Insert() will always fail to insert the
    * surface, but the inverse is not true: Insert() may take more information
    * into account than just image size when deciding whether to cache the
    * surface, so Insert() may still fail even if CanHold() returns true.
    *
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -2908,17 +2908,17 @@ class FunctionCompiler
     template<typename T>
     MDefinition *constructSimd(MDefinition *x, MDefinition *y, MDefinition *z, MDefinition *w,
                                MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(type));
-        T *ins = T::New(alloc(), type, x, y, z, w);
+        T *ins = T::NewAsmJS(alloc(), type, x, y, z, w);
         curBlock_->add(ins);
         return ins;
     }
 
     /***************************************************************** Calls */
 
     // The IonMonkey backend maintains a single stack offset (from the stack
     // pointer to the base of the frame) by adding the total amount of spill
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -988,59 +988,27 @@ static const JSClass FinalizeCounterClas
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* convert */
     finalize_counter_finalize
 };
 
-static const JSClass NurseryFinalizeCounterClass = {
-    "NurseryFinalizeCounter", JSCLASS_IS_ANONYMOUS | JSCLASS_FINALIZE_FROM_NURSERY,
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* convert */
-    finalize_counter_finalize
-};
-
 static bool
 MakeFinalizeObserver(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    bool isNursery = false;
-    if (args.length() > 0) {
-        if (!args[0].isString()) {
-            JS_ReportError(cx, "first argument must be unset or a string.");
-            return false;
-        }
-        bool isTenured;
-        if (!JS_StringEqualsAscii(cx, args[0].toString(), "nursery", &isNursery))
-            return false;
-        if (!JS_StringEqualsAscii(cx, args[0].toString(), "tenured", &isTenured))
-            return false;
-        if (!isNursery && !isTenured) {
-            JS_ReportError(cx, "first argument must be either 'nursery' or 'tenured'.");
-            return false;
-        }
-        MOZ_ASSERT(isNursery != isTenured);
-    }
-
     RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
     if (!scope)
         return false;
 
-    const JSClass *clasp = isNursery ? &NurseryFinalizeCounterClass : &FinalizeCounterClass;
-    JSObject *obj = JS_NewObjectWithGivenProto(cx, clasp, JS::NullPtr(), scope);
+    JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, JS::NullPtr(), scope);
     if (!obj)
         return false;
-    MOZ_ASSERT(isNursery == IsInsideNursery(obj));
 
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
 FinalizeCount(JSContext *cx, unsigned argc, jsval *vp)
 {
@@ -2287,21 +2255,19 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("settleFakePromise", SettleFakePromise, 1, 0,
 "settleFakePromise(promise)",
 "  'Settle' a 'promise' created by makeFakePromise(). This doesn't have any\n"
 "  observable effects outside of firing any onPromiseSettled hooks set on\n"
 "  Debugger instances that are observing the given promise's global as a\n"
 "  debuggee."),
 
     JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
-"makeFinalizeObserver(['nursery'|'tenured'])",
+"makeFinalizeObserver()",
 "  Get a special object whose finalization increases the counter returned\n"
-"  by the finalizeCount function. Pass an optional string of 'nursery' or\n"
-"  'tenured' (default of 'tenured') to the function to select a target heap\n"
-"  for the allocation."),
+"  by the finalizeCount function."),
 
     JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
 "finalizeCount()",
 "  Return the current value of the finalization counter that is incremented\n"
 "  each time an object returned by the makeFinalizeObserver is finalized."),
 
     JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
 "gcPreserveCode()",
deleted file mode 100644
--- a/js/src/jit-test/tests/gc/nursery-finalize.js
+++ /dev/null
@@ -1,16 +0,0 @@
-assertEq(0, finalizeCount());
-makeFinalizeObserver('tenured');
-minorgc();
-assertEq(0, finalizeCount());
-makeFinalizeObserver('nursery');
-minorgc();
-assertEq(1, finalizeCount());
-gc();
-assertEq(2, finalizeCount());
-
-var N = 10000;
-for (var i = 0; i < N; ++i)
-    makeFinalizeObserver('nursery');
-minorgc();
-assertEq(N + 2, finalizeCount());
-
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -9110,17 +9110,17 @@ GetTemplateObjectForClassHook(JSContext 
         Rooted<TypeDescr *> descr(cx, &args.callee().as<TypeDescr>());
         JSObject *obj = TypedObject::createZeroed(cx, descr, 1, gc::TenuredHeap);
         if (!obj)
             return false;
         templateObject.set(obj);
         return true;
     }
 
-    if (hook == SimdTypeDescr::call) {
+    if (hook == SimdTypeDescr::call && JitSupportsSimd()) {
         Rooted<SimdTypeDescr *> descr(cx, &args.callee().as<SimdTypeDescr>());
         JSObject *obj = TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap);
         if (!obj)
             return false;
         templateObject.set(obj);
         return true;
     }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4188,16 +4188,46 @@ CodeGenerator::visitNewTypedObject(LNewT
                                    (ArgList(), ImmGCPtr(templateObject), Imm32(initialHeap)),
                                    StoreRegisterTo(object));
 
     masm.createGCObject(object, temp, templateObject, initialHeap, ool->entry());
 
     masm.bind(ool->rejoin());
 }
 
+void
+CodeGenerator::visitSimdBox(LSimdBox *lir)
+{
+    FloatRegister in = ToFloatRegister(lir->input());
+    Register object = ToRegister(lir->output());
+    Register temp = ToRegister(lir->temp());
+    InlineTypedObject *templateObject = lir->mir()->templateObject();
+    gc::InitialHeap initialHeap = lir->mir()->initialHeap();
+    MIRType type = lir->mir()->input()->type();
+
+    // :TODO: We cannot spill SIMD registers (Bug 1112164) from safepoints, thus
+    // we cannot use the same oolCallVM as visitNewTypedObject for allocating
+    // SIMD Typed Objects if we are at the end of the nursery. (Bug 1119303)
+    Label bail;
+    masm.createGCObject(object, temp, templateObject, initialHeap, &bail);
+    bailoutFrom(&bail, lir->snapshot());
+
+    Address objectData(object, InlineTypedObject::offsetOfDataStart());
+    switch (type) {
+      case MIRType_Int32x4:
+        masm.storeUnalignedInt32x4(in, objectData);
+        break;
+      case MIRType_Float32x4:
+        masm.storeUnalignedFloat32x4(in, objectData);
+        break;
+      default:
+        MOZ_CRASH("Unknown SIMD kind when generating code for SimdBox.");
+    }
+}
+
 typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap);
 static const VMFunction NewDeclEnvObjectInfo =
     FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
 
 void
 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
 {
     Register objReg = ToRegister(lir->output());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -150,16 +150,17 @@ class CodeGenerator : public CodeGenerat
     void visitNewArray(LNewArray *lir);
     void visitOutOfLineNewArray(OutOfLineNewArray *ool);
     void visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite *lir);
     void visitNewArrayDynamicLength(LNewArrayDynamicLength *lir);
     void visitNewObjectVMCall(LNewObject *lir);
     void visitNewObject(LNewObject *lir);
     void visitOutOfLineNewObject(OutOfLineNewObject *ool);
     void visitNewTypedObject(LNewTypedObject *lir);
+    void visitSimdBox(LSimdBox *lir);
     void visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     void visitNewCallObject(LNewCallObject *lir);
     void visitNewSingletonCallObject(LNewSingletonCallObject *lir);
     void visitNewStringObject(LNewStringObject *lir);
     void visitNewDerivedTypedObject(LNewDerivedTypedObject *lir);
     void visitInitElem(LInitElem *lir);
     void visitInitElemGetterSetter(LInitElemGetterSetter *lir);
     void visitMutateProto(LMutateProto *lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -844,17 +844,17 @@ IonScript::New(JSContext *cx, types::Rec
     size_t paddedCacheEntriesSize = AlignBytes(cacheEntries * sizeof(uint32_t), DataAlignment);
     size_t paddedRuntimeSize = AlignBytes(runtimeSize, DataAlignment);
     size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
     size_t paddedBackedgeSize = AlignBytes(backedgeEntries * sizeof(PatchableBackedge), DataAlignment);
     size_t bytes = paddedSnapshotsSize +
                    paddedRecoversSize +
                    paddedBailoutSize +
                    paddedConstantsSize +
-                   paddedSafepointIndicesSize+
+                   paddedSafepointIndicesSize +
                    paddedOsiIndicesSize +
                    paddedCacheEntriesSize +
                    paddedRuntimeSize +
                    paddedSafepointSize +
                    paddedBackedgeSize;
     IonScript *script = cx->zone()->pod_malloc_with_extra<IonScript, uint8_t>(bytes);
     if (!script)
         return nullptr;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -780,16 +780,17 @@ class IonBuilder
     InliningStatus inlineTypedArrayLength(CallInfo &callInfo);
 
     // TypedObject intrinsics and natives.
     InliningStatus inlineObjectIsTypeDescr(CallInfo &callInfo);
     InliningStatus inlineSetTypedObjectOffset(CallInfo &callInfo);
     bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
                                                      ScalarTypeDescr::Type *arrayType);
     InliningStatus inlineConstructTypedObject(CallInfo &callInfo, TypeDescr *target);
+    InliningStatus inlineConstructSimdObject(CallInfo &callInfo, SimdTypeDescr *target);
 
     // Utility intrinsics.
     InliningStatus inlineIsCallable(CallInfo &callInfo);
     InliningStatus inlineIsObject(CallInfo &callInfo);
     InliningStatus inlineToObject(CallInfo &callInfo);
     InliningStatus inlineToInteger(CallInfo &callInfo);
     InliningStatus inlineToString(CallInfo &callInfo);
     InliningStatus inlineDump(CallInfo &callInfo);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -130,16 +130,38 @@ class LMoveGroup : public LInstructionHe
     size_t numMoves() const {
         return moves_.length();
     }
     const LMove &getMove(size_t i) const {
         return moves_[i];
     }
 };
 
+
+// Constructs a SIMD object (value type) based on the MIRType of its input.
+class LSimdBox : public LInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(SimdBox)
+
+    explicit LSimdBox(const LAllocation &simd, const LDefinition &temp)
+    {
+        setOperand(0, simd);
+        setTemp(0, temp);
+    }
+
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+
+    MSimdBox *mir() const {
+        return mir_->toSimdBox();
+    }
+};
+
 // Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4).
 class LSimdSplatX4 : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(SimdSplatX4)
     explicit LSimdSplatX4(const LAllocation &v)
     {
         setOperand(0, v);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -12,16 +12,17 @@
     _(Nop)                          \
     _(Mop)                          \
     _(OsiPoint)                     \
     _(MoveGroup)                    \
     _(Integer)                      \
     _(Pointer)                      \
     _(Double)                       \
     _(Float32)                      \
+    _(SimdBox)                      \
     _(SimdSplatX4)                  \
     _(Int32x4)                      \
     _(Float32x4)                    \
     _(SimdExtractElementI)          \
     _(SimdExtractElementF)          \
     _(SimdInsertElementI)           \
     _(SimdInsertElementF)           \
     _(SimdSignMaskX4)               \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3724,16 +3724,27 @@ LIRGenerator::visitRecompileCheck(MRecom
 void
 LIRGenerator::visitMemoryBarrier(MMemoryBarrier *ins)
 {
     LMemoryBarrier *lir = new(alloc()) LMemoryBarrier(ins->type());
     add(lir, ins);
 }
 
 void
+LIRGenerator::visitSimdBox(MSimdBox *ins)
+{
+    MOZ_ASSERT(IsSimdType(ins->input()->type()));
+    LUse in = useRegister(ins->input());
+    LSimdBox *lir = new(alloc()) LSimdBox(in, temp());
+    // :TODO: Cannot spill SIMD registers (Bug 1112164)
+    assignSnapshot(lir, Bailout_Inevitable);
+    define(lir, ins);
+}
+
+void
 LIRGenerator::visitSimdConstant(MSimdConstant *ins)
 {
     MOZ_ASSERT(IsSimdType(ins->type()));
 
     if (ins->type() == MIRType_Int32x4)
         define(new(alloc()) LInt32x4(), ins);
     else if (ins->type() == MIRType_Float32x4)
         define(new(alloc()) LFloat32x4(), ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -260,16 +260,17 @@ class LIRGenerator : public LIRGenerator
     void visitAsmJSVoidReturn(MAsmJSVoidReturn *ins);
     void visitAsmJSPassStackArg(MAsmJSPassStackArg *ins);
     void visitAsmJSCall(MAsmJSCall *ins);
     void visitSetDOMProperty(MSetDOMProperty *ins);
     void visitGetDOMProperty(MGetDOMProperty *ins);
     void visitGetDOMMember(MGetDOMMember *ins);
     void visitRecompileCheck(MRecompileCheck *ins);
     void visitMemoryBarrier(MMemoryBarrier *ins);
+    void visitSimdBox(MSimdBox *ins);
     void visitSimdExtractElement(MSimdExtractElement *ins);
     void visitSimdInsertElement(MSimdInsertElement *ins);
     void visitSimdSignMask(MSimdSignMask *ins);
     void visitSimdSwizzle(MSimdSwizzle *ins);
     void visitSimdShuffle(MSimdShuffle *ins);
     void visitSimdUnaryArith(MSimdUnaryArith *ins);
     void visitSimdBinaryComp(MSimdBinaryComp *ins);
     void visitSimdBinaryBitwise(MSimdBinaryBitwise *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -290,16 +290,19 @@ IonBuilder::InliningStatus
 IonBuilder::inlineNonFunctionCall(CallInfo &callInfo, JSObject *target)
 {
     // Inline a call to a non-function object, invoking the object's call or
     // construct hook.
 
     if (callInfo.constructing() && target->constructHook() == TypedObject::construct)
         return inlineConstructTypedObject(callInfo, &target->as<TypeDescr>());
 
+    if (!callInfo.constructing() && target->callHook() == SimdTypeDescr::call)
+        return inlineConstructSimdObject(callInfo, &target->as<SimdTypeDescr>());
+
     return InliningStatus_NotInlined;
 }
 
 types::TemporaryTypeSet *
 IonBuilder::getInlineReturnTypeSet()
 {
     return bytecodeTypes(pc);
 }
@@ -2507,10 +2510,61 @@ IonBuilder::inlineConstructTypedObject(C
     MNewTypedObject *ins = MNewTypedObject::New(alloc(), constraints(), templateObject,
                                                 templateObject->type()->initialHeap(constraints()));
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
+IonBuilder::InliningStatus
+IonBuilder::inlineConstructSimdObject(CallInfo &callInfo, SimdTypeDescr *descr)
+{
+    if (callInfo.argc() == 1)
+        return InliningStatus_NotInlined;
+
+    // Generic constructor of SIMD valuesX4.
+    MIRType simdType;
+    switch (descr->type()) {
+      case SimdTypeDescr::TYPE_INT32:
+        simdType = MIRType_Int32x4;
+        break;
+      case SimdTypeDescr::TYPE_FLOAT32:
+        simdType = MIRType_Float32x4;
+        break;
+      default:
+        MOZ_CRASH("Unknown SIMD kind when generating MSimdBox instruction.");
+        return InliningStatus_NotInlined;
+    }
+
+    // We do not inline SIMD constructors if the number of arguments does not
+    // match the number of lanes.
+    if (SimdTypeToLength(simdType) != callInfo.argc())
+        return InliningStatus_NotInlined;
+
+    // Take the templateObject out of Baseline ICs, such that we can box
+    // SIMD value type in the same kind of objects.
+    MOZ_ASSERT(size_t(descr->size(descr->type())) < InlineTypedObject::MaximumSize);
+    JSObject *templateObject = inspector->getTemplateObjectForClassHook(pc, descr->getClass());
+    if (!templateObject)
+        return InliningStatus_NotInlined;
+
+    // The previous assertion ensures this will never fail if we were able to
+    // allocate a templateObject in Baseline.
+    InlineTypedObject *inlineTypedObject = &templateObject->as<InlineTypedObject>();
+    MOZ_ASSERT(&inlineTypedObject->typeDescr() == descr);
+
+    MSimdValueX4 *values = MSimdValueX4::New(alloc(), simdType,
+                                             callInfo.getArg(0), callInfo.getArg(1),
+                                             callInfo.getArg(2), callInfo.getArg(3));
+    current->add(values);
+
+    MSimdBox *obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject,
+                                  inlineTypedObject->type()->initialHeap(constraints()));
+    current->add(obj);
+    current->push(obj);
+
+    callInfo.setImplicitlyUsedUnchecked();
+    return InliningStatus_Inlined;
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1314,43 +1314,50 @@ class MConstant : public MNullaryInstruc
     bool canProduceFloat32() const;
 
     ALLOW_CLONE(MConstant)
 };
 
 // Generic constructor of SIMD valuesX4.
 class MSimdValueX4
   : public MQuaternaryInstruction,
-    public NoTypePolicy::Data
+    public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
+                      SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
 {
   protected:
     MSimdValueX4(MIRType type, MDefinition *x, MDefinition *y, MDefinition *z, MDefinition *w)
       : MQuaternaryInstruction(x, y, z, w)
     {
         MOZ_ASSERT(IsSimdType(type));
         MOZ_ASSERT(SimdTypeToLength(type) == 4);
-        mozilla::DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type);
-        MOZ_ASSERT(scalarType == x->type());
-        MOZ_ASSERT(scalarType == y->type());
-        MOZ_ASSERT(scalarType == z->type());
-        MOZ_ASSERT(scalarType == w->type());
 
         setMovable();
         setResultType(type);
     }
 
   public:
     INSTRUCTION_HEADER(SimdValueX4)
 
     static MSimdValueX4 *New(TempAllocator &alloc, MIRType type, MDefinition *x,
                              MDefinition *y, MDefinition *z, MDefinition *w)
     {
         return new(alloc) MSimdValueX4(type, x, y, z, w);
     }
 
+    static MSimdValueX4 *NewAsmJS(TempAllocator &alloc, MIRType type, MDefinition *x,
+                                  MDefinition *y, MDefinition *z, MDefinition *w)
+    {
+        mozilla::DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type);
+        MOZ_ASSERT(scalarType == x->type());
+        MOZ_ASSERT(scalarType == y->type());
+        MOZ_ASSERT(scalarType == z->type());
+        MOZ_ASSERT(scalarType == w->type());
+        return MSimdValueX4::New(alloc, type, x, y, z, w);
+    }
+
     bool canConsumeFloat32(MUse *use) const {
         return SimdTypeToScalarType(type()) == MIRType_Float32;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 
@@ -2900,16 +2907,66 @@ class MTypedObjectDescr
     bool congruentTo(const MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
+// Generic way for constructing a SIMD object in IonMonkey, this instruction
+// takes as argument a SIMD instruction and returns a new SIMD object which
+// corresponds to the MIRType of its operand.
+class MSimdBox
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+  protected:
+    AlwaysTenured<InlineTypedObject *> templateObject_;
+    gc::InitialHeap initialHeap_;
+
+    MSimdBox(types::CompilerConstraintList *constraints,
+             MDefinition *op,
+             InlineTypedObject *templateObject,
+             gc::InitialHeap initialHeap)
+      : MUnaryInstruction(op),
+        templateObject_(templateObject),
+        initialHeap_(initialHeap)
+    {
+        MOZ_ASSERT(IsSimdType(op->type()));
+        setMovable();
+        setResultType(MIRType_Object);
+        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+    }
+
+  public:
+    INSTRUCTION_HEADER(SimdBox)
+
+    static MSimdBox *New(TempAllocator &alloc,
+                         types::CompilerConstraintList *constraints,
+                         MDefinition *op,
+                         InlineTypedObject *templateObject,
+                         gc::InitialHeap initialHeap)
+    {
+        return new(alloc) MSimdBox(constraints, op, templateObject, initialHeap);
+    }
+
+    InlineTypedObject *templateObject() const {
+        return templateObject_;
+    }
+
+    gc::InitialHeap initialHeap() const {
+        return initialHeap_;
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 // Creates a new derived type object. At runtime, this is just a call
 // to `BinaryBlock::createDerived()`. That is, the MIR itself does not
 // compile to particularly optimized code. However, using a distinct
 // MIR for creating derived type objects allows the compiler to
 // optimize ephemeral typed objects as would be created for a
 // reference like `a.b.c` -- here, the `a.b` will create an ephemeral
 // derived type object that aliases the memory of `a` itself. The
 // specific nature of `a.b` is revealed by using
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -7,16 +7,17 @@
 #ifndef jit_MOpcodes_h
 #define jit_MOpcodes_h
 
 namespace js {
 namespace jit {
 
 #define MIR_OPCODE_LIST(_)                                                  \
     _(Constant)                                                             \
+    _(SimdBox)                                                              \
     _(SimdValueX4)                                                          \
     _(SimdSplatX4)                                                          \
     _(SimdConstant)                                                         \
     _(SimdConvert)                                                          \
     _(SimdReinterpretCast)                                                  \
     _(SimdExtractElement)                                                   \
     _(SimdInsertElement)                                                    \
     _(SimdSignMask)                                                         \
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -517,16 +517,46 @@ NoFloatPolicyAfter<FirstOp>::adjustInput
     return true;
 }
 
 template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator &alloc, MInstruction *def);
 
 template <unsigned Op>
 bool
+SimdScalarPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
+{
+    MOZ_ASSERT(IsSimdType(ins->type()));
+    MIRType scalarType = SimdTypeToScalarType(ins->type());
+
+    MDefinition *in = ins->getOperand(Op);
+    if (in->type() == scalarType)
+        return true;
+
+    MInstruction *replace;
+    if (scalarType == MIRType_Int32) {
+        replace = MTruncateToInt32::New(alloc, in);
+    } else {
+        MOZ_ASSERT(scalarType == MIRType_Float32);
+        replace = MToFloat32::New(alloc, in);
+    }
+
+    ins->block()->insertBefore(ins, replace);
+    ins->replaceOperand(Op, replace);
+
+    return replace->typePolicy()->adjustInputs(alloc, replace);
+}
+
+template bool SimdScalarPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+template bool SimdScalarPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+template bool SimdScalarPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+template bool SimdScalarPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+
+template <unsigned Op>
+bool
 BoxPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MDefinition *in = ins->getOperand(Op);
     if (in->type() == MIRType_Value)
         return true;
 
     ins->replaceOperand(Op, BoxAt(alloc, ins, in));
     return true;
@@ -992,16 +1022,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >)      \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2> >)      \
     _(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>)          \
     _(Mix3Policy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >)   \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \
+    _(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
     _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >)                        \
     _(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >)   \
     _(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >)            \
     _(MixPolicy<DoublePolicy<0>, DoublePolicy<1> >)                     \
     _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >)                        \
     _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >)            \
     _(MixPolicy<ObjectPolicy<0>, IntPolicy<1> >)                        \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >)                    \
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -299,16 +299,29 @@ class ObjectPolicy MOZ_FINAL : public Ty
         return staticAdjustInputs(alloc, ins);
     }
 };
 
 // Single-object input. If the input is a Value, it is unboxed. If it is
 // a primitive, we use ValueToNonNullObject.
 typedef ObjectPolicy<0> SingleObjectPolicy;
 
+// Convert an operand to have a type identical to the scalar type of the
+// returned type of the instruction.
+template <unsigned Op>
+class SimdScalarPolicy MOZ_FINAL : public TypePolicy
+{
+  public:
+    EMPTY_DATA_;
+    static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
+        return staticAdjustInputs(alloc, def);
+    }
+};
+
 template <unsigned Op>
 class BoxPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
     virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, ins);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4517,16 +4517,33 @@ static const JSFunctionSpecWithHelp fuzz
 "  and that it knows nothing about. The reverse applies as well: the\n"
 "  original hook, that we reinstate after the call to |fun| completes,\n"
 "  might be asked for the source code of compilations that |fun|\n"
 "  performed, and which, presumably, only |hook| knows how to find.\n"),
 
     JS_FS_HELP_END
 };
 
+static const JSFunctionSpecWithHelp console_functions[] = {
+    JS_FN_HELP("log", Print, 0, 0,
+"log([exp ...])",
+"  Evaluate and print expressions to stdout.\n"
+"  This function is an alias of the print() function."),
+    JS_FS_HELP_END
+};
+
+bool
+DefineConsole(JSContext *cx, HandleObject global)
+{
+    RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
+    return obj &&
+           JS_DefineFunctionsWithHelp(cx, obj, console_functions) &&
+           JS_DefineProperty(cx, global, "console", obj, 0);
+}
+
 #ifdef MOZ_PROFILING
 # define PROFILING_FUNCTION_COUNT 5
 # ifdef MOZ_CALLGRIND
 #  define CALLGRIND_FUNCTION_COUNT 3
 # else
 #  define CALLGRIND_FUNCTION_COUNT 0
 # endif
 # ifdef MOZ_VTUNE
@@ -5214,16 +5231,18 @@ NewGlobalObject(JSContext *cx, JS::Compa
         if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe))
             return nullptr;
 
         if (!fuzzingSafe) {
             if (!JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions))
                 return nullptr;
             if (!js::DefineOS(cx, glob))
                 return nullptr;
+            if (!DefineConsole(cx, glob))
+                return nullptr;
         }
 
         /* Initialize FakeDOMObject. */
         static const js::DOMCallbacks DOMcallbacks = {
             InstanceClassHasProtoAtDepth
         };
         SetDOMCallbacks(cx->runtime(), &DOMcallbacks);
 
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -1,555 +1,204 @@
 /* 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 <stdint.h>
-#include <stdio.h>
+#include <string.h>
+#include <vector>
 
 #include "ClearKeyDecryptionManager.h"
-#include "ClearKeyUtils.h"
-#include "ClearKeyStorage.h"
-#include "ClearKeyPersistence.h"
-#include "gmp-task-utils.h"
-
+#include "gmp-decryption.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 
-using namespace mozilla;
-using namespace std;
-
-class ClearKeyDecryptor
+class ClearKeyDecryptor : public RefCounted
 {
 public:
-  ClearKeyDecryptor(GMPDecryptorCallback* aCallback, const Key& aKey);
-  ~ClearKeyDecryptor();
-
-  void InitKey();
+  MOZ_IMPLICIT ClearKeyDecryptor();
 
-  void QueueDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
+  void InitKey(const Key& aKey);
+  bool HasKey() const { return !!mKey.size(); }
 
-  uint32_t AddRef();
-  uint32_t Release();
+  GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
+                 GMPEncryptedBufferMetadata* aMetadata);
 
   const Key& DecryptionKey() const { return mKey; }
 
 private:
-  struct DecryptTask : public GMPTask
-  {
-    DecryptTask(ClearKeyDecryptor* aTarget, GMPBuffer* aBuffer,
-                GMPEncryptedBufferMetadata* aMetadata)
-      : mTarget(aTarget), mBuffer(aBuffer), mMetadata(aMetadata) { }
-
-    virtual void Run() MOZ_OVERRIDE
-    {
-      mTarget->Decrypt(mBuffer, mMetadata);
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DecryptTask() { }
-
-    ClearKeyDecryptor* mTarget;
-    GMPBuffer* mBuffer;
-    GMPEncryptedBufferMetadata* mMetadata;
-  };
-
-  struct DestroyTask : public GMPTask
-  {
-    explicit DestroyTask(ClearKeyDecryptor* aTarget) : mTarget(aTarget) { }
-
-    virtual void Run() MOZ_OVERRIDE {
-      delete mTarget;
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DestroyTask() { }
-
-    ClearKeyDecryptor* mTarget;
-  };
-
-  void Decrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
-
-  uint32_t mRefCnt;
-
-  GMPDecryptorCallback* mCallback;
-  GMPThread* mThread;
+  ~ClearKeyDecryptor();
 
   Key mKey;
 };
 
+
+/* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr;
+
+/* static */ ClearKeyDecryptionManager*
+ClearKeyDecryptionManager::Get()
+{
+  if (!sInstance) {
+    sInstance = new ClearKeyDecryptionManager();
+  }
+  return sInstance;
+}
+
 ClearKeyDecryptionManager::ClearKeyDecryptionManager()
 {
-  CK_LOGD("ClearKeyDecryptionManager ctor");
-
-  // The ClearKeyDecryptionManager maintains a self reference which is
-  // removed when the host is finished with the interface and calls
-  // DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so
-  // that the tasks to that we dispatch to call functions on it won't end up
-  // derefing a null reference after DecryptingComplete() is called.
-  AddRef();
+  CK_LOGD("ClearKeyDecryptionManager::ClearKeyDecryptionManager");
 }
 
 ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
 {
-  CK_LOGD("ClearKeyDecryptionManager dtor");
-  MOZ_ASSERT(mRefCount == 1);
+  CK_LOGD("ClearKeyDecryptionManager::~ClearKeyDecryptionManager");
+
+  sInstance = nullptr;
+
+  for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
+    it->second->Release();
+  }
+  mDecryptors.clear();
 }
 
-void
-ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback)
+bool
+ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::Init");
-  mCallback = aCallback;
-  mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
-                             GMP_EME_CAP_DECRYPT_VIDEO);
-  ClearKeyPersistence::EnsureInitialized();
+  CK_LOGD("ClearKeyDecryptionManager::HasSeenKeyId");
+  return mDecryptors.find(aKeyId) != mDecryptors.end();
 }
 
-void
-ClearKeyDecryptionManager::CreateSession(uint32_t aCreateSessionToken,
-                                         uint32_t aPromiseId,
-                                         const char* aInitDataType,
-                                         uint32_t aInitDataTypeSize,
-                                         const uint8_t* aInitData,
-                                         uint32_t aInitDataSize,
-                                         GMPSessionType aSessionType)
+bool
+ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::CreateSession type:%s", aInitDataType);
-
-  // initDataType must be "cenc".
-  if (strcmp("cenc", aInitDataType)) {
-    mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                             nullptr /* message */, 0 /* messageLen */);
-    return;
-  }
-
-  if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
-                                                        aCreateSessionToken,
-                                                        aPromiseId,
-                                                        aInitData,
-                                                        aInitDataSize,
-                                                        aSessionType)) {
-    return;
-  }
-
-  string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
-  MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end());
-
-  ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
-  session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize);
-  mSessions[sessionId] = session;
-
-  const vector<KeyId>& sessionKeys = session->GetKeyIds();
-  vector<KeyId> neededKeys;
-  for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
-    if (!Contains(mDecryptors, *it)) {
-      // Need to request this key ID from the client.
-      neededKeys.push_back(*it);
-    } else {
-      // We already have a key for this key ID. Mark as usable.
-      mCallback->KeyIdUsable(sessionId.c_str(), sessionId.length(),
-                             &(*it)[0], it->size());
-    }
-  }
-
-  if (neededKeys.empty()) {
-    CK_LOGD("No keys needed from client.");
-    return;
-  }
-
-  // Send a request for needed key data.
-  string request;
-  ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
-  mCallback->SessionMessage(&sessionId[0], sessionId.length(),
-                            (uint8_t*)&request[0], request.length(),
-                            "" /* destination url */, 0);
+  CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForKeyId");
+  const auto& decryptor = mDecryptors.find(aKeyId);
+  return decryptor != mDecryptors.end() && !decryptor->second->HasKey();
 }
 
-void
-ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId,
-                                       const char* aSessionId,
-                                       uint32_t aSessionIdLength)
+bool
+ClearKeyDecryptionManager::HasKeyForKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::LoadSession");
-
-  if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
-    return;
-  }
+  CK_LOGD("ClearKeyDecryptionManager::HasKeyForKeyId");
+  const auto& decryptor = mDecryptors.find(aKeyId);
+  return decryptor != mDecryptors.end() && decryptor->second->HasKey();
+}
 
-  if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
-                                                      aPromiseId,
-                                                      aSessionId,
-                                                      aSessionIdLength)) {
-    return;
-  }
-
-  string sid(aSessionId, aSessionId + aSessionIdLength);
-  if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
-    return;
-  }
-
-  // Callsback PersistentSessionDataLoaded with results...
-  ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
+const Key&
+ClearKeyDecryptionManager::GetDecryptionKey(const KeyId& aKeyId)
+{
+  MOZ_ASSERT(HasKeyForKeyId(aKeyId));
+  return mDecryptors[aKeyId]->DecryptionKey();
 }
 
 void
-ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus,
-                                                       uint32_t aPromiseId,
-                                                       const string& aSessionId,
-                                                       const uint8_t* aKeyData,
-                                                       uint32_t aKeyDataSize)
-{
-  if (GMP_FAILED(aStatus) ||
-      Contains(mSessions, aSessionId) ||
-      (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
-    return;
-  }
-
-  ClearKeySession* session = new ClearKeySession(aSessionId,
-                                                 mCallback,
-                                                 kGMPPersistentSession);
-  mSessions[aSessionId] = session;
-
-  uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN);
-  for (uint32_t i = 0; i < numKeys; i ++) {
-    const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i;
-
-    KeyId keyId(base, base + CLEARKEY_KEY_LEN);
-    MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
-
-    Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
-    MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
-
-    session->AddKeyId(keyId);
-
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key);
-    }
-    mDecryptors[keyId]->AddRef();
-    mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(),
-                           &keyId[0], keyId.size());
-  }
-
-  mCallback->ResolveLoadSessionPromise(aPromiseId, true);
-}
-
-void
-ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength,
-                                         const uint8_t* aResponse,
-                                         uint32_t aResponseSize)
+ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey)
 {
-  CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
-
-  auto itr = mSessions.find(sessionId);
-  if (itr == mSessions.end() || !(itr->second)) {
-    CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
-    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
-    return;
-  }
-  ClearKeySession* session = itr->second;
-
-  // Parse the response for any (key ID, key) pairs.
-  vector<KeyIdPair> keyPairs;
-  if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
-    CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
-    mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
-    return;
-  }
-
-  for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
-    KeyId& keyId = it->mKeyId;
-
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, it->mKey);
-      mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
-                             &keyId[0], keyId.size());
-    }
-
-    mDecryptors[keyId]->AddRef();
-  }
-
-  if (session->Type() != kGMPPersistentSession) {
-    mCallback->ResolvePromise(aPromiseId);
-    return;
-  }
-
-  // Store the keys on disk. We store a record whose name is the sessionId,
-  // and simply append each keyId followed by its key.
-  vector<uint8_t> keydata;
-  Serialize(session, keydata);
-  GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
-  static const char* message = "Couldn't store cenc key init data";
-  GMPTask* reject = WrapTask(mCallback,
-                             &GMPDecryptorCallback::RejectPromise,
-                             aPromiseId,
-                             kGMPInvalidStateError,
-                             message,
-                             strlen(message));
-  StoreData(sessionId, keydata, resolve, reject);
-}
-
-void
-ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession,
-                                     std::vector<uint8_t>& aOutKeyData)
-{
-  const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
-  for (size_t i = 0; i < keyIds.size(); i++) {
-    const KeyId& keyId = keyIds[i];
-    if (!Contains(mDecryptors, keyId)) {
-      continue;
-    }
-    MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
-    aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
-    const Key& key = mDecryptors[keyId]->DecryptionKey();
-    MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
-    aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
+  CK_LOGD("ClearKeyDecryptionManager::InitKey");
+  if (IsExpectingKeyForKeyId(aKeyId)) {
+    mDecryptors[aKeyId]->InitKey(aKey);
   }
 }
 
 void
-ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId,
-                                        const char* aSessionId,
-                                        uint32_t aSessionIdLength)
+ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId)
 {
-  CK_LOGD("ClearKeyDecryptionManager::CloseSession");
-
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
-  auto itr = mSessions.find(sessionId);
-  if (itr == mSessions.end()) {
-    CK_LOGW("ClearKey CDM couldn't close non-existent session.");
-    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
-    return;
+  CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId");
+  if (!HasSeenKeyId(aKeyId)) {
+    mDecryptors[aKeyId] = new ClearKeyDecryptor();
   }
-
-  ClearKeySession* session = itr->second;
-  MOZ_ASSERT(session);
-
-  ClearInMemorySessionData(session);
-  mCallback->ResolvePromise(aPromiseId);
-  mCallback->SessionClosed(aSessionId, aSessionIdLength);
-}
-
-void
-ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession)
-{
-  MOZ_ASSERT(aSession);
-
-  const vector<KeyId>& keyIds = aSession->GetKeyIds();
-  for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
-    MOZ_ASSERT(Contains(mDecryptors, *it));
-    if (!mDecryptors[*it]->Release()) {
-      mDecryptors.erase(*it);
-      mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(),
-                                &(*it)[0], it->size());
-    }
-  }
-
-  mSessions.erase(aSession->Id());
-  delete aSession;
+  mDecryptors[aKeyId]->AddRef();
 }
 
 void
-ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength)
+ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId)
 {
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
-  auto itr = mSessions.find(sessionId);
-  if (itr == mSessions.end()) {
-    CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
-    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
-    return;
+  CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId");
+  ClearKeyDecryptor* decryptor = mDecryptors[aKeyId];
+  if (!decryptor->Release()) {
+    mDecryptors.erase(aKeyId);
   }
-
-  ClearKeySession* session = itr->second;
-  MOZ_ASSERT(session);
-  string sid = session->Id();
-  bool isPersistent = session->Type() == kGMPPersistentSession;
-  ClearInMemorySessionData(session);
-
-  if (!isPersistent) {
-    mCallback->ResolvePromise(aPromiseId);
-    return;
-  }
-
-  ClearKeyPersistence::PersistentSessionRemoved(sid);
-
-  // Overwrite the record storing the sessionId's key data with a zero
-  // length record to delete it.
-  vector<uint8_t> emptyKeydata;
-  GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
-  static const char* message = "Could not remove session";
-  GMPTask* reject = WrapTask(mCallback,
-                             &GMPDecryptorCallback::RejectPromise,
-                             aPromiseId,
-                             kGMPInvalidAccessError,
-                             message,
-                             strlen(message));
-  StoreData(sessionId, emptyKeydata, resolve, reject);
 }
 
-void
-ClearKeyDecryptionManager::SetServerCertificate(uint32_t aPromiseId,
-                                                const uint8_t* aServerCert,
-                                                uint32_t aServerCertSize)
-{
-  // ClearKey CDM doesn't support this method by spec.
-  CK_LOGD("ClearKeyDecryptionManager::SetServerCertificate");
-  mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                           nullptr /* message */, 0 /* messageLen */);
-}
-
-void
-ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer,
+GMPErr
+ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                                    GMPEncryptedBufferMetadata* aMetadata)
 {
   CK_LOGD("ClearKeyDecryptionManager::Decrypt");
   KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
 
-  if (!Contains(mDecryptors, keyId)) {
-    CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr");
-    mCallback->Decrypted(aBuffer, GMPNoKeyErr);
-    return;
+  if (!HasKeyForKeyId(keyId)) {
+    return GMPNoKeyErr;
   }
 
-  mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
+  return mDecryptors[keyId]->Decrypt(aBuffer, aBufferSize, aMetadata);
+}
+
+ClearKeyDecryptor::ClearKeyDecryptor()
+{
+  CK_LOGD("ClearKeyDecryptor ctor");
+}
+
+ClearKeyDecryptor::~ClearKeyDecryptor()
+{
+  CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]);
 }
 
 void
-ClearKeyDecryptionManager::DecryptingComplete()
+ClearKeyDecryptor::InitKey(const Key& aKey)
 {
-  CK_LOGD("ClearKeyDecryptionManager::DecryptingComplete");
-
-  for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
-    delete it->second;
-  }
-  mSessions.clear();
-
-  for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
-    delete it->second;
-  }
-  mDecryptors.clear();
-
-  Release();
+  mKey = aKey;
 }
 
-void
-ClearKeyDecryptor::QueueDecrypt(GMPBuffer* aBuffer,
-                                GMPEncryptedBufferMetadata* aMetadata)
-{
-  CK_LOGD("ClearKeyDecryptor::QueueDecrypt");
-  mThread->Post(new DecryptTask(this, aBuffer, aMetadata));
-}
-
-void
-ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer,
+GMPErr
+ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                            GMPEncryptedBufferMetadata* aMetadata)
 {
-  if (!mThread) {
-    mCallback->Decrypted(aBuffer, GMPGenericErr);
-  }
-
+  CK_LOGD("ClearKeyDecryptor::Decrypt");
   // If the sample is split up into multiple encrypted subsamples, we need to
   // stitch them into one continuous buffer for decryption.
-  vector<uint8_t> tmp(aBuffer->Size());
+  std::vector<uint8_t> tmp(aBufferSize);
 
   if (aMetadata->NumSubsamples()) {
     // Take all encrypted parts of subsamples and stitch them into one
     // continuous encrypted buffer.
-    unsigned char* data = aBuffer->Data();
+    unsigned char* data = aBuffer;
     unsigned char* iter = &tmp[0];
     for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
       data += aMetadata->ClearBytes()[i];
       uint32_t cipherBytes = aMetadata->CipherBytes()[i];
 
       memcpy(iter, data, cipherBytes);
 
       data += cipherBytes;
       iter += cipherBytes;
     }
 
     tmp.resize((size_t)(iter - &tmp[0]));
   } else {
-    memcpy(&tmp[0], aBuffer->Data(), aBuffer->Size());
+    memcpy(&tmp[0], aBuffer, aBufferSize);
   }
 
   MOZ_ASSERT(aMetadata->IVSize() == 8 || aMetadata->IVSize() == 16);
-  vector<uint8_t> iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize());
+  std::vector<uint8_t> iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize());
   iv.insert(iv.end(), CLEARKEY_KEY_LEN - aMetadata->IVSize(), 0);
 
   ClearKeyUtils::DecryptAES(mKey, tmp, iv);
 
   if (aMetadata->NumSubsamples()) {
     // Take the decrypted buffer, split up into subsamples, and insert those
     // subsamples back into their original position in the original buffer.
-    unsigned char* data = aBuffer->Data();
+    unsigned char* data = aBuffer;
     unsigned char* iter = &tmp[0];
     for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
       data += aMetadata->ClearBytes()[i];
       uint32_t cipherBytes = aMetadata->CipherBytes()[i];
 
       memcpy(data, iter, cipherBytes);
 
       data += cipherBytes;
       iter += cipherBytes;
     }
   } else {
-    memcpy(aBuffer->Data(), &tmp[0], aBuffer->Size());
+    memcpy(aBuffer, &tmp[0], aBufferSize);
   }
 
-  mCallback->Decrypted(aBuffer, GMPNoErr);
-}
-
-ClearKeyDecryptor::ClearKeyDecryptor(GMPDecryptorCallback* aCallback,
-                                     const Key& aKey)
-  : mRefCnt(0)
-  , mCallback(aCallback)
-  , mThread(nullptr)
-  , mKey(aKey)
-{
-  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
-    CK_LOGD("failed to create thread in clearkey cdm");
-    mThread = nullptr;
-    return;
-  }
-}
-
-ClearKeyDecryptor::~ClearKeyDecryptor()
-{
-  CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]);
+  return GMPNoErr;
 }
-
-uint32_t
-ClearKeyDecryptor::AddRef()
-{
-  return ++mRefCnt;
-}
-
-uint32_t
-ClearKeyDecryptor::Release()
-{
-  uint32_t newCount = --mRefCnt;
-  if (!newCount) {
-    if (mThread) {
-      // Shutdown mThread. We cache a pointer to mThread, as the DestroyTask
-      // may run and delete |this| before Post() returns.
-      GMPThread* thread = mThread;
-      thread->Post(new DestroyTask(this));
-      thread->Join();
-    } else {
-      delete this;
-    }
-  }
-
-  return newCount;
-}
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -1,80 +1,47 @@
 /* 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 __ClearKeyDecryptor_h__
-#define __ClearKeyDecryptor_h__
+#ifndef __ClearKeyDecryptionManager_h__
+#define __ClearKeyDecryptionManager_h__
 
 #include <map>
-#include <string>
-#include <vector>
 
-#include "ClearKeySession.h"
 #include "ClearKeyUtils.h"
-#include "gmp-api/gmp-decryption.h"
-#include "ScopedNSSTypes.h"
 #include "RefCounted.h"
 
 class ClearKeyDecryptor;
-class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
-                                          , public RefCounted
-{
-public:
-  ClearKeyDecryptionManager();
-
-  virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
 
-  virtual void CreateSession(uint32_t aCreateSessionToken,
-                             uint32_t aPromiseId,
-                             const char* aInitDataType,
-                             uint32_t aInitDataTypeSize,
-                             const uint8_t* aInitData,
-                             uint32_t aInitDataSize,
-                             GMPSessionType aSessionType) MOZ_OVERRIDE;
+class ClearKeyDecryptionManager : public RefCounted
+{
+private:
+  ClearKeyDecryptionManager();
+  ~ClearKeyDecryptionManager();
 
-  virtual void LoadSession(uint32_t aPromiseId,
-                           const char* aSessionId,
-                           uint32_t aSessionIdLength) MOZ_OVERRIDE;
+  static ClearKeyDecryptionManager* sInstance;
+
+public:
+  static ClearKeyDecryptionManager* Get();
 
-  virtual void UpdateSession(uint32_t aPromiseId,
-                             const char* aSessionId,
-                             uint32_t aSessionIdLength,
-                             const uint8_t* aResponse,
-                             uint32_t aResponseSize) MOZ_OVERRIDE;
+  bool HasSeenKeyId(const KeyId& aKeyId) const;
+  bool HasKeyForKeyId(const KeyId& aKeyId) const;
 
-  virtual void CloseSession(uint32_t aPromiseId,
-                            const char* aSessionId,
-                            uint32_t aSessionIdLength) MOZ_OVERRIDE;
-
-  virtual void RemoveSession(uint32_t aPromiseId,
-                             const char* aSessionId,
-                             uint32_t aSessionIdLength) MOZ_OVERRIDE;
+  const Key& GetDecryptionKey(const KeyId& aKeyId);
 
-  virtual void SetServerCertificate(uint32_t aPromiseId,
-                                    const uint8_t* aServerCert,
-                                    uint32_t aServerCertSize) MOZ_OVERRIDE;
-
-  virtual void Decrypt(GMPBuffer* aBuffer,
-                       GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
+  // Create a decryptor for the given KeyId if one does not already exist.
+  void InitKey(KeyId aKeyId, Key aKey);
+  void ExpectKeyId(KeyId aKeyId);
+  void ReleaseKeyId(KeyId aKeyId);
 
-  virtual void DecryptingComplete() MOZ_OVERRIDE;
+  GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
+                 GMPEncryptedBufferMetadata* aMetadata);
 
-  void PersistentSessionDataLoaded(GMPErr aStatus,
-                                   uint32_t aPromiseId,
-                                   const std::string& aSessionId,
-                                   const uint8_t* aKeyData,
-                                   uint32_t aKeyDataSize);
+  void Shutdown();
 
 private:
-  ~ClearKeyDecryptionManager();
-
-  void ClearInMemorySessionData(ClearKeySession* aSession);
-  void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
-
-  GMPDecryptorCallback* mCallback;
+  bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const;
 
   std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
-  std::map<std::string, ClearKeySession*> mSessions;
 };
 
-#endif // __ClearKeyDecryptor_h__
+#endif // __ClearKeyDecryptionManager_h__
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClearKeyPersistence.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
-#include "ClearKeyDecryptionManager.h"
+#include "ClearKeySessionManager.h"
 #include "mozilla/RefPtr.h"
 
 #include <stdint.h>
 #include <set>
 #include <vector>
 #include <sstream>
 
 using namespace mozilla;
@@ -92,17 +92,17 @@ ClearKeyPersistence::GetNewSessionId(GMP
   sNextSessionId++;
 
   return sessionId;
 }
 
 
 class CreateSessionTask : public GMPTask {
 public:
-  CreateSessionTask(ClearKeyDecryptionManager* aTarget,
+  CreateSessionTask(ClearKeySessionManager* aTarget,
                     uint32_t aCreateSessionToken,
                     uint32_t aPromiseId,
                     const uint8_t* aInitData,
                     uint32_t aInitDataSize,
                     GMPSessionType aSessionType)
     : mTarget(aTarget)
     , mCreateSessionToken(aCreateSessionToken)
     , mPromiseId(aPromiseId)
@@ -120,26 +120,26 @@ public:
                            &mInitData.front(),
                            mInitData.size(),
                            mSessionType);
   }
   virtual void Destroy() MOZ_OVERRIDE {
     delete this;
   }
 private:
-  RefPtr<ClearKeyDecryptionManager> mTarget;
+  RefPtr<ClearKeySessionManager> mTarget;
   uint32_t mCreateSessionToken;
   uint32_t mPromiseId;
   vector<uint8_t> mInitData;
   GMPSessionType mSessionType;
 };
 
 
 /* static */ bool
-ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
                                                   uint32_t aCreateSessionToken,
                                                   uint32_t aPromiseId,
                                                   const uint8_t* aInitData,
                                                   uint32_t aInitDataSize,
                                                   GMPSessionType aSessionType)
 {
   if (sPersistentKeyState >= LOADED)  {
     return false;
@@ -151,17 +151,17 @@ ClearKeyPersistence::DeferCreateSessionI
                                      aInitDataSize,
                                      aSessionType);
   sTasksBlockedOnSessionIdLoad.push_back(t);
   return true;
 }
 
 class LoadSessionTask : public GMPTask {
 public:
-  LoadSessionTask(ClearKeyDecryptionManager* aTarget,
+  LoadSessionTask(ClearKeySessionManager* aTarget,
                   uint32_t aPromiseId,
                   const char* aSessionId,
                   uint32_t aSessionIdLength)
     : mTarget(aTarget)
     , mPromiseId(aPromiseId)
     , mSessionId(aSessionId, aSessionId + aSessionIdLength)
   {
   }
@@ -169,23 +169,23 @@ public:
     mTarget->LoadSession(mPromiseId,
                          mSessionId.c_str(),
                          mSessionId.size());
   }
   virtual void Destroy() MOZ_OVERRIDE {
     delete this;
   }
 private:
-  RefPtr<ClearKeyDecryptionManager> mTarget;
+  RefPtr<ClearKeySessionManager> mTarget;
   uint32_t mPromiseId;
   string mSessionId;
 };
 
 /* static */ bool
-ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
                                                 uint32_t aPromiseId,
                                                 const char* aSessionId,
                                                 uint32_t aSessionIdLength)
 {
   if (sPersistentKeyState >= LOADED)  {
     return false;
   }
   GMPTask* t = new LoadSessionTask(aInstance,
@@ -199,39 +199,39 @@ ClearKeyPersistence::DeferLoadSessionIfN
 /* static */ bool
 ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
 {
   return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
 }
 
 class LoadSessionFromKeysTask : public ReadContinuation {
 public:
-  LoadSessionFromKeysTask(ClearKeyDecryptionManager* aTarget,
+  LoadSessionFromKeysTask(ClearKeySessionManager* aTarget,
                           const string& aSessionId,
                           uint32_t aPromiseId)
     : mTarget(aTarget)
     , mSessionId(aSessionId)
     , mPromiseId(aPromiseId)
   {
   }
 
   virtual void ReadComplete(GMPErr aStatus,
                             const uint8_t* aData,
                             uint32_t aLength) MOZ_OVERRIDE
   {
     mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
   }
 private:
-  RefPtr<ClearKeyDecryptionManager> mTarget;
+  RefPtr<ClearKeySessionManager> mTarget;
   string mSessionId;
   uint32_t mPromiseId;
 };
 
 /* static */ void
-ClearKeyPersistence::LoadSessionData(ClearKeyDecryptionManager* aInstance,
+ClearKeyPersistence::LoadSessionData(ClearKeySessionManager* aInstance,
                                      const string& aSid,
                                      uint32_t aPromiseId)
 {
   LoadSessionFromKeysTask* loadTask =
     new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
   ReadData(aSid, loadTask);
 }
 
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -3,38 +3,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __ClearKeyPersistence_h__
 #define __ClearKeyPersistence_h__
 
 #include <string>
 #include "gmp-decryption.h"
 
-class ClearKeyDecryptionManager;
+class ClearKeySessionManager;
 
 class ClearKeyPersistence {
 public:
   static void EnsureInitialized();
 
   static std::string GetNewSessionId(GMPSessionType aSessionType);
 
-  static bool DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+  static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
                                            uint32_t aCreateSessionToken,
                                            uint32_t aPromiseId,
                                            const uint8_t* aInitData,
                                            uint32_t aInitDataSize,
                                            GMPSessionType aSessionType);
 
-  static bool DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+  static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
                                          uint32_t aPromiseId,
                                          const char* aSessionId,
                                          uint32_t aSessionIdLength);
 
   static bool IsPersistentSessionId(const std::string& aSid);
 
-  static void LoadSessionData(ClearKeyDecryptionManager* aInstance,
+  static void LoadSessionData(ClearKeySessionManager* aInstance,
                               const std::string& aSid,
                               uint32_t aPromiseId);
 
   static void PersistentSessionRemoved(const std::string& aSid);
 };
 
 #endif // __ClearKeyPersistence_h__
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -27,17 +27,17 @@ public:
   void Init(uint32_t aCreateSessionToken,
             uint32_t aPromiseId,
             const uint8_t* aInitData, uint32_t aInitDataSize);
 
   GMPSessionType Type() const;
 
   void AddKeyId(const KeyId& aKeyId);
 
-  const std::string Id() const { return mSessionId; }
+  const std::string& Id() const { return mSessionId; }
 
 private:
   const std::string mSessionId;
   std::vector<KeyId> mKeyIds;
 
   GMPDecryptorCallback* mCallback;
   const GMPSessionType mSessionType;
 };
copy from media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
copy to media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -1,129 +1,65 @@
 /* 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 <stdint.h>
 #include <stdio.h>
 
 #include "ClearKeyDecryptionManager.h"
+#include "ClearKeySessionManager.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
 #include "ClearKeyPersistence.h"
 #include "gmp-task-utils.h"
 
 #include "mozilla/Assertions.h"
 
 using namespace mozilla;
 using namespace std;
 
-class ClearKeyDecryptor
+ClearKeySessionManager::ClearKeySessionManager()
+  : mDecryptionManager(ClearKeyDecryptionManager::Get())
 {
-public:
-  ClearKeyDecryptor(GMPDecryptorCallback* aCallback, const Key& aKey);
-  ~ClearKeyDecryptor();
-
-  void InitKey();
-
-  void QueueDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
-
-  uint32_t AddRef();
-  uint32_t Release();
-
-  const Key& DecryptionKey() const { return mKey; }
-
-private:
-  struct DecryptTask : public GMPTask
-  {
-    DecryptTask(ClearKeyDecryptor* aTarget, GMPBuffer* aBuffer,
-                GMPEncryptedBufferMetadata* aMetadata)
-      : mTarget(aTarget), mBuffer(aBuffer), mMetadata(aMetadata) { }
-
-    virtual void Run() MOZ_OVERRIDE
-    {
-      mTarget->Decrypt(mBuffer, mMetadata);
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DecryptTask() { }
-
-    ClearKeyDecryptor* mTarget;
-    GMPBuffer* mBuffer;
-    GMPEncryptedBufferMetadata* mMetadata;
-  };
+  CK_LOGD("ClearKeySessionManager ctor");
+  AddRef();
 
-  struct DestroyTask : public GMPTask
-  {
-    explicit DestroyTask(ClearKeyDecryptor* aTarget) : mTarget(aTarget) { }
-
-    virtual void Run() MOZ_OVERRIDE {
-      delete mTarget;
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DestroyTask() { }
-
-    ClearKeyDecryptor* mTarget;
-  };
-
-  void Decrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
-
-  uint32_t mRefCnt;
-
-  GMPDecryptorCallback* mCallback;
-  GMPThread* mThread;
-
-  Key mKey;
-};
-
-ClearKeyDecryptionManager::ClearKeyDecryptionManager()
-{
-  CK_LOGD("ClearKeyDecryptionManager ctor");
-
-  // The ClearKeyDecryptionManager maintains a self reference which is
-  // removed when the host is finished with the interface and calls
-  // DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so
-  // that the tasks to that we dispatch to call functions on it won't end up
-  // derefing a null reference after DecryptingComplete() is called.
-  AddRef();
+  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
+    CK_LOGD("failed to create thread in clearkey cdm");
+    mThread = nullptr;
+  }
 }
 
-ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
+ClearKeySessionManager::~ClearKeySessionManager()
 {
-  CK_LOGD("ClearKeyDecryptionManager dtor");
-  MOZ_ASSERT(mRefCount == 1);
+  CK_LOGD("ClearKeySessionManager dtor");
+   MOZ_ASSERT(!mRefCount);
 }
 
 void
-ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback)
+ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback)
 {
-  CK_LOGD("ClearKeyDecryptionManager::Init");
+  CK_LOGD("ClearKeySessionManager::Init");
   mCallback = aCallback;
   mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
                              GMP_EME_CAP_DECRYPT_VIDEO);
   ClearKeyPersistence::EnsureInitialized();
 }
 
 void
-ClearKeyDecryptionManager::CreateSession(uint32_t aCreateSessionToken,
-                                         uint32_t aPromiseId,
-                                         const char* aInitDataType,
-                                         uint32_t aInitDataTypeSize,
-                                         const uint8_t* aInitData,
-                                         uint32_t aInitDataSize,
-                                         GMPSessionType aSessionType)
+ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
+                                      uint32_t aPromiseId,
+                                      const char* aInitDataType,
+                                      uint32_t aInitDataTypeSize,
+                                      const uint8_t* aInitData,
+                                      uint32_t aInitDataSize,
+                                      GMPSessionType aSessionType)
 {
-  CK_LOGD("ClearKeyDecryptionManager::CreateSession type:%s", aInitDataType);
+  CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
 
   // initDataType must be "cenc".
   if (strcmp("cenc", aInitDataType)) {
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                              nullptr /* message */, 0 /* messageLen */);
     return;
   }
 
@@ -141,45 +77,43 @@ ClearKeyDecryptionManager::CreateSession
 
   ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
   session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize);
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
   for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
-    if (!Contains(mDecryptors, *it)) {
-      // Need to request this key ID from the client.
-      neededKeys.push_back(*it);
-    } else {
-      // We already have a key for this key ID. Mark as usable.
-      mCallback->KeyIdUsable(sessionId.c_str(), sessionId.length(),
-                             &(*it)[0], it->size());
-    }
+    // Need to request this key ID from the client. We always send a key
+    // request, whether or not another session has sent a request with the same
+    // key ID. Otherwise a script can end up waiting for another script to
+    // respond to the request (which may not necessarily happen).
+    neededKeys.push_back(*it);
+    mDecryptionManager->ExpectKeyId(*it);
   }
 
   if (neededKeys.empty()) {
     CK_LOGD("No keys needed from client.");
     return;
   }
 
   // Send a request for needed key data.
   string request;
   ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
   mCallback->SessionMessage(&sessionId[0], sessionId.length(),
                             (uint8_t*)&request[0], request.length(),
                             "" /* destination url */, 0);
 }
 
 void
-ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId,
-                                       const char* aSessionId,
-                                       uint32_t aSessionIdLength)
+ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
+                                    const char* aSessionId,
+                                    uint32_t aSessionIdLength)
 {
-  CK_LOGD("ClearKeyDecryptionManager::LoadSession");
+  CK_LOGD("ClearKeySessionManager::LoadSession");
 
   if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
     mCallback->ResolveLoadSessionPromise(aPromiseId, false);
     return;
   }
 
   if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
                                                       aPromiseId,
@@ -194,22 +128,23 @@ ClearKeyDecryptionManager::LoadSession(u
     return;
   }
 
   // Callsback PersistentSessionDataLoaded with results...
   ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
 }
 
 void
-ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus,
-                                                       uint32_t aPromiseId,
-                                                       const string& aSessionId,
-                                                       const uint8_t* aKeyData,
-                                                       uint32_t aKeyDataSize)
+ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
+                                                    uint32_t aPromiseId,
+                                                    const string& aSessionId,
+                                                    const uint8_t* aKeyData,
+                                                    uint32_t aKeyDataSize)
 {
+  CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded");
   if (GMP_FAILED(aStatus) ||
       Contains(mSessions, aSessionId) ||
       (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) {
     mCallback->ResolveLoadSessionPromise(aPromiseId, false);
     return;
   }
 
   ClearKeySession* session = new ClearKeySession(aSessionId,
@@ -224,35 +159,34 @@ ClearKeyDecryptionManager::PersistentSes
     KeyId keyId(base, base + CLEARKEY_KEY_LEN);
     MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
 
     Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
     MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
 
     session->AddKeyId(keyId);
 
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key);
-    }
-    mDecryptors[keyId]->AddRef();
-    mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(),
+    mDecryptionManager->ExpectKeyId(keyId);
+    mDecryptionManager->InitKey(keyId, key);
+    mKeyIds.insert(key);
+    mCallback->KeyIdUsable(&aSessionId[0], aSessionId.size(),
                            &keyId[0], keyId.size());
   }
 
   mCallback->ResolveLoadSessionPromise(aPromiseId, true);
 }
 
 void
-ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength,
-                                         const uint8_t* aResponse,
-                                         uint32_t aResponseSize)
+ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
+                                      const char* aSessionId,
+                                      uint32_t aSessionIdLength,
+                                      const uint8_t* aResponse,
+                                      uint32_t aResponseSize)
 {
-  CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
+  CK_LOGD("ClearKeySessionManager::UpdateSession");
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
 
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end() || !(itr->second)) {
     CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
@@ -262,25 +196,20 @@ ClearKeyDecryptionManager::UpdateSession
   vector<KeyIdPair> keyPairs;
   if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
     CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
     mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
     return;
   }
 
   for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
-    KeyId& keyId = it->mKeyId;
-
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, it->mKey);
-      mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
-                             &keyId[0], keyId.size());
-    }
-
-    mDecryptors[keyId]->AddRef();
+    mDecryptionManager->InitKey(it->mKeyId, it->mKey);
+    mKeyIds.insert(it->mKeyId);
+    mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
+                           &it->mKeyId[0], it->mKeyId.size());
   }
 
   if (session->Type() != kGMPPersistentSession) {
     mCallback->ResolvePromise(aPromiseId);
     return;
   }
 
   // Store the keys on disk. We store a record whose name is the sessionId,
@@ -294,39 +223,39 @@ ClearKeyDecryptionManager::UpdateSession
                              aPromiseId,
                              kGMPInvalidStateError,
                              message,
                              strlen(message));
   StoreData(sessionId, keydata, resolve, reject);
 }
 
 void
-ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession,
-                                     std::vector<uint8_t>& aOutKeyData)
+ClearKeySessionManager::Serialize(const ClearKeySession* aSession,
+                                  std::vector<uint8_t>& aOutKeyData)
 {
   const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
   for (size_t i = 0; i < keyIds.size(); i++) {
     const KeyId& keyId = keyIds[i];
-    if (!Contains(mDecryptors, keyId)) {
+    if (!mDecryptionManager->HasKeyForKeyId(keyId)) {
       continue;
     }
     MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
     aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
-    const Key& key = mDecryptors[keyId]->DecryptionKey();
+    const Key& key = mDecryptionManager->GetDecryptionKey(keyId);
     MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
     aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
   }
 }
 
 void
-ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId,
-                                        const char* aSessionId,
-                                        uint32_t aSessionIdLength)
+ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
+                                     const char* aSessionId,
+                                     uint32_t aSessionIdLength)
 {
-  CK_LOGD("ClearKeyDecryptionManager::CloseSession");
+  CK_LOGD("ClearKeySessionManager::CloseSession");
 
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end()) {
     CK_LOGW("ClearKey CDM couldn't close non-existent session.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
@@ -335,39 +264,40 @@ ClearKeyDecryptionManager::CloseSession(
   MOZ_ASSERT(session);
 
   ClearInMemorySessionData(session);
   mCallback->ResolvePromise(aPromiseId);
   mCallback->SessionClosed(aSessionId, aSessionIdLength);
 }
 
 void
-ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession)
+ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession)
 {
   MOZ_ASSERT(aSession);
 
   const vector<KeyId>& keyIds = aSession->GetKeyIds();
   for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
-    MOZ_ASSERT(Contains(mDecryptors, *it));
-    if (!mDecryptors[*it]->Release()) {
-      mDecryptors.erase(*it);
-      mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(),
-                                &(*it)[0], it->size());
-    }
+    MOZ_ASSERT(mDecryptionManager->HasKeyForKeyId(*it));
+    mDecryptionManager->ReleaseKeyId(*it);
+
+    const string& sessionId = aSession->Id();
+    mCallback->KeyIdNotUsable(&sessionId[0], sessionId.size(),
+                              &(*it)[0], it->size());
   }
 
   mSessions.erase(aSession->Id());
   delete aSession;
 }
 
 void
-ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength)
+ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
+                                      const char* aSessionId,
+                                      uint32_t aSessionIdLength)
 {
+  CK_LOGD("ClearKeySessionManager::RemoveSession");
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end()) {
     CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
 
@@ -394,162 +324,70 @@ ClearKeyDecryptionManager::RemoveSession
                              aPromiseId,
                              kGMPInvalidAccessError,
                              message,
                              strlen(message));
   StoreData(sessionId, emptyKeydata, resolve, reject);
 }
 
 void
-ClearKeyDecryptionManager::SetServerCertificate(uint32_t aPromiseId,
-                                                const uint8_t* aServerCert,
-                                                uint32_t aServerCertSize)
+ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId,
+                                             const uint8_t* aServerCert,
+                                             uint32_t aServerCertSize)
 {
   // ClearKey CDM doesn't support this method by spec.
-  CK_LOGD("ClearKeyDecryptionManager::SetServerCertificate");
+  CK_LOGD("ClearKeySessionManager::SetServerCertificate");
   mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                            nullptr /* message */, 0 /* messageLen */);
 }
 
 void
-ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer,
-                                   GMPEncryptedBufferMetadata* aMetadata)
+ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer,
+                                GMPEncryptedBufferMetadata* aMetadata)
 {
-  CK_LOGD("ClearKeyDecryptionManager::Decrypt");
-  KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
+  CK_LOGD("ClearKeySessionManager::Decrypt");
 
-  if (!Contains(mDecryptors, keyId)) {
-    CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr");
-    mCallback->Decrypted(aBuffer, GMPNoKeyErr);
+  if (!mThread) {
+    CK_LOGW("No decrypt thread");
+    mCallback->Decrypted(aBuffer, GMPGenericErr);
     return;
   }
 
-  mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
+  mThread->Post(WrapTask(this,
+                         &ClearKeySessionManager::DoDecrypt,
+                         aBuffer, aMetadata));
 }
 
 void
-ClearKeyDecryptionManager::DecryptingComplete()
+ClearKeySessionManager::DoDecrypt(GMPBuffer* aBuffer,
+                                  GMPEncryptedBufferMetadata* aMetadata)
 {
-  CK_LOGD("ClearKeyDecryptionManager::DecryptingComplete");
+  CK_LOGD("ClearKeySessionManager::DoDecrypt");
+
+  GMPErr rv = mDecryptionManager->Decrypt(aBuffer->Data(), aBuffer->Size(),
+                                              aMetadata);
+  CK_LOGD("DeDecrypt finished with code %x\n", rv);
+  mCallback->Decrypted(aBuffer, rv);
+}
+
+void
+ClearKeySessionManager::Shutdown()
+{
+  CK_LOGD("ClearKeySessionManager::Shutdown");
 
   for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
     delete it->second;
   }
   mSessions.clear();
-
-  for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
-    delete it->second;
-  }
-  mDecryptors.clear();
-
-  Release();
-}
-
-void
-ClearKeyDecryptor::QueueDecrypt(GMPBuffer* aBuffer,
-                                GMPEncryptedBufferMetadata* aMetadata)
-{
-  CK_LOGD("ClearKeyDecryptor::QueueDecrypt");
-  mThread->Post(new DecryptTask(this, aBuffer, aMetadata));
 }
 
 void
-ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer,
-                           GMPEncryptedBufferMetadata* aMetadata)
+ClearKeySessionManager::DecryptingComplete()
 {
-  if (!mThread) {
-    mCallback->Decrypted(aBuffer, GMPGenericErr);
-  }
-
-  // If the sample is split up into multiple encrypted subsamples, we need to
-  // stitch them into one continuous buffer for decryption.
-  vector<uint8_t> tmp(aBuffer->Size());
-
-  if (aMetadata->NumSubsamples()) {
-    // Take all encrypted parts of subsamples and stitch them into one
-    // continuous encrypted buffer.
-    unsigned char* data = aBuffer->Data();
-    unsigned char* iter = &tmp[0];
-    for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
-      data += aMetadata->ClearBytes()[i];
-      uint32_t cipherBytes = aMetadata->CipherBytes()[i];
-
-      memcpy(iter, data, cipherBytes);
-
-      data += cipherBytes;
-      iter += cipherBytes;
-    }
-
-    tmp.resize((size_t)(iter - &tmp[0]));
-  } else {
-    memcpy(&tmp[0], aBuffer->Data(), aBuffer->Size());
-  }
-
-  MOZ_ASSERT(aMetadata->IVSize() == 8 || aMetadata->IVSize() == 16);
-  vector<uint8_t> iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize());
-  iv.insert(iv.end(), CLEARKEY_KEY_LEN - aMetadata->IVSize(), 0);
-
-  ClearKeyUtils::DecryptAES(mKey, tmp, iv);
-
-  if (aMetadata->NumSubsamples()) {
-    // Take the decrypted buffer, split up into subsamples, and insert those
-    // subsamples back into their original position in the original buffer.
-    unsigned char* data = aBuffer->Data();
-    unsigned char* iter = &tmp[0];
-    for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
-      data += aMetadata->ClearBytes()[i];
-      uint32_t cipherBytes = aMetadata->CipherBytes()[i];
-
-      memcpy(data, iter, cipherBytes);
+  CK_LOGD("ClearKeySessionManager::DecryptingComplete");
 
-      data += cipherBytes;
-      iter += cipherBytes;
-    }
-  } else {
-    memcpy(aBuffer->Data(), &tmp[0], aBuffer->Size());
-  }
-
-  mCallback->Decrypted(aBuffer, GMPNoErr);
-}
-
-ClearKeyDecryptor::ClearKeyDecryptor(GMPDecryptorCallback* aCallback,
-                                     const Key& aKey)
-  : mRefCnt(0)
-  , mCallback(aCallback)
-  , mThread(nullptr)
-  , mKey(aKey)
-{
-  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
-    CK_LOGD("failed to create thread in clearkey cdm");
-    mThread = nullptr;
-    return;
-  }
-}
+  GMPThread* thread = mThread;
+  thread->Join();
 
-ClearKeyDecryptor::~ClearKeyDecryptor()
-{
-  CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]);
-}
-
-uint32_t
-ClearKeyDecryptor::AddRef()
-{
-  return ++mRefCnt;
+  Shutdown();
+  mDecryptionManager = nullptr;
+  Release();
 }
-
-uint32_t
-ClearKeyDecryptor::Release()
-{
-  uint32_t newCount = --mRefCnt;
-  if (!newCount) {
-    if (mThread) {
-      // Shutdown mThread. We cache a pointer to mThread, as the DestroyTask
-      // may run and delete |this| before Post() returns.
-      GMPThread* thread = mThread;
-      thread->Post(new DestroyTask(this));
-      thread->Join();
-    } else {
-      delete this;
-    }
-  }
-
-  return newCount;
-}
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h
@@ -0,0 +1,88 @@
+/* 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 __ClearKeyDecryptor_h__
+#define __ClearKeyDecryptor_h__
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "ClearKeyDecryptionManager.h"
+#include "ClearKeySession.h"
+#include "ClearKeyUtils.h"
+#include "gmp-api/gmp-decryption.h"
+#include "mozilla/RefPtr.h"
+#include "ScopedNSSTypes.h"
+#include "RefCounted.h"
+
+class ClearKeySessionManager MOZ_FINAL : public GMPDecryptor
+                                       , public RefCounted
+{
+public:
+  ClearKeySessionManager();
+
+  virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
+
+  virtual void CreateSession(uint32_t aCreateSessionToken,
+                             uint32_t aPromiseId,
+                             const char* aInitDataType,
+                             uint32_t aInitDataTypeSize,
+                             const uint8_t* aInitData,
+                             uint32_t aInitDataSize,
+                             GMPSessionType aSessionType) MOZ_OVERRIDE;
+
+  virtual void LoadSession(uint32_t aPromiseId,
+                           const char* aSessionId,
+                           uint32_t aSessionIdLength) MOZ_OVERRIDE;
+
+  virtual void UpdateSession(uint32_t aPromiseId,
+                             const char* aSessionId,
+                             uint32_t aSessionIdLength,
+                             const uint8_t* aResponse,
+                             uint32_t aResponseSize) MOZ_OVERRIDE;
+
+  virtual void CloseSession(uint32_t aPromiseId,
+                            const char* aSessionId,
+                            uint32_t aSessionIdLength) MOZ_OVERRIDE;
+
+  virtual void RemoveSession(uint32_t aPromiseId,
+                             const char* aSessionId,
+                             uint32_t aSessionIdLength) MOZ_OVERRIDE;
+
+  virtual void SetServerCertificate(uint32_t aPromiseId,
+                                    const uint8_t* aServerCert,
+                                    uint32_t aServerCertSize) MOZ_OVERRIDE;
+
+  virtual void Decrypt(GMPBuffer* aBuffer,
+                       GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
+
+  virtual void DecryptingComplete() MOZ_OVERRIDE;
+
+  void PersistentSessionDataLoaded(GMPErr aStatus,
+                                   uint32_t aPromiseId,
+                                   const std::string& aSessionId,
+                                   const uint8_t* aKeyData,
+                                   uint32_t aKeyDataSize);
+
+private:
+  ~ClearKeySessionManager();
+
+  void DoDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
+  void Shutdown();
+
+  void ClearInMemorySessionData(ClearKeySession* aSession);
+  void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
+
+  mozilla::RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
+
+  GMPDecryptorCallback* mCallback;
+  GMPThread* mThread;
+
+  std::set<KeyId> mKeyIds;
+  std::map<std::string, ClearKeySession*> mSessions;
+};
+
+#endif // __ClearKeyDecryptor_h__
--- a/media/gmp-clearkey/0.1/RefCounted.h
+++ b/media/gmp-clearkey/0.1/RefCounted.h
@@ -7,22 +7,22 @@
 
 // Note: Not thread safe!
 class RefCounted {
 public:
   void AddRef() {
     ++mRefCount;
   }
 
-  void Release() {
-    if (mRefCount == 1) {
+  uint32_t Release() {
+    uint32_t newCount = --mRefCount;
+    if (!newCount) {
       delete this;
-    } else {
-      --mRefCount;
     }
+    return newCount;
   }
 
 protected:
   RefCounted()
     : mRefCount(0)
   {
   }
   virtual ~RefCounted()
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdio.h>
 #include <string.h>
 
-#include "ClearKeyDecryptionManager.h"
+#include "ClearKeySessionManager.h"
 
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-platform.h"
 #include "mozilla/Attributes.h"
 
 static GMPPlatformAPI* sPlatform = nullptr;
 GMPPlatformAPI*
 GetPlatform()
@@ -29,17 +29,17 @@ GMPInit(GMPPlatformAPI* aPlatformAPI)
 
 MOZ_EXPORT GMPErr
 GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
 {
   if (strcmp(aApiName, GMP_API_DECRYPTOR)) {
     return GMPNotImplementedErr;
   }
 
-  *aPluginAPI = new ClearKeyDecryptionManager();
+  *aPluginAPI = new ClearKeySessionManager();
 
   return GMPNoErr;
 }
 
 MOZ_EXPORT GMPErr
 GMPShutdown(void)
 {
   return GMPNoErr;
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -7,16 +7,17 @@
 SharedLibrary('clearkey')
 
 FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
 
 UNIFIED_SOURCES += [
     'ClearKeyDecryptionManager.cpp',
     'ClearKeyPersistence.cpp',
     'ClearKeySession.cpp',
+    'ClearKeySessionManager.cpp',
     'ClearKeyStorage.cpp',
     'ClearKeyUtils.cpp',
     'gmp-clearkey.cpp',
     'openaes/oaes_lib.c',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/media/gmp',
--- a/netwerk/base/src/nsBaseChannel.cpp
+++ b/netwerk/base/src/nsBaseChannel.cpp
@@ -284,20 +284,25 @@ void
 nsBaseChannel::ClassifyURI()
 {
   // For channels created in the child process, delegate to the parent to
   // classify URIs.
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
+  nsresult rv;
+
   if (mLoadFlags & LOAD_CLASSIFY_URI) {
     nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
     if (classifier) {
-      classifier->Start(this);
+      rv = classifier->Start(this);
+      if (NS_FAILED(rv)) {
+        Cancel(rv);
+      }
     } else {
       Cancel(NS_ERROR_OUT_OF_MEMORY);
     }
   }
 }
 
 //-----------------------------------------------------------------------------
 // nsBaseChannel::nsISupports
--- a/netwerk/base/src/nsChannelClassifier.cpp
+++ b/netwerk/base/src/nsChannelClassifier.cpp
@@ -44,18 +44,17 @@ static PRLogModuleInfo *gChannelClassifi
 #endif
 #undef LOG
 #define LOG(args)     PR_LOG(gChannelClassifierLog, PR_LOG_DEBUG, args)
 
 NS_IMPL_ISUPPORTS(nsChannelClassifier,
                   nsIURIClassifierCallback)
 
 nsChannelClassifier::nsChannelClassifier()
-  : mIsAllowListed(false),
-    mSuspendedChannel(false)
+  : mIsAllowListed(false)
 {
 #if defined(PR_LOGGING)
     if (!gChannelClassifierLog)
         gChannelClassifierLog = PR_NewLogModule("nsChannelClassifier");
 #endif
 }
 
 nsresult
@@ -205,83 +204,71 @@ nsChannelClassifier::NotifyTrackingProte
     doc->SetHasTrackingContentLoaded(true);
     securityUI->GetState(&state);
     state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
     eventSink->OnSecurityChange(nullptr, state);
 
     return NS_OK;
 }
 
-void
+nsresult
 nsChannelClassifier::Start(nsIChannel *aChannel)
 {
-  mChannel = aChannel;
-  nsresult rv = StartInternal(aChannel);
-  if (NS_FAILED(rv)) {
-    // If we aren't getting a callback for any reason, assume a good verdict and
-    // make sure we resume the channel if necessary.
-    OnClassifyComplete(NS_OK);
-  }
-}
-
-nsresult
-nsChannelClassifier::StartInternal(nsIChannel *aChannel)
-{
     // Should only be called in the parent process.
     MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
     // Don't bother to run the classifier on a load that has already failed.
     // (this might happen after a redirect)
     nsresult status;
     aChannel->GetStatus(&status);
     if (NS_FAILED(status))
-        return status;
+        return NS_OK;
 
     // Don't bother to run the classifier on a cached load that was
-    // previously classified as good.
+    // previously classified.
     if (HasBeenClassified(aChannel)) {
-        return NS_ERROR_UNEXPECTED;
+        return NS_OK;
     }
 
     nsCOMPtr<nsIURI> uri;
     nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Don't bother checking certain types of URIs.
     bool hasFlags;
     rv = NS_URIChainHasFlags(uri,
                              nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (hasFlags) return NS_ERROR_UNEXPECTED;
+    if (hasFlags) return NS_OK;
 
     rv = NS_URIChainHasFlags(uri,
                              nsIProtocolHandler::URI_IS_LOCAL_FILE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (hasFlags) return NS_ERROR_UNEXPECTED;
+    if (hasFlags) return NS_OK;
 
     rv = NS_URIChainHasFlags(uri,
                              nsIProtocolHandler::URI_IS_UI_RESOURCE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (hasFlags) return NS_ERROR_UNEXPECTED;
+    if (hasFlags) return NS_OK;
 
     rv = NS_URIChainHasFlags(uri,
                              nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (hasFlags) return NS_ERROR_UNEXPECTED;
+    if (hasFlags) return NS_OK;
 
     nsCOMPtr<nsIURIClassifier> uriClassifier =
         do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
     if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
         rv == NS_ERROR_NOT_AVAILABLE) {
         // no URI classifier, ignore this failure.
-        return NS_ERROR_NOT_AVAILABLE;
+        return NS_OK;
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIScriptSecurityManager> securityManager =
         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIPrincipal> principal;
@@ -290,38 +277,34 @@ nsChannelClassifier::StartInternal(nsICh
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool expectCallback;
     bool trackingProtectionEnabled = false;
     (void)ShouldEnableTrackingProtection(aChannel, &trackingProtectionEnabled);
 
     rv = uriClassifier->Classify(principal, trackingProtectionEnabled, this,
                                  &expectCallback);
-    if (NS_FAILED(rv)) {
-        return rv;
-    }
+    if (NS_FAILED(rv)) return rv;
 
     if (expectCallback) {
         // Suspend the channel, it will be resumed when we get the classifier
         // callback.
         rv = aChannel->Suspend();
         if (NS_FAILED(rv)) {
             // Some channels (including nsJSChannel) fail on Suspend.  This
             // shouldn't be fatal, but will prevent malware from being
             // blocked on these channels.
-            LOG(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
-            return rv;
+            return NS_OK;
         }
 
-        mSuspendedChannel = true;
+        mSuspendedChannel = aChannel;
+#ifdef DEBUG
         LOG(("nsChannelClassifier[%p]: suspended channel %p",
-             this, mChannel.get()));
-    } else {
-        LOG(("nsChannelClassifier[%p]: not expecting callback", this));
-        return NS_ERROR_FAILURE;
+             this, mSuspendedChannel.get()));
+#endif
     }
 
     return NS_OK;
 }
 
 // Note in the cache entry that this URL was classified, so that future
 // cached loads don't need to be checked.
 void
@@ -330,17 +313,18 @@ nsChannelClassifier::MarkEntryClassified
     // Should only be called in the parent process.
     MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
     // Don't cache tracking classifications because we support allowlisting.
     if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) {
         return;
     }
 
-    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
+    nsCOMPtr<nsICachingChannel> cachingChannel =
+        do_QueryInterface(mSuspendedChannel);
     if (!cachingChannel) {
         return;
     }
 
     nsCOMPtr<nsISupports> cacheToken;
     cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
     if (!cacheToken) {
         return;
@@ -455,47 +439,41 @@ nsChannelClassifier::SetBlockedTrackingC
 }
 
 NS_IMETHODIMP
 nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
 {
     // Should only be called in the parent process.
     MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
-    LOG(("nsChannelClassifier[%p]:OnClassifyComplete", this));
     if (mSuspendedChannel) {
         MarkEntryClassified(aErrorCode);
 
         if (NS_FAILED(aErrorCode)) {
 #ifdef DEBUG
             nsCOMPtr<nsIURI> uri;
-            mChannel->GetURI(getter_AddRefs(uri));
+            mSuspendedChannel->GetURI(getter_AddRefs(uri));
             nsCString spec;
             uri->GetSpec(spec);
             LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
-                 "with error code: %x", this, mChannel.get(),
+                 "with error code: %x", this, mSuspendedChannel.get(),
                  spec.get(), aErrorCode));
 #endif
 
             // Channel will be cancelled (page element blocked) due to tracking.
             // Do update the security state of the document and fire a security
             // change event.
             if (aErrorCode == NS_ERROR_TRACKING_URI) {
-              SetBlockedTrackingContent(mChannel);
+              SetBlockedTrackingContent(mSuspendedChannel);
             }
 
-            mChannel->Cancel(aErrorCode);
-        }
+            mSuspendedChannel->Cancel(aErrorCode);
+          }
+#ifdef DEBUG
         LOG(("nsChannelClassifier[%p]: resuming channel %p from "
-             "OnClassifyComplete", this, mChannel.get()));
-        mChannel->Resume();
+             "OnClassifyComplete", this, mSuspendedChannel.get()));
+#endif
+        mSuspendedChannel->Resume();
+        mSuspendedChannel = nullptr;
     }
-    nsresult rv;
-    nsCOMPtr<nsIHttpChannelInternal> channel = do_QueryInterface(mChannel, &rv);
-    // Even if we have cancelled the channel, we need to call
-    // ContinueBeginConnect so that we abort appropriately.
-    if (NS_SUCCEEDED(rv)) {
-        channel->ContinueBeginConnect();
-    }
-    mChannel = nullptr;
 
     return NS_OK;
 }
--- a/netwerk/base/src/nsChannelClassifier.h
+++ b/netwerk/base/src/nsChannelClassifier.h
@@ -15,37 +15,26 @@ class nsIChannel;
 class nsChannelClassifier MOZ_FINAL : public nsIURIClassifierCallback
 {
 public:
     nsChannelClassifier();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIURICLASSIFIERCALLBACK
 
-    // Calls nsIURIClassifier.Classify with the principal of the given channel,
-    // and cancels the channel on a bad verdict.  If aChannel is
-    // nsIHttpChannelInternal, nsChannelClassifier must call
-    // ContinueBeginConnect once Start has successfully returned.
-    void Start(nsIChannel *aChannel);
+    nsresult Start(nsIChannel *aChannel);
 
 private:
-    // True if the channel is on the allow list.
+    nsCOMPtr<nsIChannel> mSuspendedChannel;
+    // Set true if the channel is on the allow list.
     bool mIsAllowListed;
-    // True if the channel has been suspended.
-    bool mSuspendedChannel;
-    nsCOMPtr<nsIChannel> mChannel;
 
     ~nsChannelClassifier() {}
-    // Caches good classifications for the channel principal.
     void MarkEntryClassified(nsresult status);
     bool HasBeenClassified(nsIChannel *aChannel);
-    // Helper function so that we ensure we call ContinueBeginConnect once
-    // Start is called. Returns NS_OK if and only if we will get a callback
-    // from the classifier service.
-    nsresult StartInternal(nsIChannel *aChannel);
     // Whether or not tracking protection should be enabled on this channel.
     nsresult ShouldEnableTrackingProtection(nsIChannel *aChannel, bool *result);
 
 public:
     // If we are blocking tracking content, update the corresponding flag in
     // the respective docshell and call nsISecurityEventSink::onSecurityChange.
     static nsresult SetBlockedTrackingContent(nsIChannel *channel);
     static nsresult NotifyTrackingProtectionDisabled(nsIChannel *aChannel);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1405,25 +1405,16 @@ HttpBaseChannel::RedirectTo(nsIURI *newU
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-HttpBaseChannel::ContinueBeginConnect()
-{
-  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default,
-             "The parent overrides this");
-  MOZ_ASSERT(false, "This method must be overridden");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 HttpBaseChannel::GetTopWindowURI(nsIURI **aTopWindowURI)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<mozIThirdPartyUtil> util;
   // Only compute the top window URI once. In e10s, this must be computed in the
   // child. The parent gets the top window URI through HttpChannelOpenArgs.
   if (!mTopWindowURI) {
     util = do_GetService(THIRDPARTYUTIL_CONTRACTID);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -183,17 +183,16 @@ public:
   NS_IMETHOD TakeAllSecurityMessages(nsCOMArray<nsISecurityConsoleMessage> &aMessages) MOZ_OVERRIDE;
   NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable) MOZ_OVERRIDE;
   NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable) MOZ_OVERRIDE;
   NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect) MOZ_OVERRIDE;
   NS_IMETHOD ForcePending(bool aForcePending) MOZ_OVERRIDE;
   NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) MOZ_OVERRIDE;
   NS_IMETHOD ForceNoIntercept() MOZ_OVERRIDE;
   NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) MOZ_OVERRIDE;
-  NS_IMETHOD ContinueBeginConnect();
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) MOZ_OVERRIDE;
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -17,17 +17,16 @@
 #include "nsICacheEntry.h"
 #include "nsICryptoHash.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIStringBundle.h"
 #include "nsIStreamListenerTee.h"
 #include "nsISeekableStream.h"
 #include "nsILoadGroupChild.h"
 #include "nsIProtocolProxyService2.h"
-#include "nsIURIClassifier.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "prprf.h"
 #include "prnetdb.h"
 #include "nsEscape.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsDNSPrefetch.h"
@@ -35,17 +34,16 @@
 #include "nsIRedirectResultListener.h"
 #include "mozilla/TimeStamp.h"
 #include "nsError.h"
 #include "nsPrintfCString.h"
 #include "nsAlgorithm.h"
 #include "GeckoProfiler.h"
 #include "nsIConsoleService.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/VisualEventTracer.h"
 #include "nsISSLSocketControl.h"
 #include "sslt.h"
 #include "nsContentUtils.h"
 #include "nsIClassOfService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
@@ -237,17 +235,16 @@ nsHttpChannel::nsHttpChannel()
     , mCacheEntryIsReadOnly(false)
     , mCacheEntryIsWriteOnly(false)
     , mCacheEntriesToWaitFor(0)
     , mHasQueryString(0)
     , mConcurentCacheAccess(0)
     , mIsPartialRequest(0)
     , mHasAutoRedirectVetoNotifier(0)
     , mPushedStream(nullptr)
-    , mLocalBlocklist(false)
     , mDidReval(false)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     mChannelCreationTime = PR_Now();
     mChannelCreationTimestamp = TimeStamp::Now();
 }
 
 nsHttpChannel::~nsHttpChannel()
@@ -324,17 +321,17 @@ nsHttpChannel::Connect()
     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
         return NS_ERROR_UNKNOWN_HOST;
 
     // Finalize ConnectionInfo flags before SpeculativeConnect
     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
     mConnectionInfo->SetPrivate(mPrivateBrowsing);
     mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
 
-    // Consider opening a TCP connection right away.
+    // Consider opening a TCP connection right away
     SpeculativeConnect();
 
     // Don't allow resuming when cache must be used
     if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
         LOG(("Resuming from cache is not supported yet"));
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
@@ -431,21 +428,21 @@ nsHttpChannel::ContinueConnect()
 }
 
 void
 nsHttpChannel::SpeculativeConnect()
 {
     // Before we take the latency hit of dealing with the cache, try and
     // get the TCP (and SSL) handshakes going so they can overlap.
 
-    // don't speculate if we are on a local blocklist, on uses of the offline
-    // application cache, if we are offline, when doing http upgrade (i.e.
-    // websockets bootstrap), or if we can't do keep-alive (because then we
-    // couldn't reuse the speculative connection anyhow).
-    if (mLocalBlocklist || mApplicationCache || gIOService->IsOffline() ||
+    // don't speculate on uses of the offline application cache,
+    // if we are offline, when doing http upgrade (i.e. websockets bootstrap),
+    // or if we can't do keep-alive (because then we couldn't reuse
+    // the speculative connection anyhow).
+    if (mApplicationCache || gIOService->IsOffline() ||
         mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE))
         return;
 
     // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
     // LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network,
     // so skip preconnects for them.
     if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE |
                       LOAD_NO_NETWORK_IO | LOAD_CHECK_OFFLINE_CACHE))
@@ -4871,31 +4868,16 @@ nsHttpChannel::BeginConnect()
     // notify "http-on-modify-request" observers
     CallOnModifyRequestObservers();
 
     // Check to see if we should redirect this channel elsewhere by
     // nsIHttpChannel.redirectTo API request
     if (mAPIRedirectToURI) {
         return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
     }
-    // Check to see if this principal exists on local blocklists.
-    if (mLoadFlags & LOAD_CLASSIFY_URI) {
-        nsCOMPtr<nsIURIClassifier> classifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
-        if (classifier) {
-            nsCOMPtr<nsIPrincipal> principal = GetPrincipal(false);
-            bool tp = Preferences::GetBool("privacy.trackingprotection.enabled",
-                                           false);
-            nsresult response = NS_OK;
-            classifier->ClassifyLocal(principal, tp, &response);
-            if (NS_FAILED(response)) {
-                LOG(("nsHttpChannel::Found principal on local blocklist [this=%p]", this));
-                mLocalBlocklist = true;
-            }
-        }
-    }
 
     // If mTimingEnabled flag is not set after OnModifyRequest() then
     // clear the already recorded AsyncOpen value for consistency.
     if (!mTimingEnabled)
         mAsyncOpenTime = TimeStamp();
 
     // when proxying only use the pipeline bit if ProxyPipelining() allows it.
     if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
@@ -4905,17 +4887,17 @@ nsHttpChannel::BeginConnect()
     }
 
     // if this somehow fails we can go on without it
     gHttpHandler->AddConnectionHeader(&mRequestHead.Headers(), mCaps);
 
     if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
         mCaps |= NS_HTTP_REFRESH_DNS;
 
-    if (!mLocalBlocklist && !mConnectionInfo->UsingHttpProxy() &&
+    if (!mConnectionInfo->UsingHttpProxy() &&
         !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
         // Start a DNS lookup very early in case the real open is queued the DNS can
         // happen in parallel. Do not do so in the presence of an HTTP proxy as
         // all lookups other than for the proxy itself are done by the proxy.
         // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
         // LOAD_ONLY_FROM_CACHE flags are set.
         //
         // We keep the DNS prefetch object around so that we can retrieve
@@ -4949,27 +4931,37 @@ nsHttpChannel::BeginConnect()
     // Force-Reload should reset the persistent connection pool for this host
     if (mLoadFlags & LOAD_FRESH_CONNECTION) {
         // just the initial document resets the whole pool
         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
             gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(mConnectionInfo);
         }
         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
     }
-    if (mCanceled || !mLocalBlocklist) {
-        return ContinueBeginConnect();
-    }
-    MOZ_ASSERT(!mCanceled && mLocalBlocklist);
-    // nsChannelClassifier must call ContinueBeginConnect after optionally
-    // cancelling the channel once we have a remote verdict. We call a concrete
-    // class instead of an nsI* that might be overridden.
-    nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
-    LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
-         classifier.get(), this));
-    classifier->Start(this);
+
+    // We may have been cancelled already, either by on-modify-request
+    // listeners or by load group observers; in that case, we should
+    // not send the request to the server
+    if (mCanceled)
+        rv = mStatus;
+    else
+        rv = Connect();
+    if (NS_FAILED(rv)) {
+        LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
+        CloseCacheEntry(true);
+        AsyncAbort(rv);
+    } else if (mLoadFlags & LOAD_CLASSIFY_URI) {
+        nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
+        rv = classifier->Start(this);
+        if (NS_FAILED(rv)) {
+            Cancel(rv);
+            return rv;
+        }
+    }
+
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -4997,40 +4989,16 @@ nsHttpChannel::SetPriority(int32_t value
         return NS_OK;
     mPriority = newValue;
     if (mTransaction)
         gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsHttpChannel::nsIHttpChannelInternal
-//-----------------------------------------------------------------------------
-NS_IMETHODIMP
-nsHttpChannel::ContinueBeginConnect()
-{
-    LOG(("nsHttpChannel::ContinueBeginConnect [this=%p]", this));
-    nsresult rv;
-    // We may have been cancelled already, either by on-modify-request
-    // listeners or load group observers or nsChannelClassifier; in that case,
-    // we should not send the request to the server
-    if (mCanceled) {
-        rv = mStatus;
-    } else {
-        rv = Connect();
-    }
-    if (NS_FAILED(rv)) {
-        LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
-        CloseCacheEntry(true);
-        AsyncAbort(rv);
-    }
-    return rv;
-}
-
-//-----------------------------------------------------------------------------
 // HttpChannel::nsIClassOfService
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 nsHttpChannel::SetClassFlags(uint32_t inFlags)
 {
     mClassOfService = inFlags;
     return NS_OK;
 }
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -114,17 +114,16 @@ public:
     NS_IMETHOD Cancel(nsresult status) MOZ_OVERRIDE;
     NS_IMETHOD Suspend() MOZ_OVERRIDE;
     NS_IMETHOD Resume() MOZ_OVERRIDE;
     // nsIChannel
     NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) MOZ_OVERRIDE;
     NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) MOZ_OVERRIDE;
     // nsIHttpChannelInternal
     NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) MOZ_OVERRIDE;
-    NS_IMETHOD ContinueBeginConnect();
     // nsISupportsPriority
     NS_IMETHOD SetPriority(int32_t value) MOZ_OVERRIDE;
     // nsIClassOfService
     NS_IMETHOD SetClassFlags(uint32_t inFlags) MOZ_OVERRIDE;
     NS_IMETHOD AddClassFlags(uint32_t inFlags) MOZ_OVERRIDE;
     NS_IMETHOD ClearClassFlags(uint32_t inFlags) MOZ_OVERRIDE;
 
     // nsIResumableChannel
@@ -461,19 +460,16 @@ private:
     uint32_t                          mHasAutoRedirectVetoNotifier : 1;
 
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     // Needed for accurate DNS timing
     nsRefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     Http2PushedStream                 *mPushedStream;
-    // True if the channel's principal was found on a phishing, malware, or
-    // tracking (if tracking protection is enabled) blocklist
-    bool                              mLocalBlocklist;
 
     nsresult WaitForRedirectCallback();
     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 
     nsCString mUsername;
 
 protected:
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -219,15 +219,9 @@ interface nsIHttpChannelInternal : nsISu
      * interception and proceed immediately to the network/cache.
      */
     void forceNoIntercept();
 
     /**
      * The URI of the top-level window that's associated with this channel.
      */
     readonly attribute nsIURI topWindowURI;
-
-    /**
-     * Used only by nsChannelClassifier to resume connecting or abort the
-     * channel after a remote classification verdict.
-     */
-    void continueBeginConnect();
 };
--- a/parser/html/nsHtml5DocumentBuilder.h
+++ b/parser/html/nsHtml5DocumentBuilder.h
@@ -97,18 +97,18 @@ public:
   void SetDocumentMode(nsHtml5DocumentMode m);
 
   void SetNodeInfoManager(nsNodeInfoManager* aManager)
   {
     mNodeInfoManager = aManager;
   }
 
   // nsContentSink methods
-  virtual void UpdateChildCounts();
-  virtual nsresult FlushTags();
+  virtual void UpdateChildCounts() MOZ_OVERRIDE;
+  virtual nsresult FlushTags() MOZ_OVERRIDE;
 
 protected:
 
   explicit nsHtml5DocumentBuilder(bool aRunsToCompletion);
   virtual ~nsHtml5DocumentBuilder();
 
 protected:
   nsAutoTArray<nsCOMPtr<nsIContent>, 32> mOwnedElements;
--- a/parser/html/nsHtml5Module.cpp
+++ b/parser/html/nsHtml5Module.cpp
@@ -87,17 +87,17 @@ nsHtml5Module::Initialize(nsIParser* aPa
 
 class nsHtml5ParserThreadTerminator MOZ_FINAL : public nsIObserver
 {
   public:
     NS_DECL_ISUPPORTS
     explicit nsHtml5ParserThreadTerminator(nsIThread* aThread)
       : mThread(aThread)
     {}
-    NS_IMETHODIMP Observe(nsISupports *, const char *topic, const char16_t *)
+    NS_IMETHODIMP Observe(nsISupports *, const char *topic, const char16_t *) MOZ_OVERRIDE
     {
       NS_ASSERTION(!strcmp(topic, "xpcom-shutdown-threads"), 
                    "Unexpected topic");
       if (mThread) {
         mThread->Shutdown();
         mThread = nullptr;
       }
       return NS_OK;
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -183,17 +183,17 @@ nsHtml5Parser::Parse(nsIURI* aURL,
                   "Can't call this Parse() variant on script-created parser");
   GetStreamParser()->SetObserver(aObserver);
   GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
   mExecutor->SetStreamParser(GetStreamParser());
   mExecutor->SetParser(this);
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
                      void* aKey,
                      const nsACString& aContentType,
                      bool aLastCall,
                      nsDTDMode aMode) // ignored
 {
   nsresult rv;
   if (NS_FAILED(rv = mExecutor->IsBroken())) {
@@ -531,24 +531,16 @@ nsHtml5Parser::CancelParsingEvents()
 
 void
 nsHtml5Parser::Reset()
 {
   NS_NOTREACHED("Don't call this!");
 }
 
 bool
-nsHtml5Parser::CanInterrupt()
-{
-  // nsContentSink needs this to let nsContentSink::DidProcessATokenImpl
-  // interrupt.
-  return true;
-}
-
-bool
 nsHtml5Parser::IsInsertionPointDefined()
 {
   return !mExecutor->IsFlushing() &&
     (!GetStreamParser() || mParserInsertedScriptsBeingEvaluated);
 }
 
 void
 nsHtml5Parser::BeginEvaluatingParserInsertedScript()
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -37,189 +37,184 @@ class nsHtml5Parser MOZ_FINAL : public n
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5Parser, nsIParser)
 
     nsHtml5Parser();
 
     /* Start nsIParser */
     /**
      * No-op for backwards compat.
      */
-    NS_IMETHOD_(void) SetContentSink(nsIContentSink* aSink);
+    NS_IMETHOD_(void) SetContentSink(nsIContentSink* aSink) MOZ_OVERRIDE;
 
     /**
      * Returns the tree op executor for backwards compat.
      */
-    NS_IMETHOD_(nsIContentSink*) GetContentSink();
+    NS_IMETHOD_(nsIContentSink*) GetContentSink() MOZ_OVERRIDE;
 
     /**
      * Always returns "view" for backwards compat.
      */
-    NS_IMETHOD_(void) GetCommand(nsCString& aCommand);
+    NS_IMETHOD_(void) GetCommand(nsCString& aCommand) MOZ_OVERRIDE;
 
     /**
      * No-op for backwards compat.
      */
-    NS_IMETHOD_(void) SetCommand(const char* aCommand);
+    NS_IMETHOD_(void) SetCommand(const char* aCommand) MOZ_OVERRIDE;
 
     /**
      * No-op for backwards compat.
      */
-    NS_IMETHOD_(void) SetCommand(eParserCommands aParserCommand);
+    NS_IMETHOD_(void) SetCommand(eParserCommands aParserCommand) MOZ_OVERRIDE;
 
     /**
      *  Call this method once you've created a parser, and want to instruct it
      *  about what charset to load
      *
      *  @param   aCharset the charset of a document
      *  @param   aCharsetSource the source of the charset
      */
-    NS_IMETHOD_(void) SetDocumentCharset(const nsACString& aCharset, int32_t aSource);
+    NS_IMETHOD_(void) SetDocumentCharset(const nsACString& aCharset, int32_t aSource) MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface compat only.
      */
-    NS_IMETHOD_(void) GetDocumentCharset(nsACString& aCharset, int32_t& aSource)
+    NS_IMETHOD_(void) GetDocumentCharset(nsACString& aCharset, int32_t& aSource) MOZ_OVERRIDE
     {
       NS_NOTREACHED("No one should call this.");
     }
 
     /**
      * Get the channel associated with this parser
      * @param aChannel out param that will contain the result
      * @return NS_OK if successful or NS_NOT_AVAILABLE if not
      */
-    NS_IMETHOD GetChannel(nsIChannel** aChannel);
+    NS_IMETHOD GetChannel(nsIChannel** aChannel) MOZ_OVERRIDE;
 
     /**
      * Return |this| for backwards compat.
      */
-    NS_IMETHOD GetDTD(nsIDTD** aDTD);
+    NS_IMETHOD GetDTD(nsIDTD** aDTD) MOZ_OVERRIDE;
 
     /**
      * Get the stream parser for this parser
      */
-    virtual nsIStreamListener* GetStreamListener();
+    virtual nsIStreamListener* GetStreamListener() MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface compat only.
      */
-    NS_IMETHOD ContinueInterruptedParsing();
+    NS_IMETHOD ContinueInterruptedParsing() MOZ_OVERRIDE;
 
     /**
      * Blocks the parser.
      */
-    NS_IMETHOD_(void) BlockParser();
+    NS_IMETHOD_(void) BlockParser() MOZ_OVERRIDE;
 
     /**
      * Unblocks the parser.
      */
-    NS_IMETHOD_(void) UnblockParser();
+    NS_IMETHOD_(void) UnblockParser() MOZ_OVERRIDE;
 
     /**
      * Asynchronously continues parsing.
      */
-    NS_IMETHOD_(void) ContinueInterruptedParsingAsync();
+    NS_IMETHOD_(void) ContinueInterruptedParsingAsync() MOZ_OVERRIDE;
 
     /**
      * Query whether the parser is enabled (i.e. not blocked) or not.
      */
-    NS_IMETHOD_(bool) IsParserEnabled();
+    NS_IMETHOD_(bool) IsParserEnabled() MOZ_OVERRIDE;
 
     /**
      * Query whether the parser thinks it's done with parsing.
      */
-    NS_IMETHOD_(bool) IsComplete();
+    NS_IMETHOD_(bool) IsComplete() MOZ_OVERRIDE;
 
     /**
      * Set up request observer.
      *
      * @param   aURL used for View Source title
      * @param   aListener a listener to forward notifications to
      * @param   aKey the root context key (used for document.write)
      * @param   aMode ignored (for interface compat only)
      */
     NS_IMETHOD Parse(nsIURI* aURL,
                      nsIRequestObserver* aListener = nullptr,
                      void* aKey = 0,
-                     nsDTDMode aMode = eDTDMode_autodetect);
+                     nsDTDMode aMode = eDTDMode_autodetect) MOZ_OVERRIDE;
 
     /**
      * document.write and document.close
      *
      * @param   aSourceBuffer the argument of document.write (empty for .close())
      * @param   aKey a key unique to the script element that caused this call
      * @param   aContentType "text/html" for HTML mode, else text/plain mode
      * @param   aLastCall true if .close() false if .write()
      * @param   aMode ignored (for interface compat only)
      */
-    NS_IMETHOD Parse(const nsAString& aSourceBuffer,
-                     void* aKey,
-                     const nsACString& aContentType,
-                     bool aLastCall,
-                     nsDTDMode aMode = eDTDMode_autodetect);
+    nsresult Parse(const nsAString& aSourceBuffer,
+                   void* aKey,
+                   const nsACString& aContentType,
+                   bool aLastCall,
+                   nsDTDMode aMode = eDTDMode_autodetect);
 
     /**
      * Stops the parser prematurely
      */
-    NS_IMETHOD Terminate();
+    NS_IMETHOD Terminate() MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface backwards compat only.
      */
     NS_IMETHOD ParseFragment(const nsAString& aSourceBuffer,
-                             nsTArray<nsString>& aTagStack);
+                             nsTArray<nsString>& aTagStack) MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface compat only.
      */
-    NS_IMETHOD BuildModel();
+    NS_IMETHOD BuildModel() MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface compat only.
      */
-    NS_IMETHODIMP CancelParsingEvents();
+    NS_IMETHODIMP CancelParsingEvents() MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface compat only.
      */
-    virtual void Reset();
-    
-    /**
-     * True in fragment mode and during synchronous document.write
-     */
-    virtual bool CanInterrupt();
+    virtual void Reset() MOZ_OVERRIDE;
 
     /**
      * True if the insertion point (per HTML5) is defined.
      */
-    virtual bool IsInsertionPointDefined();
+    virtual bool IsInsertionPointDefined() MOZ_OVERRIDE;
 
     /**
      * Call immediately before starting to evaluate a parser-inserted script.
      */
-    virtual void BeginEvaluatingParserInsertedScript();
+    virtual void BeginEvaluatingParserInsertedScript() MOZ_OVERRIDE;
 
     /**
      * Call immediately after having evaluated a parser-inserted script.
      */
-    virtual void EndEvaluatingParserInsertedScript();
+    virtual void EndEvaluatingParserInsertedScript() MOZ_OVERRIDE;
 
     /**
      * Marks the HTML5 parser as not a script-created parser: Prepares the 
      * parser to be able to read a stream.
      *
      * @param aCommand the parser command (Yeah, this is bad API design. Let's
      * make this better when retiring nsIParser)
      */
-    virtual void MarkAsNotScriptCreated(const char* aCommand);
+    virtual void MarkAsNotScriptCreated(const char* aCommand) MOZ_OVERRIDE;
 
     /**
      * True if this is a script-created HTML5 parser.
      */
-    virtual bool IsScriptCreated();
+    virtual bool IsScriptCreated() MOZ_OVERRIDE;
 
     /* End nsIParser  */
 
     // Not from an external interface
     // Non-inherited methods
 
   public:
 
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -132,17 +132,17 @@ class nsHtml5StreamParser : public nsICh
     nsresult OnStopRequest(nsIRequest* aRequest,
                            nsISupports* aContext,
                            nsresult status);
 
     // nsICharsetDetectionObserver
     /**
      * Chardet calls this to report the detection result
      */
-    NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf);
+    NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf) MOZ_OVERRIDE;
 
     // EncodingDeclarationHandler
     // http://hg.mozilla.org/projects/htmlparser/file/tip/src/nu/validator/htmlparser/common/EncodingDeclarationHandler.java
     /**
      * Tree builder uses this to report a late <meta charset>
      */
     bool internalEncodingDeclaration(nsString* aEncoding);
 
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -104,86 +104,80 @@ class nsHtml5TreeOpExecutor MOZ_FINAL : 
 
   public:
 
     // nsIContentSink
 
     /**
      * Unimplemented. For interface compat only.
      */
-    NS_IMETHOD WillParse();
+    NS_IMETHOD WillParse() MOZ_OVERRIDE;
 
     /**
      * 
      */
-    NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
+    NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) MOZ_OVERRIDE;
 
     /**
      * Emits EOF.
      */
-    NS_IMETHOD DidBuildModel(bool aTerminated);
+    NS_IMETHOD DidBuildModel(bool aTerminated) MOZ_OVERRIDE;
 
     /**
      * Forwards to nsContentSink
      */
-    NS_IMETHOD WillInterrupt();
+    NS_IMETHOD WillInterrupt() MOZ_OVERRIDE;
 
     /**
      * Unimplemented. For interface compat only.
      */
-    NS_IMETHOD WillResume();
+    NS_IMETHOD WillResume() MOZ_OVERRIDE;
 
     /**
      * Sets the parser.
      */
-    NS_IMETHOD SetParser(nsParserBase* aParser);
+    NS_IMETHOD SetParser(nsParserBase* aParser) MOZ_OVERRIDE;
 
     /**
      * No-op for backwards compat.
      */
-    virtual void FlushPendingNotifications(mozFlushType aType);
+    virtual void FlushPendingNotifications(mozFlushType aType) MOZ_OVERRIDE;
 
     /**
      * Don't call. For interface compat only.
      */
-    NS_IMETHOD SetDocumentCharset(nsACString& aCharset) {
+    NS_IMETHOD SetDocumentCharset(nsACString& aCharset) MOZ_OVERRIDE {
     	NS_NOTREACHED("No one should call this.");
     	return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     /**
      * Returns the document.
      */
-    virtual nsISupports *GetTarget();
+    virtual nsISupports *GetTarget() MOZ_OVERRIDE;
   
-    virtual void ContinueInterruptedParsingAsync();
- 
-    // XXX Does anyone need this?
-    nsIDocShell* GetDocShell()
-    {
-      return mDocShell;
-    }
+    virtual void ContinueInterruptedParsingAsync() MOZ_OVERRIDE;
 
-    bool IsScriptExecuting()
+    bool IsScriptExecuting() MOZ_OVERRIDE
     {
       return IsScriptExecutingImpl();
     }
 
     // Not from interface
 
     void SetStreamParser(nsHtml5StreamParser* aStreamParser)
     {
       mStreamParser = aStreamParser;
     }
     
     void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, int32_t aLine);
 
     bool IsScriptEnabled();
 
-    virtual nsresult MarkAsBroken(nsresult aReason);
+    virtual nsresult MarkAsBroken(nsresult aReason) MOZ_OVERRIDE;
 
     void StartLayout();
     
     void FlushSpeculativeLoads();
                   
     void RunFlushLoop();
 
     nsresult FlushDocumentWrite();
@@ -225,17 +219,17 @@ class nsHtml5TreeOpExecutor MOZ_FINAL : 
 #endif
     
     void RunScript(nsIContent* aScriptElement);
     
     /**
      * Flush the operations from the tree operations from the argument
      * queue unconditionally. (This is for the main thread case.)
      */
-    virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
+    virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue) MOZ_OVERRIDE;
     
     nsHtml5TreeOpStage* GetStage()
     {
       return &mStage;
     }
     
     void StartReadingFromStage()
     {
--- a/parser/htmlparser/nsIDTD.h
+++ b/parser/htmlparser/nsIDTD.h
@@ -122,17 +122,17 @@ public:
      * DTD actually is using, as it may differ from aParserContext.mDTDMode.
      */
     NS_IMETHOD_(nsDTDMode) GetMode() const = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDTD, NS_IDTD_IID)
 
 #define NS_DECL_NSIDTD \
-    NS_IMETHOD WillBuildModel(  const CParserContext& aParserContext, nsITokenizer* aTokenizer, nsIContentSink* aSink);\
-    NS_IMETHOD DidBuildModel(nsresult anErrorCode);\
-    NS_IMETHOD BuildModel(nsITokenizer* aTokenizer, nsIContentSink* aSink);\
-    NS_IMETHOD_(bool) CanContain(int32_t aParent,int32_t aChild) const;\
-    NS_IMETHOD_(bool) IsContainer(int32_t aTag) const;\
-    NS_IMETHOD_(void)  Terminate();\
-    NS_IMETHOD_(int32_t) GetType();\
-    NS_IMETHOD_(nsDTDMode) GetMode() const;
+    NS_IMETHOD WillBuildModel(  const CParserContext& aParserContext, nsITokenizer* aTokenizer, nsIContentSink* aSink) MOZ_OVERRIDE;\
+    NS_IMETHOD DidBuildModel(nsresult anErrorCode) MOZ_OVERRIDE;\
+    NS_IMETHOD BuildModel(nsITokenizer* aTokenizer, nsIContentSink* aSink) MOZ_OVERRIDE;\
+    NS_IMETHOD_(bool) CanContain(int32_t aParent,int32_t aChild) const MOZ_OVERRIDE;\
+    NS_IMETHOD_(bool) IsContainer(int32_t aTag) const MOZ_OVERRIDE;\
+    NS_IMETHOD_(void)  Terminate() MOZ_OVERRIDE;\
+    NS_IMETHOD_(int32_t) GetType() MOZ_OVERRIDE;\
+    NS_IMETHOD_(nsDTDMode) GetMode() const MOZ_OVERRIDE;
 #endif /* nsIDTD_h___ */
--- a/parser/htmlparser/nsITokenizer.h
+++ b/parser/htmlparser/nsITokenizer.h
@@ -32,13 +32,13 @@ public:
 
   NS_IMETHOD                     WillTokenize(bool aIsFinalChunk)=0;
   NS_IMETHOD                     ConsumeToken(nsScanner& aScanner,bool& aFlushTokens)=0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsITokenizer, NS_ITOKENIZER_IID)
 
 #define NS_DECL_NSITOKENIZER \
-  NS_IMETHOD                     WillTokenize(bool aIsFinalChunk);\
-  NS_IMETHOD                     ConsumeToken(nsScanner& aScanner,bool& aFlushTokens);\
+  NS_IMETHOD                     WillTokenize(bool aIsFinalChunk) MOZ_OVERRIDE;\
+  NS_IMETHOD                     ConsumeToken(nsScanner& aScanner,bool& aFlushTokens) MOZ_OVERRIDE;\
 
 
 #endif
--- a/parser/htmlparser/nsParser.h
+++ b/parser/htmlparser/nsParser.h
@@ -93,103 +93,103 @@ class nsParser MOZ_FINAL : public nsIPar
     nsParser();
 
     /**
      * Select given content sink into parser for parser output
      * @update	gess5/11/98
      * @param   aSink is the new sink to be used by parser
      * @return  old sink, or nullptr
      */
-    NS_IMETHOD_(void) SetContentSink(nsIContentSink* aSink);
+    NS_IMETHOD_(void) SetContentSink(nsIContentSink* aSink) MOZ_OVERRIDE;
 
     /**
      * retrive the sink set into the parser 
      * @update	gess5/11/98
      * @param   aSink is the new sink to be used by parser
      * @return  old sink, or nullptr
      */
-    NS_IMETHOD_(nsIContentSink*) GetContentSink(void);
+    NS_IMETHOD_(nsIContentSink*) GetContentSink(void) MOZ_OVERRIDE;
     
     /**
      *  Call this method once you've created a parser, and want to instruct it
      *  about the command which caused the parser to be constructed. For example,
      *  this allows us to select a DTD which can do, say, view-source.
      *  
      *  @update  gess 3/25/98
      *  @param   aCommand -- ptrs to string that contains command
      *  @return	 nada
      */
-    NS_IMETHOD_(void) GetCommand(nsCString& aCommand);
-    NS_IMETHOD_(void) SetCommand(const char* aCommand);
-    NS_IMETHOD_(void) SetCommand(eParserCommands aParserCommand);
+    NS_IMETHOD_(void) GetCommand(nsCString& aCommand) MOZ_OVERRIDE;
+    NS_IMETHOD_(void) SetCommand(const char* aCommand) MOZ_OVERRIDE;
+    NS_IMETHOD_(void) SetCommand(eParserCommands aParserCommand) MOZ_OVERRIDE;
 
     /**
      *  Call this method once you've created a parser, and want to instruct it
      *  about what charset to load
      *  
      *  @update  ftang 4/23/99
      *  @param   aCharset- the charset of a document
      *  @param   aCharsetSource- the source of the charset
      *  @return	 nada
      */
-    NS_IMETHOD_(void) SetDocumentCharset(const nsACString& aCharset, int32_t aSource);
+    NS_IMETHOD_(void) SetDocumentCharset(const nsACString& aCharset, int32_t aSource) MOZ_OVERRIDE;
 
-    NS_IMETHOD_(void) GetDocumentCharset(nsACString& aCharset, int32_t& aSource)
+    NS_IMETHOD_(void) GetDocumentCharset(nsACString& aCharset, int32_t& aSource) MOZ_OVERRIDE
     {
          aCharset = mCharset;
          aSource = mCharsetSource;
     }
 
     /**
      * Cause parser to parse input from given URL 
      * @update	gess5/11/98
      * @param   aURL is a descriptor for source document
      * @param   aListener is a listener to forward notifications to
      * @return  TRUE if all went well -- FALSE otherwise
      */
     NS_IMETHOD Parse(nsIURI* aURL,
                      nsIRequestObserver* aListener = nullptr,
                      void* aKey = 0,
-                     nsDTDMode aMode = eDTDMode_autodetect);
+                     nsDTDMode aMode = eDTDMode_autodetect) MOZ_OVERRIDE;
 
     /**
      * This method needs documentation
      */
     NS_IMETHOD ParseFragment(const nsAString& aSourceBuffer,
-                             nsTArray<nsString>& aTagStack);
+                             nsTArray<nsString>& aTagStack) MOZ_OVERRIDE;
                              
     /**
      * This method gets called when the tokens have been consumed, and it's time
      * to build the model via the content sink.
      * @update	gess5/11/98
      * @return  YES if model building went well -- NO otherwise.
      */
-    NS_IMETHOD BuildModel(void);
+    NS_IMETHOD BuildModel(void) MOZ_OVERRIDE;
 
-    NS_IMETHOD        ContinueInterruptedParsing();
-    NS_IMETHOD_(void) BlockParser();
-    NS_IMETHOD_(void) UnblockParser();
-    NS_IMETHOD_(void) ContinueInterruptedParsingAsync();
-    NS_IMETHOD        Terminate(void);
+    NS_IMETHOD        ContinueInterruptedParsing() MOZ_OVERRIDE;
+    NS_IMETHOD_(void) BlockParser() MOZ_OVERRIDE;
+    NS_IMETHOD_(void) UnblockParser() MOZ_OVERRIDE;
+    NS_IMETHOD_(void) ContinueInterruptedParsingAsync() MOZ_OVERRIDE;
+    NS_IMETHOD        Terminate(void) MOZ_OVERRIDE;
 
     /**
      * Call this to query whether the parser is enabled or not.
      *
      *  @update  vidur 4/12/99
      *  @return  current state
      */
-    NS_IMETHOD_(bool) IsParserEnabled();
+    NS_IMETHOD_(bool) IsParserEnabled() MOZ_OVERRIDE;
 
     /**
      * Call this to query whether the parser thinks it's done with parsing.
      *
      *  @update  rickg 5/12/01
      *  @return  complete state
      */
-    NS_IMETHOD_(bool) IsComplete();
+    NS_IMETHOD_(bool) IsComplete() MOZ_OVERRIDE;
 
     /**
      *  This rather arcane method (hack) is used as a signal between the
      *  DTD and the parser. It allows the DTD to tell the parser that content
      *  that comes through (parser::parser(string)) but not consumed should
      *  propagate into the next string based parse call.
      *  
      *  @update  gess 9/1/98
@@ -222,64 +222,64 @@ class nsParser MOZ_FINAL : public nsIPar
     CParserContext*   PeekContext() {return mParserContext;}
 
     /** 
      * Get the channel associated with this parser
      * @update harishd,gagan 07/17/01
      * @param aChannel out param that will contain the result
      * @return NS_OK if successful
      */
-    NS_IMETHOD GetChannel(nsIChannel** aChannel);
+    NS_IMETHOD GetChannel(nsIChannel** aChannel) MOZ_OVERRIDE;
 
     /** 
      * Get the DTD associated with this parser
      * @update vidur 9/29/99
      * @param aDTD out param that will contain the result
      * @return NS_OK if successful, NS_ERROR_FAILURE for runtime error
      */
-    NS_IMETHOD GetDTD(nsIDTD** aDTD);
+    NS_IMETHOD GetDTD(nsIDTD** aDTD) MOZ_OVERRIDE;
   
     /**
      * Get the nsIStreamListener for this parser
      */
-    virtual nsIStreamListener* GetStreamListener();
+    virtual nsIStreamListener* GetStreamListener() MOZ_OVERRIDE;
 
     void SetSinkCharset(nsACString& aCharset);
 
     /**
      *  Removes continue parsing events
      *  @update  kmcclusk 5/18/98
      */
 
-    NS_IMETHODIMP CancelParsingEvents();
+    NS_IMETHODIMP CancelParsingEvents() MOZ_OVERRIDE;
 
     /**
      * Return true.
      */
-    virtual bool IsInsertionPointDefined();
+    virtual bool IsInsertionPointDefined() MOZ_OVERRIDE;
 
     /**
      * No-op.
      */
-    virtual void BeginEvaluatingParserInsertedScript();
+    virtual void BeginEvaluatingParserInsertedScript() MOZ_OVERRIDE;
 
     /**
      * No-op.
      */
-    virtual void EndEvaluatingParserInsertedScript();
+    virtual void EndEvaluatingParserInsertedScript() MOZ_OVERRIDE;
 
     /**
      * No-op.
      */
-    virtual void MarkAsNotScriptCreated(const char* aCommand);
+    virtual void MarkAsNotScriptCreated(const char* aCommand) MOZ_OVERRIDE;
 
     /**
      * Always false.
      */
-    virtual bool IsScriptCreated();
+    virtual bool IsScriptCreated() MOZ_OVERRIDE;
 
     /**  
      *  Set to parser state to indicate whether parsing tokens can be interrupted
      *  @param aCanInterrupt true if parser can be interrupted, false if it can not be interrupted.
      *  @update  kmcclusk 5/18/98
      */
     void SetCanInterrupt(bool aCanInterrupt);
 
@@ -294,17 +294,17 @@ class nsParser MOZ_FINAL : public nsIPar
     nsresult PostContinueEvent();
 
     /**
      *  Fired when the continue parse event is triggered.
      *  @update  kmcclusk 5/18/98
      */
     void HandleParserContinueEvent(class nsParserContinueEvent *);
 
-    virtual void Reset() {
+    virtual void Reset() MOZ_OVERRIDE {
       Cleanup();
       Initialize();
     }
 
     bool IsScriptExecuting() {
       return mSink && mSink->IsScriptExecuting();
     }
 
--- a/parser/htmlparser/nsParserService.h
+++ b/parser/htmlparser/nsParserService.h
@@ -16,27 +16,27 @@ extern "C" int MOZ_XMLTranslateEntity(co
 class nsParserService : public nsIParserService {
   virtual ~nsParserService();
 
 public:
   nsParserService();
 
   NS_DECL_ISUPPORTS
 
-  int32_t HTMLAtomTagToId(nsIAtom* aAtom) const;
+  int32_t HTMLAtomTagToId(nsIAtom* aAtom) const MOZ_OVERRIDE;
 
-  int32_t HTMLCaseSensitiveAtomTagToId(nsIAtom* aAtom) const;
+  int32_t HTMLCaseSensitiveAtomTagToId(nsIAtom* aAtom) const MOZ_OVERRIDE;
 
-  int32_t HTMLStringTagToId(const nsAString& aTag) const;
+  int32_t HTMLStringTagToId(const nsAString& aTag) const MOZ_OVERRIDE;
 
-  const char16_t *HTMLIdToStringTag(int32_t aId) const;
+  const char16_t *HTMLIdToStringTag(int32_t aId) const MOZ_OVERRIDE;
   
-  nsIAtom *HTMLIdToAtomTag(int32_t aId) const;
+  nsIAtom *HTMLIdToAtomTag(int32_t aId) const MOZ_OVERRIDE;
 
   NS_IMETHOD HTMLConvertEntityToUnicode(const nsAString& aEntity, 
-                                        int32_t* aUnicode) const;
+                                        int32_t* aUnicode) const MOZ_OVERRIDE;
   NS_IMETHOD HTMLConvertUnicodeToEntity(int32_t aUnicode,
-                                        nsCString& aEntity) const;
-  NS_IMETHOD IsContainer(int32_t aId, bool& aIsContainer) const;
-  NS_IMETHOD IsBlock(int32_t aId, bool& aIsBlock) const;
+                                        nsCString& aEntity) const MOZ_OVERRIDE;
+  NS_IMETHOD IsContainer(int32_t aId, bool& aIsContainer) const MOZ_OVERRIDE;
+  NS_IMETHOD IsBlock(int32_t aId, bool& aIsBlock) const MOZ_OVERRIDE;
 };
 
 #endif
--- a/parser/xml/nsSAXXMLReader.h
+++ b/parser/xml/nsSAXXMLReader.h
@@ -36,45 +36,45 @@ public:
   NS_DECL_NSIEXTENDEDEXPATSINK
   NS_DECL_NSISAXXMLREADER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
 
   nsSAXXMLReader();
 
   //nsIContentSink
-  NS_IMETHOD WillParse()
+  NS_IMETHOD WillParse() MOZ_OVERRIDE
   {
     return NS_OK;
   }
 
-  NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
-  NS_IMETHOD DidBuildModel(bool aTerminated);
-  NS_IMETHOD SetParser(nsParserBase* aParser);
+  NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) MOZ_OVERRIDE;
+  NS_IMETHOD DidBuildModel(bool aTerminated) MOZ_OVERRIDE;
+  NS_IMETHOD SetParser(nsParserBase* aParser) MOZ_OVERRIDE;
   
-  NS_IMETHOD WillInterrupt()
+  NS_IMETHOD WillInterrupt() MOZ_OVERRIDE
   {
     return NS_OK;
   }
 
-  NS_IMETHOD WillResume()
+  NS_IMETHOD WillResume() MOZ_OVERRIDE
   {
     return NS_OK;
   }
   
-  virtual void FlushPendingNotifications(mozFlushType aType)
+  virtual void FlushPendingNotifications(mozFlushType aType) MOZ_OVERRIDE
   {
   }
   
-  NS_IMETHOD SetDocumentCharset(nsACString& aCharset)
+  NS_IMETHOD SetDocumentCharset(nsACString& aCharset) MOZ_OVERRIDE
   {
     return NS_OK;
   }
   
-  virtual nsISupports *GetTarget()
+  virtual nsISupports *GetTarget() MOZ_OVERRIDE
   {
     return nullptr;
   }
 
 private:
   ~nsSAXXMLReader() {}
 
   nsCOMPtr<nsISAXContentHandler> mContentHandler;
new file mode 100644
--- /dev/null
+++ b/python/redo/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: redo
+Version: 1.4
+Summary: Utilities to retry Python callables.
+Home-page: https://github.com/bhearsum/redo
+Author: Ben Hearsum
+Author-email: ben@hearsum.ca
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
new file mode 100644
--- /dev/null
+++ b/python/redo/README
@@ -0,0 +1,4 @@
+Redo - Utilities to retry Python callables
+******************************************
+
+Redo provides various means to add seamless retriability to any Python callable. Redo includes a plain function (redo.retry), a decorator (redo.retriable), and a context manager (redo.retrying) to enable you to integrate it in the best possible way for your project. As a bonus, a standalone interface is also included ("retry"). For details and sample invocations have a look at the docstrings in redo/__init__.py.
new file mode 100644
--- /dev/null
+++ b/python/redo/redo.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: redo
+Version: 1.4
+Summary: Utilities to retry Python callables.
+Home-page: https://github.com/bhearsum/redo
+Author: Ben Hearsum
+Author-email: ben@hearsum.ca
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
new file mode 100644
--- /dev/null
+++ b/python/redo/redo.egg-info/SOURCES.txt
@@ -0,0 +1,9 @@
+README
+setup.py
+redo/__init__.py
+redo/cmd.py
+redo.egg-info/PKG-INFO
+redo.egg-info/SOURCES.txt
+redo.egg-info/dependency_links.txt
+redo.egg-info/entry_points.txt
+redo.egg-info/top_level.txt
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/python/redo/redo.egg-info/dependency_links.txt
@@ -0,0 +1,1 @@
+
new file mode 100644
--- /dev/null
+++ b/python/redo/redo.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+retry = redo.cmd:main
+
new file mode 100644
--- /dev/null
+++ b/python/redo/redo.egg-info/top_level.txt
@@ -0,0 +1,1 @@
+redo
new file mode 100644
--- /dev/null
+++ b/python/redo/redo/__init__.py
@@ -0,0 +1,218 @@
+# ***** BEGIN LICENSE BLOCK *****
+# 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/.
+# ***** END LICENSE BLOCK *****
+
+import time
+from functools import wraps
+from contextlib import contextmanager
+import logging
+import random
+log = logging.getLogger(__name__)
+
+
+def retrier(attempts=5, sleeptime=10, max_sleeptime=300, sleepscale=1.5, jitter=1):
+    """
+    A generator function that sleeps between retries, handles exponential
+    backoff and jitter. The action you are retrying is meant to run after
+    retrier yields.
+
+    At each iteration, we sleep for sleeptime + random.randint(-jitter, jitter).
+    Afterwards sleeptime is multiplied by sleepscale for the next iteration.
+
+    Args:
+        attempts (int): maximum number of times to try; defaults to 5
+        sleeptime (float): how many seconds to sleep between tries; defaults to
+                           60s (one minute)
+        max_sleeptime (float): the longest we'll sleep, in seconds; defaults to
+                               300s (five minutes)
+        sleepscale (float): how much to multiply the sleep time by each
+                            iteration; defaults to 1.5
+        jitter (int): random jitter to introduce to sleep time each iteration.
+                      the amount is chosen at random between [-jitter, +jitter]
+                      defaults to 1
+
+    Yields:
+        None, a maximum of `attempts` number of times
+
+    Example:
+        >>> n = 0
+        >>> for _ in retrier(sleeptime=0, jitter=0):
+        ...     if n == 3:
+        ...         # We did the thing!
+        ...         break
+        ...     n += 1
+        >>> n
+        3
+
+        >>> n = 0
+        >>> for _ in retrier(sleeptime=0, jitter=0):
+        ...     if n == 6:
+        ...         # We did the thing!
+        ...         break
+        ...     n += 1
+        ... else:
+        ...     print "max tries hit"
+        max tries hit
+    """
+    for _ in range(attempts):
+        log.debug("attempt %i/%i", _ + 1, attempts)
+        yield
+        if jitter:
+            sleeptime += random.randint(-jitter, jitter)
+            sleeptime = max(sleeptime, 0)
+
+        if _ == attempts - 1:
+            # Don't need to sleep the last time
+            break
+        log.debug("sleeping for %.2fs (attempt %i/%i)", sleeptime, _ + 1, attempts)
+        time.sleep(sleeptime)
+        sleeptime *= sleepscale
+        if sleeptime > max_sleeptime:
+            sleeptime = max_sleeptime
+
+
+def retry(action, attempts=5, sleeptime=60, max_sleeptime=5 * 60,
+          sleepscale=1.5, jitter=1, retry_exceptions=(Exception,),
+          cleanup=None, args=(), kwargs={}):
+    """
+    Calls an action function until it succeeds, or we give up.
+
+    Args:
+        action (callable): the function to retry
+        attempts (int): maximum number of times to try; defaults to 5
+        sleeptime (float): how many seconds to sleep between tries; defaults to
+                           60s (one minute)
+        max_sleeptime (float): the longest we'll sleep, in seconds; defaults to
+                               300s (five minutes)
+        sleepscale (float): how much to multiply the sleep time by each
+                            iteration; defaults to 1.5
+        jitter (int): random jitter to introduce to sleep time each iteration.
+                      the amount is chosen at random between [-jitter, +jitter]
+                      defaults to 1
+        retry_exceptions (tuple): tuple of exceptions to be caught. If other
+                                  exceptions are raised by action(), then these
+                                  are immediately re-raised to the caller.
+        cleanup (callable): optional; called if one of `retry_exceptions` is
+                            caught. No arguments are passed to the cleanup
+                            function; if your cleanup requires arguments,
+                            consider using functools.partial or a lambda
+                            function.
+        args (tuple): positional arguments to call `action` with
+        hwargs (dict): keyword arguments to call `action` with
+
+    Returns:
+        Whatever action(*args, **kwargs) returns
+
+    Raises:
+        Whatever action(*args, **kwargs) raises. `retry_exceptions` are caught
+        up until the last attempt, in which case they are re-raised.
+
+    Example:
+        >>> count = 0
+        >>> def foo():
+        ...     global count
+        ...     count += 1
+        ...     print count
+        ...     if count < 3:
+        ...         raise ValueError("count is too small!")
+        ...     return "success!"
+        >>> retry(foo, sleeptime=0, jitter=0)
+        1
+        2
+        3
+        'success!'
+    """
+    assert callable(action)
+    assert not cleanup or callable(cleanup)
+    if max_sleeptime < sleeptime:
+        log.debug("max_sleeptime %d less than sleeptime %d" % (
+            max_sleeptime, sleeptime))
+
+    n = 1
+    for _ in retrier(attempts=attempts, sleeptime=sleeptime,
+                     max_sleeptime=max_sleeptime, sleepscale=sleepscale,
+                     jitter=jitter):
+        try:
+            log.info("retry: Calling %s with args: %s, kwargs: %s, "
+                     "attempt #%d" % (action, str(args), str(kwargs), n))
+            return action(*args, **kwargs)
+        except retry_exceptions:
+            log.debug("retry: Caught exception: ", exc_info=True)
+            if cleanup:
+                cleanup()
+            if n == attempts:
+                log.info("retry: Giving up on %s" % action)
+                raise
+            continue
+        finally:
+            n += 1
+
+
+def retriable(*retry_args, **retry_kwargs):
+    """
+    A decorator factory for retry(). Wrap your function in @retriable(...) to
+    give it retry powers!
+
+    Arguments:
+        Same as for `retry`, with the exception of `action`, `args`, and `kwargs`,
+        which are left to the normal function definition.
+
+    Returns:
+        A function decorator
+
+    Example:
+        >>> count = 0
+        >>> @retriable(sleeptime=0, jitter=0)
+        ... def foo():
+        ...     global count
+        ...     count += 1
+        ...     print count
+        ...     if count < 3:
+        ...         raise ValueError("count too small")
+        ...     return "success!"
+        >>> foo()
+        1
+        2
+        3
+        'success!'
+    """
+    def _retriable_factory(func):
+        @wraps(func)
+        def _retriable_wrapper(*args, **kwargs):
+            return retry(func, args=args, kwargs=kwargs, *retry_args,
+                         **retry_kwargs)
+        return _retriable_wrapper
+    return _retriable_factory
+
+
+@contextmanager
+def retrying(func, *retry_args, **retry_kwargs):
+    """
+    A context manager for wrapping functions with retry functionality.
+
+    Arguments:
+        func (callable): the function to wrap
+        other arguments as per `retry`
+
+    Returns:
+        A context manager that returns retriable(func) on __enter__
+
+    Example:
+        >>> count = 0
+        >>> def foo():
+        ...     global count
+        ...     count += 1
+        ...     print count
+        ...     if count < 3:
+        ...         raise ValueError("count too small")
+        ...     return "success!"
+        >>> with retrying(foo, sleeptime=0, jitter=0) as f:
+        ...     f()
+        1
+        2
+        3
+        'success!'
+    """
+    yield retriable(*retry_args, **retry_kwargs)(func)
new file mode 100644
--- /dev/null
+++ b/python/redo/redo/cmd.py
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# 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/.
+# ***** END LICENSE BLOCK *****
+import logging
+from subprocess import check_call, CalledProcessError
+import sys
+
+from redo import retrying
+
+log = logging.getLogger(__name__)
+
+
+def main():
+    from argparse import ArgumentParser
+
+    parser = ArgumentParser()
+    parser.add_argument(
+        "-a", "--attempts", type=int, default=5,
+        help="How many times to retry.")
+    parser.add_argument(
+        "-s", "--sleeptime", type=int, default=60,
+        help="How long to sleep between attempts. Sleeptime doubles after each attempt.")
+    parser.add_argument(
+        "-m", "--max-sleeptime", type=int, default=5*60,
+        help="Maximum length of time to sleep between attempts (limits backoff length).")
+    parser.add_argument("-v", "--verbose", action="store_true", default=False)
+    parser.add_argument("cmd", nargs="+", help="Command to run. Eg: wget http://blah")
+
+    args = parser.parse_args()
+
+    if args.verbose:
+        logging.basicConfig(level=logging.INFO)
+        logging.getLogger("retry").setLevel(logging.INFO)
+    else:
+        logging.basicConfig(level=logging.ERROR)
+        logging.getLogger("retry").setLevel(logging.ERROR)
+
+    try:
+        with retrying(check_call, attempts=args.attempts, sleeptime=args.sleeptime,
+                      max_sleeptime=args.max_sleeptime,
+                      retry_exceptions=(CalledProcessError,)) as r_check_call:
+            r_check_call(args.cmd)
+    except KeyboardInterrupt:
+        sys.exit(-1)
+    except Exception as e:
+        log.error("Unable to run command after %d attempts" % args.attempts, exc_info=True)
+        rc = getattr(e, "returncode", -2)
+        sys.exit(rc)
+
+if __name__ == "__main__":
+    main()
new file mode 100644
--- /dev/null
+++ b/python/redo/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
new file mode 100644
--- /dev/null
+++ b/python/redo/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+setup(
+    name="redo",
+    version="1.4",
+    description="Utilities to retry Python callables.",
+    author="Ben Hearsum",
+    author_email="ben@hearsum.ca",
+    packages=["redo"],
+    entry_points={
+        "console_scripts": ["retry = redo.cmd:main"],
+    },
+    url="https://github.com/bhearsum/redo",
+)
--- a/services/datareporting/DataReportingService.js
+++ b/services/datareporting/DataReportingService.js
@@ -66,16 +66,20 @@ this.DataReportingService = function () 
                .getService(Ci.nsIObserverService);
 
   this._clientID = null;
   this._loadClientIdTask = null;
   this._saveClientIdTask = null;
 
   this._stateDir = null;
   this._stateFilePath = null;
+
+  // Used for testing only, when true results in getSessionRecorder() returning
+  // undefined. Controlled via simulate* methods.
+  this._simulateNoSessionRecorder = false;
 }
 
 DataReportingService.prototype = Object.freeze({
   classID: Components.ID("{41f6ae36-a79f-4613-9ac3-915e70f83789}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
@@ -392,17 +396,27 @@ DataReportingService.prototype = Object.
   }),
 
   /**
    * Returns the SessionRecorder instance associated with the data reporting service.
    * Returns an actual object only if FHR is enabled and after initialization,
    * else returns undefined.
    */
   getSessionRecorder: function() {
-    return this.sessionRecorder;
+    return this._simulateNoSessionRecorder ? undefined : this.sessionRecorder;
+  },
+
+  // These two simulate* methods below are only used for testings and control
+  // whether getSessionRecorder() behaves normally or forced to return undefined
+  simulateNoSessionRecorder() {
+    this._simulateNoSessionRecorder = true;
+  },
+
+  simulateRestoreSessionRecorder() {
+    this._simulateNoSessionRecorder = false;
   },
 
   /*
    * Simulate a restart of the service. This is for testing only.
    */
   _reset: Task.async(function* () {
     yield this._loadClientIdTask;
     yield this._saveClientIdTask;
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -368,17 +368,17 @@ Tester.prototype = {
 
       if (this.currentTest.passCount === 0 &&
           this.currentTest.failCount === 0 &&
           this.currentTest.todoCount === 0) {
         this.currentTest.addResult(new testResult(false, "This test contains no passes, no fails and no todos. Maybe it threw a silent exception? Make sure you use waitForExplicitFinish() if you need it.", "", false));
       }
 
       if (testScope.__expected == 'fail' && testScope.__num_failed <= 0) {
-        this.currentTest.addResult(new testResult(false, "We expected at least one assertion to fail because this test file was marked as fail-if in the manifest", "", false));
+        this.currentTest.addResult(new testResult(false, "We expected at least one assertion to fail because this test file was marked as fail-if in the manifest!", "", true));
       }
 
       this.Promise.Debugging.flushUncaughtErrors();
 
       let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
       if (winUtils.isTestControllingRefreshes) {
         this.currentTest.addResult(new testResult(false, "test left refresh driver under test control", "", false));
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -966,18 +966,18 @@ SimpleTest.finish = function() {
         } else {
             dump(err + '\n');
         }
     }
 
     if (SimpleTest.expected == 'fail' && SimpleTest.num_failed <= 0) {
         msg = 'We expected at least one failure';
         var test = {'result': false, 'name': 'fail-if condition in manifest', 'diag': msg};
-        var successInfo = {status:"PASS", expected:"PASS", message:"TEST-PASS"};
-        var failureInfo = {status:"FAIL", expected:"FAIL", message:"TEST-KNOWN-FAIL"};
+        var successInfo = {status:"FAIL", expected:"FAIL", message:"TEST-KNOWN-FAIL"};
+        var failureInfo = {status:"PASS", expected:"FAIL", message:"TEST-UNEXPECTED-PASS"};
 
         SimpleTest._logResult(test, successInfo, failureInfo);
         SimpleTest._tests.push(test);
     }
 
     SimpleTest.testsLength = SimpleTest._tests.length;
 
     SimpleTest._alreadyFinished = true;
--- a/testing/mozbase/mozinstall/mozinstall/mozinstall.py
+++ b/testing/mozbase/mozinstall/mozinstall/mozinstall.py
@@ -119,19 +119,19 @@ def install(src, dest):
             install_dir = mozfile.extract(src, dest)[0]
         elif src.lower().endswith('.dmg'):
             install_dir = _install_dmg(src, dest)
         elif src.lower().endswith('.exe'):
             install_dir = _install_exe(src, dest)
 
         return install_dir
 
-    except Exception:
+    except Exception, ex:
         cls, exc, trbk = sys.exc_info()
-        error = InstallError('Failed to install "%s"' % src)
+        error = InstallError('Failed to install "%s (%s)"' % src, str(ex))
         raise InstallError, error, trbk
 
     finally:
         # trbk won't get GC'ed due to circular reference
         # http://docs.python.org/library/sys.html#sys.exc_info
         del trbk
 
 
@@ -208,19 +208,19 @@ def uninstall(install_folder):
                 # folder has been removed or until we run into a timeout.
                 end_time = time.time() + TIMEOUT_UNINSTALL
                 while os.path.exists(uninstall_folder):
                     time.sleep(1)
 
                     if time.time() > end_time:
                         raise Exception('Failure removing uninstall folder.')
 
-            except Exception:
+            except Exception, ex:
                 cls, exc, trbk = sys.exc_info()
-                error = UninstallError('Failed to uninstall %s' % install_folder)
+                error = UninstallError('Failed to uninstall %s (%s)' % install_folder, str(ex))
                 raise UninstallError, error, trbk
 
             finally:
                 # trbk won't get GC'ed due to circular reference
                 # http://docs.python.org/library/sys.html#sys.exc_info
                 del trbk
 
     # Ensure that we remove any trace of the installation. Even the uninstaller
--- a/testing/web-platform/meta/gamepad/idlharness.html.ini
+++ b/testing/web-platform/meta/gamepad/idlharness.html.ini
@@ -1,8 +1,5 @@
 [idlharness.html]
   type: testharness
-  [Gamepad interface: attribute timestamp]
-    expected: FAIL
-
   [GamepadEvent interface: existence and properties of interface object]
     expected: FAIL
 
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
@@ -43,24 +43,25 @@ const NUMBER_OF_THREADS_TO_LAUNCH = 30;
 let gNumberOfThreadsLaunched = 0;
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_ENABLED = PREF_BRANCH + "enabled";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 const PREF_FHR_SERVICE_ENABLED = "datareporting.healthreport.service.enabled";
 
 const HAS_DATAREPORTINGSERVICE = "@mozilla.org/datareporting/service;1" in Cc;
+const SESSION_RECORDER_EXPECTED = HAS_DATAREPORTINGSERVICE &&
+                                  Preferences.get(PREF_FHR_SERVICE_ENABLED, true);
 
 const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
 
 let gHttpServer = new HttpServer();
 let gServerStarted = false;
 let gRequestIterator = null;
 let gDataReportingClientID = null;
-let gOrigFhrServiceEnabled = true;
 
 XPCOMUtils.defineLazyGetter(this, "gDatareportingService",
   () => Cc["@mozilla.org/datareporting/service;1"]
           .getService(Ci.nsISupports)
           .wrappedJSObject);
 
 function sendPing () {
   TelemetryPing.gatherStartup();
@@ -220,17 +221,17 @@ function checkPayload(request, reason, s
   do_check_true(payload.simpleMeasurements.uptime >= 0);
   do_check_true(payload.simpleMeasurements.startupInterrupted === 1);
   do_check_eq(payload.simpleMeasurements.shutdownDuration, SHUTDOWN_TIME);
   do_check_eq(payload.simpleMeasurements.savedPings, 1);
   do_check_true("maximalNumberOfConcurrentThreads" in payload.simpleMeasurements);
   do_check_true(payload.simpleMeasurements.maximalNumberOfConcurrentThreads >= gNumberOfThreadsLaunched);
 
   let activeTicks = payload.simpleMeasurements.activeTicks;
-  do_check_true(HAS_DATAREPORTINGSERVICE ? activeTicks >= 0 : activeTicks == -1);
+  do_check_true(SESSION_RECORDER_EXPECTED ? activeTicks >= 0 : activeTicks == -1);
 
   do_check_eq(payload.simpleMeasurements.failedProfileLockCount,
               FAILED_PROFILE_LOCK_ATTEMPTS);
   let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile);
   let failedProfileLocksFile = profileDirectory.clone();
   failedProfileLocksFile.append("Telemetry.FailedProfileLocks.txt");
   do_check_true(!failedProfileLocksFile.exists());
 
@@ -434,37 +435,26 @@ function run_test() {
   try {
     let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
     gfxInfo.spoofVendorID("0xabcd");
     gfxInfo.spoofDeviceID("0x1234");
   } catch (x) {
     // If we can't test gfxInfo, that's fine, we'll note it later.
   }
 
-  // make sure getSessionRecorder() can be called before the DRS init.
-  // It's not a requirement that it returns undefined, but that's how it behaves
-  // now - so just let this test fail if this behavior changes.
-  if (HAS_DATAREPORTINGSERVICE) {
-    do_check_true(gDatareportingService.getSessionRecorder() === undefined);
-  }
-
   // Addon manager needs a profile directory
   do_get_profile();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   Services.prefs.setBoolPref(PREF_ENABLED, true);
   Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true);
 
   // Send the needed startup notifications to the datareporting service
   // to ensure that it has been initialized.
   if (HAS_DATAREPORTINGSERVICE) {
-    // Initially disable FHR to verify that activeTicks ends up as -1
-    gOrigFhrServiceEnabled = Services.prefs.getBoolPref(PREF_FHR_SERVICE_ENABLED, true);
-    Services.prefs.setBoolPref(PREF_FHR_SERVICE_ENABLED, false);
-
     gDatareportingService.observe(null, "app-startup", null);
     gDatareportingService.observe(null, "profile-after-change", null);
   }
 
   // Make it look like we've previously failed to lock a profile a couple times.
   write_fake_failedprofilelocks_file();
 
   // Make it look like we've shutdown before.
@@ -508,27 +498,27 @@ function actualTest() {
   registerFakePluginHost();
 
   run_next_test();
 }
 
 add_task(function* asyncSetup() {
   yield TelemetryPing.setup();
 
-  // When FHR is disabled or no DRS, the payload's activeTicks should be -1.
-  do_check_true(TelemetryPing.getPayload().simpleMeasurements.activeTicks == -1);
+  if (HAS_DATAREPORTINGSERVICE) {
+    // force getSessionRecorder()==undefined to check the payload's activeTicks
+    gDatareportingService.simulateNoSessionRecorder();
+  }
+
+  // When no DRS or no DRS.getSessionRecorder(), activeTicks should be -1.
+  do_check_eq(TelemetryPing.getPayload().simpleMeasurements.activeTicks, -1);
 
   if (HAS_DATAREPORTINGSERVICE) {
-    // after we got the no-FHR activeTicks, re-enable FHR and re-init the DRS.
-    // Note: this relies on the fact that the data reporting service reinitializes
-    // itself when calling its 'observe' method, without checking if it's already
-    // initialized. If this DRS behavior changes, this test would need to be adapted.
-    Services.prefs.setBoolPref(PREF_FHR_SERVICE_ENABLED, gOrigFhrServiceEnabled);
-    gDatareportingService.observe(null, "app-startup", null);
-    gDatareportingService.observe(null, "profile-after-change", null);
+    // Restore normal behavior for getSessionRecorder()
+    gDatareportingService.simulateRestoreSessionRecorder();
 
     gDataReportingClientID = yield gDatareportingService.getClientID();
 
     // We should have cached the client id now. Lets confirm that by
     // checking the client id before the async ping setup is finished.
     let promisePingSetup = TelemetryPing.reset();
     do_check_eq(TelemetryPing.clientID, gDataReportingClientID);
     yield promisePingSetup;