Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 18 Jan 2017 14:17:10 -0800
changeset 330018 96cb95af530477edb66ae48d98c18533476e57bb
parent 330017 ef4c8016909ecef30588b8b30c1fa2d113f3c27f (current diff)
parent 329935 7715d62057e50fdafddeca167ecf2d1dbd79b35b (diff)
child 330019 85d46919b0ea62b39d76f3a9d3a9e93fb7d6e956
child 330118 f0e76631e0eb450d7a10a5474a4b25c057033377
push id85864
push userkwierso@gmail.com
push dateWed, 18 Jan 2017 23:39:58 +0000
treeherdermozilla-inbound@85d46919b0ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly mac
96cb95af5304 / 53.0a1 / 20170119030211 / files
nightly win32
96cb95af5304 / 53.0a1 / 20170119030211 / files
nightly win64
96cb95af5304 / 53.0a1 / 20170119030211 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge MozReview-Commit-ID: 3cGydlfeaPN
dom/url/tests/test_url.html
layout/base/PresShell.cpp
media/gmp-clearkey/0.1/AnnexB.cpp
media/gmp-clearkey/0.1/AnnexB.h
media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp
media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h
media/gmp-clearkey/0.1/clearkey.info.in
media/gmp-clearkey/0.1/gmp-task-utils-generated.h
media/gmp-clearkey/0.1/gmp-task-utils.h
taskcluster/taskgraph/transforms/tests.py
testing/mochitest/runtests.py
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -797,16 +797,16 @@ bin/libfreebl_32int64_3.so
 #endif
 
 #ifdef PACKAGE_MOZTT
 @RESPATH@/fonts/*
 #endif
 
 ; media
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
-@RESPATH@/gmp-clearkey/0.1/clearkey.info
+@RESPATH@/gmp-clearkey/0.1/manifest.json
 
 #ifdef PKG_LOCALE_MANIFEST
 #include @PKG_LOCALE_MANIFEST@
 #endif
 
 @RESPATH@/components/simpleServices.js
 @RESPATH@/components/utils.manifest
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -792,17 +792,17 @@ bin/libfreebl_32int64_3.so
 
 #if defined(MOZ_ASAN) && defined(CLANG_CL)
 @BINPATH@/clang_rt.asan_dynamic-*.dll
 #endif
 
 
 ; media
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
-@RESPATH@/gmp-clearkey/0.1/clearkey.info
+@RESPATH@/gmp-clearkey/0.1/manifest.json
 
 ; gfx
 #ifdef XP_WIN
 @RESPATH@/components/GfxSanityTest.manifest
 @RESPATH@/components/SanityTest.js
 #endif
 
 #ifdef MOZ_MULET
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -164,17 +164,20 @@ nsPrincipal::GetOriginInternal(nsACStrin
     return NS_OK;
   }
 
   // This URL can be a blobURL. In this case, we should use the 'parent'
   // principal instead.
   nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(origin);
   if (uriWithPrincipal) {
     nsCOMPtr<nsIPrincipal> uriPrincipal;
-    if (uriWithPrincipal) {
+    rv = uriWithPrincipal->GetPrincipal(getter_AddRefs(uriPrincipal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (uriPrincipal) {
       return uriPrincipal->GetOriginNoSuffix(aOrigin);
     }
   }
 
   // If we reached this branch, we can only create an origin if we have a
   // nsIStandardURL.  So, we query to a nsIStandardURL, and fail if we aren't
   // an instance of an nsIStandardURL nsIStandardURLs have the good property
   // of escaping the '^' character in their specs, which means that we can be
--- a/devtools/client/shared/widgets/MdnDocsWidget.js
+++ b/devtools/client/shared/widgets/MdnDocsWidget.js
@@ -263,16 +263,19 @@ function MdnDocsWidget(tooltipContainer)
     syntax: tooltipContainer.querySelector(".mdn-syntax"),
     info: tooltipContainer.querySelector(".mdn-property-info"),
     linkToMdn: tooltipContainer.querySelector(".mdn-visit-page")
   };
 
   // get the localized string for the link text
   this.elements.linkToMdn.textContent = L10N.getStr("docsTooltip.visitMDN");
 
+  // force using LTR because we use the en-US version of MDN
+  tooltipContainer.setAttribute("dir", "ltr");
+
   // listen for clicks and open in the browser window instead
   let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
   this.elements.linkToMdn.addEventListener("click", (e) => {
     e.stopPropagation();
     e.preventDefault();
     mainWindow.openUILinkIn(e.target.href, "tab");
     this.emit("visitlink");
   });
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -687,18 +687,17 @@ ReadDirectoryInternal(JSStructuredCloneR
   path.SetLength(aPathLength);
   size_t charSize = sizeof(nsString::char_type);
   if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(),
                     aPathLength * charSize)) {
     return nullptr;
   }
 
   nsCOMPtr<nsIFile> file;
-  nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
-                                      getter_AddRefs(file));
+  nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   RefPtr<Directory> directory =
     Directory::Create(aHolder->ParentDuringRead(), file);
   return directory.forget();
 }
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -369,18 +369,18 @@ nsContentIterator::Init(nsIDOMRange* aDO
       // XXX: In the future, if start offset is after the last
       //      character in the cdata node, should we set mFirst to
       //      the next sibling?
 
       // If the node has no child, the child may be <br> or something.
       // So, we shouldn't skip the empty node if the start offset is 0.
       // In other words, if the offset is 1, the node should be ignored.
       if (!startIsData && startIndx) {
-        mFirst = NextNode(startNode);
-        NS_WARNING_ASSERTION(mFirst, "NextNode returned null");
+        mFirst = GetNextSibling(startNode);
+        NS_WARNING_ASSERTION(mFirst, "GetNextSibling returned null");
 
         // Does mFirst node really intersect the range?  The range could be
         // 'degenerate', i.e., not collapsed but still contain no content.
         if (mFirst &&
             NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode,
                                                startIndx, endNode, endIndx))) {
           mFirst = nullptr;
         }
@@ -425,18 +425,18 @@ nsContentIterator::Init(nsIDOMRange* aDO
       if (NS_WARN_IF(!endNode->IsContent())) {
         // Not much else to do here...
         mLast = nullptr;
       } else {
         // If the end node is an empty element and the end offset is 0,
         // the last element should be the previous node (i.e., shouldn't
         // include the end node in the range).
         if (!endIsData && !endNode->HasChildren() && !endIndx) {
-          mLast = PrevNode(endNode);
-          NS_WARNING_ASSERTION(mLast, "PrevNode returned null");
+          mLast = GetPrevSibling(endNode);
+          NS_WARNING_ASSERTION(mLast, "GetPrevSibling returned null");
           if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
                                                  startNode, startIndx,
                                                  endNode, endIndx))) {
             mLast = nullptr;
           }
         } else {
           mLast = endNode->AsContent();
         }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4575,18 +4575,26 @@ nsDocument::SetScriptGlobalObject(nsIScr
     // clients.  We only do this for content principal documents
     // since we can never observe system or null principals.
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->AddObserver(this, "service-worker-get-client", /* ownsWeak */ false);
     }
   }
 
+  // BlockOnload() might be called before mScriptGlobalObject is set.
+  // We may need to add the blocker once mScriptGlobalObject is set.
+  bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
+
   mScriptGlobalObject = aScriptGlobalObject;
 
+  if (needOnloadBlocker) {
+    EnsureOnloadBlocker();
+  }
+
   if (aScriptGlobalObject) {
     // Go back to using the docshell for the layout history state
     mLayoutHistoryState = nullptr;
     SetScopeObject(aScriptGlobalObject);
     mHasHadDefaultView = true;
 #ifdef DEBUG
     if (!mWillReparent) {
       // We really shouldn't have a wrapper here but if we do we need to make sure
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -94,29 +94,36 @@ private:
 
   // Weak ref to the stream control actor.  The actor will always call either
   // CloseStream() or CloseStreamWithoutReporting() before it's destroyed.  The
   // weak ref is cleared in the resulting NoteClosedOnOwningThread() or
   // ForgetOnOwningThread() method call.
   StreamControl* mControl;
 
   const nsID mId;
-  nsCOMPtr<nsIInputStream> mStream;
-  nsCOMPtr<nsIInputStream> mSnappyStream;
   nsCOMPtr<nsIThread> mOwningThread;
 
   enum State
   {
     Open,
     Closed,
     NumStates
   };
   Atomic<State> mState;
   Atomic<bool> mHasEverBeenRead;
 
+
+  // The wrapped stream objects may not be threadsafe.  We need to be able
+  // to close a stream on our owning thread while an IO thread is simultaneously
+  // reading the same stream.  Therefore, protect all access to these stream
+  // objects with a mutex.
+  Mutex mMutex;
+  nsCOMPtr<nsIInputStream> mStream;
+  nsCOMPtr<nsIInputStream> mSnappyStream;
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::ReadStream::Inner, override)
 };
 
 // ----------------------------------------------------------------------------
 
 // Runnable to notify actors that the ReadStream has closed.  This must
 // be done on the thread associated with the PBackground actor.  Must be
 // cancelable to execute on Worker threads (which can occur when the
@@ -185,20 +192,22 @@ private:
 };
 
 // ----------------------------------------------------------------------------
 
 ReadStream::Inner::Inner(StreamControl* aControl, const nsID& aId,
                          nsIInputStream* aStream)
   : mControl(aControl)
   , mId(aId)
+  , mOwningThread(NS_GetCurrentThread())
+  , mState(Open)
+  , mHasEverBeenRead(false)
+  , mMutex("dom::cache::ReadStream")
   , mStream(aStream)
   , mSnappyStream(new SnappyUncompressInputStream(aStream))
-  , mOwningThread(NS_GetCurrentThread())
-  , mState(Open)
 {
   MOZ_DIAGNOSTIC_ASSERT(mStream);
   MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->AddReadStream(this);
 }
 
 void
 ReadStream::Inner::Serialize(CacheReadStreamOrVoid* aReadStreamOut,
@@ -223,17 +232,21 @@ ReadStream::Inner::Serialize(CacheReadSt
     aRv.ThrowTypeError<MSG_CACHE_STREAM_CLOSED>();
     return;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(mControl);
 
   aReadStreamOut->id() = mId;
   mControl->SerializeControl(aReadStreamOut);
-  mControl->SerializeStream(aReadStreamOut, mStream, aStreamCleanupList);
+
+  {
+    MutexAutoLock lock(mMutex);
+    mControl->SerializeStream(aReadStreamOut, mStream, aStreamCleanupList);
+  }
 
   MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut->stream().type() ==
                         IPCStream::TInputStreamParamsWithFds);
 
   // We're passing ownership across the IPC barrier with the control, so
   // do not signal that the stream is closed here.
   Forget();
 }
@@ -265,41 +278,53 @@ ReadStream::Inner::HasEverBeenRead() con
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
   return mHasEverBeenRead;
 }
 
 nsresult
 ReadStream::Inner::Close()
 {
   // stream ops can happen on any thread
-  nsresult rv = mStream->Close();
+  nsresult rv = NS_OK;
+  {
+    MutexAutoLock lock(mMutex);
+    rv = mSnappyStream->Close();
+  }
   NoteClosed();
   return rv;
 }
 
 nsresult
 ReadStream::Inner::Available(uint64_t* aNumAvailableOut)
 {
   // stream ops can happen on any thread
-  nsresult rv = mSnappyStream->Available(aNumAvailableOut);
+  nsresult rv = NS_OK;
+  {
+    MutexAutoLock lock(mMutex);
+    rv = mSnappyStream->Available(aNumAvailableOut);
+  }
 
   if (NS_FAILED(rv)) {
     Close();
   }
 
   return rv;
 }
 
 nsresult
 ReadStream::Inner::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
 {
   // stream ops can happen on any thread
   MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 
-  nsresult rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
+  nsresult rv = NS_OK;
+  {
+    MutexAutoLock lock(mMutex);
+    rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
+  }
 
   if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
       *aNumReadOut == 0) {
     Close();
   }
 
   mHasEverBeenRead = true;
 
@@ -312,18 +337,22 @@ ReadStream::Inner::ReadSegments(nsWriteS
 {
   // stream ops can happen on any thread
   MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 
   if (aCount) {
     mHasEverBeenRead = true;
   }
 
-  nsresult rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount,
-                                            aNumReadOut);
+
+  nsresult rv = NS_OK;
+  {
+    MutexAutoLock lock(mMutex);
+    rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount, aNumReadOut);
+  }
 
   if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK &&
                         rv != NS_ERROR_NOT_IMPLEMENTED) || *aNumReadOut == 0) {
     Close();
   }
 
   // Verify bytes were actually read before marking as being ever read.  For
   // example, code can test if the stream supports ReadSegments() by calling
@@ -335,16 +364,17 @@ ReadStream::Inner::ReadSegments(nsWriteS
 
   return rv;
 }
 
 nsresult
 ReadStream::Inner::IsNonBlocking(bool* aNonBlockingOut)
 {
   // stream ops can happen on any thread
+  MutexAutoLock lock(mMutex);
   return mSnappyStream->IsNonBlocking(aNonBlockingOut);
 }
 
 ReadStream::Inner::~Inner()
 {
   // Any thread
   MOZ_DIAGNOSTIC_ASSERT(mState == Closed);
   MOZ_DIAGNOSTIC_ASSERT(!mControl);
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -333,18 +333,19 @@ class HTMLInputElementState final : publ
           MOZ_ASSERT(file);
 
           OwningFileOrDirectory* element = aResult.AppendElement();
           element->SetAsFile() = file;
         } else {
           MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eDirectoryPath);
 
           nsCOMPtr<nsIFile> file;
-          NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath);
-          nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
+          nsresult rv =
+            NS_NewLocalFile(mBlobImplsOrDirectoryPaths[i].mDirectoryPath,
+                            true, getter_AddRefs(file));
           if (NS_WARN_IF(NS_FAILED(rv))) {
             continue;
           }
 
           RefPtr<Directory> directory = Directory::Create(aWindow, file);
           MOZ_ASSERT(directory);
 
           OwningFileOrDirectory* element = aResult.AppendElement();
@@ -481,18 +482,17 @@ LastUsedDirectory(const OwningFileOrDire
     ErrorResult error;
     aData.GetAsFile()->GetMozFullPathInternal(path, error);
     if (error.Failed() || path.IsEmpty()) {
       error.SuppressException();
       return nullptr;
     }
 
     nsCOMPtr<nsIFile> localFile;
-    nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
-                                        getter_AddRefs(localFile));
+    nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(localFile));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
 
     nsCOMPtr<nsIFile> parentFile;
     rv = localFile->GetParent(getter_AddRefs(parentFile));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
@@ -2688,18 +2688,17 @@ HTMLInputElement::MozSetFileNameArray(co
   SetFilesOrDirectories(files, true);
 }
 
 void
 HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath,
                                   ErrorResult& aRv)
 {
   nsCOMPtr<nsIFile> file;
-  NS_ConvertUTF16toUTF8 path(aDirectoryPath);
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
+  aRv = NS_NewLocalFile(aDirectoryPath, true, getter_AddRefs(file));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -191,16 +191,17 @@
 #include "nsDeviceStorage.h"
 #include "DomainPolicy.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/widget/PuppetBidiKeyboard.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 #include "GMPServiceChild.h"
+#include "GfxInfoBase.h"
 #include "gfxPlatform.h"
 #include "nscore.h" // for NS_FREE_PERMANENT_DATA
 #include "VRManagerChild.h"
 
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::ipc;
@@ -606,16 +607,20 @@ ContentChild::Init(MessageLoop* aIOLoop,
 #ifdef NS_PRINTING
   // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
   // PrintingParent, is always available for printing initiated from the parent.
   RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
 #endif
 
   SetProcessName(NS_LITERAL_STRING("Web Content"), true);
 
+  nsTArray<mozilla::dom::GfxInfoFeatureStatus> featureStatus;
+  SendGetGfxInfoFeatureStatus(&featureStatus);
+  GfxInfoBase::SetFeatureStatus(featureStatus);
+
   return true;
 }
 
 void
 ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
 {
   if (!mCanOverrideProcessName) {
     return;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3758,28 +3758,31 @@ ContentParent::RecvRecordingDeviceEvents
                          aRecordingStatus.get());
   } else {
     NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
-                                            int32_t* aStatus,
-                                            nsCString* aFailureId,
-                                            bool* aSuccess)
+ContentParent::RecvGetGfxInfoFeatureStatus(nsTArray<mozilla::dom::GfxInfoFeatureStatus>* aFS)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (!gfxInfo) {
-    *aSuccess = false;
     return IPC_OK();
   }
 
-  *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, *aFailureId, aStatus));
+  for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
+    int32_t status = 0;
+    nsAutoCString failureId;
+    gfxInfo->GetFeatureStatus(i, failureId, &status);
+    mozilla::dom::GfxInfoFeatureStatus fs(i, status, failureId);
+    aFS->AppendElement(Move(fs));
+  }
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvAddIdleObserver(const uint64_t& aObserver,
                                    const uint32_t& aIdleTimeInS)
 {
   nsresult rv;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -429,16 +429,18 @@ public:
 
   virtual PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent() override;
 
   virtual mozilla::ipc::IPCResult RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
                                                             const nsString& aPageURL,
                                                             const bool& aIsAudio,
                                                             const bool& aIsVideo) override;
 
+  virtual mozilla::ipc::IPCResult RecvGetGfxInfoFeatureStatus(nsTArray<mozilla::dom::GfxInfoFeatureStatus>* aFS) override;
+
   bool CycleCollectWithLogs(bool aDumpAllTraces,
                             nsICycleCollectorLogSink* aSink,
                             nsIDumpGCAndCCLogsCallback* aCallback);
 
   virtual PBlobParent*
   SendPBlobConstructor(PBlobParent* aActor,
                        const BlobConstructorParams& aParams) override;
 
@@ -987,21 +989,16 @@ private:
   virtual void ProcessingError(Result aCode, const char* aMsgName) override;
 
   virtual mozilla::ipc::IPCResult RecvAllocateLayerTreeId(const ContentParentId& aCpId,
                                                           const TabId& aTabId,
                                                           uint64_t* aId) override;
 
   virtual mozilla::ipc::IPCResult RecvDeallocateLayerTreeId(const uint64_t& aId) override;
 
-  virtual mozilla::ipc::IPCResult RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
-                                                               int32_t* aStatus,
-                                                               nsCString* aFailureId,
-                                                               bool* aSuccess) override;
-
   virtual mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override;
 
   virtual mozilla::ipc::IPCResult
   RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
                             bool* aOutCrashed) override;
 
   virtual mozilla::ipc::IPCResult RecvEndDriverCrashGuard(const uint32_t& aGuardType) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -340,16 +340,23 @@ struct GMPAPITags
 
 struct GMPCapabilityData
 {
     nsCString name;
     nsCString version;
     GMPAPITags[] capabilities;
 };
 
+struct GfxInfoFeatureStatus
+{
+    int32_t feature;
+    int32_t status;
+    nsCString failureId;
+};
+
 /**
  * The PContent protocol is a top-level protocol between the UI process
  * and a content process. There is exactly one PContentParent/PContentChild pair
  * for each content process.
  */
 nested(upto inside_cpow) sync protocol PContent
 {
     parent spawns PPluginModule;
@@ -929,18 +936,17 @@ parent:
      * @param isAudio recording start with microphone
      * @param isVideo recording start with camera
      */
     async RecordingDeviceEvents(nsString recordingStatus,
                                 nsString pageURL,
                                 bool isAudio,
                                 bool isVideo);
 
-    sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, nsCString aFailureCode,
-                                                             bool aSuccess);
+    sync GetGfxInfoFeatureStatus() returns (GfxInfoFeatureStatus[] features);
 
     // Graphics errors
     async GraphicsError(nsCString aError);
 
     // Driver crash guards. aGuardType must be a member of CrashGuardType.
     sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
     sync EndDriverCrashGuard(uint32_t aGuardType);
 
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -293,22 +293,19 @@ GMPChild::GetAPI(const char* aAPIName,
 mozilla::ipc::IPCResult
 GMPChild::RecvPreloadLibs(const nsCString& aLibs)
 {
 #ifdef XP_WIN
   // Pre-load DLLs that need to be used by the EME plugin but that can't be
   // loaded after the sandbox has started
   // Items in this must be lowercase!
   static const char *const whitelist[] = {
-    "d3d9.dll", // Create an `IDirect3D9` to get adapter information
     "dxva2.dll", // Get monitor information
     "evr.dll", // MFGetStrideForBitmapInfoHeader
     "mfplat.dll", // MFCreateSample, MFCreateAlignedMemoryBuffer, MFCreateMediaType
-    "msauddecmft.dll", // AAC decoder (on Windows 8)
-    "msmpeg2adec.dll", // AAC decoder (on Windows 7)
     "msmpeg2vdec.dll", // H.264 decoder
   };
 
   nsTArray<nsCString> libs;
   SplitAt(", ", aLibs, libs);
   for (nsCString lib : libs) {
     ToLowerCase(lib);
     for (const char* whiteListedLib : whitelist) {
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -938,32 +938,64 @@ GMPParent::ParseChromiumManifest(const n
                                  m.mX_cdm_host_versions.ToInteger(&ignored))) {
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   mDisplayName = NS_ConvertUTF16toUTF8(m.mName);
   mDescription = NS_ConvertUTF16toUTF8(m.mDescription);
   mVersion = NS_ConvertUTF16toUTF8(m.mVersion);
 
+  nsCString kEMEKeySystem;
+
+  // We hard code a few of the settings because they can't be stored in the
+  // widevine manifest without making our API different to widevine's.
+  if (mDisplayName.EqualsASCII("clearkey")) {
+    kEMEKeySystem = kEMEKeySystemClearkey;
+#if XP_WIN
+    mLibs = NS_LITERAL_CSTRING("dxva2.dll, msmpeg2vdec.dll, evr.dll, mfh264dec.dll, mfplat.dll");
+#endif
+  } else if (mDisplayName.EqualsASCII("WidevineCdm")) {
+    kEMEKeySystem = kEMEKeySystemWidevine;
+#if XP_WIN
+    mLibs = NS_LITERAL_CSTRING("dxva2.dll");
+#endif
+  } else {
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+  }
+
   GMPCapability video(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER));
-  video.mAPITags.AppendElement(NS_LITERAL_CSTRING("h264"));
-  video.mAPITags.AppendElement(NS_LITERAL_CSTRING("vp8"));
-  video.mAPITags.AppendElement(NS_LITERAL_CSTRING("vp9"));
-  video.mAPITags.AppendElement(kEMEKeySystemWidevine);
+
+  nsCString codecsString = NS_ConvertUTF16toUTF8(m.mX_cdm_codecs);
+  nsTArray<nsCString> codecs;
+  SplitAt(",", codecsString, codecs);
+
+  for (const nsCString& chromiumCodec : codecs) {
+    nsCString codec;
+    if (chromiumCodec.EqualsASCII("vp8")) {
+      codec = NS_LITERAL_CSTRING("vp8");
+    } else if (chromiumCodec.EqualsASCII("vp9.0")) {
+      codec = NS_LITERAL_CSTRING("vp9");
+    } else if (chromiumCodec.EqualsASCII("avc1")) {
+      codec = NS_LITERAL_CSTRING("h264");
+    } else {
+      return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+    }
+
+    video.mAPITags.AppendElement(codec);
+  }
+
+  video.mAPITags.AppendElement(kEMEKeySystem);
   mCapabilities.AppendElement(Move(video));
 
   GMPCapability decrypt(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR));
-  decrypt.mAPITags.AppendElement(kEMEKeySystemWidevine);
+
+  decrypt.mAPITags.AppendElement(kEMEKeySystem);
   mCapabilities.AppendElement(Move(decrypt));
 
-  MOZ_ASSERT(mName.EqualsLiteral("widevinecdm"));
   mAdapter = NS_LITERAL_STRING("widevine");
-#ifdef XP_WIN
-  mLibs = NS_LITERAL_CSTRING("dxva2.dll");
-#endif
 
   return GenericPromise::CreateAndResolve(true, __func__);
 }
 
 bool
 GMPParent::CanBeSharedCrossNodeIds() const
 {
   return !mAsyncShutdownInProgress &&
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -383,18 +383,22 @@ GMPVideoDecoderParent::RecvInputDataExha
 mozilla::ipc::IPCResult
 GMPVideoDecoderParent::RecvDrainComplete()
 {
   LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete() frameCount=%d", this, mFrameCount));
   nsAutoString msg;
   msg.AppendLiteral("GMPVideoDecoderParent::RecvDrainComplete() outstanding frames=");
   msg.AppendInt(mFrameCount);
   LogToBrowserConsole(msg);
+
   if (!mCallback) {
-    return IPC_FAIL_NO_REASON(this);
+    // We anticipate shutting down in the middle of a drain in the
+    // `UnblockResetAndDrain` method, which is called when we shutdown, so
+    // everything is sunny.
+    return IPC_OK();
   }
 
   if (!mIsAwaitingDrainComplete) {
     return IPC_OK();
   }
   mIsAwaitingDrainComplete = false;
 
   // Ignore any return code. It is OK for this to fail without killing the process.
@@ -406,17 +410,20 @@ GMPVideoDecoderParent::RecvDrainComplete
 mozilla::ipc::IPCResult
 GMPVideoDecoderParent::RecvResetComplete()
 {
   LOGD(("GMPVideoDecoderParent[%p]::RecvResetComplete()", this));
 
   CancelResetCompleteTimeout();
 
   if (!mCallback) {
-    return IPC_FAIL_NO_REASON(this);
+    // We anticipate shutting down in the middle of a reset in the
+    // `UnblockResetAndDrain` method, which is called when we shutdown, so
+    // everything is good if we reach here.
+    return IPC_OK();
   }
 
   if (!mIsAwaitingResetComplete) {
     return IPC_OK();
   }
   mIsAwaitingResetComplete = false;
   mFrameCount = 0;
 
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -59,16 +59,17 @@ EXPORTS += [
     'GMPVideoDecoderProxy.h',
     'GMPVideoEncodedFrameImpl.h',
     'GMPVideoEncoderChild.h',
     'GMPVideoEncoderParent.h',
     'GMPVideoEncoderProxy.h',
     'GMPVideoHost.h',
     'GMPVideoi420FrameImpl.h',
     'GMPVideoPlaneImpl.h',
+    'widevine-adapter/content_decryption_module.h',
 ]
 
 # We link GMPLoader into xul on Android and Linux as its code does not
 # need to be covered by a DRM vendor's voucher.
 if CONFIG['OS_ARCH'] == 'Linux':
     SOURCES += [
       'GMPLoader.cpp',
     ]
@@ -103,17 +104,17 @@ UNIFIED_SOURCES += [
     'GMPUtils.cpp',
     'GMPVideoDecoderChild.cpp',
     'GMPVideoDecoderParent.cpp',
     'GMPVideoEncodedFrameImpl.cpp',
     'GMPVideoEncoderChild.cpp',
     'GMPVideoEncoderParent.cpp',
     'GMPVideoHost.cpp',
     'GMPVideoi420FrameImpl.cpp',
-    'GMPVideoPlaneImpl.cpp',
+    'GMPVideoPlaneImpl.cpp'
 ]
 
 DIRS += [
     'rlz',
     'widevine-adapter',
 ]
 
 IPDL_SOURCES += [
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -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/. */
 
 #include "WidevineAdapter.h"
 #include "content_decryption_module.h"
 #include "VideoUtils.h"
 #include "WidevineDecryptor.h"
+#include "WidevineDummyDecoder.h"
 #include "WidevineUtils.h"
 #include "WidevineVideoDecoder.h"
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
 
 static const GMPPlatformAPI* sPlatform = nullptr;
@@ -84,58 +85,64 @@ WidevineAdapter::GMPInit(const GMPPlatfo
 
 GMPErr
 WidevineAdapter::GMPGetAPI(const char* aAPIName,
                            void* aHostAPI,
                            void** aPluginAPI,
                            uint32_t aDecryptorId)
 {
   Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
-      aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
+      aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
   if (!strcmp(aAPIName, GMP_API_DECRYPTOR)) {
     if (WidevineDecryptor::GetInstance(aDecryptorId)) {
       // We only support one CDM instance per PGMPDecryptor. Fail!
       Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per IPDL actor! FAIL!");
       return GMPQuotaExceededErr;
     }
     auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
       PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
     if (!create) {
       Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to find CreateCdmInstance",
-        aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
+        aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
       return GMPGenericErr;
     }
 
     auto* decryptor = new WidevineDecryptor();
 
     auto cdm = reinterpret_cast<cdm::ContentDecryptionModule*>(
       create(cdm::ContentDecryptionModule::kVersion,
              kEMEKeySystemWidevine.get(),
              kEMEKeySystemWidevine.Length(),
              &GetCdmHost,
              decryptor));
     if (!cdm) {
       Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to create cdm",
-          aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
+          aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
       return GMPGenericErr;
     }
     Log("cdm: 0x%x", cdm);
     RefPtr<CDMWrapper> wrapper(new CDMWrapper(cdm, decryptor));
     decryptor->SetCDM(wrapper, aDecryptorId);
     *aPluginAPI = decryptor;
 
   } else if (!strcmp(aAPIName, GMP_API_VIDEO_DECODER)) {
     RefPtr<CDMWrapper> wrapper = WidevineDecryptor::GetInstance(aDecryptorId);
+
+    // There is a possible race condition, where the decryptor will be destroyed
+    // before we are able to create the video decoder, so we create a dummy
+    // decoder to avoid crashing.
     if (!wrapper) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder",
-          aAPIName, aHostAPI, aPluginAPI, thiss, aDecryptorId);
-      return GMPGenericErr;
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder. Using a DummyDecoder",
+          aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
+
+      *aPluginAPI = new WidevineDummyDecoder();
+    } else {
+      *aPluginAPI = new WidevineVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI),
+                                             wrapper);
     }
-    *aPluginAPI = new WidevineVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI),
-                                           wrapper);
   }
   return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
 }
 
 void
 WidevineAdapter::GMPShutdown()
 {
   Log("WidevineAdapter::GMPShutdown()");
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
@@ -28,23 +28,23 @@ WidevineDecryptor::GetInstance(uint32_t 
   }
   return nullptr;
 }
 
 
 WidevineDecryptor::WidevineDecryptor()
   : mCallback(nullptr)
 {
-  Log("WidevineDecryptor created this=%p", this);
+  Log("WidevineDecryptor created this=%p, instanceId=%u", this, mInstanceId);
   AddRef(); // Released in DecryptingComplete().
 }
 
 WidevineDecryptor::~WidevineDecryptor()
 {
-  Log("WidevineDecryptor destroyed this=%p", this);
+  Log("WidevineDecryptor destroyed this=%p, instanceId=%u", this, mInstanceId);
 }
 
 void
 WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId)
 {
   mCDM = aCDM;
   mInstanceId = aInstanceId;
   sDecryptors[mInstanceId] = aCDM.forget();
@@ -219,17 +219,17 @@ WidevineDecryptor::Decrypt(GMPBuffer* aB
            decrypted.DecryptedBuffer()->Size());
   }
   mCallback->Decrypted(aBuffer, ToGMPErr(rv));
 }
 
 void
 WidevineDecryptor::DecryptingComplete()
 {
-  Log("WidevineDecryptor::DecryptingComplete() this=%p", this);
+  Log("WidevineDecryptor::DecryptingComplete() this=%p, instanceId=%u", this, mInstanceId);
   // Drop our references to the CDMWrapper. When any other references
   // held elsewhere are dropped (for example references held by a
   // WidevineVideoDecoder, or a runnable), the CDMWrapper destroys
   // the CDM.
   mCDM = nullptr;
   sDecryptors.erase(mInstanceId);
   mCallback = nullptr;
   Release();
@@ -308,16 +308,27 @@ void
 WidevineDecryptor::OnResolveNewSessionPromise(uint32_t aPromiseId,
                                               const char* aSessionId,
                                               uint32_t aSessionIdSize)
 {
   if (!mCallback) {
     Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId);
     return;
   }
+
+  // This is laid out in the API. If we fail to load a session we should
+  // call OnResolveNewSessionPromise with nullptr as the sessionId.
+  // We can safely assume this means that we have failed to load a session
+  // as the other methods specify calling 'OnRejectPromise' when they fail.
+  if (!aSessionId) {
+    Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) Failed to load session", aPromiseId);
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
   Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId);
   auto iter = mPromiseIdToNewSessionTokens.find(aPromiseId);
   if (iter == mPromiseIdToNewSessionTokens.end()) {
     Log("FAIL: Decryptor::OnResolveNewSessionPromise(aPromiseId=%d) unknown aPromiseId", aPromiseId);
     return;
   }
   mCallback->SetSessionId(iter->second, aSessionId, aSessionIdSize);
   mCallback->ResolvePromise(aPromiseId);
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineDummyDecoder.cpp
@@ -0,0 +1,57 @@
+#include "WidevineDummyDecoder.h"
+#include "WidevineUtils.h"
+
+using namespace cdm;
+
+namespace mozilla {
+WidevineDummyDecoder::WidevineDummyDecoder()
+{
+  Log("WidevineDummyDecoder created");
+}
+
+void WidevineDummyDecoder::InitDecode(const GMPVideoCodec & aCodecSettings,
+                                      const uint8_t * aCodecSpecific,
+                                      uint32_t aCodecSpecificLength,
+                                      GMPVideoDecoderCallback * aCallback,
+                                      int32_t aCoreCount)
+{
+  Log("WidevineDummyDecoder::InitDecode");
+
+  mCallback = aCallback;
+  mCallback->Error(GMPErr::GMPNotImplementedErr);
+}
+
+void WidevineDummyDecoder::Decode(GMPVideoEncodedFrame * aInputFrame,
+                                  bool aMissingFrames,
+                                  const uint8_t * aCodecSpecificInfo,
+                                  uint32_t aCodecSpecificInfoLength,
+                                  int64_t aRenderTimeMs)
+{
+  Log("WidevineDummyDecoder::Decode");
+  mCallback->Error(GMPErr::GMPNotImplementedErr);
+}
+
+void WidevineDummyDecoder::Reset()
+{
+  Log("WidevineDummyDecoder::Reset");
+  mCallback->Error(GMPErr::GMPNotImplementedErr);
+}
+
+void WidevineDummyDecoder::Drain()
+{
+  Log("WidevineDummyDecoder::Drain");
+  mCallback->Error(GMPErr::GMPNotImplementedErr);
+}
+
+void WidevineDummyDecoder::DecodingComplete()
+{
+  Log("WidevineDummyDecoder::DecodingComplete");
+
+  mCallback = nullptr;
+  delete this;
+}
+
+WidevineDummyDecoder::~WidevineDummyDecoder() {
+  Log("WidevineDummyDecoder destroyed");
+}
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineDummyDecoder.h
@@ -0,0 +1,43 @@
+#ifndef WidevineDummyDecoder_h_
+#define WidevineDummyDecoder_h_
+
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "gmp-api/gmp-video-decode.h"
+#include "gmp-api/gmp-video-host.h"
+#include "WidevineDecryptor.h"
+#include "WidevineVideoFrame.h"
+
+
+namespace mozilla {
+
+class WidevineDummyDecoder : public GMPVideoDecoder {
+public:
+  WidevineDummyDecoder();
+
+  void InitDecode(const GMPVideoCodec& aCodecSettings,
+                  const uint8_t* aCodecSpecific,
+                  uint32_t aCodecSpecificLength,
+                  GMPVideoDecoderCallback* aCallback,
+                  int32_t aCoreCount) override;
+
+  void Decode(GMPVideoEncodedFrame* aInputFrame,
+              bool aMissingFrames,
+              const uint8_t* aCodecSpecificInfo,
+              uint32_t aCodecSpecificInfoLength,
+              int64_t aRenderTimeMs = -1) override;
+
+  void Reset() override;
+
+  void Drain() override;
+
+  void DecodingComplete() override;
+
+private:
+  ~WidevineDummyDecoder();
+
+  GMPVideoDecoderCallback* mCallback;
+};
+}
+
+#endif
\ No newline at end of file
--- a/dom/media/gmp/widevine-adapter/moz.build
+++ b/dom/media/gmp/widevine-adapter/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'WidevineAdapter.cpp',
     'WidevineDecryptor.cpp',
+    'WidevineDummyDecoder.cpp',
     'WidevineFileIO.cpp',
     'WidevineUtils.cpp',
     'WidevineVideoDecoder.cpp',
     'WidevineVideoFrame.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -73,33 +73,44 @@ namespace dom {
 // AudioContext.
 static dom::AudioContext::AudioContextId gAudioContextId = 1;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDestination)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseGripArray)
   if (!tmp->mIsStarted) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveNodes)
   }
+  // mDecodeJobs owns the WebAudioDecodeJob objects whose lifetime is managed explicitly.
+  // mAllNodes is an array of weak pointers, ignore it here.
+  // mPannerNodes is an array of weak pointers, ignore it here.
+  // mBasicWaveFormCache cannot participate in cycles, ignore it here.
+
   // Remove weak reference on the global window as the context is not usable
   // without mDestination.
   tmp->DisconnectFromWindow();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioContext,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDestination)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromiseGripArray)
   if (!tmp->mIsStarted) {
     MOZ_ASSERT(tmp->mIsOffline,
                "Online AudioContexts should always be started");
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveNodes)
   }
+  // mDecodeJobs owns the WebAudioDecodeJob objects whose lifetime is managed explicitly.
+  // mAllNodes is an array of weak pointers, ignore it here.
+  // mPannerNodes is an array of weak pointers, ignore it here.
+  // mBasicWaveFormCache cannot participate in cycles, ignore it here.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(AudioContext, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(AudioContext, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioContext)
   NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
@@ -628,16 +639,22 @@ AudioContext::Shutdown()
 {
   mIsShutDown = true;
 
   if (!mIsOffline) {
     ErrorResult dummy;
     RefPtr<Promise> ignored = Close(dummy);
   }
 
+  for (auto p : mPromiseGripArray) {
+    p->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+  }
+
+  mPromiseGripArray.Clear();
+
   // Release references to active nodes.
   // Active AudioNodes don't unregister in destructors, at which point the
   // Node is already unregistered.
   mActiveNodes.Clear();
 
   // For offline contexts, we can destroy the MediaStreamGraph at this point.
   if (mIsOffline && mDestination) {
     mDestination->OfflineShutdown();
@@ -773,19 +790,25 @@ AudioContext::OnStateChanged(void* aProm
 
   MOZ_ASSERT(
     mIsOffline || aPromise || aNewState == AudioContextState::Running,
     "We should have a promise here if this is a real-time AudioContext."
     "Or this is the first time we switch to \"running\".");
 
   if (aPromise) {
     Promise* promise = reinterpret_cast<Promise*>(aPromise);
-    promise->MaybeResolveWithUndefined();
-    DebugOnly<bool> rv = mPromiseGripArray.RemoveElement(promise);
-    MOZ_ASSERT(rv, "Promise wasn't in the grip array?");
+    // It is possible for the promise to have been removed from
+    // mPromiseGripArray if the cycle collector has severed our connections. DO
+    // NOT dereference the promise pointer in that case since it may point to
+    // already freed memory.
+    if (mPromiseGripArray.Contains(promise)) {
+      promise->MaybeResolveWithUndefined();
+      DebugOnly<bool> rv = mPromiseGripArray.RemoveElement(promise);
+      MOZ_ASSERT(rv, "Promise wasn't in the grip array?");
+    }
   }
 
   if (mAudioContextState != aNewState) {
     RefPtr<OnStateChangeTask> onStateChangeTask =
       new OnStateChangeTask(this);
     NS_DispatchToMainThread(onStateChangeTask);
   }
 
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1628,22 +1628,20 @@ XMLHttpRequestWorker::ReleaseProxy(Relea
         mProxy->mOuterEventStreamId++;
       }
 
       // We need to make a sync call here.
       RefPtr<SyncTeardownRunnable> runnable =
         new SyncTeardownRunnable(mWorkerPrivate, mProxy);
       mProxy = nullptr;
 
-      ErrorResult forAssertionsOnly;
+      IgnoredErrorResult forAssertionsOnly;
       // This runnable _must_ be executed.
-      runnable->Dispatch(Killing, forAssertionsOnly);
-      if (forAssertionsOnly.Failed()) {
-        NS_ERROR("Failed to dispatch teardown runnable!");
-      }
+      runnable->Dispatch(Dead, forAssertionsOnly);
+      MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly.Failed());
     }
   }
 }
 
 void
 XMLHttpRequestWorker::MaybePin(ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
--- a/gfx/skia/skia/src/core/SkBlitMask_D32.cpp
+++ b/gfx/skia/skia/src/core/SkBlitMask_D32.cpp
@@ -139,46 +139,24 @@ static void A8_RowProc_Blend(
     const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
     for (int i = 0; i < count; ++i) {
         if (mask[i]) {
             dst[i] = SkBlendARGB32(src[i], dst[i], mask[i]);
         }
     }
 }
 
-// expand the steps that SkAlphaMulQ performs, but this way we can
-//  exand.. add.. combine
-// instead of
-// expand..combine add expand..combine
-//
-#define EXPAND0(v, m, s)    ((v) & (m)) * (s)
-#define EXPAND1(v, m, s)    (((v) >> 8) & (m)) * (s)
-#define COMBINE(e0, e1, m)  ((((e0) >> 8) & (m)) | ((e1) & ~(m)))
-
 static void A8_RowProc_Opaque(
         SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
     const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
     for (int i = 0; i < count; ++i) {
         int m = mask[i];
         if (m) {
             m += (m >> 7);
-#if 1
-            // this is slightly slower than the expand/combine version, but it
-            // is much closer to the old results, so we use it for now to reduce
-            // rebaselining.
-            dst[i] = SkAlphaMulQ(src[i], m) + SkAlphaMulQ(dst[i], 256 - m);
-#else
-            uint32_t v = src[i];
-            uint32_t s0 = EXPAND0(v, rbmask, m);
-            uint32_t s1 = EXPAND1(v, rbmask, m);
-            v = dst[i];
-            uint32_t d0 = EXPAND0(v, rbmask, m);
-            uint32_t d1 = EXPAND1(v, rbmask, m);
-            dst[i] = COMBINE(s0 + d0, s1 + d1, rbmask);
-#endif
+            dst[i] = SkPMLerp(src[i], dst[i], m);
         }
     }
 }
 
 static int upscale31To255(int value) {
     value = (value << 3) | (value >> 2);
     return value;
 }
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -411,16 +411,26 @@ struct ParamTraits<nsString> : ParamTrai
 };
 
 template <>
 struct ParamTraits<nsLiteralString> : ParamTraits<nsAString>
 {
   typedef nsLiteralString paramType;
 };
 
+#ifdef MOZILLA_INTERNAL_API
+
+template<>
+struct ParamTraits<nsAutoString> : ParamTraits<nsString>
+{
+  typedef nsAutoString paramType;
+};
+
+#endif  // MOZILLA_INTERNAL_API
+
 // Pickle::ReadBytes and ::WriteBytes take the length in ints, so we must
 // ensure there is no overflow. This returns |false| if it would overflow.
 // Otherwise, it returns |true| and places the byte length in |aByteLength|.
 bool ByteLengthIsValid(uint32_t aNumElements, size_t aElementSize, int* aByteLength);
 
 // Note: IPDL will sometimes codegen specialized implementations of
 // nsTArray serialization and deserialization code in
 // implementSpecialArrayPickling(). This is needed when ParamTraits<E>
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -6,16 +6,17 @@ auto-regress/bug675251.js
 auto-regress/bug729797.js
 baseline/bug847446.js
 baseline/bug852175.js
 basic/bug632964-regexp.js
 basic/bug656261.js
 basic/bug677957-2.js
 basic/bug753283.js
 basic/bug867946.js
+basic/destructuring-iterator.js
 basic/testAtomize.js
 basic/testBug614653.js
 basic/testBug686274.js
 basic/testManyVars.js
 basic/testTypedArrayInit.js
 debug/DebuggeeWouldRun-01.js
 debug/DebuggeeWouldRun-02.js
 gc/bug-1014972.js
--- a/js/src/jit-test/tests/debug/optimized-out-01.js
+++ b/js/src/jit-test/tests/debug/optimized-out-01.js
@@ -1,13 +1,16 @@
 // Tests that we can reflect optimized out values.
 //
 // Unfortunately these tests are brittle. They depend on opaque JIT heuristics
 // kicking in.
 
+// Use gczeal 0 to keep CGC from invalidating Ion code and causing test failures.
+gczeal(0);
+
 load(libdir + "jitopts.js");
 
 if (!jitTogglesMatch(Opts_Ion2NoOffthreadCompilation))
   quit(0);
 
 withJitOptions(Opts_Ion2NoOffthreadCompilation, function () {
   var g = newGlobal();
   var dbg = new Debugger;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1329933.js
@@ -0,0 +1,10 @@
+
+function g(f) {
+    for (var j = 0; j < 999; ++j) {
+        f(0 / 0);
+    }
+}
+function h(x) {
+    x < 1 ? 0 : Math.imul(x || 0);
+}
+g(h);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1331405.js
@@ -0,0 +1,4 @@
+// |jit-test| error:ReferenceError
+
+++f();
+try {} catch (e) {}
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1295,17 +1295,17 @@ BacktrackingAllocator::processBundle(MIR
             }
 
             // If that worked, we're done!
             if (success)
                 return true;
 
             // If that didn't work, but we have one or more non-fixed bundles
             // known to be conflicting, maybe we can evict them and try again.
-            if (attempt < MAX_ATTEMPTS &&
+            if ((attempt < MAX_ATTEMPTS || minimalBundle(bundle)) &&
                 !fixed &&
                 !conflicting.empty() &&
                 maximumSpillWeight(conflicting) < computeSpillWeight(bundle))
                 {
                     for (size_t i = 0; i < conflicting.length(); i++) {
                         if (!evictBundle(conflicting[i]))
                             return false;
                     }
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -13,30 +13,37 @@
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
 
+class AutoStubFrame;
+
 // BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
 class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
 {
     // Some Baseline IC stubs can be used in IonMonkey through SharedStubs.
     // Those stubs have different machine code, so we need to track whether
     // we're compiling for Baseline or Ion.
     ICStubEngine engine_;
 
     uint32_t stubDataOffset_;
     bool inStubFrame_;
     bool makesGCCalls_;
 
     MOZ_MUST_USE bool callVM(MacroAssembler& masm, const VMFunction& fun);
 
+    MOZ_MUST_USE bool callTypeUpdateIC(AutoStubFrame& stubFrame, Register obj, ValueOperand val,
+                                       Register scratch, LiveGeneralRegisterSet saveRegs);
+
+    MOZ_MUST_USE bool emitStoreSlotShared(bool isFixed);
+
   public:
     friend class AutoStubFrame;
 
     BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, ICStubEngine engine,
                             uint32_t stubDataOffset)
       : CacheIRCompiler(cx, writer, Mode::Baseline),
         engine_(engine),
         stubDataOffset_(stubDataOffset),
@@ -60,16 +67,18 @@ class MOZ_RAII BaselineCacheIRCompiler :
     }
 };
 
 #define DEFINE_SHARED_OP(op) \
     bool BaselineCacheIRCompiler::emit##op() { return CacheIRCompiler::emit##op(); }
     CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
 #undef DEFINE_SHARED_OP
 
+enum class CallCanGC { CanGC, CanNotGC };
+
 // Instructions that have to perform a callVM require a stub frame. Use
 // AutoStubFrame before allocating any registers, then call its enter() and
 // leave() methods to enter/leave the stub frame.
 class MOZ_RAII AutoStubFrame
 {
     BaselineCacheIRCompiler& compiler;
 #ifdef DEBUG
     uint32_t framePushedAtEnterStubFrame_;
@@ -88,29 +97,30 @@ class MOZ_RAII AutoStubFrame
         tail()
     {
         // We use ICTailCallReg when entering the stub frame, so ensure it's not
         // used for something else.
         if (compiler.allocator.isAllocatable(ICTailCallReg))
             tail.emplace(compiler.allocator, compiler.masm, ICTailCallReg);
     }
 
-    void enter(MacroAssembler& masm, Register scratch) {
+    void enter(MacroAssembler& masm, Register scratch, CallCanGC canGC = CallCanGC::CanGC) {
         if (compiler.engine_ == ICStubEngine::Baseline) {
             EmitBaselineEnterStubFrame(masm, scratch);
 #ifdef DEBUG
             framePushedAtEnterStubFrame_ = masm.framePushed();
 #endif
         } else {
             EmitIonEnterStubFrame(masm, scratch);
         }
 
         MOZ_ASSERT(!compiler.inStubFrame_);
         compiler.inStubFrame_ = true;
-        compiler.makesGCCalls_ = true;
+        if (canGC == CallCanGC::CanGC)
+            compiler.makesGCCalls_ = true;
     }
     void leave(MacroAssembler& masm, bool calledIntoIon = false) {
         MOZ_ASSERT(compiler.inStubFrame_);
         compiler.inStubFrame_ = false;
 
         if (compiler.engine_ == ICStubEngine::Baseline) {
 #ifdef DEBUG
             masm.setFramePushed(framePushedAtEnterStubFrame_);
@@ -645,16 +655,122 @@ BaselineCacheIRCompiler::emitLoadEnviron
     masm.branchTestMagic(Assembler::Equal, slot, failure->label());
 
     // Load the value.
     masm.loadValue(slot, output.valueReg());
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::callTypeUpdateIC(AutoStubFrame& stubFrame, Register obj, ValueOperand val,
+                                          Register scratch, LiveGeneralRegisterSet saveRegs)
+{
+    // R0 contains the value that needs to be typechecked.
+    MOZ_ASSERT(val == R0);
+    MOZ_ASSERT(scratch == R1.scratchReg());
+
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+    static const bool CallClobbersTailReg = false;
+#else
+    static const bool CallClobbersTailReg = true;
+#endif
+
+    // Call the first type update stub.
+    if (CallClobbersTailReg)
+        masm.push(ICTailCallReg);
+    masm.push(ICStubReg);
+    masm.loadPtr(Address(ICStubReg, ICUpdatedStub::offsetOfFirstUpdateStub()),
+                 ICStubReg);
+    masm.call(Address(ICStubReg, ICStub::offsetOfStubCode()));
+    masm.pop(ICStubReg);
+    if (CallClobbersTailReg)
+        masm.pop(ICTailCallReg);
+
+    // The update IC will store 0 or 1 in |scratch|, R1.scratchReg(), reflecting
+    // if the value in R0 type-checked properly or not.
+    Label done;
+    masm.branch32(Assembler::Equal, scratch, Imm32(1), &done);
+
+    stubFrame.enter(masm, scratch, CallCanGC::CanNotGC);
+
+    masm.PushRegsInMask(saveRegs);
+
+    masm.Push(val);
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+    masm.Push(ICStubReg);
+
+    // Load previous frame pointer, push BaselineFrame*.
+    masm.loadPtr(Address(BaselineFrameReg, 0), scratch);
+    masm.pushBaselineFramePtr(scratch, scratch);
+
+    if (!callVM(masm, DoTypeUpdateFallbackInfo))
+        return false;
+
+    masm.PopRegsInMask(saveRegs);
+
+    stubFrame.leave(masm);
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitStoreSlotShared(bool isFixed)
+{
+    ObjOperandId objId = reader.objOperandId();
+    Address offsetAddr = stubAddress(reader.stubOffset());
+
+    // Allocate the fixed registers first. These need to be fixed for
+    // callTypeUpdateIC.
+    AutoStubFrame stubFrame(*this);
+    AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
+    ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
+
+    Register obj = allocator.useRegister(masm, objId);
+
+    LiveGeneralRegisterSet saveRegs;
+    saveRegs.add(obj);
+    saveRegs.add(val);
+    if (!callTypeUpdateIC(stubFrame, obj, val, scratch, saveRegs))
+        return false;
+
+    masm.load32(offsetAddr, scratch);
+
+    if (isFixed) {
+        BaseIndex slot(obj, scratch, TimesOne);
+        EmitPreBarrier(masm, slot, MIRType::Value);
+        masm.storeValue(val, slot);
+    } else {
+        // To avoid running out of registers on x86, use ICStubReg as scratch.
+        // We don't need it anymore.
+        Register slots = ICStubReg;
+        masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), slots);
+        BaseIndex slot(slots, scratch, TimesOne);
+        EmitPreBarrier(masm, slot, MIRType::Value);
+        masm.storeValue(val, slot);
+    }
+
+    if (cx_->gc.nursery.exists())
+        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitStoreFixedSlot()
+{
+    return emitStoreSlotShared(true);
+}
+
+bool
+BaselineCacheIRCompiler::emitStoreDynamicSlot()
+{
+    return emitStoreSlotShared(false);
+}
+
+bool
 BaselineCacheIRCompiler::emitTypeMonitorResult()
 {
     allocator.discardStack(masm);
     EmitEnterTypeMonitorIC(masm);
     return true;
 }
 
 bool
@@ -743,16 +859,17 @@ BaselineCacheIRCompiler::init(CacheKind 
     AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputs));
 
     switch (kind) {
       case CacheKind::GetProp:
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, R0);
         break;
       case CacheKind::GetElem:
+      case CacheKind::SetProp:
         MOZ_ASSERT(numInputs == 2);
         allocator.initInputLocation(0, R0);
         allocator.initInputLocation(1, R1);
         break;
       case CacheKind::GetName:
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, R0.scratchReg(), JSVAL_TYPE_OBJECT);
 #if defined(JS_NUNBOX32)
@@ -781,19 +898,32 @@ jit::AttachBaselineCacheIRStub(JSContext
 
     if (writer.failed())
         return nullptr;
 
     // Just a sanity check: the caller should ensure we don't attach an
     // unlimited number of stubs.
     MOZ_ASSERT(stub->numOptimizedStubs() < MaxOptimizedCacheIRStubs);
 
-    MOZ_ASSERT(kind == CacheKind::GetProp || kind == CacheKind::GetElem ||
-               kind == CacheKind::GetName, "sizeof needs to change for SetProp!");
-    uint32_t stubDataOffset = sizeof(ICCacheIR_Monitored);
+    enum class CacheIRStubKind { Monitored, Updated };
+
+    uint32_t stubDataOffset;
+    CacheIRStubKind stubKind;
+    switch (kind) {
+      case CacheKind::GetProp:
+      case CacheKind::GetElem:
+      case CacheKind::GetName:
+        stubDataOffset = sizeof(ICCacheIR_Monitored);
+        stubKind = CacheIRStubKind::Monitored;
+        break;
+      case CacheKind::SetProp:
+        stubDataOffset = sizeof(ICCacheIR_Updated);
+        stubKind = CacheIRStubKind::Updated;
+        break;
+    }
 
     JitCompartment* jitCompartment = cx->compartment()->jitCompartment();
 
     // Check if we already have JitCode for this stub.
     CacheIRStubInfo* stubInfo;
     CacheIRStubKey::Lookup lookup(kind, engine, writer.codeStart(), writer.codeLength());
     JitCode* code = jitCompartment->getCacheIRStubCode(lookup, &stubInfo);
     if (!code) {
@@ -817,31 +947,44 @@ jit::AttachBaselineCacheIRStub(JSContext
 
         CacheIRStubKey key(stubInfo);
         if (!jitCompartment->putCacheIRStubCode(lookup, key, code))
             return nullptr;
     }
 
     MOZ_ASSERT(code);
     MOZ_ASSERT(stubInfo);
-    MOZ_ASSERT(stub->isMonitoredFallback());
     MOZ_ASSERT(stubInfo->stubDataSize() == writer.stubDataSize());
 
     // Ensure we don't attach duplicate stubs. This can happen if a stub failed
     // for some reason and the IR generator doesn't check for exactly the same
     // conditions.
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
-        if (!iter->isCacheIR_Monitored())
-            continue;
-
-        ICCacheIR_Monitored* otherStub = iter->toCacheIR_Monitored();
-        if (otherStub->stubInfo() != stubInfo)
-            continue;
-        if (!writer.stubDataEquals(otherStub->stubDataStart()))
-            continue;
+        switch (stubKind) {
+          case CacheIRStubKind::Monitored: {
+            if (!iter->isCacheIR_Monitored())
+                continue;
+            auto otherStub = iter->toCacheIR_Monitored();
+            if (otherStub->stubInfo() != stubInfo)
+                continue;
+            if (!writer.stubDataEquals(otherStub->stubDataStart()))
+                continue;
+            break;
+          }
+          case CacheIRStubKind::Updated: {
+            if (!iter->isCacheIR_Updated())
+                continue;
+            auto otherStub = iter->toCacheIR_Updated();
+            if (otherStub->stubInfo() != stubInfo)
+                continue;
+            if (!writer.stubDataEquals(otherStub->stubDataStart()))
+                continue;
+            break;
+          }
+        }
 
         // We found a stub that's exactly the same as the stub we're about to
         // attach. Just return nullptr, the caller should do nothing in this
         // case.
         return nullptr;
     }
 
     // Time to allocate and attach a new stub.
@@ -849,30 +992,52 @@ jit::AttachBaselineCacheIRStub(JSContext
     size_t bytesNeeded = stubInfo->stubDataOffset() + stubInfo->stubDataSize();
 
     ICStubSpace* stubSpace = ICStubCompiler::StubSpaceForStub(stubInfo->makesGCCalls(),
                                                               outerScript, engine);
     void* newStubMem = stubSpace->alloc(bytesNeeded);
     if (!newStubMem)
         return nullptr;
 
-    ICStub* monitorStub = stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
-    auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
+    switch (stubKind) {
+      case CacheIRStubKind::Monitored: {
+        ICStub* monitorStub =
+            stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
+        auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
+        writer.copyStubData(newStub->stubDataStart());
+        stub->addNewStub(newStub);
+        return newStub;
+      }
+      case CacheIRStubKind::Updated: {
+        auto newStub = new(newStubMem) ICCacheIR_Updated(code, stubInfo);
+        if (!newStub->initUpdatingChain(cx, stubSpace)) {
+            cx->recoverFromOutOfMemory();
+            return nullptr;
+        }
+        writer.copyStubData(newStub->stubDataStart());
+        stub->addNewStub(newStub);
+        return newStub;
+      }
+    }
 
-    writer.copyStubData(newStub->stubDataStart());
-    stub->addNewStub(newStub);
-    return newStub;
+    MOZ_CRASH("Invalid kind");
 }
 
 uint8_t*
 ICCacheIR_Monitored::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
+uint8_t*
+ICCacheIR_Updated::stubDataStart()
+{
+    return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
+}
+
 /* static */ ICCacheIR_Monitored*
 ICCacheIR_Monitored::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
                            ICCacheIR_Monitored& other)
 {
     const CacheIRStubInfo* stubInfo = other.stubInfo();
     MOZ_ASSERT(stubInfo->makesGCCalls());
 
     size_t bytesNeeded = stubInfo->stubDataOffset() + stubInfo->stubDataSize();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -275,24 +275,28 @@ DoTypeUpdateFallback(JSContext* cx, Base
 
     FallbackICSpew(cx, stub->getChainFallback(), "TypeUpdate(%s)",
                    ICStub::KindString(stub->kind()));
 
     RootedScript script(cx, frame->script());
     RootedObject obj(cx, &objval.toObject());
     RootedId id(cx);
 
-    switch(stub->kind()) {
+    switch (stub->kind()) {
+      case ICStub::CacheIR_Updated:
+        id = stub->toCacheIR_Updated()->updateStubId();
+        MOZ_ASSERT(id != JSID_EMPTY);
+        AddTypePropertyId(cx, obj, id, value);
+        break;
       case ICStub::SetElem_DenseOrUnboxedArray:
       case ICStub::SetElem_DenseOrUnboxedArrayAdd: {
         id = JSID_VOID;
         AddTypePropertyId(cx, obj, id, value);
         break;
       }
-      case ICStub::SetProp_Native:
       case ICStub::SetProp_NativeAdd:
       case ICStub::SetProp_Unboxed: {
         MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
         jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
         if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL)
             id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc));
         else
             id = NameToId(script->getName(pc));
@@ -733,33 +737,16 @@ LastPropertyForSetProp(JSObject* obj)
         UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
         return expando ? expando->lastProperty() : nullptr;
     }
 
     return nullptr;
 }
 
 static bool
-IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, Shape* propertyShape)
-{
-    // Object shape must not have changed during the property set.
-    if (LastPropertyForSetProp(obj) != oldShape)
-        return false;
-
-    if (!propertyShape->hasSlot() ||
-        !propertyShape->hasDefaultSetter() ||
-        !propertyShape->writable())
-    {
-        return false;
-    }
-
-    return true;
-}
-
-static bool
 IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape,
                           jsid id, Shape* propertyShape, size_t* protoChainDepth)
 {
     // The property must be the last added property of the object.
     if (LastPropertyForSetProp(obj) != propertyShape)
         return false;
 
     // Object must be extensible, oldShape must be immediate parent of current shape.
@@ -1526,17 +1513,17 @@ ICSetElem_DenseOrUnboxedArray::Compiler:
         // objects can be written to also need update stubs.
         masm.Push(R1);
         masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1);
 
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(R0);
         saveRegs.addUnchecked(obj);
         saveRegs.add(ICStubReg);
-        emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs);
+        BaselineEmitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs, cx);
 
         masm.Pop(R1);
     }
 
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     if (unboxedType_ == JSVAL_TYPE_MAGIC) {
@@ -1734,17 +1721,17 @@ ICSetElemDenseOrUnboxedArrayAddCompiler:
         // objects can be written to also need update stubs.
         masm.Push(R1);
         masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1);
 
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(R0);
         saveRegs.addUnchecked(obj);
         saveRegs.add(ICStubReg);
-        emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs);
+        BaselineEmitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs, cx);
 
         masm.Pop(R1);
     }
 
     // Reset register set.
     regs = availableGeneralRegs(2);
     scratchReg = regs.takeAny();
 
@@ -2631,51 +2618,16 @@ TryAttachSetValuePropStub(JSContext* cx,
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
-    if (IsCacheableSetPropWriteSlot(obj, oldShape, shape)) {
-        // For some property writes, such as the initial overwrite of global
-        // properties, TI will not mark the property as having been
-        // overwritten. Don't attach a stub in this case, so that we don't
-        // execute another write to the property without TI seeing that write.
-        EnsureTrackPropertyTypes(cx, obj, id);
-        if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
-            *attached = true;
-            return true;
-        }
-
-        bool isFixedSlot;
-        uint32_t offset;
-        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-        JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.PROP) stub");
-        MOZ_ASSERT(LastPropertyForSetProp(obj) == oldShape,
-                   "Should this really be a SetPropWriteSlot?");
-        ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
-        ICSetProp_Native* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-        if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
-            return false;
-
-        if (IsPreliminaryObject(obj))
-            newStub->notePreliminaryObject();
-        else
-            StripPreliminaryObjectStubs(cx, stub);
-
-        stub->addNewStub(newStub);
-        *attached = true;
-        return true;
-    }
-
     return true;
 }
 
 // Try to update existing SetProp setter call stubs for the given holder in
 // place with a new shape and setter.
 static bool
 UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
                                ICStub::Kind kind,
@@ -2930,16 +2882,40 @@ DoSetPropFallback(JSContext* cx, Baselin
     if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
         lhs.isObject() &&
         !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldGuard, name, id,
                                       rhs, &attached, &isTemporarilyUnoptimizable))
     {
         return false;
     }
 
+    if (!attached &&
+        stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
+        !JitOptions.disableCacheIR)
+    {
+        RootedValue idVal(cx, StringValue(name));
+        SetPropIRGenerator gen(cx, pc, CacheKind::SetProp, &isTemporarilyUnoptimizable,
+                               lhs, idVal, rhs);
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        ICStubEngine::Baseline, frame->script(), stub);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                attached = true;
+
+                newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+
+                if (gen.shouldNotePreliminaryObjectStub())
+                    newStub->toCacheIR_Updated()->notePreliminaryObject();
+                else if (gen.shouldUnlinkPreliminaryObjectStubs())
+                    StripPreliminaryObjectStubs(cx, stub);
+            }
+        }
+    }
+
     if (op == JSOP_INITPROP ||
         op == JSOP_INITLOCKEDPROP ||
         op == JSOP_INITHIDDENPROP)
     {
         if (!InitPropertyOperation(cx, op, obj, id, rhs))
             return false;
     } else if (op == JSOP_SETNAME ||
                op == JSOP_STRICTSETNAME ||
@@ -3102,87 +3078,16 @@ GuardGroupAndShapeMaybeUnboxedExpando(Ma
         masm.jump(failure);
         masm.bind(&done);
         masm.pop(object);
     } else {
         masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
     }
 }
 
-bool
-ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-
-    // Guard input is an object.
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratch = regs.takeAny();
-
-    GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
-                                          ICSetProp_Native::offsetOfGroup(),
-                                          ICSetProp_Native::offsetOfShape(),
-                                          &failure);
-
-    // Stow both R0 and R1 (object and value).
-    EmitStowICValues(masm, 2);
-
-    // Type update stub expects the value to check in R0.
-    masm.moveValue(R1, R0);
-
-    // Call the type-update stub.
-    if (!callTypeUpdateIC(masm, sizeof(Value)))
-        return false;
-
-    // Unstow R0 and R1 (object and key)
-    EmitUnstowICValues(masm, 2);
-
-    regs.add(R0);
-    regs.takeUnchecked(objReg);
-
-    Register holderReg;
-    if (obj_->is<UnboxedPlainObject>()) {
-        // We are loading off the expando object, so use that for the holder.
-        holderReg = regs.takeAny();
-        masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
-        if (!isFixedSlot_)
-            masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
-    } else if (isFixedSlot_) {
-        holderReg = objReg;
-    } else {
-        holderReg = regs.takeAny();
-        masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
-    }
-
-    // Perform the store.
-    masm.load32(Address(ICStubReg, ICSetProp_Native::offsetOfOffset()), scratch);
-    EmitPreBarrier(masm, BaseIndex(holderReg, scratch, TimesOne), MIRType::Value);
-    masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
-    if (holderReg != objReg)
-        regs.add(holderReg);
-    if (cx->runtime()->gc.nursery.exists()) {
-        Register scr = regs.takeAny();
-        LiveGeneralRegisterSet saveRegs;
-        saveRegs.add(R1);
-        emitPostWriteBarrierSlot(masm, objReg, R1, scr, saveRegs);
-        regs.add(scr);
-    }
-
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 ICUpdatedStub*
 ICSetPropNativeAddCompiler::getStub(ICStubSpace* space)
 {
     Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
     if (!shapes.append(oldShape_))
         return nullptr;
 
     if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
@@ -3318,17 +3223,17 @@ ICSetPropNativeAddCompiler::generateStub
 
     if (holderReg != objReg)
         regs.add(holderReg);
 
     if (cx->runtime()->gc.nursery.exists()) {
         Register scr = regs.takeAny();
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(R1);
-        emitPostWriteBarrierSlot(masm, objReg, R1, scr, saveRegs);
+        BaselineEmitPostWriteBarrierSlot(masm, objReg, R1, scr, saveRegs, cx);
     }
 
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failureUnstow);
     EmitUnstowICValues(masm, 2);
 
@@ -3375,17 +3280,17 @@ ICSetProp_Unboxed::Compiler::generateStu
 
         // Trigger post barriers here on the values being written. Fields which
         // objects can be written to also need update stubs.
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(R0);
         saveRegs.add(R1);
         saveRegs.addUnchecked(object);
         saveRegs.add(ICStubReg);
-        emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
+        BaselineEmitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs, cx);
     }
 
     // Compute the address being written to.
     masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch);
     BaseIndex address(object, scratch, TimesOne);
 
     EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_);
     masm.storeUnboxedProperty(address, fieldType_,
@@ -3442,17 +3347,17 @@ ICSetProp_TypedObject::Compiler::generat
 
         // Trigger post barriers here on the values being written. Descriptors
         // which can write objects also need update stubs.
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(R0);
         saveRegs.add(R1);
         saveRegs.addUnchecked(object);
         saveRegs.add(ICStubReg);
-        emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
+        BaselineEmitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs, cx);
     }
 
     // Save the rhs on the stack so we can get a second scratch register.
     Label failurePopRHS;
     masm.pushValue(R1);
     regs = availableGeneralRegs(1);
     regs.takeUnchecked(object);
     regs.take(scratch);
@@ -6677,38 +6582,16 @@ ICGetIntrinsic_Constant::~ICGetIntrinsic
 ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape,
                                              JSObject* prototypeObj, uint32_t slot)
   : ICStub(InstanceOf_Function, stubCode),
     shape_(shape),
     prototypeObj_(prototypeObj),
     slot_(slot)
 { }
 
-ICSetProp_Native::ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape* shape,
-                                   uint32_t offset)
-  : ICUpdatedStub(SetProp_Native, stubCode),
-    group_(group),
-    shape_(shape),
-    offset_(offset)
-{ }
-
-ICSetProp_Native*
-ICSetProp_Native::Compiler::getStub(ICStubSpace* space)
-{
-    RootedObjectGroup group(cx, JSObject::getGroup(cx, obj_));
-    if (!group)
-        return nullptr;
-
-    RootedShape shape(cx, LastPropertyForSetProp(obj_));
-    ICSetProp_Native* stub = newStub<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
-    if (!stub || !stub->initUpdatingChain(cx, space))
-        return nullptr;
-    return stub;
-}
-
 ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode* stubCode, ObjectGroup* group,
                                          size_t protoChainDepth,
                                          Shape* newShape,
                                          ObjectGroup* newGroup,
                                          uint32_t offset)
   : ICUpdatedStub(SetProp_NativeAdd, stubCode),
     group_(group),
     newShape_(newShape),
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1106,79 +1106,16 @@ class ICSetProp_Fallback : public ICFall
         { }
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICSetProp_Fallback>(space, getStubCode());
         }
     };
 };
 
-// Optimized SETPROP/SETGNAME/SETNAME stub.
-class ICSetProp_Native : public ICUpdatedStub
-{
-    friend class ICStubSpace;
-
-  protected: // Protected to silence Clang warning.
-    GCPtrObjectGroup group_;
-    GCPtrShape shape_;
-    uint32_t offset_;
-
-    ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape* shape, uint32_t offset);
-
-  public:
-    GCPtrObjectGroup& group() {
-        return group_;
-    }
-    GCPtrShape& shape() {
-        return shape_;
-    }
-    void notePreliminaryObject() {
-        extra_ = 1;
-    }
-    bool hasPreliminaryObject() const {
-        return extra_;
-    }
-    static size_t offsetOfGroup() {
-        return offsetof(ICSetProp_Native, group_);
-    }
-    static size_t offsetOfShape() {
-        return offsetof(ICSetProp_Native, shape_);
-    }
-    static size_t offsetOfOffset() {
-        return offsetof(ICSetProp_Native, offset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-        RootedObject obj_;
-        bool isFixedSlot_;
-        uint32_t offset_;
-
-      protected:
-        virtual int32_t getKey() const {
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(isFixedSlot_) << 17) |
-                  (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18);
-        }
-
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        Compiler(JSContext* cx, HandleObject obj, bool isFixedSlot, uint32_t offset)
-          : ICStubCompiler(cx, ICStub::SetProp_Native, Engine::Baseline),
-            obj_(cx, obj),
-            isFixedSlot_(isFixedSlot),
-            offset_(offset)
-        {}
-
-        ICSetProp_Native* getStub(ICStubSpace* space);
-    };
-};
-
-
 template <size_t ProtoChainDepth> class ICSetProp_NativeAddImpl;
 
 class ICSetProp_NativeAdd : public ICUpdatedStub
 {
   public:
     static const size_t MAX_PROTO_CHAIN_DEPTH = 4;
 
   protected: // Protected to silence Clang warning.
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -64,17 +64,16 @@ namespace jit {
     _(GetName_Fallback)                          \
                                                  \
     _(BindName_Fallback)                         \
                                                  \
     _(GetIntrinsic_Fallback)                     \
     _(GetIntrinsic_Constant)                     \
                                                  \
     _(SetProp_Fallback)                          \
-    _(SetProp_Native)                            \
     _(SetProp_NativeAdd)                         \
     _(SetProp_Unboxed)                           \
     _(SetProp_TypedObject)                       \
     _(SetProp_CallScripted)                      \
     _(SetProp_CallNative)                        \
                                                  \
     _(TableSwitch)                               \
                                                  \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -166,16 +166,59 @@ GetCacheIRReceiverForUnboxedProperty(ICC
 
     if (!reader.matchOp(CacheOp::GuardGroup, objId))
         return false;
     receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
 
     return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId);
 }
 
+static bool
+GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub, ReceiverGuard* receiver)
+{
+    // We match either:
+    //
+    //   GuardIsObject 0
+    //   GuardGroup 0
+    //   GuardShape 0
+    //   StoreFixedSlot 0 or StoreDynamicSlot 0
+    //
+    // or
+    //
+    //   GuardIsObject 0
+    //   GuardGroup 0
+    //   1: GuardAndLoadUnboxedExpando 0
+    //   GuardShape 1
+    //   StoreFixedSlot 1 or StoreDynamicSlot 1
+
+    *receiver = ReceiverGuard();
+    CacheIRReader reader(stub->stubInfo());
+
+    ObjOperandId objId = ObjOperandId(0);
+    if (!reader.matchOp(CacheOp::GuardIsObject, objId))
+        return false;
+
+    if (!reader.matchOp(CacheOp::GuardGroup, objId))
+        return false;
+    ObjectGroup* group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
+
+    if (reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId))
+        objId = reader.objOperandId();
+
+    if (!reader.matchOp(CacheOp::GuardShape, objId))
+        return false;
+    Shape* shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+
+    if (!reader.matchOpEither(CacheOp::StoreFixedSlot, CacheOp::StoreDynamicSlot))
+        return false;
+
+    *receiver = ReceiverGuard(group, shape);
+    return true;
+}
+
 bool
 BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
                                           ObjectGroupVector& convertUnboxedGroups)
 {
     // Return a list of the receivers seen by the baseline IC for the current
     // op. Empty lists indicate no receivers are known, or there was an
     // uncacheable access. convertUnboxedGroups is used for unboxed object
     // groups which have been seen, but have had instances converted to native
@@ -194,19 +237,21 @@ BaselineInspector::maybeInfoForPropertyO
         ReceiverGuard receiver;
         if (stub->isCacheIR_Monitored()) {
             if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) &&
                 !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver))
             {
                 receivers.clear();
                 return true;
             }
-        } else if (stub->isSetProp_Native()) {
-            receiver = ReceiverGuard(stub->toSetProp_Native()->group(),
-                                     stub->toSetProp_Native()->shape());
+        } else if (stub->isCacheIR_Updated()) {
+            if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(), &receiver)) {
+                receivers.clear();
+                return true;
+            }
         } else if (stub->isSetProp_Unboxed()) {
             receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr);
         } else {
             receivers.clear();
             return true;
         }
 
         if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1518,8 +1518,146 @@ IRGenerator::maybeGuardInt32Index(const 
         StringOperandId strId = writer.guardIsString(indexId);
         *int32Index = uint32_t(indexSigned);
         *int32IndexId = writer.guardAndGetIndexFromString(strId);
         return true;
     }
 
     return false;
 }
+
+SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind,
+                                       bool* isTemporarilyUnoptimizable, HandleValue lhsVal,
+                                       HandleValue idVal, HandleValue rhsVal)
+  : IRGenerator(cx, pc, cacheKind),
+    lhsVal_(lhsVal),
+    idVal_(idVal),
+    rhsVal_(rhsVal),
+    isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
+    preliminaryObjectAction_(PreliminaryObjectAction::None),
+    updateStubId_(cx, JSID_EMPTY),
+    needUpdateStub_(false)
+{}
+
+bool
+SetPropIRGenerator::tryAttachStub()
+{
+    AutoAssertNoPendingException aanpe(cx_);
+
+    ValOperandId lhsValId(writer.setInputOperandId(0));
+    ValOperandId rhsValId(writer.setInputOperandId(1));
+
+    RootedId id(cx_);
+    bool nameOrSymbol;
+    if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
+        cx_->clearPendingException();
+        return false;
+    }
+
+    if (lhsVal_.isObject()) {
+        RootedObject obj(cx_, &lhsVal_.toObject());
+        if (obj->watched())
+            return false;
+
+        ObjOperandId objId = writer.guardIsObject(lhsValId);
+        if (nameOrSymbol) {
+            if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
+                return true;
+            if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
+                return true;
+        }
+        return false;
+    }
+
+    return false;
+}
+
+static void
+EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId, NativeObject* nobj, Shape* shape,
+                       ValOperandId rhsId)
+{
+    if (nobj->isFixedSlot(shape->slot())) {
+        size_t offset = NativeObject::getFixedSlotOffset(shape->slot());
+        writer.storeFixedSlot(objId, offset, rhsId);
+    } else {
+        size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value);
+        writer.storeDynamicSlot(objId, offset, rhsId);
+    }
+    writer.returnFromIC();
+}
+
+static Shape*
+LookupShapeForSetSlot(NativeObject* obj, jsid id)
+{
+    Shape* shape = obj->lookupPure(id);
+    if (shape && shape->hasSlot() && shape->hasDefaultSetter() && shape->writable())
+        return shape;
+    return nullptr;
+}
+
+bool
+SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
+                                           ValOperandId rhsId)
+{
+    if (!obj->isNative())
+        return false;
+
+    RootedShape propShape(cx_, LookupShapeForSetSlot(&obj->as<NativeObject>(), id));
+    if (!propShape)
+        return false;
+
+    RootedObjectGroup group(cx_, JSObject::getGroup(cx_, obj));
+    if (!group) {
+        cx_->recoverFromOutOfMemory();
+        return false;
+    }
+
+    // For some property writes, such as the initial overwrite of global
+    // properties, TI will not mark the property as having been
+    // overwritten. Don't attach a stub in this case, so that we don't
+    // execute another write to the property without TI seeing that write.
+    EnsureTrackPropertyTypes(cx_, obj, id);
+    if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
+        *isTemporarilyUnoptimizable_ = true;
+        return false;
+    }
+
+    // For Baseline, we have to guard on both the shape and group, because the
+    // type update IC applies to a single group. When we port the Ion IC, we can
+    // do a bit better and avoid the group guard if we don't have to guard on
+    // the property types.
+    NativeObject* nobj = &obj->as<NativeObject>();
+    writer.guardGroup(objId, nobj->group());
+    writer.guardShape(objId, nobj->lastProperty());
+
+    if (IsPreliminaryObject(obj))
+        preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
+    else
+        preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
+
+    setUpdateStubInfo(id);
+    EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId);
+    return true;
+}
+
+bool
+SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId,
+                                                   HandleId id, ValOperandId rhsId)
+{
+    if (!obj->is<UnboxedPlainObject>())
+        return false;
+
+    UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+    if (!expando)
+        return false;
+
+    Shape* propShape = LookupShapeForSetSlot(expando, id);
+    if (!propShape)
+        return false;
+
+    writer.guardGroup(objId, obj->group());
+    ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
+    writer.guardShape(expandoId, expando->lastProperty());
+
+    setUpdateStubInfo(id);
+    EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
+    return true;
+}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -129,16 +129,17 @@ class TypedOperandId : public OperandId
     JSValueType type() const { return type_; }
 };
 
 enum class CacheKind : uint8_t
 {
     GetProp,
     GetElem,
     GetName,
+    SetProp,
 };
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
@@ -163,16 +164,19 @@ enum class CacheKind : uint8_t
     _(LoadEnclosingEnvironment)           \
                                           \
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueGuardGeneration) \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
+    _(StoreFixedSlot)                     \
+    _(StoreDynamicSlot)                   \
+                                          \
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
     _(LoadUnboxedArrayElementResult)      \
@@ -542,16 +546,27 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     ValOperandId loadDOMExpandoValueIgnoreGeneration(ObjOperandId obj) {
         ValOperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::LoadDOMExpandoValueIgnoreGeneration, obj);
         writeOperandId(res);
         return res;
     }
 
+    void storeFixedSlot(ObjOperandId obj, size_t offset, ValOperandId rhs) {
+        writeOpWithOperandId(CacheOp::StoreFixedSlot, obj);
+        addStubField(offset, StubField::Type::RawWord);
+        writeOperandId(rhs);
+    }
+    void storeDynamicSlot(ObjOperandId obj, size_t offset, ValOperandId rhs) {
+        writeOpWithOperandId(CacheOp::StoreDynamicSlot, obj);
+        addStubField(offset, StubField::Type::RawWord);
+        writeOperandId(rhs);
+    }
+
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
     }
     void loadFixedSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadFixedSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
     void loadDynamicSlotResult(ObjOperandId obj, size_t offset) {
@@ -819,12 +834,57 @@ class MOZ_RAII GetNameIRGenerator : publ
 
   public:
     GetNameIRGenerator(JSContext* cx, jsbytecode* pc, HandleScript script,
                        HandleObject env, HandlePropertyName name);
 
     bool tryAttachStub();
 };
 
+// SetPropIRGenerator generates CacheIR for a SetProp IC.
+class MOZ_RAII SetPropIRGenerator : public IRGenerator
+{
+    HandleValue lhsVal_;
+    HandleValue idVal_;
+    HandleValue rhsVal_;
+    bool* isTemporarilyUnoptimizable_;
+
+    enum class PreliminaryObjectAction { None, Unlink, NotePreliminary };
+    PreliminaryObjectAction preliminaryObjectAction_;
+
+    // If Baseline needs an update stub, this contains information to create it.
+    RootedId updateStubId_;
+    bool needUpdateStub_;
+
+    void setUpdateStubInfo(jsid id) {
+        MOZ_ASSERT(!needUpdateStub_);
+        needUpdateStub_ = true;
+        updateStubId_ = id;
+    }
+
+    bool tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
+                                 ValOperandId rhsId);
+    bool tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
+                                        ValOperandId rhsId);
+
+  public:
+    SetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind,
+                       bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal,
+                       HandleValue rhsVal);
+
+    bool tryAttachStub();
+
+    bool shouldUnlinkPreliminaryObjectStubs() const {
+        return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink;
+    }
+    bool shouldNotePreliminaryObjectStub() const {
+        return preliminaryObjectAction_ == PreliminaryObjectAction::NotePreliminary;
+    }
+    jsid updateStubId() const {
+        MOZ_ASSERT(needUpdateStub_);
+        return updateStubId_;
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CacheIR_h */
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -56,16 +56,52 @@ CacheRegisterAllocator::useValueRegister
       }
       case OperandLocation::Uninitialized:
         break;
     }
 
     MOZ_CRASH();
 }
 
+ValueOperand
+CacheRegisterAllocator::useFixedValueRegister(MacroAssembler& masm, ValOperandId valId,
+                                              ValueOperand reg)
+{
+    allocateFixedValueRegister(masm, reg);
+
+    OperandLocation& loc = operandLocations_[valId.id()];
+    switch (loc.kind()) {
+      case OperandLocation::ValueReg:
+        masm.moveValue(loc.valueReg(), reg);
+        MOZ_ASSERT(!currentOpRegs_.aliases(loc.valueReg()), "Register shouldn't be in use");
+        availableRegs_.add(loc.valueReg());
+        break;
+      case OperandLocation::ValueStack:
+        popValue(masm, &loc, reg);
+        break;
+      case OperandLocation::Constant:
+        masm.moveValue(loc.constant(), reg);
+        break;
+      case OperandLocation::PayloadReg:
+        masm.tagValue(loc.payloadType(), loc.payloadReg(), reg);
+        MOZ_ASSERT(!currentOpRegs_.has(loc.payloadReg()), "Register shouldn't be in use");
+        availableRegs_.add(loc.payloadReg());
+        break;
+      case OperandLocation::PayloadStack:
+        popPayload(masm, &loc, reg.scratchReg());
+        masm.tagValue(loc.payloadType(), reg.scratchReg(), reg);
+        break;
+      case OperandLocation::Uninitialized:
+        MOZ_CRASH();
+    }
+
+    loc.setValueReg(reg);
+    return reg;
+}
+
 Register
 CacheRegisterAllocator::useRegister(MacroAssembler& masm, TypedOperandId typedId)
 {
     OperandLocation& loc = operandLocations_[typedId.id()];
     switch (loc.kind()) {
       case OperandLocation::PayloadReg:
         currentOpRegs_.add(loc.payloadReg());
         return loc.payloadReg();
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -334,16 +334,17 @@ class MOZ_RAII CacheRegisterAllocator
 
     // Removes spilled values from the native stack. This should only be
     // called after all registers have been allocated.
     void discardStack(MacroAssembler& masm);
 
     // Returns the register for the given operand. If the operand is currently
     // not in a register, it will load it into one.
     ValueOperand useValueRegister(MacroAssembler& masm, ValOperandId val);
+    ValueOperand useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg);
     Register useRegister(MacroAssembler& masm, TypedOperandId typedId);
 
     // Allocates an output register for the given operand.
     Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
     ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
 
     // Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
     JSValueType knownType(ValOperandId val) const;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -252,16 +252,17 @@ CodeGenerator::visitOutOfLineICFallback(
 
         StoreValueTo(getPropIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
 
         masm.jump(ool->rejoin());
         return;
       }
       case CacheKind::GetName:
+      case CacheKind::SetProp:
         MOZ_CRASH("Baseline-specific for now");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
 {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5707,57 +5707,71 @@ IonBuilder::compareTrySharedStub(bool* e
     current->add(unbox);
     current->push(unbox);
 
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
+static bool
+IsCallOpcode(JSOp op)
+{
+    // TODO: Support tracking optimizations for inlining a call and regular
+    // optimization tracking at the same time.
+    return op == JSOP_CALL || op == JSOP_CALLITER || op == JSOP_NEW || op == JSOP_SUPERCALL ||
+           op == JSOP_EVAL || op == JSOP_STRICTEVAL;
+}
+
 AbortReasonOr<Ok>
 IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length)
 {
     MOZ_ASSERT(*emitted == false);
 
-    trackOptimizationAttempt(TrackedStrategy::NewArray_TemplateObject);
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationAttempt(TrackedStrategy::NewArray_TemplateObject);
 
     if (!templateObject) {
-        trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
+        if (!IsCallOpcode(JSOp(*pc)))
+            trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
         return Ok();
     }
 
     if (templateObject->is<UnboxedArrayObject>()) {
         MOZ_ASSERT(templateObject->as<UnboxedArrayObject>().capacity() >= length);
         if (!templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
-            trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsUnboxedWithoutInlineElements);
+            if (!IsCallOpcode(JSOp(*pc)))
+                trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsUnboxedWithoutInlineElements);
             return Ok();
         }
     }
 
     MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
 
     size_t arraySlots =
         gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
 
     if (length > arraySlots) {
-        trackOptimizationOutcome(TrackedOutcome::LengthTooBig);
+        if (!IsCallOpcode(JSOp(*pc)))
+            trackOptimizationOutcome(TrackedOutcome::LengthTooBig);
         return Ok();
     }
 
     // Emit fastpath.
 
     gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc);
     current->add(ins);
     current->push(ins);
 
-    trackOptimizationSuccess();
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::newArrayTrySharedStub(bool* emitted)
 {
     MOZ_ASSERT(*emitted == false);
@@ -5765,57 +5779,61 @@ IonBuilder::newArrayTrySharedStub(bool* 
     // Try to emit a shared stub cache.
 
     if (JitOptions.disableSharedStubs)
         return Ok();
 
     if (*pc != JSOP_NEWINIT && *pc != JSOP_NEWARRAY)
         return Ok();
 
-    trackOptimizationAttempt(TrackedStrategy::NewArray_SharedCache);
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationAttempt(TrackedStrategy::NewArray_SharedCache);
 
     MInstruction* stub = MNullarySharedStub::New(alloc());
     current->add(stub);
     current->push(stub);
 
     MOZ_TRY(resumeAfter(stub));
 
     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
     current->add(unbox);
     current->push(unbox);
 
-    trackOptimizationSuccess();
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationSuccess();
 
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length)
 {
     MOZ_ASSERT(*emitted == false);
 
     // Emit a VM call.
-    trackOptimizationAttempt(TrackedStrategy::NewArray_Call);
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationAttempt(TrackedStrategy::NewArray_Call);
 
     gc::InitialHeap heap = gc::DefaultHeap;
     MConstant* templateConst = MConstant::New(alloc(), NullValue());
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     }
 
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::NewVM(alloc(), constraints(), length, templateConst, heap, pc);
     current->add(ins);
     current->push(ins);
 
-    trackOptimizationSuccess();
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_newarray(uint32_t length)
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
@@ -5830,17 +5848,18 @@ IonBuilder::jsop_newarray(uint32_t lengt
 
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_newarray(JSObject* templateObject, uint32_t length)
 {
     bool emitted = false;
-    startTrackingOptimizations();
+    if (!IsCallOpcode(JSOp(*pc)))
+        startTrackingOptimizations();
 
     if (!forceInlineCaches()) {
         MOZ_TRY(newArrayTryTemplateObject(&emitted, templateObject, length));
         if (emitted)
             return Ok();
     }
 
     MOZ_TRY(newArrayTrySharedStub(&emitted));
@@ -5876,24 +5895,27 @@ IonBuilder::jsop_newarray_copyonwrite()
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::newObjectTryTemplateObject(bool* emitted, JSObject* templateObject)
 {
     MOZ_ASSERT(*emitted == false);
 
-    trackOptimizationAttempt(TrackedStrategy::NewObject_TemplateObject);
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationAttempt(TrackedStrategy::NewObject_TemplateObject);
     if (!templateObject) {
-        trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
+        if (!IsCallOpcode(JSOp(*pc)))
+            trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
         return Ok();
     }
 
     if (templateObject->is<PlainObject>() && templateObject->as<PlainObject>().hasDynamicSlots()) {
-        trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsPlainObjectWithDynamicSlots);
+        if (!IsCallOpcode(JSOp(*pc)))
+            trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsPlainObjectWithDynamicSlots);
         return Ok();
     }
 
     // Emit fastpath.
 
     MNewObject::Mode mode;
     if (JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT)
         mode = MNewObject::ObjectLiteral;
@@ -5905,55 +5927,59 @@ IonBuilder::newObjectTryTemplateObject(b
     current->add(templateConst);
 
     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, mode);
     current->add(ins);
     current->push(ins);
 
     MOZ_TRY(resumeAfter(ins));
 
-    trackOptimizationSuccess();
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::newObjectTrySharedStub(bool* emitted)
 {
     MOZ_ASSERT(*emitted == false);
 
     // Try to emit a shared stub cache.
 
     if (JitOptions.disableSharedStubs)
         return Ok();
 
-    trackOptimizationAttempt(TrackedStrategy::NewObject_SharedCache);
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationAttempt(TrackedStrategy::NewObject_SharedCache);
 
     MInstruction* stub = MNullarySharedStub::New(alloc());
     current->add(stub);
     current->push(stub);
 
     MOZ_TRY(resumeAfter(stub));
 
     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
     current->add(unbox);
     current->push(unbox);
 
-    trackOptimizationSuccess();
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::newObjectTryVM(bool* emitted, JSObject* templateObject)
 {
     // Emit a VM call.
     MOZ_ASSERT(JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT);
 
-    trackOptimizationAttempt(TrackedStrategy::NewObject_Call);
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationAttempt(TrackedStrategy::NewObject_Call);
 
     gc::InitialHeap heap = gc::DefaultHeap;
     MConstant* templateConst = MConstant::New(alloc(), NullValue());
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     }
@@ -5962,17 +5988,18 @@ IonBuilder::newObjectTryVM(bool* emitted
 
     MNewObject* ins = MNewObject::NewVM(alloc(), constraints(), templateConst, heap,
                                         MNewObject::ObjectLiteral);
     current->add(ins);
     current->push(ins);
 
     MOZ_TRY(resumeAfter(ins));
 
-    trackOptimizationSuccess();
+    if (!IsCallOpcode(JSOp(*pc)))
+        trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_newobject()
 {
     bool emitted = false;
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -816,16 +816,28 @@ IonCacheIRCompiler::emitLoadEnvironmentF
 
 bool
 IonCacheIRCompiler::emitLoadEnvironmentDynamicSlotResult()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
+IonCacheIRCompiler::emitStoreFixedSlot()
+{
+    MOZ_CRASH("Baseline-specific op");
+}
+
+bool
+IonCacheIRCompiler::emitStoreDynamicSlot()
+{
+    MOZ_CRASH("Baseline-specific op");
+}
+
+bool
 IonCacheIRCompiler::emitLoadTypedObjectResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegister scratch2(allocator, masm);
 
     TypedThingLayout layout = reader.typedThingLayout();
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -96,40 +96,39 @@ ControlFlowGraph::init(TempAllocator& al
         if (!alloc.ensureBallast())
             return false;
 
         CFGControlInstruction* copy = nullptr;
         CFGControlInstruction* ins = blocks[i]->stopIns();
         switch (ins->type()) {
           case CFGControlInstruction::Type_Goto: {
             CFGBlock* successor = &blocks_[ins->getSuccessor(0)->id()];
-            copy = CFGGoto::New(alloc, successor, ins->toGoto()->popAmount());
+            copy = CFGGoto::CopyWithNewTargets(alloc, ins->toGoto(), successor);
             break;
           }
           case CFGControlInstruction::Type_BackEdge: {
             CFGBlock* successor = &blocks_[ins->getSuccessor(0)->id()];
-            copy = CFGBackEdge::New(alloc, successor);
+            copy = CFGBackEdge::CopyWithNewTargets(alloc, ins->toBackEdge(), successor);
             break;
           }
           case CFGControlInstruction::Type_LoopEntry: {
             CFGLoopEntry* old = ins->toLoopEntry();
             CFGBlock* successor = &blocks_[ins->getSuccessor(0)->id()];
-            copy = CFGLoopEntry::New(alloc, successor, old->canOsr(), old->stackPhiCount(),
-                                     old->loopStopPc());
+            copy = CFGLoopEntry::CopyWithNewTargets(alloc, old, successor);
             break;
           }
           case CFGControlInstruction::Type_Throw: {
             copy = CFGThrow::New(alloc);
             break;
           }
           case CFGControlInstruction::Type_Test: {
             CFGTest* old = ins->toTest();
             CFGBlock* trueBranch = &blocks_[old->trueBranch()->id()];
             CFGBlock* falseBranch = &blocks_[old->falseBranch()->id()];
-            copy = CFGTest::New(alloc, trueBranch, falseBranch, old->mustKeepCondition());
+            copy = CFGTest::CopyWithNewTargets(alloc, old, trueBranch, falseBranch);
             break;
           }
           case CFGControlInstruction::Type_Compare: {
             CFGCompare* old = ins->toCompare();
             CFGBlock* trueBranch = &blocks_[old->trueBranch()->id()];
             CFGBlock* falseBranch = &blocks_[old->falseBranch()->id()];
             copy = CFGCompare::CopyWithNewTargets(alloc, old, trueBranch, falseBranch);
             break;
@@ -143,17 +142,17 @@ ControlFlowGraph::init(TempAllocator& al
             break;
           }
           case CFGControlInstruction::Type_Try: {
             CFGTry* old = ins->toTry();
             CFGBlock* tryBlock = &blocks_[old->tryBlock()->id()];
             CFGBlock* merge = nullptr;
             if (old->numSuccessors() == 2)
                 merge = &blocks_[old->afterTryCatchBlock()->id()];
-            copy = CFGTry::New(alloc, tryBlock, old->catchStartPc(), merge);
+            copy = CFGTry::CopyWithNewTargets(alloc, old, tryBlock, merge);
             break;
           }
           case CFGControlInstruction::Type_TableSwitch: {
             CFGTableSwitch* old = ins->toTableSwitch();
             CFGTableSwitch* tableSwitch =
                 CFGTableSwitch::New(alloc, old->low(), old->high());
             if (!tableSwitch->addDefault(&blocks_[old->defaultCase()->id()]))
                 return false;
@@ -333,16 +332,17 @@ ControlFlowGenerator::snoopControlFlow(J
 
       case JSOP_LABEL:
         return processLabel();
 
       case JSOP_TRY:
         return processTry();
 
       case JSOP_OPTIMIZE_SPREADCALL:
+      case JSOP_THROWMSG:
         // Not implemented yet.
         return ControlStatus::Abort;
 
       default:
         break;
     }
     return ControlStatus::None;
 }
--- a/js/src/jit/IonControlFlow.h
+++ b/js/src/jit/IonControlFlow.h
@@ -214,16 +214,22 @@ class CFGTry : public CFGControlInstruct
         catchStartPc_(catchStartPc),
         mergePoint_(mergePoint)
     { }
 
   public:
     CFG_CONTROL_HEADER(Try)
     TRIVIAL_CFG_NEW_WRAPPERS
 
+    static CFGTry* CopyWithNewTargets(TempAllocator& alloc, CFGTry* old,
+                                      CFGBlock* tryBlock, CFGBlock* merge)
+    {
+        return new(alloc) CFGTry(tryBlock, old->catchStartPc(), merge);
+    }
+
     size_t numSuccessors() const final override {
         return mergePoint_ ? 2 : 1;
     }
     CFGBlock* getSuccessor(size_t i) const final override {
         MOZ_ASSERT(i < numSuccessors());
         return (i == 0) ? tryBlock_ : mergePoint_;
     }
     void replaceSuccessor(size_t i, CFGBlock* succ) final override {
@@ -355,17 +361,16 @@ class CFGCompare : public CFGAryControlI
     }
 
     static CFGCompare* CopyWithNewTargets(TempAllocator& alloc, CFGCompare* old,
                                           CFGBlock* succ1, CFGBlock* succ2)
     {
         return new(alloc) CFGCompare(succ1, old->truePopAmount(), succ2, old->falsePopAmount());
     }
 
-
     CFGBlock* trueBranch() const {
         return getSuccessor(0);
     }
     CFGBlock* falseBranch() const {
         return getSuccessor(1);
     }
     size_t truePopAmount() const {
         return truePopAmount_;
@@ -401,16 +406,22 @@ class CFGTest : public CFGAryControlInst
         replaceSuccessor(0, succ1);
         replaceSuccessor(1, succ2);
     }
 
   public:
     CFG_CONTROL_HEADER(Test);
     TRIVIAL_CFG_NEW_WRAPPERS
 
+    static CFGTest* CopyWithNewTargets(TempAllocator& alloc, CFGTest* old,
+                                       CFGBlock* succ1, CFGBlock* succ2)
+    {
+        return new(alloc) CFGTest(succ1, succ2, old->mustKeepCondition());
+    }
+
     CFGBlock* trueBranch() const {
         return getSuccessor(0);
     }
     CFGBlock* falseBranch() const {
         return getSuccessor(1);
     }
     void keepCondition() {
         keepCondition_ = true;
@@ -478,32 +489,37 @@ class CFGUnaryControlInstruction : publi
  * CFGGOTO
  *
  * POP (x popAmount)
  * JMP block
  *
  */
 class CFGGoto : public CFGUnaryControlInstruction
 {
-    size_t popAmount_;
+    const size_t popAmount_;
 
     explicit CFGGoto(CFGBlock* block)
       : CFGUnaryControlInstruction(block),
         popAmount_(0)
     {}
 
     CFGGoto(CFGBlock* block, size_t popAmount_)
       : CFGUnaryControlInstruction(block),
         popAmount_(popAmount_)
     {}
 
   public:
     CFG_CONTROL_HEADER(Goto);
     TRIVIAL_CFG_NEW_WRAPPERS
 
+    static CFGGoto* CopyWithNewTargets(TempAllocator& alloc, CFGGoto* old, CFGBlock* block)
+    {
+        return new(alloc) CFGGoto(block, old->popAmount());
+    }
+
     size_t popAmount() const {
         return popAmount_;
     }
 };
 
 /**
  * CFGBackEdge
  *
@@ -516,16 +532,21 @@ class CFGBackEdge : public CFGUnaryContr
 {
     explicit CFGBackEdge(CFGBlock* block)
       : CFGUnaryControlInstruction(block)
     {}
 
   public:
     CFG_CONTROL_HEADER(BackEdge);
     TRIVIAL_CFG_NEW_WRAPPERS
+
+    static CFGBackEdge* CopyWithNewTargets(TempAllocator& alloc, CFGBackEdge* old, CFGBlock* block)
+    {
+        return new(alloc) CFGBackEdge(block);
+    }
 };
 
 /**
  * CFGLOOPENTRY
  *
  * Indicates the jumping block is the start of a loop.
  * That block is the only block allowed to have a backedge.
  *
@@ -551,16 +572,23 @@ class CFGLoopEntry : public CFGUnaryCont
         stackPhiCount_(stackPhiCount),
         loopStopPc_(loopStopPc)
     {}
 
   public:
     CFG_CONTROL_HEADER(LoopEntry);
     TRIVIAL_CFG_NEW_WRAPPERS
 
+    static CFGLoopEntry* CopyWithNewTargets(TempAllocator& alloc, CFGLoopEntry* old,
+                                            CFGBlock* loopEntry)
+    {
+        return new(alloc) CFGLoopEntry(loopEntry, old->canOsr(), old->stackPhiCount(),
+                                       old->loopStopPc());
+    }
+
     void setCanOsr() {
         canOsr_ = true;
     }
 
     bool canOsr() const {
         return canOsr_;
     }
 
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -37,16 +37,17 @@ IonIC::scratchRegisterForEntryJump()
       case CacheKind::GetElem: {
         Register temp = asGetPropertyIC()->maybeTemp();
         if (temp != InvalidReg)
             return temp;
         TypedOrValueRegister output = asGetPropertyIC()->output();
         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
       }
       case CacheKind::GetName:
+      case CacheKind::SetProp:
         MOZ_CRASH("Baseline-specific for now");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
 IonIC::reset(Zone* zone)
--- a/js/src/jit/Label.h
+++ b/js/src/jit/Label.h
@@ -15,18 +15,18 @@ namespace jit {
 struct LabelBase
 {
   protected:
     // offset_ >= 0 means that the label is either bound or has incoming
     // uses and needs to be bound.
     int32_t offset_ : 31;
     bool bound_   : 1;
 
-    // Disallow assignment.
-    void operator =(const LabelBase& label);
+    void operator =(const LabelBase& label) = delete;
+
   public:
     static const int32_t INVALID_OFFSET = -1;
 
     LabelBase() : offset_(INVALID_OFFSET), bound_(false)
     { }
 
     // If the label is bound, all incoming edges have been patched and any
     // future incoming edges will be immediately patched.
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -483,17 +483,17 @@ class MacroAssembler : public MacroAssem
 #endif // DEBUG
 
   public:
     // ===============================================================
     // Simple call functions.
 
     CodeOffset call(Register reg) PER_SHARED_ARCH;
     CodeOffset call(Label* label) PER_SHARED_ARCH;
-    void call(const Address& addr) DEFINED_ON(x86_shared);
+    void call(const Address& addr) DEFINED_ON(x86_shared, arm, arm64);
     void call(ImmWord imm) PER_SHARED_ARCH;
     // Call a target native function, which is neither traceable nor movable.
     void call(ImmPtr imm) PER_SHARED_ARCH;
     void call(wasm::SymbolicAddress imm) PER_SHARED_ARCH;
     // Call a target JitCode, which must be traceable, and may be movable.
     void call(JitCode* c) PER_SHARED_ARCH;
 
     inline void call(const wasm::CallSiteDesc& desc, const Register reg);
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -188,19 +188,24 @@ ICStub::NonCacheIRStubMakesGCCalls(Kind 
       default:
         return false;
     }
 }
 
 bool
 ICStub::makesGCCalls() const
 {
-    if (isCacheIR_Monitored())
+    switch (kind()) {
+      case CacheIR_Monitored:
         return toCacheIR_Monitored()->stubInfo()->makesGCCalls();
-    return NonCacheIRStubMakesGCCalls(kind());
+      case CacheIR_Updated:
+        return toCacheIR_Updated()->stubInfo()->makesGCCalls();
+      default:
+        return NonCacheIRStubMakesGCCalls(kind());
+    }
 }
 
 void
 ICStub::traceCode(JSTracer* trc, const char* name)
 {
     JitCode* stubJitCode = jitCode();
     TraceManuallyBarrieredEdge(trc, &stubJitCode, name);
 }
@@ -346,22 +351,16 @@ ICStub::trace(JSTracer* trc)
         TraceEdge(trc, &inStub->shape(), "baseline-in-dense-shape");
         break;
       }
       case ICStub::GetIntrinsic_Constant: {
         ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
         TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
         break;
       }
-      case ICStub::SetProp_Native: {
-        ICSetProp_Native* propStub = toSetProp_Native();
-        TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
-        TraceEdge(trc, &propStub->group(), "baseline-setpropnative-stub-group");
-        break;
-      }
       case ICStub::SetProp_NativeAdd: {
         ICSetProp_NativeAdd* propStub = toSetProp_NativeAdd();
         TraceEdge(trc, &propStub->group(), "baseline-setpropnativeadd-stub-group");
         TraceEdge(trc, &propStub->newShape(), "baseline-setpropnativeadd-stub-newshape");
         TraceNullableEdge(trc, &propStub->newGroup(), "baseline-setpropnativeadd-stub-new-group");
         JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4);
         switch (propStub->protoChainDepth()) {
           case 0: propStub->toImpl<0>()->traceShapes(trc); break;
@@ -420,16 +419,22 @@ ICStub::trace(JSTracer* trc)
       case ICStub::Rest_Fallback: {
         ICRest_Fallback* stub = toRest_Fallback();
         TraceEdge(trc, &stub->templateObject(), "baseline-rest-template");
         break;
       }
       case ICStub::CacheIR_Monitored:
         TraceCacheIRStub(trc, this, toCacheIR_Monitored()->stubInfo());
         break;
+      case ICStub::CacheIR_Updated: {
+        ICCacheIR_Updated* stub = toCacheIR_Updated();
+        TraceEdge(trc, &stub->updateStubId(), "baseline-updated-id");
+        TraceCacheIRStub(trc, this, stub->stubInfo());
+        break;
+      }
       default:
         break;
     }
 }
 
 void
 ICFallbackStub::unlinkStub(Zone* zone, ICStub* prev, ICStub* stub)
 {
@@ -726,31 +731,32 @@ ICStubCompiler::pushStubPayload(MacroAss
 void
 ICStubCompiler::PushStubPayload(MacroAssembler& masm, Register scratch)
 {
     pushStubPayload(masm, scratch);
     masm.adjustFrame(sizeof(intptr_t));
 }
 
 void
-ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
-                                         Register scratch, LiveGeneralRegisterSet saveRegs)
+BaselineEmitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
+                                 Register scratch, LiveGeneralRegisterSet saveRegs,
+                                 JSRuntime* rt)
 {
     Label skipBarrier;
     masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, &skipBarrier);
     masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier);
 
     // void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     saveRegs.add(ICTailCallReg);
 #endif
     saveRegs.set() = GeneralRegisterSet::Intersect(saveRegs.set(), GeneralRegisterSet::Volatile());
     masm.PushRegsInMask(saveRegs);
     masm.setupUnalignedABICall(scratch);
-    masm.movePtr(ImmPtr(cx->runtime()), scratch);
+    masm.movePtr(ImmPtr(rt), scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(obj);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
     masm.PopRegsInMask(saveRegs);
 
     masm.bind(&skipBarrier);
 }
 
@@ -1970,17 +1976,17 @@ StripPreliminaryObjectStubs(JSContext* c
     // both the old and new number of fixed slots, the stub will look
     // polymorphic to IonBuilder when it is actually monomorphic. To avoid
     // this, strip out any stubs for preliminary objects before attaching a new
     // stub which isn't on a preliminary object.
 
     for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
         if (iter->isCacheIR_Monitored() && iter->toCacheIR_Monitored()->hasPreliminaryObject())
             iter.unlink(cx);
-        else if (iter->isSetProp_Native() && iter->toSetProp_Native()->hasPreliminaryObject())
+        else if (iter->isCacheIR_Updated() && iter->toCacheIR_Updated()->hasPreliminaryObject())
             iter.unlink(cx);
     }
 }
 
 JSObject*
 GetDOMProxyProto(JSObject* obj)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -503,17 +503,17 @@ class ICStub
 #undef DEF_ENUM_KIND
         LIMIT
     };
 
     static bool IsValidKind(Kind k) {
         return (k > INVALID) && (k < LIMIT);
     }
     static bool IsCacheIRKind(Kind k) {
-        return k == CacheIR_Monitored;
+        return k == CacheIR_Monitored || k == CacheIR_Updated;
     }
 
     static const char* KindString(Kind k) {
         switch(k) {
 #define DEF_KIND_STR(kindName) case kindName: return #kindName;
             IC_BASELINE_STUB_KIND_LIST(DEF_KIND_STR)
             IC_SHARED_STUB_KIND_LIST(DEF_KIND_STR)
 #undef DEF_KIND_STR
@@ -951,16 +951,46 @@ class ICUpdatedStub : public ICStub
         return numOptimizedStubs_;
     }
 
     static inline size_t offsetOfFirstUpdateStub() {
         return offsetof(ICUpdatedStub, firstUpdateStub_);
     }
 };
 
+class ICCacheIR_Updated : public ICUpdatedStub
+{
+    const CacheIRStubInfo* stubInfo_;
+    GCPtrId updateStubId_;
+
+  public:
+    ICCacheIR_Updated(JitCode* stubCode, const CacheIRStubInfo* stubInfo)
+      : ICUpdatedStub(ICStub::CacheIR_Updated, stubCode),
+        stubInfo_(stubInfo),
+        updateStubId_(JSID_EMPTY)
+    {}
+
+    GCPtrId& updateStubId() {
+        return updateStubId_;
+    }
+
+    void notePreliminaryObject() {
+        extra_ = 1;
+    }
+    bool hasPreliminaryObject() const {
+        return extra_;
+    }
+
+    const CacheIRStubInfo* stubInfo() const {
+        return stubInfo_;
+    }
+
+    uint8_t* stubDataStart();
+};
+
 // Base class for stubcode compilers.
 class ICStubCompiler
 {
     // Prevent GC in the middle of stub compilation.
     js::gc::AutoSuppressGC suppressGC;
 
   public:
     using Engine = ICStubEngine;
@@ -1057,19 +1087,16 @@ class ICStubCompiler
           default:
             MOZ_CRASH("Invalid numInputs");
         }
 
         return regs;
     }
 
   protected:
-    void emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
-                                  Register scratch, LiveGeneralRegisterSet saveRegs);
-
     template <typename T, typename... Args>
     T* newStub(Args&&... args) {
         return ICStub::New<T>(cx, mozilla::Forward<Args>(args)...);
     }
 
   public:
     virtual ICStub* getStub(ICStubSpace* space) = 0;
 
@@ -1081,16 +1108,20 @@ class ICStubCompiler
         }
         return outerScript->zone()->jitZone()->optimizedStubSpace();
     }
     ICStubSpace* getStubSpace(JSScript* outerScript) {
         return StubSpaceForStub(ICStub::NonCacheIRStubMakesGCCalls(kind), outerScript, engine_);
     }
 };
 
+void BaselineEmitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
+                                      Register scratch, LiveGeneralRegisterSet saveRegs,
+                                      JSRuntime* rt);
+
 class SharedStubInfo
 {
     BaselineFrame* maybeFrame_;
     RootedScript outerScript_;
     RootedScript innerScript_;
     ICEntry* icEntry_;
 
   public:
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -33,14 +33,15 @@ namespace jit {
     _(Compare_Object)                            \
     _(Compare_ObjectWithUndefined)               \
     _(Compare_Int32WithBoolean)                  \
                                                  \
     _(GetProp_Fallback)                          \
     _(GetProp_Generic)                           \
                                                  \
     _(CacheIR_Monitored)                         \
+    _(CacheIR_Updated)                           \
                                                  \
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_SharedICList_h */
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -5017,16 +5017,23 @@ MacroAssembler::call(ImmPtr imm)
 void
 MacroAssembler::call(wasm::SymbolicAddress imm)
 {
     movePtr(imm, CallReg);
     call(CallReg);
 }
 
 void
+MacroAssembler::call(const Address& addr)
+{
+    loadPtr(addr, CallReg);
+    call(CallReg);
+}
+
+void
 MacroAssembler::call(JitCode* c)
 {
     BufferOffset bo = m_buffer.nextOffset();
     addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
     ScratchRegisterScope scratch(*this);
     ma_movPatchable(ImmPtr(c->raw()), scratch, Always);
     callJitNoProfiler(scratch);
 }
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -524,16 +524,26 @@ MacroAssembler::call(wasm::SymbolicAddre
     vixl::UseScratchRegisterScope temps(this);
     const Register scratch = temps.AcquireX().asUnsized();
     syncStackPtr();
     movePtr(imm, scratch);
     call(scratch);
 }
 
 void
+MacroAssembler::call(const Address& addr)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    syncStackPtr();
+    loadPtr(addr, scratch);
+    call(scratch);
+}
+
+void
 MacroAssembler::call(JitCode* c)
 {
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
     syncStackPtr();
     BufferOffset off = immPool64(scratch64, uint64_t(c->raw()));
     addPendingJump(off, ImmPtr(c->raw()), Relocation::JITCODE);
     blr(scratch64);
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6915,17 +6915,17 @@ DispatchPointerFromMouseOrTouch(PresShel
     //    dispatched to DOM.
     if (!mouseEvent->convertToPointer ||
         !aEvent->IsAllowedToDispatchDOMEvent()) {
       return NS_OK;
     }
     int16_t button = mouseEvent->button;
     switch (mouseEvent->mMessage) {
     case eMouseMove:
-      button = -1;
+      button = WidgetMouseEvent::eNoButton;
       pointerMessage = ePointerMove;
       break;
     case eMouseUp:
       pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
       break;
     case eMouseDown:
       pointerMessage =
         mouseEvent->buttons & ~nsContentUtils::GetButtonsFlagForButton(button) ?
@@ -6946,24 +6946,28 @@ DispatchPointerFromMouseOrTouch(PresShel
                      0.0f;
     event.convertToPointer = mouseEvent->convertToPointer = false;
     PreHandlePointerEventsPreventDefault(&event, aEvent);
     aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
                         aTargetContent);
     PostHandlePointerEventsPreventDefault(&event, aEvent);
   } else if (aEvent->mClass == eTouchEventClass) {
     WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+    int16_t button = WidgetMouseEvent::eLeftButton;
+    int16_t buttons = WidgetMouseEvent::eLeftButtonFlag;
     // loop over all touches and dispatch pointer events on each touch
     // copy the event
     switch (touchEvent->mMessage) {
     case eTouchMove:
       pointerMessage = ePointerMove;
+      button = WidgetMouseEvent::eNoButton;
       break;
     case eTouchEnd:
       pointerMessage = ePointerUp;
+      buttons = WidgetMouseEvent::eNoButtonFlag;
       break;
     case eTouchStart:
       pointerMessage = ePointerDown;
       break;
     case eTouchCancel:
       pointerMessage = ePointerCancel;
       break;
     default:
@@ -6984,19 +6988,18 @@ DispatchPointerFromMouseOrTouch(PresShel
       event.mModifiers = touchEvent->mModifiers;
       event.mWidth = touch->RadiusX();
       event.mHeight = touch->RadiusY();
       event.tiltX = touch->tiltX;
       event.tiltY = touch->tiltY;
       event.mTime = touchEvent->mTime;
       event.mTimeStamp = touchEvent->mTimeStamp;
       event.mFlags = touchEvent->mFlags;
-      event.button = WidgetMouseEvent::eLeftButton;
-      event.buttons = pointerMessage == ePointerUp ?
-                        0 : WidgetMouseEvent::eLeftButtonFlag;
+      event.button = button;
+      event.buttons = buttons;
       event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
       event.convertToPointer = touch->convertToPointer = false;
       PreHandlePointerEventsPreventDefault(&event, aEvent);
       aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
                           aTargetContent);
       PostHandlePointerEventsPreventDefault(&event, aEvent);
     }
   }
--- a/layout/base/tests/bug970964_inner.html
+++ b/layout/base/tests/bug970964_inner.html
@@ -268,18 +268,18 @@ function runTests() {
   d1.onpointermove = function(e) {
     is(e.buttons, 0, "Buttons must be 0 on pointer generated from mousemove");
     is(e.button, -1, "Button must be -1 on pointer generated from mousemove when no buttons pressed");
     is(e.pointerType, "mouse", "Pointer type must be mouse");
   };
   cwu.sendMouseEvent("mousemove", 4, 4, 0, 0, 0, false, 0, 0);
 
   d1.onpointermove = function(e) {
-    is(e.buttons, 1, "Buttons must be 1 on pointer generated from touch event");
-    is(e.button, 0, "Button must be 0 on pointer generated from touch eventd");
+    is(e.buttons, 1, "Buttons must be 1 on pointermove generated from touch event");
+    is(e.button, -1, "Button must be -1 on pointermove generated from touch event");
     is(e.pointerType, "touch", "Pointer type must be touch");
   };
   sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 2), 0);
 
   // Test for cancel trigger pointerOut (Touch Pointer must be at d1 now)
   pointerCancelTriggered = 0;
   var pointerOutTriggeredForCancelEvent = 0;
   var pointerLeaveTriggeredForCancelEvent = 0;
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -364,27 +364,17 @@ class RefTest(object):
             ix = v.find("=")
             if ix <= 0:
                 print "Error: syntax error in --setenv=" + v
                 return None
             browserEnv[v[:ix]] = v[ix + 1:]
 
         # Enable leaks detection to its own log file.
         self.leakLogFile = os.path.join(profileDir, "runreftest_leaks.log")
-
-        # Leak checking was broken in reftest unnoticed for a length of time. During
-        # this time, a leak slipped into the crashtest suite. The leak checking was
-        # fixed by bug 1325148, but it couldn't land until the regression in crashtest
-        # was also fixed or backed out. Rather than waiting and risking new regressions,
-        # temporarily disable leak checking in crashtest. Fix is tracked by bug 1325215.
-        if options.suite == 'crashtest' and mozinfo.info['os'] == 'linux':
-            self.log.warning('WARNING | leakcheck disabled due to bug 1325215')
-        else:
-            browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leakLogFile
-
+        browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leakLogFile
         return browserEnv
 
     def killNamedOrphans(self, pname):
         """ Kill orphan processes matching the given command name """
         self.log.info("Checking for orphan %s processes..." % pname)
 
         def _psInfo(line):
             if pname in line:
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/AnnexB.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AnnexB.h"
-#include "BigEndian.h"
-
-#include <cstring>
-
-using mozilla::BigEndian;
-
-static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 };
-
-/* static */ void
-AnnexB::ConvertFrameInPlace(std::vector<uint8_t>& aBuffer)
-{
-  for (size_t i = 0; i < aBuffer.size() - 4 - sizeof(kAnnexBDelimiter) + 1; ) {
-    uint32_t nalLen = BigEndian::readUint32(&aBuffer[i]);
-    memcpy(&aBuffer[i], kAnnexBDelimiter, sizeof(kAnnexBDelimiter));
-    i += nalLen + 4;
-  }
-}
-
-static void
-ConvertParamSetToAnnexB(std::vector<uint8_t>::const_iterator& aIter,
-                        size_t aCount,
-                        std::vector<uint8_t>& aOutAnnexB)
-{
-  for (size_t i = 0; i < aCount; i++) {
-    aOutAnnexB.insert(aOutAnnexB.end(), kAnnexBDelimiter,
-                      kAnnexBDelimiter + sizeof(kAnnexBDelimiter));
-
-    uint16_t len = BigEndian::readUint16(&*aIter); aIter += 2;
-    aOutAnnexB.insert(aOutAnnexB.end(), aIter, aIter + len); aIter += len;
-  }
-}
-
-/* static */ void
-AnnexB::ConvertConfig(const std::vector<uint8_t>& aBuffer,
-                      std::vector<uint8_t>& aOutAnnexB)
-{
-  // Skip past irrelevant headers
-  auto it = aBuffer.begin() + 5;
-
-  if (it >= aBuffer.end()) {
-    return;
-  }
-
-  size_t count = *(it++) & 31;
-
-  // Check that we have enough bytes for the Annex B conversion
-  // and the next size field. Bail if not.
-  if (it + count * 2 >= aBuffer.end()) {
-    return;
-  }
-
-  ConvertParamSetToAnnexB(it, count, aOutAnnexB);
-
-  // Check that we have enough bytes for the Annex B conversion.
-  count = *(it++);
-  if (it + count * 2 > aBuffer.end()) {
-    aOutAnnexB.clear();
-    return;
-  }
-
-  ConvertParamSetToAnnexB(it, count, aOutAnnexB);
-}
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/AnnexB.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __AnnexB_h__
-#define __AnnexB_h__
-
-#include <cstdint>
-#include <vector>
-
-class AnnexB
-{
-public:
-  static void ConvertFrameInPlace(std::vector<uint8_t>& aBuffer);
-
-  static void ConvertConfig(const std::vector<uint8_t>& aBuffer,
-                            std::vector<uint8_t>& aOutAnnexB);
-};
-
-#endif // __AnnexB_h__
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ClearKeyAsyncShutdown.h"
-#include "gmp-task-utils.h"
-
-ClearKeyAsyncShutdown::ClearKeyAsyncShutdown(GMPAsyncShutdownHost *aHostAPI)
-  : mHost(aHostAPI)
-{
-  CK_LOGD("ClearKeyAsyncShutdown::ClearKeyAsyncShutdown");
-  AddRef();
-}
-
-ClearKeyAsyncShutdown::~ClearKeyAsyncShutdown()
-{
-  CK_LOGD("ClearKeyAsyncShutdown::~ClearKeyAsyncShutdown");
-}
-
-void ShutdownTask(ClearKeyAsyncShutdown* aSelf, GMPAsyncShutdownHost* aHost)
-{
-  // Dumb implementation that just immediately reports completion.
-  // Real GMPs should ensure they are properly shutdown.
-  CK_LOGD("ClearKeyAsyncShutdown::BeginShutdown calling ShutdownComplete");
-  aHost->ShutdownComplete();
-  aSelf->Release();
-}
-
-void ClearKeyAsyncShutdown::BeginShutdown()
-{
-  CK_LOGD("ClearKeyAsyncShutdown::BeginShutdown dispatching asynchronous shutdown task");
-  GetPlatform()->runonmainthread(WrapTaskNM(ShutdownTask, this, mHost));
-}
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ClearKeyAsyncShutdown_h__
-#define __ClearKeyAsyncShutdown_h__
-
-#include "gmp-api/gmp-async-shutdown.h"
-#include "RefCounted.h"
-
-class ClearKeyAsyncShutdown : public GMPAsyncShutdown
-                            , public RefCounted
-{
-public:
-  explicit ClearKeyAsyncShutdown(GMPAsyncShutdownHost *aHostAPI);
-
-  void BeginShutdown() override;
-
-private:
-  virtual ~ClearKeyAsyncShutdown();
-
-  GMPAsyncShutdownHost* mHost;
-};
-
-#endif // __ClearKeyAsyncShutdown_h__
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyCDM.cpp
@@ -0,0 +1,196 @@
+#include "ClearKeyCDM.h"
+
+#include "ClearKeyUtils.h"
+
+using namespace cdm;
+
+ClearKeyCDM::ClearKeyCDM(Host_8* aHost)
+{
+  mHost = aHost;
+  mSessionManager = new ClearKeySessionManager(mHost);
+}
+
+void
+ClearKeyCDM::Initialize(bool aAllowDistinctiveIdentifier,
+                        bool aAllowPersistentState)
+{
+  mSessionManager->Init(aAllowDistinctiveIdentifier,
+                        aAllowPersistentState);
+}
+
+void
+ClearKeyCDM::SetServerCertificate(uint32_t aPromiseId,
+                                  const uint8_t* aServerCertificateData,
+                                  uint32_t aServerCertificateDataSize)
+{
+  mSessionManager->SetServerCertificate(aPromiseId,
+                                        aServerCertificateData,
+                                        aServerCertificateDataSize);
+}
+
+void
+ClearKeyCDM::CreateSessionAndGenerateRequest(uint32_t aPromiseId,
+                                             SessionType aSessionType,
+                                             InitDataType aInitDataType,
+                                             const uint8_t* aInitData,
+                                             uint32_t aInitDataSize)
+{
+  mSessionManager->CreateSession(aPromiseId,
+                                 aInitDataType,
+                                 aInitData,
+                                 aInitDataSize,
+                                 aSessionType);
+}
+
+void
+ClearKeyCDM::LoadSession(uint32_t aPromiseId,
+                         SessionType aSessionType,
+                         const char* aSessionId,
+                         uint32_t aSessionIdSize)
+{
+  mSessionManager->LoadSession(aPromiseId,
+                               aSessionId,
+                               aSessionIdSize);
+}
+
+void
+ClearKeyCDM::UpdateSession(uint32_t aPromiseId,
+                           const char* aSessionId,
+                           uint32_t aSessionIdSize,
+                           const uint8_t* aResponse,
+                           uint32_t aResponseSize)
+{
+  mSessionManager->UpdateSession(aPromiseId,
+                                 aSessionId,
+                                 aSessionIdSize,
+                                 aResponse,
+                                 aResponseSize);
+}
+
+void
+ClearKeyCDM::CloseSession(uint32_t aPromiseId,
+                          const char* aSessionId,
+                          uint32_t aSessionIdSize)
+{
+  mSessionManager->CloseSession(aPromiseId,
+                                aSessionId,
+                                aSessionIdSize);
+}
+
+void
+ClearKeyCDM::RemoveSession(uint32_t aPromiseId,
+                           const char* aSessionId,
+                           uint32_t aSessionIdSize)
+{
+  mSessionManager->RemoveSession(aPromiseId,
+                                 aSessionId,
+                                 aSessionIdSize);
+}
+
+void
+ClearKeyCDM::TimerExpired(void* aContext)
+{
+  // Clearkey is not interested in timers, so this method has not been
+  // implemented.
+  assert(false);
+}
+
+Status
+ClearKeyCDM::Decrypt(const InputBuffer& aEncryptedBuffer,
+                     DecryptedBlock* aDecryptedBuffer)
+{
+  return mSessionManager->Decrypt(aEncryptedBuffer, aDecryptedBuffer);
+}
+
+Status
+ClearKeyCDM::InitializeAudioDecoder(
+  const AudioDecoderConfig& aAudioDecoderConfig)
+{
+  // Audio decoding is not supported by Clearkey because Widevine doesn't
+  // support it and Clearkey's raison d'etre is to provide test coverage
+  // for paths that Widevine will exercise in the wild.
+  return Status::kDecodeError;
+}
+
+Status
+ClearKeyCDM::InitializeVideoDecoder(
+  const VideoDecoderConfig& aVideoDecoderConfig)
+{
+#ifdef ENABLE_WMF
+  mVideoDecoder = new VideoDecoder(mHost);
+  return mVideoDecoder->InitDecode(aVideoDecoderConfig);
+#else
+  return Status::kDecodeError;
+#endif
+}
+
+void
+ClearKeyCDM::DeinitializeDecoder(StreamType aDecoderType)
+{
+#ifdef ENABLE_WMF
+  if (aDecoderType == StreamType::kStreamTypeVideo) {
+    mVideoDecoder->DecodingComplete();
+    mVideoDecoder = nullptr;
+  }
+#endif
+}
+
+void
+ClearKeyCDM::ResetDecoder(StreamType aDecoderType)
+{
+#ifdef ENABLE_WMF
+  if (aDecoderType == StreamType::kStreamTypeVideo) {
+    mVideoDecoder->Reset();
+  }
+#endif
+}
+
+Status
+ClearKeyCDM::DecryptAndDecodeFrame(const InputBuffer& aEncryptedBuffer,
+                                   VideoFrame* aVideoFrame)
+{
+#ifdef ENABLE_WMF
+  return mVideoDecoder->Decode(aEncryptedBuffer, aVideoFrame);
+#else
+  return Status::kDecodeError;
+#endif
+}
+
+Status
+ClearKeyCDM::DecryptAndDecodeSamples(const InputBuffer& aEncryptedBuffer,
+                                     AudioFrames* aAudioFrame)
+{
+  // Audio decoding is not supported by Clearkey because Widevine doesn't
+  // support it and Clearkey's raison d'etre is to provide test coverage
+  // for paths that Widevine will exercise in the wild.
+  return Status::kDecodeError;
+}
+
+void
+ClearKeyCDM::OnPlatformChallengeResponse(
+  const PlatformChallengeResponse& aResponse)
+{
+  // This function should never be called and is not supported.
+  assert(false);
+}
+
+void
+ClearKeyCDM::OnQueryOutputProtectionStatus(QueryResult aResult,
+                                           uint32_t aLinkMask,
+                                           uint32_t aOutputProtectionMask)
+{
+  // This function should never be called and is not supported.
+  assert(false);
+}
+
+void
+ClearKeyCDM::Destroy()
+{
+  mSessionManager->DecryptingComplete();
+#ifdef ENABLE_WMF
+  // If we have called 'DeinitializeDecoder' mVideoDecoder will be null.
+  if (mVideoDecoder) {
+    mVideoDecoder->DecodingComplete();
+  }
+#endif
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyCDM.h
@@ -0,0 +1,98 @@
+#ifndef ClearKeyCDM_h_
+#define ClearKeyCDM_h_
+
+#include "ClearKeySessionManager.h"
+
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
+
+#ifdef ENABLE_WMF
+#include "WMFUtils.h"
+#include "VideoDecoder.h"
+#endif
+
+class ClearKeyCDM : public cdm::ContentDecryptionModule_8
+{
+private:
+  RefPtr<ClearKeySessionManager> mSessionManager;
+#ifdef ENABLE_WMF
+  RefPtr<VideoDecoder> mVideoDecoder;
+#endif
+
+protected:
+  cdm::Host_8* mHost;
+
+public:
+  explicit ClearKeyCDM(cdm::Host_8* mHost);
+
+  void Initialize(bool aAllowDistinctiveIdentifier,
+                  bool aAllowPersistentState) override;
+
+  void SetServerCertificate(uint32_t aPromiseId,
+                            const uint8_t* aServerCertificateData,
+                            uint32_t aServerCertificateDataSize)
+                            override;
+
+  void CreateSessionAndGenerateRequest(uint32_t aPromiseId,
+                                       cdm::SessionType aSessionType,
+                                       cdm::InitDataType aInitDataType,
+                                       const uint8_t* aInitData,
+                                       uint32_t aInitDataSize)
+                                       override;
+
+  void LoadSession(uint32_t aPromiseId,
+                   cdm::SessionType aSessionType,
+                   const char* aSessionId,
+                   uint32_t aSessionIdSize) override;
+
+  void UpdateSession(uint32_t aPromiseId,
+                     const char* aSessionId,
+                     uint32_t aSessionIdSize,
+                     const uint8_t* aResponse,
+                     uint32_t aResponseSize) override;
+
+  void CloseSession(uint32_t aPromiseId,
+                    const char* aSessionId,
+                    uint32_t aSessionIdSize) override;
+
+  void RemoveSession(uint32_t aPromiseId,
+                     const char* aSessionId,
+                     uint32_t aSessionIdSize) override;
+
+  void TimerExpired(void* aContext) override;
+
+  cdm::Status Decrypt(const cdm::InputBuffer& aEncryptedBuffer,
+                      cdm::DecryptedBlock* aDecryptedBuffer) override;
+
+  cdm::Status InitializeAudioDecoder(
+    const cdm::AudioDecoderConfig& aAudioDecoderConfig) override;
+
+  cdm::Status InitializeVideoDecoder(
+    const cdm::VideoDecoderConfig& aVideoDecoderConfig) override;
+
+  void DeinitializeDecoder(cdm::StreamType aDecoderType) override;
+
+  void ResetDecoder(cdm::StreamType aDecoderType) override;
+
+  cdm::Status DecryptAndDecodeFrame(
+    const cdm::InputBuffer& aEncryptedBuffer,
+    cdm::VideoFrame* aVideoFrame) override;
+
+  cdm::Status DecryptAndDecodeSamples(
+    const cdm::InputBuffer& aEncryptedBuffer,
+    cdm::AudioFrames* aAudioFrame) override;
+
+  void OnPlatformChallengeResponse(
+    const cdm::PlatformChallengeResponse& aResponse) override;
+
+  void
+    OnQueryOutputProtectionStatus(cdm::QueryResult aResult,
+                                  uint32_t aLinkMask,
+                                  uint32_t aOutputProtectionMask) override;
+
+  void Destroy() override;
+};
+
+#endif
\ No newline at end of file
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -9,45 +9,48 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
+#include "ClearKeyDecryptionManager.h"
+
+#include "psshparser/PsshParser.h"
+
+#include <assert.h>
 #include <string.h>
 #include <vector>
 
-#include "ClearKeyDecryptionManager.h"
-#include "psshparser/PsshParser.h"
-#include "gmp-api/gmp-decryption.h"
-#include <assert.h>
+using namespace cdm;
 
 class ClearKeyDecryptor : public RefCounted
 {
 public:
   ClearKeyDecryptor();
 
   void InitKey(const Key& aKey);
   bool HasKey() const { return !!mKey.size(); }
 
-  GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
+  Status Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                  const CryptoMetaData& aMetadata);
 
   const Key& DecryptionKey() const { return mKey; }
 
 private:
   ~ClearKeyDecryptor();
 
   Key mKey;
 };
 
 
-/* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr;
+/* static */ ClearKeyDecryptionManager*
+ClearKeyDecryptionManager::sInstance = nullptr;
 
 /* static */ ClearKeyDecryptionManager*
 ClearKeyDecryptionManager::Get()
 {
   if (!sInstance) {
     sInstance = new ClearKeyDecryptionManager();
   }
   return sInstance;
@@ -68,24 +71,27 @@ ClearKeyDecryptionManager::~ClearKeyDecr
     it->second->Release();
   }
   mDecryptors.clear();
 }
 
 bool
 ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s", mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f");
+  CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s",
+          mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f");
   return mDecryptors.find(aKeyId) != mDecryptors.end();
 }
 
 bool
 ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForId %08x...", *(uint32_t*)&aKeyId[0]);
+  CK_LOGARRAY("ClearKeyDecryptionManager::IsExpectingKeyForId ",
+              aKeyId.data(),
+              aKeyId.size());
   const auto& decryptor = mDecryptors.find(aKeyId);
   return decryptor != mDecryptors.end() && !decryptor->second->HasKey();
 }
 
 bool
 ClearKeyDecryptionManager::HasKeyForKeyId(const KeyId& aKeyId) const
 {
   CK_LOGD("ClearKeyDecryptionManager::HasKeyForKeyId");
@@ -98,26 +104,33 @@ ClearKeyDecryptionManager::GetDecryption
 {
   assert(HasKeyForKeyId(aKeyId));
   return mDecryptors[aKeyId]->DecryptionKey();
 }
 
 void
 ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey)
 {
-  CK_LOGD("ClearKeyDecryptionManager::InitKey %08x...", *(uint32_t*)&aKeyId[0]);
+  CK_LOGD("ClearKeyDecryptionManager::InitKey ",
+          aKeyId.data(),
+          aKeyId.size());
   if (IsExpectingKeyForKeyId(aKeyId)) {
+    CK_LOGARRAY("Initialized Key ", aKeyId.data(), aKeyId.size());
     mDecryptors[aKeyId]->InitKey(aKey);
+  } else {
+    CK_LOGARRAY("Failed to initialize key ", aKeyId.data(), aKeyId.size());
   }
 }
 
 void
 ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId)
 {
-  CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId %08x...", *(uint32_t*)&aKeyId[0]);
+  CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId ",
+          aKeyId.data(),
+          aKeyId.size());
   if (!HasSeenKeyId(aKeyId)) {
     mDecryptors[aKeyId] = new ClearKeyDecryptor();
   }
   mDecryptors[aKeyId]->AddRef();
 }
 
 void
 ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId)
@@ -126,56 +139,66 @@ ClearKeyDecryptionManager::ReleaseKeyId(
   assert(HasSeenKeyId(aKeyId));
 
   ClearKeyDecryptor* decryptor = mDecryptors[aKeyId];
   if (!decryptor->Release()) {
     mDecryptors.erase(aKeyId);
   }
 }
 
-GMPErr
+Status
 ClearKeyDecryptionManager::Decrypt(std::vector<uint8_t>& aBuffer,
                                    const CryptoMetaData& aMetadata)
 {
   return Decrypt(&aBuffer[0], aBuffer.size(), aMetadata);
 }
 
-GMPErr
+Status
 ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                                    const CryptoMetaData& aMetadata)
 {
   CK_LOGD("ClearKeyDecryptionManager::Decrypt");
   if (!HasKeyForKeyId(aMetadata.mKeyId)) {
-    return GMPNoKeyErr;
+    CK_LOGARRAY("Unable to find decryptor for keyId: ",
+                aMetadata.mKeyId.data(),
+                aMetadata.mKeyId.size());
+    return Status::kNoKey;
   }
 
-  return mDecryptors[aMetadata.mKeyId]->Decrypt(aBuffer, aBufferSize, aMetadata);
+  CK_LOGARRAY("Found decryptor for keyId: ",
+              aMetadata.mKeyId.data(),
+              aMetadata.mKeyId.size());
+  return mDecryptors[aMetadata.mKeyId]->Decrypt(aBuffer,
+                                                aBufferSize,
+                                                aMetadata);
 }
 
 ClearKeyDecryptor::ClearKeyDecryptor()
 {
   CK_LOGD("ClearKeyDecryptor ctor");
 }
 
 ClearKeyDecryptor::~ClearKeyDecryptor()
 {
   if (HasKey()) {
-    CK_LOGD("ClearKeyDecryptor dtor; key = %08x...", *(uint32_t*)&mKey[0]);
+    CK_LOGARRAY("ClearKeyDecryptor dtor; key = ",
+                mKey.data(),
+                mKey.size());
   } else {
     CK_LOGD("ClearKeyDecryptor dtor");
   }
 }
 
 void
 ClearKeyDecryptor::InitKey(const Key& aKey)
 {
   mKey = aKey;
 }
 
-GMPErr
+Status
 ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                            const CryptoMetaData& aMetadata)
 {
   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.
   std::vector<uint8_t> tmp(aBufferSize);
 
@@ -184,17 +207,17 @@ ClearKeyDecryptor::Decrypt(uint8_t* aBuf
     // continuous encrypted buffer.
     uint8_t* data = aBuffer;
     uint8_t* iter = &tmp[0];
     for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
       data += aMetadata.mClearBytes[i];
       uint32_t cipherBytes = aMetadata.mCipherBytes[i];
       if (data + cipherBytes > aBuffer + aBufferSize) {
         // Trying to read past the end of the buffer!
-        return GMPCryptoErr;
+        return Status::kDecryptError;
       }
 
       memcpy(iter, data, cipherBytes);
 
       data += cipherBytes;
       iter += cipherBytes;
     }
 
@@ -222,10 +245,10 @@ ClearKeyDecryptor::Decrypt(uint8_t* aBuf
 
       data += cipherBytes;
       iter += cipherBytes;
     }
   } else {
     memcpy(aBuffer, &tmp[0], aBufferSize);
   }
 
-  return GMPNoErr;
+  return Status::kSuccess;
 }
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -12,59 +12,70 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeyDecryptionManager_h__
 #define __ClearKeyDecryptionManager_h__
 
-#include <map>
+#include "ClearKeyUtils.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "RefCounted.h"
 
-#include "ClearKeyUtils.h"
-#include "RefCounted.h"
+#include <map>
 
 class ClearKeyDecryptor;
 
-class CryptoMetaData {
+class CryptoMetaData
+{
 public:
   CryptoMetaData() {}
 
-  explicit CryptoMetaData(const GMPEncryptedBufferMetadata* aCrypto)
+  explicit CryptoMetaData(const cdm::InputBuffer* aInputBuffer)
   {
-    Init(aCrypto);
+    Init(aInputBuffer);
   }
 
-  void Init(const GMPEncryptedBufferMetadata* aCrypto)
+  void Init(const cdm::InputBuffer* aInputBuffer)
   {
-    if (!aCrypto) {
+    if (!aInputBuffer) {
       assert(!IsValid());
       return;
     }
-    Assign(mKeyId, aCrypto->KeyId(), aCrypto->KeyIdSize());
-    Assign(mIV, aCrypto->IV(), aCrypto->IVSize());
-    Assign(mClearBytes, aCrypto->ClearBytes(), aCrypto->NumSubsamples());
-    Assign(mCipherBytes, aCrypto->CipherBytes(), aCrypto->NumSubsamples());
+
+    Assign(mKeyId, aInputBuffer->key_id, aInputBuffer->key_id_size);
+    Assign(mIV, aInputBuffer->iv, aInputBuffer->iv_size);
+
+    for (uint32_t i = 0; i < aInputBuffer->num_subsamples; ++i) {
+      const cdm::SubsampleEntry& subsample = aInputBuffer->subsamples[i];
+
+      mCipherBytes.push_back(subsample.cipher_bytes);
+      mClearBytes.push_back(subsample.clear_bytes);
+    }
   }
 
   bool IsValid() const {
     return !mKeyId.empty() &&
            !mIV.empty() &&
            !mCipherBytes.empty() &&
            !mClearBytes.empty();
   }
 
   size_t NumSubsamples() const {
     assert(mClearBytes.size() == mCipherBytes.size());
     return mClearBytes.size();
   }
 
   std::vector<uint8_t> mKeyId;
   std::vector<uint8_t> mIV;
-  std::vector<uint16_t> mClearBytes;
+  std::vector<uint32_t> mClearBytes;
   std::vector<uint32_t> mCipherBytes;
 };
 
 class ClearKeyDecryptionManager : public RefCounted
 {
 private:
   ClearKeyDecryptionManager();
   ~ClearKeyDecryptionManager();
@@ -80,22 +91,20 @@ public:
   const Key& GetDecryptionKey(const KeyId& aKeyId);
 
   // 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);
 
   // Decrypts buffer *in place*.
-  GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
-                 const CryptoMetaData& aMetadata);
-  GMPErr Decrypt(std::vector<uint8_t>& aBuffer,
-                 const CryptoMetaData& aMetadata);
-
-  void Shutdown();
+  cdm::Status Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
+                      const CryptoMetaData& aMetadata);
+  cdm::Status Decrypt(std::vector<uint8_t>& aBuffer,
+                      const CryptoMetaData& aMetadata);
 
 private:
   bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const;
 
   std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
 };
 
 #endif // __ClearKeyDecryptionManager_h__
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -10,251 +10,159 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "ClearKeyPersistence.h"
+
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
 #include "ClearKeySessionManager.h"
 #include "RefCounted.h"
 
+#include <assert.h>
 #include <stdint.h>
+#include <sstream>
 #include <string.h>
-#include <set>
-#include <vector>
-#include <sstream>
-#include <assert.h>
 
 using namespace std;
+using namespace cdm;
 
-// Whether we've loaded the persistent session ids from GMPStorage yet.
-enum PersistentKeyState {
-  UNINITIALIZED,
-  LOADING,
-  LOADED
-};
-static PersistentKeyState sPersistentKeyState = UNINITIALIZED;
+void
+ClearKeyPersistence::ReadAllRecordsFromIndex(function<void()>&& aOnComplete) {
+  // Clear what we think the index file contains, we're about to read it again.
+  mPersistentSessionIds.clear();
 
-// Set of session Ids of the persistent sessions created or residing in
-// storage.
-static set<uint32_t> sPersistentSessionIds;
-
-static vector<GMPTask*> sTasksBlockedOnSessionIdLoad;
+  // Hold a reference to the persistence manager, so it isn't released before
+  // we try and use it.
+  RefPtr<ClearKeyPersistence> self(this);
+  function<void(const uint8_t*, uint32_t)> onIndexSuccess =
+    [self, aOnComplete] (const uint8_t* data, uint32_t size)
+  {
+    CK_LOGD("ClearKeyPersistence: Loaded index file!");
+    const char* charData = (const char*)data;
 
-static void
-ReadAllRecordsFromIterator(GMPRecordIterator* aRecordIterator,
-                           void* aUserArg,
-                           GMPErr aStatus)
-{
-  assert(sPersistentKeyState == LOADING);
-  if (GMP_SUCCEEDED(aStatus)) {
-    // Extract the record names which are valid uint32_t's; they're
-    // the persistent session ids.
-    const char* name = nullptr;
-    uint32_t len = 0;
-    while (GMP_SUCCEEDED(aRecordIterator->GetName(&name, &len))) {
-      if (ClearKeyUtils::IsValidSessionId(name, len)) {
-        assert(name[len] == 0);
-        sPersistentSessionIds.insert(atoi(name));
+    stringstream ss(string(charData, charData + size));
+    string name;
+    while (getline(ss, name)) {
+      if (ClearKeyUtils::IsValidSessionId(name.data(), name.size())) {
+        self->mPersistentSessionIds.insert(atoi(name.c_str()));
       }
-      aRecordIterator->NextRecord();
     }
-  }
-  sPersistentKeyState = LOADED;
-  aRecordIterator->Close();
+
+    self->mPersistentKeyState = PersistentKeyState::LOADED;
+    aOnComplete();
+  };
 
-  for (size_t i = 0; i < sTasksBlockedOnSessionIdLoad.size(); i++) {
-    sTasksBlockedOnSessionIdLoad[i]->Run();
-    sTasksBlockedOnSessionIdLoad[i]->Destroy();
-  }
-  sTasksBlockedOnSessionIdLoad.clear();
+  function<void()> onIndexFailed =
+    [self, aOnComplete] ()
+  {
+    CK_LOGD("ClearKeyPersistence: Failed to load index file (it might not exist");
+    self->mPersistentKeyState = PersistentKeyState::LOADED;
+    aOnComplete();
+  };
+
+  string filename = "index";
+  ReadData(mHost, filename, move(onIndexSuccess), move(onIndexFailed));
 }
 
-/* static */ void
-ClearKeyPersistence::EnsureInitialized()
+void
+ClearKeyPersistence::WriteIndex() {
+  function <void()> onIndexSuccess =
+    [] ()
+  {
+    CK_LOGD("ClearKeyPersistence: Wrote index file");
+  };
+
+  function <void()> onIndexFail =
+    [] ()
+  {
+    CK_LOGD("ClearKeyPersistence: Failed to write index file (this is bad)");
+  };
+
+  stringstream ss;
+
+  for (const uint32_t& sessionId : mPersistentSessionIds) {
+    ss << sessionId;
+    ss << '\n';
+  }
+
+  string dataString = ss.str();
+  uint8_t* dataArray = (uint8_t*)dataString.data();
+  vector<uint8_t> data(dataArray, dataArray + dataString.size());
+
+  string filename = "index";
+  WriteData(mHost,
+            filename,
+            data,
+            move(onIndexSuccess),
+            move(onIndexFail));
+}
+
+
+ClearKeyPersistence::ClearKeyPersistence(Host_8* aHost)
 {
-  if (sPersistentKeyState == UNINITIALIZED) {
-    sPersistentKeyState = LOADING;
-    if (GMP_FAILED(EnumRecordNames(&ReadAllRecordsFromIterator))) {
-      sPersistentKeyState = LOADED;
-    }
+  this->mHost = aHost;
+}
+
+void
+ClearKeyPersistence::EnsureInitialized(bool aPersistentStateAllowed,
+                                       function<void()>&& aOnInitialized)
+{
+  if (aPersistentStateAllowed &&
+      mPersistentKeyState == PersistentKeyState::UNINITIALIZED) {
+    mPersistentKeyState = LOADING;
+    ReadAllRecordsFromIndex(move(aOnInitialized));
+  } else {
+    mPersistentKeyState = PersistentKeyState::LOADED;
+    aOnInitialized();
   }
 }
 
-/* static */ string
-ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType)
+bool ClearKeyPersistence::IsLoaded() const
+{
+  return mPersistentKeyState == PersistentKeyState::LOADED;
+}
+
+string
+ClearKeyPersistence::GetNewSessionId(SessionType aSessionType)
 {
   static uint32_t sNextSessionId = 1;
 
   // Ensure we don't re-use a session id that was persisted.
-  while (Contains(sPersistentSessionIds, sNextSessionId)) {
+  while (Contains(mPersistentSessionIds, sNextSessionId)) {
     sNextSessionId++;
   }
 
   string sessionId;
   stringstream ss;
   ss << sNextSessionId;
   ss >> sessionId;
 
-  if (aSessionType == kGMPPersistentSession) {
-    sPersistentSessionIds.insert(sNextSessionId);
+  if (aSessionType == SessionType::kPersistentLicense) {
+    mPersistentSessionIds.insert(sNextSessionId);
+
+    // Save the updated index file.
+    WriteIndex();
   }
 
   sNextSessionId++;
 
   return sessionId;
 }
 
-
-class CreateSessionTask : public GMPTask {
-public:
-  CreateSessionTask(ClearKeySessionManager* aTarget,
-                    uint32_t aCreateSessionToken,
-                    uint32_t aPromiseId,
-                    const string& aInitDataType,
-                    const uint8_t* aInitData,
-                    uint32_t aInitDataSize,
-                    GMPSessionType aSessionType)
-    : mTarget(aTarget)
-    , mCreateSessionToken(aCreateSessionToken)
-    , mPromiseId(aPromiseId)
-    , mInitDataType(aInitDataType)
-    , mSessionType(aSessionType)
-  {
-    mInitData.insert(mInitData.end(),
-                     aInitData,
-                     aInitData + aInitDataSize);
-  }
-  virtual void Run() override {
-    mTarget->CreateSession(mCreateSessionToken,
-                           mPromiseId,
-                           mInitDataType.c_str(),
-                           mInitDataType.size(),
-                           &mInitData.front(),
-                           mInitData.size(),
-                           mSessionType);
-  }
-  virtual void Destroy() override {
-    delete this;
-  }
-private:
-  RefPtr<ClearKeySessionManager> mTarget;
-  uint32_t mCreateSessionToken;
-  uint32_t mPromiseId;
-  const string mInitDataType;
-  vector<uint8_t> mInitData;
-  GMPSessionType mSessionType;
-};
-
-
-/* static */ bool
-ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
-                                                  uint32_t aCreateSessionToken,
-                                                  uint32_t aPromiseId,
-                                                  const string& aInitDataType,
-                                                  const uint8_t* aInitData,
-                                                  uint32_t aInitDataSize,
-                                                  GMPSessionType aSessionType)
+bool
+ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
 {
-  if (sPersistentKeyState >= LOADED)  {
-    return false;
-  }
-  GMPTask* t = new CreateSessionTask(aInstance,
-                                     aCreateSessionToken,
-                                     aPromiseId,
-                                     aInitDataType,
-                                     aInitData,
-                                     aInitDataSize,
-                                     aSessionType);
-  sTasksBlockedOnSessionIdLoad.push_back(t);
-  return true;
+  return Contains(mPersistentSessionIds, atoi(aSessionId.c_str()));
 }
 
-class LoadSessionTask : public GMPTask {
-public:
-  LoadSessionTask(ClearKeySessionManager* aTarget,
-                  uint32_t aPromiseId,
-                  const char* aSessionId,
-                  uint32_t aSessionIdLength)
-    : mTarget(aTarget)
-    , mPromiseId(aPromiseId)
-    , mSessionId(aSessionId, aSessionId + aSessionIdLength)
-  {
-  }
-  virtual void Run() override {
-    mTarget->LoadSession(mPromiseId,
-                         mSessionId.c_str(),
-                         mSessionId.size());
-  }
-  virtual void Destroy() override {
-    delete this;
-  }
-private:
-  RefPtr<ClearKeySessionManager> mTarget;
-  uint32_t mPromiseId;
-  string mSessionId;
-};
-
-/* static */ bool
-ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
-                                                uint32_t aPromiseId,
-                                                const char* aSessionId,
-                                                uint32_t aSessionIdLength)
+void
+ClearKeyPersistence::PersistentSessionRemoved(string& aSessionId)
 {
-  if (sPersistentKeyState >= LOADED)  {
-    return false;
-  }
-  GMPTask* t = new LoadSessionTask(aInstance,
-                                   aPromiseId,
-                                   aSessionId,
-                                   aSessionIdLength);
-  sTasksBlockedOnSessionIdLoad.push_back(t);
-  return true;
-}
+  mPersistentSessionIds.erase(atoi(aSessionId.c_str()));
 
-/* static */ bool
-ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
-{
-  return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
+  // Update the index file.
+  WriteIndex();
 }
-
-class LoadSessionFromKeysTask : public ReadContinuation {
-public:
-  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) override
-  {
-    mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
-  }
-private:
-  RefPtr<ClearKeySessionManager> mTarget;
-  string mSessionId;
-  uint32_t mPromiseId;
-};
-
-/* static */ void
-ClearKeyPersistence::LoadSessionData(ClearKeySessionManager* aInstance,
-                                     const string& aSid,
-                                     uint32_t aPromiseId)
-{
-  LoadSessionFromKeysTask* loadTask =
-    new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
-  ReadData(aSid, loadTask);
-}
-
-/* static */ void
-ClearKeyPersistence::PersistentSessionRemoved(const string& aSessionId)
-{
-  sPersistentSessionIds.erase(atoi(aSessionId.c_str()));
-}
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -12,42 +12,56 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeyPersistence_h__
 #define __ClearKeyPersistence_h__
 
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "RefCounted.h"
+
+#include <functional>
+#include <set>
 #include <string>
-#include "gmp-api/gmp-decryption.h"
+#include <vector>
+
 
 class ClearKeySessionManager;
 
-class ClearKeyPersistence {
+// Whether we've loaded the persistent session ids yet.
+enum PersistentKeyState {
+  UNINITIALIZED,
+  LOADING,
+  LOADED
+};
+
+class ClearKeyPersistence : public RefCounted
+{
 public:
-  static void EnsureInitialized();
+  explicit ClearKeyPersistence(cdm::Host_8* aHost);
 
-  static std::string GetNewSessionId(GMPSessionType aSessionType);
+  void EnsureInitialized(bool aPersistentStateAllowed,
+                         std::function<void()>&& aOnInitialized);
 
-  static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
-                                           uint32_t aCreateSessionToken,
-                                           uint32_t aPromiseId,
-                                           const std::string& aInitDataType,
-                                           const uint8_t* aInitData,
-                                           uint32_t aInitDataSize,
-                                           GMPSessionType aSessionType);
+  bool IsLoaded() const;
+
+  std::string GetNewSessionId(cdm::SessionType aSessionType);
+
+  bool IsPersistentSessionId(const std::string& aSid);
 
-  static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
-                                         uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength);
+  void PersistentSessionRemoved(std::string& aSid);
+private:
+  cdm::Host_8* mHost = nullptr;
 
-  static bool IsPersistentSessionId(const std::string& aSid);
+  PersistentKeyState mPersistentKeyState = PersistentKeyState::UNINITIALIZED;
 
-  static void LoadSessionData(ClearKeySessionManager* aInstance,
-                              const std::string& aSid,
-                              uint32_t aPromiseId);
+  std::set<uint32_t> mPersistentSessionIds;
 
-  static void PersistentSessionRemoved(const std::string& aSid);
+  void ReadAllRecordsFromIndex(std::function<void()>&& aOnComplete);
+  void WriteIndex();
 };
 
 #endif // __ClearKeyPersistence_h__
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -15,78 +15,64 @@
  */
 
 #include "BigEndian.h"
 #include "ClearKeyDecryptionManager.h"
 #include "ClearKeySession.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
 #include "psshparser/PsshParser.h"
-#include "gmp-task-utils.h"
-#include "gmp-api/gmp-decryption.h"
+
 #include <assert.h>
 #include <string.h>
 
 using namespace mozilla;
+using namespace cdm;
+using namespace std;
 
 ClearKeySession::ClearKeySession(const std::string& aSessionId,
-                                 GMPDecryptorCallback* aCallback,
-                                 GMPSessionType aSessionType)
+                                 SessionType aSessionType)
   : mSessionId(aSessionId)
-  , mCallback(aCallback)
   , mSessionType(aSessionType)
 {
   CK_LOGD("ClearKeySession ctor %p", this);
 }
 
 ClearKeySession::~ClearKeySession()
 {
   CK_LOGD("ClearKeySession dtor %p", this);
-
-  std::vector<GMPMediaKeyInfo> key_infos;
-  for (const KeyId& keyId : mKeyIds) {
-    assert(ClearKeyDecryptionManager::Get()->HasSeenKeyId(keyId));
-    ClearKeyDecryptionManager::Get()->ReleaseKeyId(keyId);
-    key_infos.push_back(GMPMediaKeyInfo(&keyId[0], keyId.size(), kGMPUnknown));
-  }
-  mCallback->BatchedKeyStatusChanged(&mSessionId[0], mSessionId.size(),
-                                     key_infos.data(), key_infos.size());
 }
 
-void
-ClearKeySession::Init(uint32_t aCreateSessionToken,
-                      uint32_t aPromiseId,
-                      const std::string& aInitDataType,
-                      const uint8_t* aInitData, uint32_t aInitDataSize)
+bool
+ClearKeySession::Init(InitDataType aInitDataType,
+                      const uint8_t* aInitData,
+                      uint32_t aInitDataSize)
 {
   CK_LOGD("ClearKeySession::Init");
 
-  if (aInitDataType == "cenc") {
+  if (aInitDataType == InitDataType::kCenc) {
     ParseCENCInitData(aInitData, aInitDataSize, mKeyIds);
-  } else if (aInitDataType == "keyids") {
+  } else if (aInitDataType == InitDataType::kKeyIds) {
     ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds);
-  } else if (aInitDataType == "webm" && aInitDataSize <= kMaxWebmInitDataSize) {
+  } else if (aInitDataType == InitDataType::kWebM &&
+             aInitDataSize <= kMaxWebmInitDataSize) {
     // "webm" initData format is simply the raw bytes of the keyId.
     vector<uint8_t> keyId;
     keyId.assign(aInitData, aInitData+aInitDataSize);
     mKeyIds.push_back(keyId);
   }
 
   if (!mKeyIds.size()) {
-    const char message[] = "Couldn't parse init data";
-    mCallback->RejectPromise(aPromiseId, kGMPTypeError, message, strlen(message));
-    return;
+    return false;
   }
 
-  mCallback->SetSessionId(aCreateSessionToken, &mSessionId[0], mSessionId.length());
-
-  mCallback->ResolvePromise(aPromiseId);
+  return true;
 }
 
-GMPSessionType
+SessionType
 ClearKeySession::Type() const
 {
   return mSessionType;
 }
 
 void
 ClearKeySession::AddKeyId(const KeyId& aKeyId)
 {
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -13,46 +13,43 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeySession_h__
 #define __ClearKeySession_h__
 
 #include "ClearKeyUtils.h"
-#include "gmp-api/gmp-decryption.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
 
-class GMPBuffer;
-class GMPDecryptorCallback;
-class GMPDecryptorHost;
-class GMPEncryptedBufferMetadata;
+#include <string>
+#include <vector>
 
 class ClearKeySession
 {
 public:
   explicit ClearKeySession(const std::string& aSessionId,
-                           GMPDecryptorCallback* aCallback,
-                           GMPSessionType aSessionType);
+                           cdm::SessionType aSessionType);
 
   ~ClearKeySession();
 
   const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; }
 
-  void Init(uint32_t aCreateSessionToken,
-            uint32_t aPromiseId,
-            const string& aInitDataType,
+  bool Init(cdm::InitDataType aInitDataType,
             const uint8_t* aInitData, uint32_t aInitDataSize);
 
-  GMPSessionType Type() const;
+  cdm::SessionType Type() const;
 
   void AddKeyId(const KeyId& aKeyId);
 
   const std::string& Id() const { return mSessionId; }
 
 private:
   const std::string mSessionId;
   std::vector<KeyId> mKeyIds;
 
-  GMPDecryptorCallback* mCallback;
-  const GMPSessionType mSessionType;
+  const cdm::SessionType mSessionType;
 };
 
 #endif // __ClearKeySession_h__
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -9,98 +9,152 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
 #include "ClearKeyDecryptionManager.h"
 #include "ClearKeySessionManager.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
 #include "ClearKeyPersistence.h"
-#include "gmp-task-utils.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "psshparser/PsshParser.h"
+
 #include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
 
 using namespace std;
+using namespace cdm;
 
-ClearKeySessionManager::ClearKeySessionManager()
+ClearKeySessionManager::ClearKeySessionManager(Host_8* aHost)
   : mDecryptionManager(ClearKeyDecryptionManager::Get())
 {
   CK_LOGD("ClearKeySessionManager ctor %p", this);
   AddRef();
 
-  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
-    CK_LOGD("failed to create thread in clearkey cdm");
-    mThread = nullptr;
-  }
+  mHost = aHost;
+  mPersistence = new ClearKeyPersistence(mHost);
 }
 
 ClearKeySessionManager::~ClearKeySessionManager()
 {
   CK_LOGD("ClearKeySessionManager dtor %p", this);
 }
 
 void
-ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback,
-                             bool aDistinctiveIdentifierAllowed,
+ClearKeySessionManager::Init(bool aDistinctiveIdentifierAllowed,
                              bool aPersistentStateAllowed)
 {
   CK_LOGD("ClearKeySessionManager::Init");
-  mCallback = aCallback;
-  ClearKeyPersistence::EnsureInitialized();
+
+  RefPtr<ClearKeySessionManager> self(this);
+  function<void()> onPersistentStateLoaded =
+    [self] ()
+  {
+    while (!self->mDeferredInitialize.empty()) {
+      function<void()> func = self->mDeferredInitialize.front();
+      self->mDeferredInitialize.pop();
+
+      func();
+    }
+  };
+
+  mPersistence->EnsureInitialized(aPersistentStateAllowed,
+                                  move(onPersistentStateLoaded));
 }
 
 void
-ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
-                                      uint32_t aPromiseId,
-                                      const char* aInitDataType,
-                                      uint32_t aInitDataTypeSize,
+ClearKeySessionManager::CreateSession(uint32_t aPromiseId,
+                                      InitDataType aInitDataType,
                                       const uint8_t* aInitData,
                                       uint32_t aInitDataSize,
-                                      GMPSessionType aSessionType)
+                                      SessionType aSessionType)
 {
-  CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
+  CK_LOGD("ClearKeySessionManager::CreateSession type:%u", aInitDataType);
+
+  // Copy the init data so it is correctly captured by the lambda
+  vector<uint8_t> initData(aInitData, aInitData + aInitDataSize);
 
-  string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize);
-  // initDataType must be "cenc", "keyids", or "webm".
-  if (initDataType != "cenc" &&
-      initDataType != "keyids" &&
-      initDataType != "webm") {
-    string message = "'" + initDataType + "' is an initDataType unsupported by ClearKey";
-    mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                             message.c_str(), message.size());
+  RefPtr<ClearKeySessionManager> self(this);
+  function<void()> deferrer =
+    [self, aPromiseId, aInitDataType, initData, aSessionType] ()
+  {
+    self->CreateSession(aPromiseId,
+                        aInitDataType,
+                        initData.data(),
+                        initData.size(),
+                        aSessionType);
+  };
+
+  // If we haven't loaded, don't do this yet
+  if (MaybeDeferTillInitialized(deferrer)) {
+    CK_LOGD("Deferring CreateSession");
     return;
   }
 
-  if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
-                                                        aCreateSessionToken,
-                                                        aPromiseId,
-                                                        initDataType,
-                                                        aInitData,
-                                                        aInitDataSize,
-                                                        aSessionType)) {
+  CK_LOGARRAY("ClearKeySessionManager::CreateSession initdata: ",
+              aInitData,
+              aInitDataSize);
+
+  // If 'DecryptingComplete' has been called mHost will be null so we can't
+  // won't be able to resolve our promise
+  if (!mHost) {
+    CK_LOGD("ClearKeySessionManager::CreateSession: mHost is nullptr");
     return;
   }
 
-  string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
+  // initDataType must be "cenc", "keyids", or "webm".
+  if (aInitDataType != InitDataType::kCenc &&
+      aInitDataType != InitDataType::kKeyIds &&
+      aInitDataType != InitDataType::kWebM) {
+
+    string message = "initDataType is not supported by ClearKey";
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kNotSupportedError,
+                           0,
+                           message.c_str(),
+                           message.size());
+
+    return;
+  }
+
+  string sessionId = mPersistence->GetNewSessionId(aSessionType);
   assert(mSessions.find(sessionId) == mSessions.end());
 
-  ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
-  session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize);
+  ClearKeySession* session = new ClearKeySession(sessionId,
+                                                 aSessionType);
+
+  if (!session->Init(aInitDataType, aInitData, aInitDataSize)) {
+
+    CK_LOGD("Failed to initialize session: %s", sessionId.c_str());
+
+    const static char* message = "Failed to initialize session";
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kUnknownError,
+                           0,
+                           message,
+                           strlen(message));
+
+    return;
+  }
+
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
+
   for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
     // 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);
   }
@@ -108,73 +162,121 @@ ClearKeySessionManager::CreateSession(ui
   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(),
-                            kGMPLicenseRequest,
-                            (uint8_t*)&request[0], request.length());
+
+  // Resolve the promise with the new session information.
+  mHost->OnResolveNewSessionPromise(aPromiseId,
+                                    sessionId.c_str(),
+                                    sessionId.size());
+
+  mHost->OnSessionMessage(sessionId.c_str(),
+                          sessionId.size(),
+                          MessageType::kLicenseRequest,
+                          request.c_str(),
+                          request.size(),
+                          nullptr,
+                          0);
 }
 
 void
 ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
                                     const char* aSessionId,
                                     uint32_t aSessionIdLength)
 {
   CK_LOGD("ClearKeySessionManager::LoadSession");
 
-  if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+  // Copy the sessionId into a string so the lambda captures it properly.
+  string sessionId(aSessionId, aSessionId + aSessionIdLength);
+
+  // Hold a reference to the SessionManager so that it isn't released before
+  // we try to use it.
+  RefPtr<ClearKeySessionManager> self(this);
+  function<void()> deferrer =
+    [self, aPromiseId, sessionId] ()
+  {
+    self->LoadSession(aPromiseId, sessionId.data(), sessionId.size());
+  };
+
+  if (MaybeDeferTillInitialized(deferrer)) {
+    CK_LOGD("Deferring LoadSession");
     return;
   }
 
-  if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
-                                                      aPromiseId,
-                                                      aSessionId,
-                                                      aSessionIdLength)) {
+  // If the SessionManager has been shutdown mHost will be null and we won't
+  // be able to resolve the promise.
+  if (!mHost) {
+    return;
+  }
+
+  if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
+    mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
     return;
   }
 
-  string sid(aSessionId, aSessionId + aSessionIdLength);
-  if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+  if (!mPersistence->IsPersistentSessionId(sessionId)) {
+    mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
     return;
   }
 
-  // Callsback PersistentSessionDataLoaded with results...
-  ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
+  function<void(const uint8_t*, uint32_t)> success =
+    [self, sessionId, aPromiseId] (const uint8_t* data, uint32_t size)
+  {
+    self->PersistentSessionDataLoaded(aPromiseId,
+                                      sessionId,
+                                      data,
+                                      size);
+  };
+
+  function<void()> failure = [self, sessionId, aPromiseId] {
+    if (!self->mHost) {
+      return;
+    }
+    // As per the API described in ContentDecryptionModule_8
+    self->mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
+  };
+
+  ReadData(mHost, sessionId, move(success), move(failure));
 }
 
 void
-ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
-                                                    uint32_t aPromiseId,
+ClearKeySessionManager::PersistentSessionDataLoaded(uint32_t aPromiseId,
                                                     const string& aSessionId,
                                                     const uint8_t* aKeyData,
                                                     uint32_t aKeyDataSize)
 {
   CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded");
-  if (GMP_FAILED(aStatus) ||
-      Contains(mSessions, aSessionId) ||
+
+  // Check that the SessionManager has not been shut down before we try and
+  // resolve any promises.
+  if (!mHost) {
+    return;
+  }
+
+  if (Contains(mSessions, aSessionId) ||
       (aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+
+    // As per the instructions in ContentDecryptionModule_8
+    mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
     return;
   }
 
   ClearKeySession* session = new ClearKeySession(aSessionId,
-                                                 mCallback,
-                                                 kGMPPersistentSession);
+                                                 SessionType::kPersistentLicense);
+
   mSessions[aSessionId] = session;
 
   uint32_t numKeys = aKeyDataSize / (2 * CENC_KEY_LEN);
 
-  vector<GMPMediaKeyInfo> key_infos;
+  vector<KeyInformation> keyInfos;
   vector<KeyIdPair> keyPairs;
   for (uint32_t i = 0; i < numKeys; i ++) {
     const uint8_t* base = aKeyData + 2 * CENC_KEY_LEN * i;
 
     KeyIdPair keyPair;
 
     keyPair.mKeyId = KeyId(base, base + CENC_KEY_LEN);
     assert(keyPair.mKeyId.size() == CENC_KEY_LEN);
@@ -182,91 +284,175 @@ ClearKeySessionManager::PersistentSessio
     keyPair.mKey = Key(base + CENC_KEY_LEN, base + 2 * CENC_KEY_LEN);
     assert(keyPair.mKey.size() == CENC_KEY_LEN);
 
     session->AddKeyId(keyPair.mKeyId);
 
     mDecryptionManager->ExpectKeyId(keyPair.mKeyId);
     mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
     mKeyIds.insert(keyPair.mKey);
+    keyPairs.push_back(keyPair);
 
-    keyPairs.push_back(keyPair);
-    key_infos.push_back(GMPMediaKeyInfo(&keyPairs[i].mKeyId[0],
-                                        keyPairs[i].mKeyId.size(),
-                                        kGMPUsable));
+    KeyInformation keyInfo = KeyInformation();
+    keyInfo.key_id = &keyPairs.back().mKeyId[0];
+    keyInfo.key_id_size = keyPair.mKeyId.size();
+    keyInfo.status = KeyStatus::kUsable;
+
+    keyInfos.push_back(keyInfo);
   }
-  mCallback->BatchedKeyStatusChanged(&aSessionId[0], aSessionId.size(),
-                                     key_infos.data(), key_infos.size());
 
-  mCallback->ResolveLoadSessionPromise(aPromiseId, true);
+  mHost->OnSessionKeysChange(&aSessionId[0],
+                             aSessionId.size(),
+                             true,
+                             keyInfos.data(),
+                             keyInfos.size());
+
+  mHost->OnResolveNewSessionPromise(aPromiseId,
+                                    aSessionId.c_str(),
+                                    aSessionId.size());
 }
 
 void
 ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
                                       const char* aSessionId,
                                       uint32_t aSessionIdLength,
                                       const uint8_t* aResponse,
                                       uint32_t aResponseSize)
 {
   CK_LOGD("ClearKeySessionManager::UpdateSession");
+
+  // Copy the method arguments so we can capture them in the lambda
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  vector<uint8_t> response(aResponse, aResponse + aResponseSize);
+
+  // Hold  a reference to the SessionManager so it isn't released before we
+  // callback.
+  RefPtr<ClearKeySessionManager> self(this);
+  function<void()> deferrer =
+    [self, aPromiseId, sessionId, response] ()
+  {
+    self->UpdateSession(aPromiseId,
+                        sessionId.data(),
+                        sessionId.size(),
+                        response.data(),
+                        response.size());
+  };
+
+  // If we haven't fully loaded, defer calling this method
+  if (MaybeDeferTillInitialized(deferrer)) {
+    CK_LOGD("Deferring LoadSession");
+    return;
+  }
+
+  // Make sure the SessionManager has not been shutdown before we try and
+  // resolve any promises.
+  if (!mHost) {
+    return;
+  }
+
+  CK_LOGD("Updating session: %s", sessionId.c_str());
 
   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);
+    CK_LOGD("Unable to find session: %s", sessionId.c_str());
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kInvalidAccessError,
+                           0,
+                           nullptr,
+                           0);
+
     return;
   }
   ClearKeySession* session = itr->second;
 
   // Verify the size of session response.
   if (aResponseSize >= kMaxSessionResponseLength) {
     CK_LOGW("Session response size is not within a reasonable size.");
-    mCallback->RejectPromise(aPromiseId, kGMPTypeError, nullptr, 0);
+    CK_LOGD("Failed to parse response for session %s", sessionId.c_str());
+
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kInvalidAccessError,
+                           0,
+                           nullptr,
+                           0);
+
     return;
   }
 
   // Parse the response for any (key ID, key) pairs.
   vector<KeyIdPair> keyPairs;
-  if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
+  if (!ClearKeyUtils::ParseJWK(aResponse,
+                               aResponseSize,
+                               keyPairs,
+                               session->Type())) {
     CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
-    mCallback->RejectPromise(aPromiseId, kGMPTypeError, nullptr, 0);
+
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kInvalidAccessError,
+                           0,
+                           nullptr,
+                           0);
+
     return;
   }
 
-  vector<GMPMediaKeyInfo> key_infos;
+  vector<KeyInformation> keyInfos;
   for (size_t i = 0; i < keyPairs.size(); i++) {
     KeyIdPair& keyPair = keyPairs[i];
     mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
     mKeyIds.insert(keyPair.mKeyId);
-    key_infos.push_back(GMPMediaKeyInfo(&keyPair.mKeyId[0],
-                                        keyPair.mKeyId.size(),
-                                        kGMPUsable));
+
+    KeyInformation keyInfo = KeyInformation();
+    keyInfo.key_id = &keyPair.mKeyId[0];
+    keyInfo.key_id_size = keyPair.mKeyId.size();
+    keyInfo.status = KeyStatus::kUsable;
+
+    keyInfos.push_back(keyInfo);
   }
-  mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdLength,
-                                     key_infos.data(), key_infos.size());
 
-  if (session->Type() != kGMPPersistentSession) {
-    mCallback->ResolvePromise(aPromiseId);
+  mHost->OnSessionKeysChange(aSessionId,
+                             aSessionIdLength,
+                             true,
+                             keyInfos.data(),
+                             keyInfos.size());
+
+  if (session->Type() != SessionType::kPersistentLicense) {
+    mHost->OnResolvePromise(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);
+
+  function<void()> resolve = [self, aPromiseId] ()
+  {
+    if (!self->mHost) {
+      return;
+    }
+    self->mHost->OnResolvePromise(aPromiseId);
+  };
+
+  function<void()> reject = [self, aPromiseId] ()
+  {
+    if (!self->mHost) {
+      return;
+    }
+
+    static const char* message = "Couldn't store cenc key init data";
+    self->mHost->OnRejectPromise(aPromiseId,
+                                 Error::kInvalidStateError,
+                                 0,
+                                 message,
+                                 strlen(message));
+  };
+
+  WriteData(mHost, sessionId, keydata, move(resolve), move(reject));
 }
 
 void
 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++) {
@@ -284,135 +470,206 @@ ClearKeySessionManager::Serialize(const 
 
 void
 ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
                                      const char* aSessionId,
                                      uint32_t aSessionIdLength)
 {
   CK_LOGD("ClearKeySessionManager::CloseSession");
 
+  // Copy the sessionId into a string so we capture it properly.
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  // Hold a reference to the session manager, so it doesn't get deleted
+  // before we need to use it.
+  RefPtr<ClearKeySessionManager> self(this);
+  function<void()> deferrer =
+    [self, aPromiseId, sessionId] ()
+  {
+    self->CloseSession(aPromiseId, sessionId.data(), sessionId.size());
+  };
+
+  // If we haven't loaded, call this method later.
+  if (MaybeDeferTillInitialized(deferrer)) {
+    CK_LOGD("Deferring CloseSession");
+    return;
+  }
+
+  // If DecryptingComplete has been called mHost will be null and we won't
+  // be able to resolve our promise.
+  if (!mHost) {
+    return;
+  }
+
   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);
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kInvalidAccessError,
+                           0,
+                           nullptr,
+                           0);
+
     return;
   }
 
   ClearKeySession* session = itr->second;
   assert(session);
 
   ClearInMemorySessionData(session);
-  mCallback->SessionClosed(aSessionId, aSessionIdLength);
-  mCallback->ResolvePromise(aPromiseId);
+
+  mHost->OnSessionClosed(aSessionId, aSessionIdLength);
+  mHost->OnResolvePromise(aPromiseId);
 }
 
 void
 ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession)
 {
   mSessions.erase(aSession->Id());
   delete aSession;
 }
 
 void
 ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
                                       const char* aSessionId,
                                       uint32_t aSessionIdLength)
 {
   CK_LOGD("ClearKeySessionManager::RemoveSession");
+
+  // Copy the sessionId into a string so it can be captured for the lambda.
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
+
+  // Hold a reference to the SessionManager, so it isn't released before we
+  // try and use it.
+  RefPtr<ClearKeySessionManager> self(this);
+  function<void()> deferrer =
+    [self, aPromiseId, sessionId] ()
+  {
+    self->RemoveSession(aPromiseId, sessionId.data(), sessionId.size());
+  };
+
+  // If we haven't fully loaded, defer calling this method.
+  if (MaybeDeferTillInitialized(deferrer)) {
+    CK_LOGD("Deferring RemoveSession");
+    return;
+  }
+
+  // Check that the SessionManager has not been shutdown before we try and
+  // resolve any promises.
+  if (!mHost) {
+    return;
+  }
+
   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);
+
+    mHost->OnRejectPromise(aPromiseId,
+                           Error::kInvalidAccessError,
+                           0,
+                           nullptr,
+                           0);
+
     return;
   }
 
   ClearKeySession* session = itr->second;
   assert(session);
   string sid = session->Id();
-  bool isPersistent = session->Type() == kGMPPersistentSession;
+  bool isPersistent = session->Type() == SessionType::kPersistentLicense;
   ClearInMemorySessionData(session);
 
   if (!isPersistent) {
-    mCallback->ResolvePromise(aPromiseId);
+    mHost->OnResolvePromise(aPromiseId);
     return;
   }
 
-  ClearKeyPersistence::PersistentSessionRemoved(sid);
+  mPersistence->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);
+
+  function<void()> resolve = [self, aPromiseId, sessionId] ()
+  {
+    if (!self->mHost) {
+      return;
+    }
+    self->mHost->OnResolvePromise(aPromiseId);
+  };
+
+  function<void()> reject = [self, aPromiseId, sessionId] ()
+  {
+    if (!self->mHost) {
+      return;
+    }
+    static const char* message = "Could not remove session";
+    self->mHost->OnRejectPromise(aPromiseId,
+                                 Error::kInvalidAccessError,
+                                 0,
+                                 message,
+                                 strlen(message));
+  };
+
+  WriteData(mHost, sessionId, emptyKeydata, move(resolve), move(reject));
 }
 
 void
 ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId,
                                              const uint8_t* aServerCert,
                                              uint32_t aServerCertSize)
 {
   // ClearKey CDM doesn't support this method by spec.
   CK_LOGD("ClearKeySessionManager::SetServerCertificate");
-  mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                           nullptr /* message */, 0 /* messageLen */);
+  mHost->OnRejectPromise(aPromiseId,
+                         Error::kNotSupportedError,
+                         0,
+                         nullptr /* message */,
+                         0 /* messageLen */);
 }
 
-void
-ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer,
-                                GMPEncryptedBufferMetadata* aMetadata)
+Status
+ClearKeySessionManager::Decrypt(const InputBuffer& aBuffer,
+                                DecryptedBlock* aDecryptedBlock)
 {
   CK_LOGD("ClearKeySessionManager::Decrypt");
 
-  if (!mThread) {
-    CK_LOGW("No decrypt thread");
-    mCallback->Decrypted(aBuffer, GMPGenericErr);
-    return;
-  }
+  CK_LOGARRAY("Key: ", aBuffer.key_id, aBuffer.key_id_size);
 
-  mThread->Post(WrapTaskRefCounted(this,
-                                   &ClearKeySessionManager::DoDecrypt,
-                                   aBuffer, aMetadata));
-}
+  Buffer* buffer = mHost->Allocate(aBuffer.data_size);
+  assert(buffer != nullptr);
+  assert(buffer->Data() != nullptr);
+  assert(buffer->Capacity() >= aBuffer.data_size);
+
+  memcpy(buffer->Data(), aBuffer.data, aBuffer.data_size);
 
-void
-ClearKeySessionManager::DoDecrypt(GMPBuffer* aBuffer,
-                                  GMPEncryptedBufferMetadata* aMetadata)
-{
-  CK_LOGD("ClearKeySessionManager::DoDecrypt");
+  Status status = mDecryptionManager->Decrypt(buffer->Data(),
+                                              buffer->Size(),
+                                              CryptoMetaData(&aBuffer));
 
-  GMPErr rv = mDecryptionManager->Decrypt(aBuffer->Data(), aBuffer->Size(),
-                                          CryptoMetaData(aMetadata));
-  CK_LOGD("DeDecrypt finished with code %x\n", rv);
-  mCallback->Decrypted(aBuffer, rv);
-}
+  aDecryptedBlock->SetDecryptedBuffer(buffer);
+  aDecryptedBlock->SetTimestamp(aBuffer.timestamp);
 
-void
-ClearKeySessionManager::Shutdown()
-{
-  CK_LOGD("ClearKeySessionManager::Shutdown %p", this);
-
-  for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
-    delete it->second;
-  }
-  mSessions.clear();
+  return status;
 }
 
 void
 ClearKeySessionManager::DecryptingComplete()
 {
   CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this);
 
-  GMPThread* thread = mThread;
-  thread->Join();
+  for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
+    delete it->second;
+  }
+  mSessions.clear();
 
-  Shutdown();
   mDecryptionManager = nullptr;
+  mHost = nullptr;
+
   Release();
 }
+
+bool ClearKeySessionManager::MaybeDeferTillInitialized(function<void()> aMaybeDefer)
+{
+  if (mPersistence->IsLoaded()) {
+    return false;
+  }
+
+  mDeferredInitialize.emplace(move(aMaybeDefer));
+  return true;
+}
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h
@@ -1,100 +1,102 @@
 /*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+* Copyright 2015, Mozilla Foundation and contributors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
 
 #ifndef __ClearKeyDecryptor_h__
 #define __ClearKeyDecryptor_h__
 
+#include "ClearKeyDecryptionManager.h"
+#include "ClearKeyPersistence.h"
+#include "ClearKeySession.h"
+#include "ClearKeyUtils.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "RefCounted.h"
+
+#include <functional>
 #include <map>
+#include <queue>
 #include <set>
 #include <string>
-#include <vector>
 
-#include "ClearKeyDecryptionManager.h"
-#include "ClearKeySession.h"
-#include "ClearKeyUtils.h"
-#include "gmp-api/gmp-decryption.h"
-#include "RefCounted.h"
-
-class ClearKeySessionManager final : public GMPDecryptor
-                                   , public RefCounted
+class ClearKeySessionManager final : public RefCounted
 {
 public:
-  ClearKeySessionManager();
+  explicit ClearKeySessionManager(cdm::Host_8* aHost);
 
-  virtual void Init(GMPDecryptorCallback* aCallback,
-                    bool aDistinctiveIdentifierAllowed,
-                    bool aPersistentStateAllowed) override;
+  void Init(bool aDistinctiveIdentifierAllowed,
+            bool aPersistentStateAllowed);
 
-  virtual void CreateSession(uint32_t aCreateSessionToken,
-                             uint32_t aPromiseId,
-                             const char* aInitDataType,
-                             uint32_t aInitDataTypeSize,
-                             const uint8_t* aInitData,
-                             uint32_t aInitDataSize,
-                             GMPSessionType aSessionType) override;
+  void CreateSession(uint32_t aPromiseId,
+                     cdm::InitDataType aInitDataType,
+                     const uint8_t* aInitData,
+                     uint32_t aInitDataSize,
+                     cdm::SessionType aSessionType);
 
-  virtual void LoadSession(uint32_t aPromiseId,
-                           const char* aSessionId,
-                           uint32_t aSessionIdLength) override;
+  void LoadSession(uint32_t aPromiseId,
+                   const char* aSessionId,
+                   uint32_t aSessionIdLength);
 
-  virtual void UpdateSession(uint32_t aPromiseId,
-                             const char* aSessionId,
-                             uint32_t aSessionIdLength,
-                             const uint8_t* aResponse,
-                             uint32_t aResponseSize) override;
+  void UpdateSession(uint32_t aPromiseId,
+                     const char* aSessionId,
+                     uint32_t aSessionIdLength,
+                     const uint8_t* aResponse,
+                     uint32_t aResponseSize);
 
-  virtual void CloseSession(uint32_t aPromiseId,
-                            const char* aSessionId,
-                            uint32_t aSessionIdLength) override;
+  void CloseSession(uint32_t aPromiseId,
+                    const char* aSessionId,
+                    uint32_t aSessionIdLength);
 
-  virtual void RemoveSession(uint32_t aPromiseId,
-                             const char* aSessionId,
-                             uint32_t aSessionIdLength) override;
+  void RemoveSession(uint32_t aPromiseId,
+                     const char* aSessionId,
+                     uint32_t aSessionIdLength);
 
-  virtual void SetServerCertificate(uint32_t aPromiseId,
-                                    const uint8_t* aServerCert,
-                                    uint32_t aServerCertSize) override;
+  void SetServerCertificate(uint32_t aPromiseId,
+                            const uint8_t* aServerCert,
+                            uint32_t aServerCertSize);
 
-  virtual void Decrypt(GMPBuffer* aBuffer,
-                       GMPEncryptedBufferMetadata* aMetadata) override;
+  cdm::Status
+  Decrypt(const cdm::InputBuffer& aBuffer,
+          cdm::DecryptedBlock* aDecryptedBlock);
 
-  virtual void DecryptingComplete() override;
+  void DecryptingComplete();
 
-  void PersistentSessionDataLoaded(GMPErr aStatus,
-                                   uint32_t aPromiseId,
+  void PersistentSessionDataLoaded(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);
+  bool MaybeDeferTillInitialized(std::function<void()> aMaybeDefer);
+  void Serialize(const ClearKeySession* aSession,
+                 std::vector<uint8_t>& aOutKeyData);
 
   RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
+  RefPtr<ClearKeyPersistence> mPersistence;
 
-  GMPDecryptorCallback* mCallback;
-  GMPThread* mThread;
+  cdm::Host_8* mHost = nullptr;
 
   std::set<KeyId> mKeyIds;
   std::map<std::string, ClearKeySession*> mSessions;
+
+  std::queue<std::function<void()>> mDeferredInitialize;
 };
 
 #endif // __ClearKeyDecryptor_h__
--- a/media/gmp-clearkey/0.1/ClearKeyStorage.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyStorage.cpp
@@ -10,185 +10,217 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "ClearKeyStorage.h"
+
 #include "ClearKeyUtils.h"
 
-#include "gmp-task-utils.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
 
 #include <assert.h>
 #include "ArrayUtils.h"
-
 #include <vector>
 
-static GMPErr
-RunOnMainThread(GMPTask* aTask)
-{
-  return GetPlatform()->runonmainthread(aTask);
-}
+using namespace cdm;
+using namespace std;
 
-GMPErr
-OpenRecord(const char* aName,
-           uint32_t aNameLength,
-           GMPRecord** aOutRecord,
-           GMPRecordClient* aClient)
+class WriteRecordClient : public FileIOClient
 {
-  return GetPlatform()->createrecord(aName, aNameLength, aOutRecord, aClient);
-}
-
-class WriteRecordClient : public GMPRecordClient {
 public:
   /*
    * This function will take the memory ownership of the parameters and
    * delete them when done.
    */
-  static void Write(const std::string& aRecordName,
-                    const std::vector<uint8_t>& aData,
-                    GMPTask* aOnSuccess,
-                    GMPTask* aOnFailure) {
-    (new WriteRecordClient(aData, aOnSuccess, aOnFailure))->Do(aRecordName);
+  static void Write(Host_8* aHost,
+                    string& aRecordName,
+                    const vector<uint8_t>& aData,
+                    function<void()>&& aOnSuccess,
+                    function<void()>&& aOnFailure)
+{
+    WriteRecordClient* client = new WriteRecordClient(aData,
+                                                      move(aOnSuccess),
+                                                      move(aOnFailure));
+    client->Do(aRecordName, aHost);
   }
 
-  virtual void OpenComplete(GMPErr aStatus) override {
-    if (GMP_FAILED(aStatus) ||
-        GMP_FAILED(mRecord->Write(&mData.front(), mData.size()))) {
-      Done(mOnFailure, mOnSuccess);
+  void OnOpenComplete(Status aStatus) override
+  {
+    // If we hit an error, fail.
+    if (aStatus != Status::kSuccess) {
+      Done(aStatus);
+    } else if (mFileIO) { // Otherwise, write our data to the file.
+      mFileIO->Write(&mData[0], mData.size());
     }
   }
 
-  virtual void ReadComplete(GMPErr aStatus,
-                            const uint8_t* aData,
-                            uint32_t aDataSize) override {
-    assert(false); // Should not reach here.
+  void OnReadComplete(Status aStatus,
+                      const uint8_t* aData,
+                      uint32_t aDataSize) override
+  {
+    // This function should never be called, we only ever write data with this
+    // client.
+    assert(false);
   }
 
-  virtual void WriteComplete(GMPErr aStatus) override {
-    if (GMP_FAILED(aStatus)) {
-      Done(mOnFailure, mOnSuccess);
-    } else {
-      Done(mOnSuccess, mOnFailure);
-    }
+  void OnWriteComplete(Status aStatus) override
+  {
+    Done(aStatus);
   }
 
 private:
-  WriteRecordClient(const std::vector<uint8_t>& aData,
-                    GMPTask* aOnSuccess,
-                    GMPTask* aOnFailure)
-    : mRecord(nullptr)
-    , mOnSuccess(aOnSuccess)
-    , mOnFailure(aOnFailure)
+  explicit WriteRecordClient(const vector<uint8_t>& aData,
+                             function<void()>&& aOnSuccess,
+                             function<void()>&& aOnFailure)
+    : mFileIO(nullptr)
+    , mOnSuccess(move(aOnSuccess))
+    , mOnFailure(move(aOnFailure))
     , mData(aData) {}
 
-  void Do(const std::string& aName) {
-    auto err = OpenRecord(aName.c_str(), aName.size(), &mRecord, this);
-    if (GMP_FAILED(err) ||
-        GMP_FAILED(mRecord->Open())) {
-      Done(mOnFailure, mOnSuccess);
-    }
+  void Do(const string& aName, Host_8* aHost)
+  {
+    // Initialize the FileIO.
+    mFileIO = aHost->CreateFileIO(this);
+    mFileIO->Open(aName.c_str(), aName.size());
   }
 
-  void Done(GMPTask* aToRun, GMPTask* aToDestroy) {
+  void Done(cdm::FileIOClient::Status aStatus)
+  {
     // Note: Call Close() before running continuation, in case the
     // continuation tries to open the same record; if we call Close()
     // after running the continuation, the Close() call will arrive
     // just after the Open() call succeeds, immediately closing the
     // record we just opened.
-    if (mRecord) {
-      mRecord->Close();
+    if (mFileIO) {
+      mFileIO->Close();
     }
-    aToDestroy->Destroy();
-    RunOnMainThread(aToRun);
+
+    if (IO_SUCCEEDED(aStatus)) {
+      mOnSuccess();
+    } else {
+      mOnFailure();
+    }
+
     delete this;
   }
 
-  GMPRecord* mRecord;
-  GMPTask* mOnSuccess;
-  GMPTask* mOnFailure;
-  const std::vector<uint8_t> mData;
+  FileIO* mFileIO = nullptr;
+
+  function<void()> mOnSuccess;
+  function<void()> mOnFailure;
+
+  const vector<uint8_t> mData;
 };
 
 void
-StoreData(const std::string& aRecordName,
-          const std::vector<uint8_t>& aData,
-          GMPTask* aOnSuccess,
-          GMPTask* aOnFailure)
+WriteData(Host_8* aHost,
+          string& aRecordName,
+          const vector<uint8_t>& aData,
+          function<void()>&& aOnSuccess,
+          function<void()>&& aOnFailure)
 {
-  WriteRecordClient::Write(aRecordName, aData, aOnSuccess, aOnFailure);
+  WriteRecordClient::Write(aHost,
+                           aRecordName,
+                           aData,
+                           move(aOnSuccess),
+                           move(aOnFailure));
 }
 
-class ReadRecordClient : public GMPRecordClient {
+class ReadRecordClient : public FileIOClient
+{
 public:
   /*
    * This function will take the memory ownership of the parameters and
    * delete them when done.
    */
-  static void Read(const std::string& aRecordName,
-                   ReadContinuation* aContinuation) {
-    assert(aContinuation);
-    (new ReadRecordClient(aContinuation))->Do(aRecordName);
+  static void Read(Host_8* aHost,
+                   string& aRecordName,
+                   function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+                   function<void()>&& aOnFailure)
+  {
+
+    (new ReadRecordClient(move(aOnSuccess), move(aOnFailure)))->
+      Do(aRecordName, aHost);
   }
 
-  virtual void OpenComplete(GMPErr aStatus) override {
+  void OnOpenComplete(Status aStatus) override
+  {
     auto err = aStatus;
-    if (GMP_FAILED(err) ||
-        GMP_FAILED(err = mRecord->Read())) {
+    if (aStatus != Status::kSuccess) {
       Done(err, nullptr, 0);
+    } else {
+      mFileIO->Read();
     }
   }
 
-  virtual void ReadComplete(GMPErr aStatus,
-                            const uint8_t* aData,
-                            uint32_t aDataSize) override {
+  void OnReadComplete(Status aStatus,
+                      const uint8_t* aData,
+                      uint32_t aDataSize) override
+  {
     Done(aStatus, aData, aDataSize);
   }
 
-  virtual void WriteComplete(GMPErr aStatus) override {
-    assert(false); // Should not reach here.
+  void OnWriteComplete(Status aStatus) override
+  {
+    // We should never reach here, this client only ever reads data.
+    assert(false);
   }
 
 private:
-  explicit ReadRecordClient(ReadContinuation* aContinuation)
-    : mRecord(nullptr)
-    , mContinuation(aContinuation) {}
+  explicit ReadRecordClient(function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+                            function<void()>&& aOnFailure)
+    : mFileIO(nullptr)
+    , mOnSuccess(move(aOnSuccess))
+    , mOnFailure(move(aOnFailure))
+  {}
 
-  void Do(const std::string& aName) {
-    auto err = OpenRecord(aName.c_str(), aName.size(), &mRecord, this);
-    if (GMP_FAILED(err) ||
-        GMP_FAILED(err = mRecord->Open())) {
-      Done(err, nullptr, 0);
-    }
+  void Do(const string& aName, Host_8* aHost)
+  {
+    mFileIO = aHost->CreateFileIO(this);
+    mFileIO->Open(aName.c_str(), aName.size());
   }
 
-  void Done(GMPErr err, const uint8_t* aData, uint32_t aDataSize) {
+  void Done(cdm::FileIOClient::Status aStatus,
+            const uint8_t* aData,
+            uint32_t aDataSize)
+  {
     // Note: Call Close() before running continuation, in case the
     // continuation tries to open the same record; if we call Close()
     // after running the continuation, the Close() call will arrive
     // just after the Open() call succeeds, immediately closing the
     // record we just opened.
-    if (mRecord) {
-      mRecord->Close();
+    if (mFileIO) {
+      mFileIO->Close();
     }
-    mContinuation->ReadComplete(err, aData, aDataSize);
-    delete mContinuation;
+
+    if (IO_SUCCEEDED(aStatus)) {
+      mOnSuccess(aData, aDataSize);
+    } else {
+      mOnFailure();
+    }
+
     delete this;
   }
 
-  GMPRecord* mRecord;
-  ReadContinuation* mContinuation;
+  FileIO* mFileIO = nullptr;
+
+  function<void(const uint8_t*, uint32_t)> mOnSuccess;
+  function<void()> mOnFailure;
 };
 
 void
-ReadData(const std::string& aRecordName,
-         ReadContinuation* aContinuation)
+ReadData(Host_8* mHost,
+         string& aRecordName,
+         function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+         function<void()>&& aOnFailure)
 {
-  ReadRecordClient::Read(aRecordName, aContinuation);
+  ReadRecordClient::Read(mHost,
+                         aRecordName,
+                         move(aOnSuccess),
+                         move(aOnFailure));
 }
-
-GMPErr
-EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc)
-{
-  return GetPlatform()->getrecordenumerator(aRecvIteratorFunc, nullptr);
-}
--- a/media/gmp-clearkey/0.1/ClearKeyStorage.h
+++ b/media/gmp-clearkey/0.1/ClearKeyStorage.h
@@ -12,37 +12,32 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeyStorage_h__
 #define __ClearKeyStorage_h__
 
-#include "gmp-api/gmp-errors.h"
-#include "gmp-api/gmp-platform.h"
+#include <functional>
+#include <stdint.h>
 #include <string>
 #include <vector>
-#include <stdint.h>
 
-class GMPTask;
+#include "ClearKeySessionManager.h"
 
-// Responsible for ensuring that both aOnSuccess and aOnFailure are destroyed.
-void StoreData(const std::string& aRecordName,
-               const std::vector<uint8_t>& aData,
-               GMPTask* aOnSuccess,
-               GMPTask* aOnFailure);
+#define IO_SUCCEEDED(x) ((x) == cdm::FileIOClient::Status::kSuccess)
+#define IO_FAILED(x) ((x) != cdm::FileIOClient::Status::kSuccess)
 
-class ReadContinuation {
-public:
-  virtual void ReadComplete(GMPErr aStatus,
-                            const uint8_t* aData,
-                            uint32_t aLength) = 0;
-  virtual ~ReadContinuation() {}
-};
+// Writes data to a file and fires the appropriate callback when complete.
+void WriteData(cdm::Host_8* aHost,
+               std::string& aRecordName,
+               const std::vector<uint8_t>& aData,
+               std::function<void()>&& aOnSuccess,
+               std::function<void()>&& aOnFailure);
 
-// Deletes aContinuation after running it to report the result.
-void ReadData(const std::string& aSessionId,
-              ReadContinuation* aContinuation);
-
-GMPErr EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc);
+// Reads data from a file and fires the appropriate callback when complete.
+void ReadData(cdm::Host_8* aHost,
+              std::string& aRecordName,
+              std::function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+              std::function<void()>&& aOnFailure);
 
 #endif // __ClearKeyStorage_h__
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
@@ -9,43 +9,87 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
+#include "ClearKeyUtils.h"
+
 #include <algorithm>
+#include <assert.h>
+#include <stdlib.h>
+#include <cctype>
 #include <ctype.h>
+#include <memory.h>
+#include <sstream>
 #include <stdarg.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <vector>
 
-#include "ClearKeyUtils.h"
+#include "ArrayUtils.h"
+#include "BigEndian.h"
 #include "ClearKeyBase64.h"
-#include "ArrayUtils.h"
-#include <assert.h>
-#include <memory.h>
-#include "BigEndian.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
 #include "openaes/oaes_lib.h"
+#include "psshparser/PsshParser.h"
 
+using namespace cdm;
 using namespace std;
 
 void
 CK_Log(const char* aFmt, ...)
 {
+  FILE* out = stdout;
+
+  if (getenv("CLEARKEY_LOG_FILE")) {
+    out = fopen(getenv("CLEARKEY_LOG_FILE"), "a");
+  }
+
   va_list ap;
 
   va_start(ap, aFmt);
-  vprintf(aFmt, ap);
+  const size_t len = 1024;
+  char buf[len];
+  vsnprintf(buf, len, aFmt, ap);
   va_end(ap);
 
-  printf("\n");
-  fflush(stdout);
+  fprintf(out, "%s\n", buf);
+  fflush(out);
+
+  if (out != stdout) {
+    fclose(out);
+  }
+}
+
+static bool
+PrintableAsString(const uint8_t* aBytes, uint32_t aLength)
+{
+  return all_of(aBytes, aBytes + aLength, [] (uint8_t c) {
+    return isprint(c) == 1;
+  });
+}
+
+void
+CK_LogArray(const char* prepend,
+            const uint8_t* aData,
+            const uint32_t aDataSize)
+{
+  // If the data is valid ascii, use that. Otherwise print the hex
+  string data = PrintableAsString(aData, aDataSize) ?
+                string(aData, aData + aDataSize) :
+                ClearKeyUtils::ToHexString(aData, aDataSize);
+
+  CK_LOGD("%s%s", prepend, data.c_str());
 }
 
 static void
 IncrementIV(vector<uint8_t>& aIV) {
   using mozilla::BigEndian;
 
   assert(aIV.size() == 16);
   BigEndian::writeUint64(&aIV[8], BigEndian::readUint64(&aIV[8]) + 1);
@@ -110,27 +154,29 @@ EncodeBase64Web(vector<uint8_t> aBinary,
     }
 
     out[i] += (*data >> (shift + 2)) & sMask;
     shift = (shift + 2) % 8;
 
     // Cast idx to size_t before using it as an array-index,
     // to pacify clang 'Wchar-subscripts' warning:
     size_t idx = static_cast<size_t>(out[i]);
-    assert(idx < MOZ_ARRAY_LENGTH(sAlphabet)); // out of bounds index for 'sAlphabet'
+
+    // out of bounds index for 'sAlphabet'
+    assert(idx < MOZ_ARRAY_LENGTH(sAlphabet));
     out[i] = sAlphabet[idx];
   }
 
   return true;
 }
 
 /* static */ void
 ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
                               string& aOutRequest,
-                              GMPSessionType aSessionType)
+                              SessionType aSessionType)
 {
   assert(aKeyIDs.size() && aOutRequest.empty());
 
   aOutRequest.append("{\"kids\":[");
   for (size_t i = 0; i < aKeyIDs.size(); i++) {
     if (i) {
       aOutRequest.append(",");
     }
@@ -384,17 +430,17 @@ ParseKeys(ParserContext& aCtx, vector<Ke
   }
 
   return GetNextSymbol(aCtx) == ']';
 }
 
 /* static */ bool
 ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
                         vector<KeyIdPair>& aOutKeys,
-                        GMPSessionType aSessionType)
+                        SessionType aSessionType)
 {
   ParserContext ctx;
   ctx.mIter = aKeyData;
   ctx.mEnd = aKeyData + aKeyDataSize;
 
   // Consume '{' from start of object.
   EXPECT_SYMBOL(ctx, '{');
 
@@ -500,25 +546,26 @@ ClearKeyUtils::ParseKeyIdsInitData(const
 
   // Consume '}' from end of object.
   EXPECT_SYMBOL(ctx, '}');
 
   return true;
 }
 
 /* static */ const char*
-ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType)
+ClearKeyUtils::SessionTypeToString(SessionType aSessionType)
 {
   switch (aSessionType) {
-    case kGMPTemporySession: return "temporary";
-    case kGMPPersistentSession: return "persistent-license";
-    default: {
-      assert(false); // Should not reach here.
-      return "invalid";
-    }
+  case SessionType::kTemporary: return "temporary";
+  case SessionType::kPersistentLicense: return "persistent-license";
+  default: {
+    // We don't support any other license types.
+    assert(false);
+    return "invalid";
+  }
   }
 }
 
 /* static */ bool
 ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength)
 {
   if (aLength > 10) {
     // 10 is the max number of characters in UINT32_MAX when
@@ -528,14 +575,20 @@ ClearKeyUtils::IsValidSessionId(const ch
   for (uint32_t i = 0; i < aLength; i++) {
     if (!isdigit(aBuff[i])) {
       return false;
     }
   }
   return true;
 }
 
-GMPMutex* GMPCreateMutex() {
-  GMPMutex* mutex;
-  auto err = GetPlatform()->createmutex(&mutex);
-  assert(mutex);
-  return GMP_FAILED(err) ? nullptr : mutex;
+string
+ClearKeyUtils::ToHexString(const uint8_t * aBytes, uint32_t aLength)
+{
+  stringstream ss;
+  ss << std::showbase << std::uppercase << std::hex;
+  for (uint32_t i = 0; i < aLength; ++i) {
+    ss << std::hex << static_cast<uint32_t>(aBytes[i]);
+    ss << " ";
+  }
+
+  return ss.str();
 }
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.h
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h
@@ -16,43 +16,54 @@
 
 #ifndef __ClearKeyUtils_h__
 #define __ClearKeyUtils_h__
 
 #include <stdint.h>
 #include <string>
 #include <vector>
 #include <assert.h>
-#include "gmp-api/gmp-decryption.h"
+
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
 
 #if 0
 void CK_Log(const char* aFmt, ...);
 #define CK_LOGE(...) CK_Log(__VA_ARGS__)
 #define CK_LOGD(...) CK_Log(__VA_ARGS__)
 #define CK_LOGW(...) CK_Log(__VA_ARGS__)
+#define CK_LOGARRAY(APREPEND, ADATA, ADATA_SIZE) CK_LogArray(APREPEND, \
+  ADATA, \
+  ADATA_SIZE)
 #else
+// Note: Enabling logging slows things down a LOT, especially when logging to
+// a file.
 #define CK_LOGE(...)
 #define CK_LOGD(...)
 #define CK_LOGW(...)
+#define CK_LOGARRAY(APREPEND, ADATA, ADATA_SIZE)
 #endif
 
-struct GMPPlatformAPI;
-extern GMPPlatformAPI* GetPlatform();
-
 typedef std::vector<uint8_t> KeyId;
 typedef std::vector<uint8_t> Key;
 
 // The session response size should be within a reasonable limit.
 // The size 64 KB is referenced from web-platform-test.
 static const uint32_t kMaxSessionResponseLength = 65536;
 
 // Provide limitation for KeyIds length and webm initData size.
 static const uint32_t kMaxWebmInitDataSize = 65536;
 static const uint32_t kMaxKeyIdsLength = 512;
 
+void CK_LogArray(const char* aPrepend,
+                 const uint8_t* aData,
+                 const uint32_t aDataSize);
+
 struct KeyIdPair
 {
   KeyId mKeyId;
   Key mKey;
 };
 
 class ClearKeyUtils
 {
@@ -61,54 +72,35 @@ public:
                          std::vector<uint8_t>& aData, std::vector<uint8_t>& aIV);
 
   static bool ParseKeyIdsInitData(const uint8_t* aInitData,
                                   uint32_t aInitDataSize,
                                   std::vector<KeyId>& aOutKeyIds);
 
   static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds,
                              std::string& aOutRequest,
-                             GMPSessionType aSessionType);
+                             cdm::SessionType aSessionType);
 
   static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
                        std::vector<KeyIdPair>& aOutKeys,
-                       GMPSessionType aSessionType);
-  static const char* SessionTypeToString(GMPSessionType aSessionType);
+                       cdm::SessionType aSessionType);
+  static const char* SessionTypeToString(cdm::SessionType aSessionType);
 
   static bool IsValidSessionId(const char* aBuff, uint32_t aLength);
+
+  static std::string ToHexString(const uint8_t * aBytes, uint32_t aLength);
 };
 
 template<class Container, class Element>
 inline bool
 Contains(const Container& aContainer, const Element& aElement)
 {
   return aContainer.find(aElement) != aContainer.end();
 }
 
-class AutoLock {
-public:
-  explicit AutoLock(GMPMutex* aMutex)
-    : mMutex(aMutex)
-  {
-    assert(aMutex);
-    if (mMutex) {
-      mMutex->Acquire();
-    }
-  }
-  ~AutoLock() {
-    if (mMutex) {
-      mMutex->Release();
-    }
-  }
-private:
-  GMPMutex* mMutex;
-};
-
-GMPMutex* GMPCreateMutex();
-
 template<typename T>
 inline void
 Assign(std::vector<T>& aVec, const T* aData, size_t aLength)
 {
   aVec.assign(aData, aData + aLength);
 }
 
 #endif // __ClearKeyUtils_h__
--- a/media/gmp-clearkey/0.1/RefCounted.h
+++ b/media/gmp-clearkey/0.1/RefCounted.h
@@ -16,51 +16,17 @@
 
 #ifndef __RefCount_h__
 #define __RefCount_h__
 
 #include <stdint.h>
 #include <assert.h>
 #include "ClearKeyUtils.h"
 
-#if defined(_MSC_VER)
 #include <atomic>
-typedef std::atomic<uint32_t> AtomicRefCount;
-#else
-class AtomicRefCount {
-public:
-  explicit AtomicRefCount(uint32_t aValue)
-    : mCount(aValue)
-    , mMutex(GMPCreateMutex())
-  {
-    assert(mMutex);
-  }
-  ~AtomicRefCount()
-  {
-    if (mMutex) {
-      mMutex->Destroy();
-    }
-  }
-  uint32_t operator--() {
-    AutoLock lock(mMutex);
-    return --mCount;
-  }
-  uint32_t operator++() {
-    AutoLock lock(mMutex);
-    return ++mCount;
-  }
-  operator uint32_t() {
-    AutoLock lock(mMutex);
-    return mCount;
-  }
-private:
-  uint32_t mCount;
-  GMPMutex* mMutex;
-};
-#endif
 
 // Note: Thread safe.
 class RefCounted {
 public:
   void AddRef() {
     ++mRefCount;
   }
 
@@ -76,41 +42,57 @@ protected:
   RefCounted()
     : mRefCount(0)
   {
   }
   virtual ~RefCounted()
   {
     assert(!mRefCount);
   }
-  AtomicRefCount mRefCount;
+  std::atomic<uint32_t> mRefCount;
 };
 
 template<class T>
 class RefPtr {
 public:
-  explicit RefPtr(T* aPtr) : mPtr(nullptr) {
-    Assign(aPtr);
+  RefPtr(const RefPtr& src) {
+    Set(src.mPtr);
   }
+
+  explicit RefPtr(T* aPtr) {
+    Set(aPtr);
+  }
+  RefPtr() { Set(nullptr); }
+
   ~RefPtr() {
-    Assign(nullptr);
+    Set(nullptr);
   }
   T* operator->() const { return mPtr; }
+  T** operator&() { return &mPtr; }
+  T* operator->() { return mPtr; }
+  operator T*() { return mPtr; }
+
+  T* Get() const { return mPtr; }
 
   RefPtr& operator=(T* aVal) {
-    Assign(aVal);
+    Set(aVal);
     return *this;
   }
 
 private:
-  void Assign(T* aPtr) {
+  T* Set(T* aPtr) {
+    if (mPtr == aPtr) {
+      return aPtr;
+    }
     if (mPtr) {
       mPtr->Release();
     }
     mPtr = aPtr;
     if (mPtr) {
       aPtr->AddRef();
     }
+    return mPtr;
   }
-  T* mPtr;
+
+  T* mPtr = nullptr;
 };
 
 #endif // __RefCount_h__
--- a/media/gmp-clearkey/0.1/VideoDecoder.cpp
+++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp
@@ -9,447 +9,321 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
+#include <algorithm>
 #include <cstdint>
 #include <limits>
 
-#include "AnnexB.h"
 #include "BigEndian.h"
 #include "ClearKeyDecryptionManager.h"
 #include "ClearKeyUtils.h"
-#include "gmp-task-utils.h"
 #include "VideoDecoder.h"
 
 using namespace wmf;
+using namespace cdm;
 
-VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI)
-  : mHostAPI(aHostAPI)
-  , mCallback(nullptr)
-  , mWorkerThread(nullptr)
-  , mMutex(nullptr)
-  , mNumInputTasks(0)
-  , mSentExtraData(false)
-  , mIsFlushing(false)
+VideoDecoder::VideoDecoder(Host_8 *aHost)
+  : mHost(aHost)
   , mHasShutdown(false)
 {
+  CK_LOGD("VideoDecoder created");
+
   // We drop the ref in DecodingComplete().
   AddRef();
+
+  mDecoder = new WMFH264Decoder();
+
+  uint32_t cores = std::max(1u, std::thread::hardware_concurrency());
+  HRESULT hr = mDecoder->Init(cores);
 }
 
 VideoDecoder::~VideoDecoder()
 {
-  if (mMutex) {
-    mMutex->Destroy();
-  }
+  CK_LOGD("VideoDecoder destroyed");
 }
 
-void
-VideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings,
-                         const uint8_t* aCodecSpecific,
-                         uint32_t aCodecSpecificLength,
-                         GMPVideoDecoderCallback* aCallback,
-                         int32_t aCoreCount)
+Status
+VideoDecoder::InitDecode(const VideoDecoderConfig& aConfig)
 {
-  mCallback = aCallback;
-  assert(mCallback);
-  mDecoder = new WMFH264Decoder();
-  HRESULT hr = mDecoder->Init(aCoreCount);
-  if (FAILED(hr)) {
-    CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
-    mCallback->Error(GMPGenericErr);
-    return;
-  }
-
-  auto err = GetPlatform()->createmutex(&mMutex);
-  if (GMP_FAILED(err)) {
-    CK_LOGD("VideoDecoder::InitDecode failed to create GMPMutex");
-    mCallback->Error(GMPGenericErr);
-    return;
-  }
+  CK_LOGD("VideoDecoder::InitDecode");
 
-  // The first byte is mPacketizationMode, which is only relevant for
-  // WebRTC/OpenH264 usecase.
-  const uint8_t* avcc = aCodecSpecific + 1;
-  const uint8_t* avccEnd = aCodecSpecific + aCodecSpecificLength;
-  mExtraData.insert(mExtraData.end(), avcc, avccEnd);
-
-  AnnexB::ConvertConfig(mExtraData, mAnnexB);
-}
+  if (!mDecoder) {
+    CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
 
-void
-VideoDecoder::EnsureWorker()
-{
-  if (!mWorkerThread) {
-    GetPlatform()->createthread(&mWorkerThread);
-    if (!mWorkerThread) {
-      mCallback->Error(GMPAllocErr);
-      return;
-    }
-  }
-}
-
-void
-VideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame,
-                     bool aMissingFrames,
-                     const uint8_t* aCodecSpecificInfo,
-                     uint32_t aCodecSpecificInfoLength,
-                     int64_t aRenderTimeMs)
-{
-  if (aInputFrame->BufferType() != GMP_BufferLength32) {
-    // Gecko should only send frames with 4 byte NAL sizes to GMPs.
-    mCallback->Error(GMPGenericErr);
-    return;
+    return Status::kDecodeError;
   }
 
-  EnsureWorker();
-
-  {
-    AutoLock lock(mMutex);
-    mNumInputTasks++;
-  }
-
-  // Note: we don't need the codec specific info on a per-frame basis.
-  // It's mostly useful for WebRTC use cases.
-
-  // Make a copy of the data, so we can release aInputFrame ASAP,
-  // to avoid too many shmem handles being held by the GMP process.
-  // If the GMP process holds on to too many shmem handles, the Gecko
-  // side can fail to allocate a shmem to send more input. This is
-  // particularly a problem in Gecko mochitests, which can open multiple
-  // actors at once which share the same pool of shmems.
-  DecodeData* data = new DecodeData();
-  Assign(data->mBuffer, aInputFrame->Buffer(), aInputFrame->Size());
-  data->mTimestamp = aInputFrame->TimeStamp();
-  data->mDuration = aInputFrame->Duration();
-  data->mIsKeyframe = (aInputFrame->FrameType() == kGMPKeyFrame);
-  const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData();
-  if (crypto) {
-    data->mCrypto.Init(crypto);
-  }
-  aInputFrame->Destroy();
-  mWorkerThread->Post(WrapTaskRefCounted(this,
-                                         &VideoDecoder::DecodeTask,
-                                         data));
+  return Status::kSuccess;
 }
 
-void
-VideoDecoder::DecodeTask(DecodeData* aData)
+Status
+VideoDecoder::Decode(const InputBuffer& aInputBuffer, VideoFrame* aVideoFrame)
 {
-  CK_LOGD("VideoDecoder::DecodeTask");
-  AutoPtr<DecodeData> d(aData);
-  HRESULT hr;
-
-  {
-    AutoLock lock(mMutex);
-    mNumInputTasks--;
-    assert(mNumInputTasks >= 0);
-  }
-
-  if (mIsFlushing) {
-    CK_LOGD("VideoDecoder::DecodeTask rejecting frame: flushing.");
-    return;
+  CK_LOGD("VideoDecoder::Decode");
+  // If the input buffer we have been passed has a null buffer, it means we
+  // should drain.
+  if (!aInputBuffer.data) {
+    // This will drain the decoder until there are no frames left to drain,
+    // whereupon it will return 'NeedsMoreData'.
+    CK_LOGD("VideoDecoder::Decode Input buffer null: Draining");
+    return Drain(aVideoFrame);
   }
 
-  if (!aData || !mHostAPI || !mDecoder) {
+  DecodeData* data = new DecodeData();
+  Assign(data->mBuffer, aInputBuffer.data, aInputBuffer.data_size);
+  data->mTimestamp = aInputBuffer.timestamp;
+  data->mCrypto = CryptoMetaData(&aInputBuffer);
+
+  AutoPtr<DecodeData> d(data);
+  HRESULT hr;
+
+  if (!data || !mDecoder) {
     CK_LOGE("Decode job not set up correctly!");
-    return;
+    return Status::kDecodeError;
   }
 
-  std::vector<uint8_t>& buffer = aData->mBuffer;
-  if (aData->mCrypto.IsValid()) {
-    // Plugin host should have set up its decryptor/key sessions
-    // before trying to decode!
-    GMPErr rv =
-      ClearKeyDecryptionManager::Get()->Decrypt(buffer, aData->mCrypto);
+  std::vector<uint8_t>& buffer = data->mBuffer;
+
+  if (data->mCrypto.IsValid()) {
+    Status rv =
+      ClearKeyDecryptionManager::Get()->Decrypt(buffer, data->mCrypto);
 
-    if (GMP_FAILED(rv)) {
-      MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv));
-      return;
+    if (STATUS_FAILED(rv)) {
+      CK_LOGARRAY("Failed to decrypt video using key ",
+                  aInputBuffer.key_id,
+                  aInputBuffer.key_id_size);
+      return rv;
     }
   }
 
-  AnnexB::ConvertFrameInPlace(buffer);
-
-  if (aData->mIsKeyframe) {
-    // We must send the SPS and PPS to Windows Media Foundation's decoder.
-    // Note: We do this *after* decryption, otherwise the subsample info
-    // would be incorrect.
-    buffer.insert(buffer.begin(), mAnnexB.begin(), mAnnexB.end());
-  }
-
   hr = mDecoder->Input(buffer.data(),
                        buffer.size(),
-                       aData->mTimestamp,
-                       aData->mDuration);
+                       data->mTimestamp);
 
-  CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
+  CK_LOGD("VideoDecoder::Decode() Input ret hr=0x%x", hr);
+
+
   if (FAILED(hr)) {
-    CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n",
-        hr,
-        ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
-    return;
+    assert(hr != MF_E_TRANSFORM_NEED_MORE_INPUT);
+
+    CK_LOGE("VideoDecoder::Decode() decode failed ret=0x%x%s",
+      hr,
+      ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
+    CK_LOGD("Decode failed. The decoder is not accepting input");
+    return Status::kDecodeError;
   }
 
-  while (hr == S_OK) {
+  return OutputFrame(aVideoFrame);
+}
+
+Status VideoDecoder::OutputFrame(VideoFrame* aVideoFrame) {
+  CK_LOGD("VideoDecoder::OutputFrame");
+
+  HRESULT hr = S_OK;
+
+  // Read all the output from the decoder. Ideally, this would be a while loop
+  // where we read the output and check the result as the condition. However,
+  // this produces a memory leak connected to assigning a new CComPtr to the
+  // address of the old one, which avoids the CComPtr cleaning up.
+  while (true) {
     CComPtr<IMFSample> output;
     hr = mDecoder->Output(&output);
-    CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr);
-    if (hr == S_OK) {
-      MaybeRunOnMainThread(
-        WrapTaskRefCounted(this,
-                           &VideoDecoder::ReturnOutput,
-                           CComPtr<IMFSample>(output),
-                           mDecoder->GetFrameWidth(),
-                           mDecoder->GetFrameHeight(),
-                           mDecoder->GetStride()));
+
+    if (hr != S_OK) {
+      break;
     }
-    if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
-      AutoLock lock(mMutex);
-      if (mNumInputTasks == 0) {
-        // We have run all input tasks. We *must* notify Gecko so that it will
-        // send us more data.
-        MaybeRunOnMainThread(
-          WrapTask(mCallback,
-                   &GMPVideoDecoderCallback::InputDataExhausted));
-      }
-    }
-    if (FAILED(hr)) {
-      CK_LOGE("VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr);
-    }
+
+    CK_LOGD("VideoDecoder::OutputFrame Decoder output ret=0x%x", hr);
+
+    mOutputQueue.push(output);
+    CK_LOGD("VideoDecoder::OutputFrame: Queue size: %u", mOutputQueue.size());
   }
-}
 
-void
-VideoDecoder::ReturnOutput(IMFSample* aSample,
-                           int32_t aWidth,
-                           int32_t aHeight,
-                           int32_t aStride)
-{
-  CK_LOGD("[%p] VideoDecoder::ReturnOutput()\n", this);
-  assert(aSample);
-
-  HRESULT hr;
-
-  GMPVideoFrame* f = nullptr;
-  auto err = mHostAPI->CreateFrame(kGMPI420VideoFrame, &f);
-  if (GMP_FAILED(err) || !f) {
-    CK_LOGE("Failed to create i420 frame!\n");
-    return;
-  }
-  if (HasShutdown()) {
-    // Note: GMPVideoHost::CreateFrame() can process messages before returning,
-    // including a message that calls VideoDecoder::DecodingComplete(), i.e.
-    // we can shutdown during the call!
-    CK_LOGD("Shutdown while waiting on GMPVideoHost::CreateFrame()!\n");
-    f->Destroy();
-    return;
+  // If we don't have any inputs, we need more data.
+  if (mOutputQueue.empty()) {
+    CK_LOGD("Decode failed. Not enought data; Requesting more input");
+    return Status::kNeedMoreData;
   }
 
-  auto vf = static_cast<GMPVideoi420Frame*>(f);
+  // We will get a MF_E_TRANSFORM_NEED_MORE_INPUT every time, as we always
+  // consume everything in the buffer.
+  if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) {
+    CK_LOGD("Decode failed output ret=0x%x", hr);
+    return Status::kDecodeError;
+  }
+
+  CComPtr<IMFSample> result = mOutputQueue.front();
+  mOutputQueue.pop();
 
-  hr = SampleToVideoFrame(aSample, aWidth, aHeight, aStride, vf);
-  ENSURE(SUCCEEDED(hr), /*void*/);
+  // The Chromium CDM API doesn't have support for negative strides, though
+  // they are theoretically possible in real world data.
+  if (mDecoder->GetStride() <= 0) {
+    CK_LOGD("VideoDecoder::OutputFrame Failed! (negative stride)");
+    return Status::kDecodeError;
+  }
 
-  mCallback->Decoded(vf);
+  hr = SampleToVideoFrame(result,
+                          mDecoder->GetFrameWidth(),
+                          mDecoder->GetFrameHeight(),
+                          mDecoder->GetStride(),
+                          aVideoFrame);
+  if (FAILED(hr)) {
+    CK_LOGD("VideoDecoder::OutputFrame Failed!");
+    return Status::kDecodeError;
+  }
+
+  CK_LOGD("VideoDecoder::OutputFrame Succeeded.");
+  return Status::kSuccess;
 }
 
 HRESULT
 VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
                                  int32_t aWidth,
                                  int32_t aHeight,
                                  int32_t aStride,
-                                 GMPVideoi420Frame* aVideoFrame)
+                                 VideoFrame* aVideoFrame)
 {
+  CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()", this);
+
   ENSURE(aSample != nullptr, E_POINTER);
   ENSURE(aVideoFrame != nullptr, E_POINTER);
 
   HRESULT hr;
   CComPtr<IMFMediaBuffer> mediaBuffer;
 
+  aVideoFrame->SetFormat(kI420);
+
   // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface.
   hr = aSample->ConvertToContiguousBuffer(&mediaBuffer);
   ENSURE(SUCCEEDED(hr), hr);
 
   // Try and use the IMF2DBuffer interface if available, otherwise fallback
   // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
   // but only some systems (Windows 8?) support it.
   BYTE* data = nullptr;
   LONG stride = 0;
   CComPtr<IMF2DBuffer> twoDBuffer;
   hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer));
   if (SUCCEEDED(hr)) {
     hr = twoDBuffer->Lock2D(&data, &stride);
     ENSURE(SUCCEEDED(hr), hr);
   } else {
-    hr = mediaBuffer->Lock(&data, NULL, NULL);
+    hr = mediaBuffer->Lock(&data, nullptr, nullptr);
     ENSURE(SUCCEEDED(hr), hr);
     stride = aStride;
   }
 
-  // The V and U planes are stored 16-row-aligned, so we need to add padding
+  // The U and V planes are stored 16-row-aligned, so we need to add padding
   // to the row heights to ensure the Y'CbCr planes are referenced properly.
-  // YV12, planar format: [YYYY....][VVVV....][UUUU....]
-  // i.e., Y, then V, then U.
+  // YV12, planar format: [YYYY....][UUUU....][VVVV....]
+  // i.e., Y, then U, then V.
   uint32_t padding = 0;
   if (aHeight % 16 != 0) {
     padding = 16 - (aHeight % 16);
   }
-  int32_t y_size = stride * (aHeight + padding);
-  int32_t v_size = stride * (aHeight + padding) / 4;
-  int32_t halfStride = (stride + 1) / 2;
-  int32_t halfHeight = (aHeight + 1) / 2;
+  uint32_t ySize = stride * (aHeight + padding);
+  uint32_t uSize = stride * (aHeight + padding) / 4;
+  uint32_t halfStride = (stride + 1) / 2;
+  uint32_t halfHeight = (aHeight + 1) / 2;
+
+  aVideoFrame->SetStride(VideoFrame::kYPlane, stride);
+  aVideoFrame->SetStride(VideoFrame::kUPlane, halfStride);
+  aVideoFrame->SetStride(VideoFrame::kVPlane, halfStride);
 
-  auto err = aVideoFrame->CreateEmptyFrame(stride, aHeight, stride, halfStride, halfStride);
-  ENSURE(GMP_SUCCEEDED(err), E_FAIL);
+  aVideoFrame->SetSize(Size(aWidth, aHeight));
+
+  uint64_t bufferSize = ySize + 2 * uSize;
 
-  err = aVideoFrame->SetWidth(aWidth);
-  ENSURE(GMP_SUCCEEDED(err), E_FAIL);
-  err = aVideoFrame->SetHeight(aHeight);
-  ENSURE(GMP_SUCCEEDED(err), E_FAIL);
+  // If the buffer is bigger than the max for a 32 bit, fail to avoid buffer
+  // overflows.
+  if (bufferSize > UINT32_MAX) {
+    CK_LOGD("VideoDecoder::SampleToFrame Buffersize bigger than UINT32_MAX");
+    return E_FAIL;
+  }
 
-  uint8_t* outBuffer = aVideoFrame->Buffer(kGMPYPlane);
-  ENSURE(outBuffer != nullptr, E_FAIL);
-  assert(aVideoFrame->AllocatedSize(kGMPYPlane) >= stride*aHeight);
-  memcpy(outBuffer, data, stride*aHeight);
+  // Get the buffer from the host.
+  Buffer* buffer = mHost->Allocate(bufferSize);
+  aVideoFrame->SetFrameBuffer(buffer);
+
+  // Make sure the buffer is non-null (allocate guarantees it will be of
+  // sufficient size).
+  if (!buffer) {
+    CK_LOGD("VideoDecoder::SampleToFrame Out of memory");
+    return E_OUTOFMEMORY;
+  }
 
-  outBuffer = aVideoFrame->Buffer(kGMPUPlane);
-  ENSURE(outBuffer != nullptr, E_FAIL);
-  assert(aVideoFrame->AllocatedSize(kGMPUPlane) >= halfStride*halfHeight);
-  memcpy(outBuffer, data+y_size, halfStride*halfHeight);
+  uint8_t* outBuffer = buffer->Data();
+
+  aVideoFrame->SetPlaneOffset(VideoFrame::kYPlane, 0);
 
-  outBuffer = aVideoFrame->Buffer(kGMPVPlane);
-  ENSURE(outBuffer != nullptr, E_FAIL);
-  assert(aVideoFrame->AllocatedSize(kGMPVPlane) >= halfStride*halfHeight);
-  memcpy(outBuffer, data + y_size + v_size, halfStride*halfHeight);
+  // Offset is the size of the copied y data.
+  aVideoFrame->SetPlaneOffset(VideoFrame::kUPlane, ySize);
+
+  // Offset is the size of the copied y data + the size of the copied u data.
+  aVideoFrame->SetPlaneOffset(VideoFrame::kVPlane, ySize + uSize);
+
+  // Copy the data.
+  memcpy(outBuffer, data, ySize + uSize * 2);
 
   if (twoDBuffer) {
     twoDBuffer->Unlock2D();
   } else {
     mediaBuffer->Unlock();
   }
 
   LONGLONG hns = 0;
   hr = aSample->GetSampleTime(&hns);
   ENSURE(SUCCEEDED(hr), hr);
+
   aVideoFrame->SetTimestamp(HNsToUsecs(hns));
 
-  hr = aSample->GetSampleDuration(&hns);
-  ENSURE(SUCCEEDED(hr), hr);
-  aVideoFrame->SetDuration(HNsToUsecs(hns));
-
   return S_OK;
 }
 
 void
-VideoDecoder::ResetCompleteTask()
-{
-  mIsFlushing = false;
-  if (mCallback) {
-    MaybeRunOnMainThread(WrapTask(mCallback,
-                                  &GMPVideoDecoderCallback::ResetComplete));
-  }
-}
-
-void
 VideoDecoder::Reset()
 {
-  mIsFlushing = true;
+  CK_LOGD("VideoDecoder::Reset");
+
   if (mDecoder) {
     mDecoder->Reset();
   }
 
-  // Schedule ResetComplete callback to run after existing frames have been
-  // flushed out of the task queue.
-  EnsureWorker();
-  mWorkerThread->Post(WrapTaskRefCounted(this,
-                                         &VideoDecoder::ResetCompleteTask));
+  // Remove all the frames from the output queue.
+  while (!mOutputQueue.empty()) {
+    mOutputQueue.pop();
+  }
 }
 
-void
-VideoDecoder::DrainTask()
+Status
+VideoDecoder::Drain(VideoFrame* aVideoFrame)
 {
+  CK_LOGD("VideoDecoder::Drain()");
+
+  if (!mDecoder) {
+    CK_LOGD("Drain failed! Decoder was not initialized");
+    return Status::kDecodeError;
+  }
+
   mDecoder->Drain();
 
   // Return any pending output.
-  HRESULT hr = S_OK;
-  while (hr == S_OK) {
-    CComPtr<IMFSample> output;
-    hr = mDecoder->Output(&output);
-    CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr);
-    if (hr == S_OK) {
-      MaybeRunOnMainThread(
-        WrapTaskRefCounted(this,
-                           &VideoDecoder::ReturnOutput,
-                           CComPtr<IMFSample>(output),
-                           mDecoder->GetFrameWidth(),
-                           mDecoder->GetFrameHeight(),
-                           mDecoder->GetStride()));
-    }
-  }
-  MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
-}
-
-void
-VideoDecoder::Drain()
-{
-  if (!mDecoder) {
-    if (mCallback) {
-      mCallback->DrainComplete();
-    }
-    return;
-  }
-  EnsureWorker();
-  mWorkerThread->Post(WrapTaskRefCounted(this,
-                                         &VideoDecoder::DrainTask));
+  return OutputFrame(aVideoFrame);
 }
 
 void
 VideoDecoder::DecodingComplete()
 {
-  if (mWorkerThread) {
-    mWorkerThread->Join();
-  }
+  CK_LOGD("VideoDecoder::DecodingComplete()");
+
   mHasShutdown = true;
 
   // Release the reference we added in the constructor. There may be
   // WrapRefCounted tasks that also hold references to us, and keep
   // us alive a little longer.
   Release();
 }
-
-void
-VideoDecoder::MaybeRunOnMainThread(GMPTask* aTask)
-{
-  class MaybeRunTask : public GMPTask
-  {
-  public:
-    MaybeRunTask(VideoDecoder* aDecoder, GMPTask* aTask)
-      : mDecoder(aDecoder), mTask(aTask)
-    { }
-
-    virtual void Run(void) {
-      if (mDecoder->HasShutdown()) {
-        CK_LOGD("Trying to dispatch to main thread after VideoDecoder has shut down");
-        return;
-      }
-
-      mTask->Run();
-    }
-
-    virtual void Destroy()
-    {
-      mTask->Destroy();
-      delete this;
-    }
-
-  private:
-    RefPtr<VideoDecoder> mDecoder;
-    GMPTask* mTask;
-  };
-
-  GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
-}
--- a/media/gmp-clearkey/0.1/VideoDecoder.h
+++ b/media/gmp-clearkey/0.1/VideoDecoder.h
@@ -13,98 +13,62 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __VideoDecoder_h__
 #define __VideoDecoder_h__
 
 #include <atomic>
+#include <queue>
+#include <thread>
 
-#include "gmp-task-utils.h"
-#include "gmp-video-decode.h"
-#include "gmp-video-host.h"
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
 #include "WMFH264Decoder.h"
 
-#include "mfobjects.h"
-
-class VideoDecoder : public GMPVideoDecoder
-                   , public RefCounted
+class VideoDecoder : public RefCounted
 {
 public:
-  explicit VideoDecoder(GMPVideoHost *aHostAPI);
+  explicit VideoDecoder(cdm::Host_8 *aHost);
 
-  virtual void InitDecode(const GMPVideoCodec& aCodecSettings,
-                          const uint8_t* aCodecSpecific,
-                          uint32_t aCodecSpecificLength,
-                          GMPVideoDecoderCallback* aCallback,
-                          int32_t aCoreCount) override;
+  cdm::Status InitDecode(const cdm::VideoDecoderConfig& aConfig);
 
-  virtual void Decode(GMPVideoEncodedFrame* aInputFrame,
-                      bool aMissingFrames,
-                      const uint8_t* aCodecSpecific,
-                      uint32_t aCodecSpecificLength,
-                      int64_t aRenderTimeMs = -1);
+  cdm::Status Decode(const cdm::InputBuffer& aEncryptedBuffer,
+                     cdm::VideoFrame* aVideoFrame);
 
-  virtual void Reset() override;
+  void Reset();
 
-  virtual void Drain() override;
-
-  virtual void DecodingComplete() override;
+  void DecodingComplete();
 
   bool HasShutdown() { return mHasShutdown; }
 
 private:
 
   virtual ~VideoDecoder();
 
-  void EnsureWorker();
-
-  void DrainTask();
+  cdm::Status Drain(cdm::VideoFrame* aVideoFrame);
 
   struct DecodeData {
-    DecodeData()
-      : mTimestamp(0)
-      , mDuration(0)
-      , mIsKeyframe(false)
-    {}
     std::vector<uint8_t> mBuffer;
-    uint64_t mTimestamp;
-    uint64_t mDuration;
-    bool mIsKeyframe;
+    uint64_t mTimestamp = 0;
     CryptoMetaData mCrypto;
   };
 
-  void DecodeTask(DecodeData* aData);
-
-  void ResetCompleteTask();
-
-  void ReturnOutput(IMFSample* aSample,
-                    int32_t aWidth,
-                    int32_t aHeight,
-                    int32_t aStride);
+  cdm::Status OutputFrame(cdm::VideoFrame* aVideoFrame);
 
   HRESULT SampleToVideoFrame(IMFSample* aSample,
                              int32_t aWidth,
                              int32_t aHeight,
                              int32_t aStride,
-                             GMPVideoi420Frame* aVideoFrame);
-
-  void MaybeRunOnMainThread(GMPTask* aTask);
+                             cdm::VideoFrame* aVideoFrame);
 
-  GMPVideoHost *mHostAPI; // host-owned, invalid at DecodingComplete
-  GMPVideoDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
-  GMPThread* mWorkerThread;
-  GMPMutex* mMutex;
+  cdm::Host_8* mHost;
   wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder;
 
-  std::vector<uint8_t> mExtraData;
-  std::vector<uint8_t> mAnnexB;
-
-  int32_t mNumInputTasks;
-  bool mSentExtraData;
-
-  std::atomic<bool> mIsFlushing;
+  std::queue<wmf::CComPtr<IMFSample>> mOutputQueue;
 
   bool mHasShutdown;
 };
 
 #endif // __VideoDecoder_h__
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
@@ -191,17 +191,16 @@ WMFH264Decoder::SendMFTMessage(MFT_MESSA
   ENSURE(SUCCEEDED(hr), hr);
   return S_OK;
 }
 
 HRESULT
 WMFH264Decoder::CreateInputSample(const uint8_t* aData,
                                   uint32_t aDataSize,
                                   Microseconds aTimestamp,
-                                  Microseconds aDuration,
                                   IMFSample** aOutSample)
 {
   HRESULT hr;
   CComPtr<IMFSample> sample;
   hr = MFCreateSample(&sample);
   ENSURE(SUCCEEDED(hr), hr);
 
   CComPtr<IMFMediaBuffer> buffer;
@@ -226,18 +225,16 @@ WMFH264Decoder::CreateInputSample(const 
   ENSURE(SUCCEEDED(hr), hr);
 
   hr = sample->AddBuffer(buffer);
   ENSURE(SUCCEEDED(hr), hr);
 
   hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
   ENSURE(SUCCEEDED(hr), hr);
 
-  sample->SetSampleDuration(UsecsToHNs(aDuration));
-
   *aOutSample = sample.Detach();
 
   return S_OK;
 }
 
 HRESULT
 WMFH264Decoder::CreateOutputSample(IMFSample** aOutSample)
 {
@@ -296,22 +293,21 @@ WMFH264Decoder::GetOutputSample(IMFSampl
   // output.pSample
   *aOutSample = sample.Detach(); // AddRefs
   return S_OK;
 }
 
 HRESULT
 WMFH264Decoder::Input(const uint8_t* aData,
                       uint32_t aDataSize,
-                      Microseconds aTimestamp,
-                      Microseconds aDuration)
+                      Microseconds aTimestamp)
 {
   HRESULT hr;
   CComPtr<IMFSample> input = nullptr;
-  hr = CreateInputSample(aData, aDataSize, aTimestamp, aDuration, &input);
+  hr = CreateInputSample(aData, aDataSize, aTimestamp, &input);
   ENSURE(SUCCEEDED(hr) && input!=nullptr, hr);
 
   hr = mDecoder->ProcessInput(0, input, 0);
   if (hr == MF_E_NOTACCEPTING) {
     // MFT *already* has enough data to produce a sample. Retrieve it.
     LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
     return MF_E_NOTACCEPTING;
   }
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.h
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.h
@@ -25,18 +25,17 @@ class WMFH264Decoder {
 public:
   WMFH264Decoder();
   ~WMFH264Decoder();
 
   HRESULT Init(int32_t aCoreCount);
 
   HRESULT Input(const uint8_t* aData,
                 uint32_t aDataSize,
-                Microseconds aTimestamp,
-                Microseconds aDuration);
+                Microseconds aTimestamp);
 
   HRESULT Output(IMFSample** aOutput);
 
   HRESULT Reset();
 
   int32_t GetFrameWidth() const;
   int32_t GetFrameHeight() const;
   const IntRect& GetPictureRegion() const;
@@ -48,17 +47,16 @@ private:
 
   HRESULT SetDecoderInputType();
   HRESULT SetDecoderOutputType();
   HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData);
 
   HRESULT CreateInputSample(const uint8_t* aData,
                             uint32_t aDataSize,
                             Microseconds aTimestamp,
-                            Microseconds aDuration,
                             IMFSample** aOutSample);
 
   HRESULT CreateOutputSample(IMFSample** aOutSample);
 
   HRESULT GetOutputSample(IMFSample** aOutSample);
   HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType);
 
   MFT_INPUT_STREAM_INFO mInputStreamInfo;
--- a/media/gmp-clearkey/0.1/WMFUtils.h
+++ b/media/gmp-clearkey/0.1/WMFUtils.h
@@ -114,18 +114,18 @@ typedef int64_t Microseconds;
 
 #ifdef ENSURE
 #undef ENSURE
 #endif
 
 #define ENSURE(condition, ret) \
 { if (!(condition)) { LOG("##condition## FAILED %S:%d\n", __FILE__, __LINE__); return ret; } }
 
-#define GMP_SUCCEEDED(x) ((x) == GMPNoErr)
-#define GMP_FAILED(x) ((x) != GMPNoErr)
+#define STATUS_SUCCEEDED(x) ((x) == Status::kSuccess)
+#define STATUS_FAILED(x) ((x) != Status::kSuccess)
 
 #define MFPLAT_FUNC(_func, _dllname) \
   extern decltype(::_func)* _func;
 #include "WMFSymbols.h"
 #undef MFPLAT_FUNC
 
 bool
 EnsureLibs();
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/clearkey.info.in
+++ /dev/null
@@ -1,10 +0,0 @@
-Name: clearkey
-Description: ClearKey Gecko Media Plugin
-Version: 1
-#ifdef ENABLE_WMF
-APIs: eme-decrypt-v9[org.w3.clearkey], decode-video[h264:org.w3.clearkey]
-Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll, evr.dll, mfheaacdec.dll, mfh264dec.dll, mfplat.dll
-#else
-APIs: eme-decrypt-v9[org.w3.clearkey]
-Libraries:
-#endif
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -13,73 +13,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
-#include "ClearKeyAsyncShutdown.h"
+#include "ClearKeyCDM.h"
 #include "ClearKeySessionManager.h"
-#include "gmp-api/gmp-async-shutdown.h"
-#include "gmp-api/gmp-decryption.h"
-#include "gmp-api/gmp-platform.h"
-
-#if defined(ENABLE_WMF)
-#include "WMFUtils.h"
-#include "VideoDecoder.h"
-#endif
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include "stddef.h"
+#include "content_decryption_module.h"
 
-#if defined(WIN32)
-#define GMP_EXPORT __declspec(dllexport)
-#else
-#define GMP_EXPORT __attribute__((visibility("default")))
-#endif
-
-static GMPPlatformAPI* sPlatform = nullptr;
-GMPPlatformAPI*
-GetPlatform()
-{
-  return sPlatform;
-}
+#ifdef ENABLE_WMF
+#include "WMFUtils.h"
+#endif // ENABLE_WMF
 
 extern "C" {
 
-GMP_EXPORT GMPErr
-GMPInit(GMPPlatformAPI* aPlatformAPI)
-{
-  sPlatform = aPlatformAPI;
-  return GMPNoErr;
+CDM_EXPORT
+void INITIALIZE_CDM_MODULE() {
+
 }
 
-GMP_EXPORT GMPErr
-GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
+CDM_EXPORT
+void* CreateCdmInstance(int cdm_interface_version,
+                        const char* key_system,
+                        uint32_t key_system_size,
+                        GetCdmHostFunc get_cdm_host_func,
+                        void* user_data)
 {
-  CK_LOGD("ClearKey GMPGetAPI |%s|", aApiName);
-  assert(!*aPluginAPI);
 
-  if (!strcmp(aApiName, GMP_API_DECRYPTOR)) {
-    *aPluginAPI = new ClearKeySessionManager();
-  }
-#if defined(ENABLE_WMF)
- else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER) &&
-             wmf::EnsureLibs()) {
-    *aPluginAPI = new VideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
+  CK_LOGE("ClearKey CreateCDMInstance");
+
+#ifdef ENABLE_WMF
+  if (!wmf::EnsureLibs()) {
+    CK_LOGE("Required libraries were not found");
+    return nullptr;
   }
 #endif
-  else if (!strcmp(aApiName, GMP_API_ASYNC_SHUTDOWN)) {
-    *aPluginAPI = new ClearKeyAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
-  } else {
-    CK_LOGE("GMPGetAPI couldn't resolve API name |%s|\n", aApiName);
-  }
 
-  return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
-}
+  cdm::Host_8* host = static_cast<cdm::Host_8*>(
+    get_cdm_host_func(cdm_interface_version, user_data));
+  ClearKeyCDM* clearKey = new ClearKeyCDM(host);
 
-GMP_EXPORT GMPErr
-GMPShutdown(void)
-{
-  CK_LOGD("ClearKey GMPShutdown");
-  return GMPNoErr;
+  CK_LOGE("Created ClearKeyCDM instance!");
+
+  return clearKey;
 }
-
 }
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/gmp-task-utils-generated.h
+++ /dev/null
@@ -1,1938 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RefCounted.h"
-
-// 0 arguments --
-template<typename M> class gmp_task_args_nm_0 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_0(M m) :
-    m_(m)  {}
-
-  void Run() {
-    m_();
-  }
-
- private:
-  M m_;
-};
-
-
-
-// 0 arguments --
-template<typename M, typename R> class gmp_task_args_nm_0_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_0_ret(M m, R *r) :
-    m_(m), r_(r)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_();
-  }
-
- private:
-  M m_;
-  R* r_;
-};
-
-
-
-// 0 arguments --
-template<typename C, typename M> class gmp_task_args_m_0 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_0(C o, M m) :
-    o_(o), m_(m)  {}
-
-  void Run() {
-    ((*o_).*m_)();
-  }
-
- private:
-  C o_;
-  M m_;
-};
-
-
-
-// 0 arguments --
-template<typename C, typename M, typename R> class gmp_task_args_m_0_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_0_ret(C o, M m, R *r) :
-    o_(o), m_(m), r_(r)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)();
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-};
-
-
-
-// 1 arguments --
-template<typename M, typename A0> class gmp_task_args_nm_1 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_1(M m, A0 a0) :
-    m_(m), a0_(a0)  {}
-
-  void Run() {
-    m_(a0_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-};
-
-
-
-// 1 arguments --
-template<typename M, typename A0, typename R> class gmp_task_args_nm_1_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_1_ret(M m, A0 a0, R *r) :
-    m_(m), r_(r), a0_(a0)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-};
-
-
-
-// 1 arguments --
-template<typename C, typename M, typename A0> class gmp_task_args_m_1 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_1(C o, M m, A0 a0) :
-    o_(o), m_(m), a0_(a0)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-};
-
-
-
-// 1 arguments --
-template<typename C, typename M, typename A0, typename R> class gmp_task_args_m_1_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_1_ret(C o, M m, A0 a0, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-};
-
-
-
-// 2 arguments --
-template<typename M, typename A0, typename A1> class gmp_task_args_nm_2 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_2(M m, A0 a0, A1 a1) :
-    m_(m), a0_(a0), a1_(a1)  {}
-
-  void Run() {
-    m_(a0_, a1_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-};
-
-
-
-// 2 arguments --
-template<typename M, typename A0, typename A1, typename R> class gmp_task_args_nm_2_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_2_ret(M m, A0 a0, A1 a1, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-};
-
-
-
-// 2 arguments --
-template<typename C, typename M, typename A0, typename A1> class gmp_task_args_m_2 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_2(C o, M m, A0 a0, A1 a1) :
-    o_(o), m_(m), a0_(a0), a1_(a1)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-};
-
-
-
-// 2 arguments --
-template<typename C, typename M, typename A0, typename A1, typename R> class gmp_task_args_m_2_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_2_ret(C o, M m, A0 a0, A1 a1, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-};
-
-
-
-// 3 arguments --
-template<typename M, typename A0, typename A1, typename A2> class gmp_task_args_nm_3 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_3(M m, A0 a0, A1 a1, A2 a2) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-};
-
-
-
-// 3 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename R> class gmp_task_args_nm_3_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_3_ret(M m, A0 a0, A1 a1, A2 a2, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-};
-
-
-
-// 3 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2> class gmp_task_args_m_3 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_3(C o, M m, A0 a0, A1 a1, A2 a2) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-};
-
-
-
-// 3 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename R> class gmp_task_args_m_3_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_3_ret(C o, M m, A0 a0, A1 a1, A2 a2, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-};
-
-
-
-// 4 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3> class gmp_task_args_nm_4 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_4(M m, A0 a0, A1 a1, A2 a2, A3 a3) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-};
-
-
-
-// 4 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename R> class gmp_task_args_nm_4_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_4_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-};
-
-
-
-// 4 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3> class gmp_task_args_m_4 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_4(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-};
-
-
-
-// 4 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R> class gmp_task_args_m_4_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_4_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-};
-
-
-
-// 5 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class gmp_task_args_nm_5 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_5(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-};
-
-
-
-// 5 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class gmp_task_args_nm_5_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_5_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-};
-
-
-
-// 5 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class gmp_task_args_m_5 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_5(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-};
-
-
-
-// 5 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class gmp_task_args_m_5_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_5_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-};
-
-
-
-// 6 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class gmp_task_args_nm_6 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_6(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-};
-
-
-
-// 6 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class gmp_task_args_nm_6_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_6_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-};
-
-
-
-// 6 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class gmp_task_args_m_6 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_6(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-};
-
-
-
-// 6 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class gmp_task_args_m_6_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_6_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-};
-
-
-
-// 7 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class gmp_task_args_nm_7 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_7(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-};
-
-
-
-// 7 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class gmp_task_args_nm_7_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_7_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-};
-
-
-
-// 7 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class gmp_task_args_m_7 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_7(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-};
-
-
-
-// 7 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class gmp_task_args_m_7_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_7_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-};
-
-
-
-// 8 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class gmp_task_args_nm_8 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_8(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-};
-
-
-
-// 8 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class gmp_task_args_nm_8_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_8_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-};
-
-
-
-// 8 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class gmp_task_args_m_8 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_8(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-};
-
-
-
-// 8 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class gmp_task_args_m_8_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_8_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-};
-
-
-
-// 9 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class gmp_task_args_nm_9 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_9(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-};
-
-
-
-// 9 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class gmp_task_args_nm_9_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_9_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-};
-
-
-
-// 9 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class gmp_task_args_m_9 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_9(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-};
-
-
-
-// 9 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class gmp_task_args_m_9_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_9_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-};
-
-
-
-// 10 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> class gmp_task_args_nm_10 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_10(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-};
-
-
-
-// 10 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> class gmp_task_args_nm_10_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_10_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-};
-
-
-
-// 10 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> class gmp_task_args_m_10 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_10(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-};
-
-
-
-// 10 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> class gmp_task_args_m_10_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_10_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-};
-
-
-
-// 11 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> class gmp_task_args_nm_11 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_11(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-};
-
-
-
-// 11 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> class gmp_task_args_nm_11_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_11_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-};
-
-
-
-// 11 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> class gmp_task_args_m_11 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_11(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-};
-
-
-
-// 11 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> class gmp_task_args_m_11_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_11_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-};
-
-
-
-// 12 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> class gmp_task_args_nm_12 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_12(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-};
-
-
-
-// 12 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> class gmp_task_args_nm_12_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_12_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-};
-
-
-
-// 12 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> class gmp_task_args_m_12 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_12(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-};
-
-
-
-// 12 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> class gmp_task_args_m_12_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_12_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-};
-
-
-
-// 13 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> class gmp_task_args_nm_13 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_13(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-};
-
-
-
-// 13 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> class gmp_task_args_nm_13_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_13_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-};
-
-
-
-// 13 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> class gmp_task_args_m_13 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_13(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-};
-
-
-
-// 13 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> class gmp_task_args_m_13_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_13_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-};
-
-
-
-// 14 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> class gmp_task_args_nm_14 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_14(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) :
-    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
-
-  void Run() {
-    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
-  }
-
- private:
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-  A13 a13_;
-};
-
-
-
-// 14 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> class gmp_task_args_nm_14_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_nm_14_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R *r) :
-    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
-  }
-
- private:
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-  A13 a13_;
-};
-
-
-
-// 14 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> class gmp_task_args_m_14 : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_14(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) :
-    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
-
-  void Run() {
-    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
-  }
-
- private:
-  C o_;
-  M m_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-  A13 a13_;
-};
-
-
-
-// 14 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> class gmp_task_args_m_14_ret : public gmp_task_args_base {
- public:
-  explicit gmp_task_args_m_14_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R *r) :
-    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
-  virtual bool returns_value() const { return true; }
-
-  void Run() {
-    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
-  }
-
- private:
-  C o_;
-  M m_;
-  R* r_;
-  A0 a0_;
-  A1 a1_;
-  A2 a2_;
-  A3 a3_;
-  A4 a4_;
-  A5 a5_;
-  A6 a6_;
-  A7 a7_;
-  A8 a8_;
-  A9 a9_;
-  A10 a10_;
-  A11 a11_;
-  A12 a12_;
-  A13 a13_;
-};
-
-
-
-
-
-
-// 0 arguments --
-template<typename M>
-gmp_task_args_nm_0<M>* WrapTaskNM(M m) {
-  return new gmp_task_args_nm_0<M>
-    (m);
-}
-
-// 0 arguments --
-template<typename M, typename R>
-gmp_task_args_nm_0_ret<M, R>* WrapTaskNMRet(M m, R* r) {
-  return new gmp_task_args_nm_0_ret<M, R>
-    (m, r);
-}
-
-// 0 arguments --
-template<typename C, typename M>
-gmp_task_args_m_0<C, M>* WrapTask(C o, M m) {
-  return new gmp_task_args_m_0<C, M>
-    (o, m);
-}
-
-// 0 arguments --
-template<typename C, typename M, typename R>
-gmp_task_args_m_0_ret<C, M, R>* WrapTaskRet(C o, M m, R* r) {
-  return new gmp_task_args_m_0_ret<C, M, R>
-    (o, m, r);
-}
-
-// 1 arguments --
-template<typename M, typename A0>
-gmp_task_args_nm_1<M, A0>* WrapTaskNM(M m, A0 a0) {
-  return new gmp_task_args_nm_1<M, A0>
-    (m, a0);
-}
-
-// 1 arguments --
-template<typename M, typename A0, typename R>
-gmp_task_args_nm_1_ret<M, A0, R>* WrapTaskNMRet(M m, A0 a0, R* r) {
-  return new gmp_task_args_nm_1_ret<M, A0, R>
-    (m, a0, r);
-}
-
-// 1 arguments --
-template<typename C, typename M, typename A0>
-gmp_task_args_m_1<C, M, A0>* WrapTask(C o, M m, A0 a0) {
-  return new gmp_task_args_m_1<C, M, A0>
-    (o, m, a0);
-}
-
-// 1 arguments --
-template<typename C, typename M, typename A0, typename R>
-gmp_task_args_m_1_ret<C, M, A0, R>* WrapTaskRet(C o, M m, A0 a0, R* r) {
-  return new gmp_task_args_m_1_ret<C, M, A0, R>
-    (o, m, a0, r);
-}
-
-// 2 arguments --
-template<typename M, typename A0, typename A1>
-gmp_task_args_nm_2<M, A0, A1>* WrapTaskNM(M m, A0 a0, A1 a1) {
-  return new gmp_task_args_nm_2<M, A0, A1>
-    (m, a0, a1);
-}
-
-// 2 arguments --
-template<typename M, typename A0, typename A1, typename R>
-gmp_task_args_nm_2_ret<M, A0, A1, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, R* r) {
-  return new gmp_task_args_nm_2_ret<M, A0, A1, R>
-    (m, a0, a1, r);
-}
-
-// 2 arguments --
-template<typename C, typename M, typename A0, typename A1>
-gmp_task_args_m_2<C, M, A0, A1>* WrapTask(C o, M m, A0 a0, A1 a1) {
-  return new gmp_task_args_m_2<C, M, A0, A1>
-    (o, m, a0, a1);
-}
-
-// 2 arguments --
-template<typename C, typename M, typename A0, typename A1, typename R>
-gmp_task_args_m_2_ret<C, M, A0, A1, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, R* r) {
-  return new gmp_task_args_m_2_ret<C, M, A0, A1, R>
-    (o, m, a0, a1, r);
-}
-
-// 3 arguments --
-template<typename M, typename A0, typename A1, typename A2>
-gmp_task_args_nm_3<M, A0, A1, A2>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2) {
-  return new gmp_task_args_nm_3<M, A0, A1, A2>
-    (m, a0, a1, a2);
-}
-
-// 3 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename R>
-gmp_task_args_nm_3_ret<M, A0, A1, A2, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, R* r) {
-  return new gmp_task_args_nm_3_ret<M, A0, A1, A2, R>
-    (m, a0, a1, a2, r);
-}
-
-// 3 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2>
-gmp_task_args_m_3<C, M, A0, A1, A2>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2) {
-  return new gmp_task_args_m_3<C, M, A0, A1, A2>
-    (o, m, a0, a1, a2);
-}
-
-// 3 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename R>
-gmp_task_args_m_3_ret<C, M, A0, A1, A2, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, R* r) {
-  return new gmp_task_args_m_3_ret<C, M, A0, A1, A2, R>
-    (o, m, a0, a1, a2, r);
-}
-
-// 4 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3>
-gmp_task_args_nm_4<M, A0, A1, A2, A3>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3) {
-  return new gmp_task_args_nm_4<M, A0, A1, A2, A3>
-    (m, a0, a1, a2, a3);
-}
-
-// 4 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename R>
-gmp_task_args_nm_4_ret<M, A0, A1, A2, A3, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) {
-  return new gmp_task_args_nm_4_ret<M, A0, A1, A2, A3, R>
-    (m, a0, a1, a2, a3, r);
-}
-
-// 4 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3>
-gmp_task_args_m_4<C, M, A0, A1, A2, A3>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) {
-  return new gmp_task_args_m_4<C, M, A0, A1, A2, A3>
-    (o, m, a0, a1, a2, a3);
-}
-
-// 4 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R>
-gmp_task_args_m_4_ret<C, M, A0, A1, A2, A3, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) {
-  return new gmp_task_args_m_4_ret<C, M, A0, A1, A2, A3, R>
-    (o, m, a0, a1, a2, a3, r);
-}
-
-// 5 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4>
-gmp_task_args_nm_5<M, A0, A1, A2, A3, A4>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) {
-  return new gmp_task_args_nm_5<M, A0, A1, A2, A3, A4>
-    (m, a0, a1, a2, a3, a4);
-}
-
-// 5 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R>
-gmp_task_args_nm_5_ret<M, A0, A1, A2, A3, A4, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) {
-  return new gmp_task_args_nm_5_ret<M, A0, A1, A2, A3, A4, R>
-    (m, a0, a1, a2, a3, a4, r);
-}
-
-// 5 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4>
-gmp_task_args_m_5<C, M, A0, A1, A2, A3, A4>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) {
-  return new gmp_task_args_m_5<C, M, A0, A1, A2, A3, A4>
-    (o, m, a0, a1, a2, a3, a4);
-}
-
-// 5 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R>
-gmp_task_args_m_5_ret<C, M, A0, A1, A2, A3, A4, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) {
-  return new gmp_task_args_m_5_ret<C, M, A0, A1, A2, A3, A4, R>
-    (o, m, a0, a1, a2, a3, a4, r);
-}
-
-// 6 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
-gmp_task_args_nm_6<M, A0, A1, A2, A3, A4, A5>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
-  return new gmp_task_args_nm_6<M, A0, A1, A2, A3, A4, A5>
-    (m, a0, a1, a2, a3, a4, a5);
-}
-
-// 6 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R>
-gmp_task_args_nm_6_ret<M, A0, A1, A2, A3, A4, A5, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) {
-  return new gmp_task_args_nm_6_ret<M, A0, A1, A2, A3, A4, A5, R>
-    (m, a0, a1, a2, a3, a4, a5, r);
-}
-
-// 6 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
-gmp_task_args_m_6<C, M, A0, A1, A2, A3, A4, A5>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
-  return new gmp_task_args_m_6<C, M, A0, A1, A2, A3, A4, A5>
-    (o, m, a0, a1, a2, a3, a4, a5);
-}
-
-// 6 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R>
-gmp_task_args_m_6_ret<C, M, A0, A1, A2, A3, A4, A5, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) {
-  return new gmp_task_args_m_6_ret<C, M, A0, A1, A2, A3, A4, A5, R>
-    (o, m, a0, a1, a2, a3, a4, a5, r);
-}
-
-// 7 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
-gmp_task_args_nm_7<M, A0, A1, A2, A3, A4, A5, A6>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
-  return new gmp_task_args_nm_7<M, A0, A1, A2, A3, A4, A5, A6>
-    (m, a0, a1, a2, a3, a4, a5, a6);
-}
-
-// 7 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R>
-gmp_task_args_nm_7_ret<M, A0, A1, A2, A3, A4, A5, A6, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) {
-  return new gmp_task_args_nm_7_ret<M, A0, A1, A2, A3, A4, A5, A6, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, r);
-}
-
-// 7 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
-gmp_task_args_m_7<C, M, A0, A1, A2, A3, A4, A5, A6>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
-  return new gmp_task_args_m_7<C, M, A0, A1, A2, A3, A4, A5, A6>
-    (o, m, a0, a1, a2, a3, a4, a5, a6);
-}
-
-// 7 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R>
-gmp_task_args_m_7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) {
-  return new gmp_task_args_m_7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, r);
-}
-
-// 8 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
-gmp_task_args_nm_8<M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
-  return new gmp_task_args_nm_8<M, A0, A1, A2, A3, A4, A5, A6, A7>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7);
-}
-
-// 8 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R>
-gmp_task_args_nm_8_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) {
-  return new gmp_task_args_nm_8_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, r);
-}
-
-// 8 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
-gmp_task_args_m_8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
-  return new gmp_task_args_m_8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7);
-}
-
-// 8 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R>
-gmp_task_args_m_8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) {
-  return new gmp_task_args_m_8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, r);
-}
-
-// 9 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
-gmp_task_args_nm_9<M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
-  return new gmp_task_args_nm_9<M, A0, A1, A2, A3, A4, A5, A6, A7, A8>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8);
-}
-
-// 9 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R>
-gmp_task_args_nm_9_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) {
-  return new gmp_task_args_nm_9_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r);
-}
-
-// 9 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
-gmp_task_args_m_9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
-  return new gmp_task_args_m_9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8);
-}
-
-// 9 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R>
-gmp_task_args_m_9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) {
-  return new gmp_task_args_m_9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r);
-}
-
-// 10 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
-gmp_task_args_nm_10<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
-  return new gmp_task_args_nm_10<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
-}
-
-// 10 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R>
-gmp_task_args_nm_10_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R* r) {
-  return new gmp_task_args_nm_10_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, r);
-}
-
-// 10 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
-gmp_task_args_m_10<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
-  return new gmp_task_args_m_10<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
-}
-
-// 10 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R>
-gmp_task_args_m_10_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R* r) {
-  return new gmp_task_args_m_10_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, r);
-}
-
-// 11 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
-gmp_task_args_nm_11<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) {
-  return new gmp_task_args_nm_11<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
-}
-
-// 11 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R>
-gmp_task_args_nm_11_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R* r) {
-  return new gmp_task_args_nm_11_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, r);
-}
-
-// 11 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
-gmp_task_args_m_11<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) {
-  return new gmp_task_args_m_11<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
-}
-
-// 11 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R>
-gmp_task_args_m_11_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R* r) {
-  return new gmp_task_args_m_11_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, r);
-}
-
-// 12 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
-gmp_task_args_nm_12<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) {
-  return new gmp_task_args_nm_12<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
-}
-
-// 12 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R>
-gmp_task_args_nm_12_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R* r) {
-  return new gmp_task_args_nm_12_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, r);
-}
-
-// 12 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
-gmp_task_args_m_12<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) {
-  return new gmp_task_args_m_12<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
-}
-
-// 12 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R>
-gmp_task_args_m_12_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R* r) {
-  return new gmp_task_args_m_12_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, r);
-}
-
-// 13 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
-gmp_task_args_nm_13<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) {
-  return new gmp_task_args_nm_13<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
-}
-
-// 13 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R>
-gmp_task_args_nm_13_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R* r) {
-  return new gmp_task_args_nm_13_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, r);
-}
-
-// 13 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
-gmp_task_args_m_13<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) {
-  return new gmp_task_args_m_13<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
-}
-
-// 13 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R>
-gmp_task_args_m_13_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R* r) {
-  return new gmp_task_args_m_13_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, r);
-}
-
-// 14 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
-gmp_task_args_nm_14<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) {
-  return new gmp_task_args_nm_14<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
-}
-
-// 14 arguments --
-template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R>
-gmp_task_args_nm_14_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R* r) {
-  return new gmp_task_args_nm_14_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>
-    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, r);
-}
-
-// 14 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
-gmp_task_args_m_14<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) {
-  return new gmp_task_args_m_14<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
-}
-
-// 14 arguments --
-template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R>
-gmp_task_args_m_14_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R* r) {
-  return new gmp_task_args_m_14_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>
-    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, r);
-}
-
-class RefCountTaskWrapper : public gmp_task_args_base {
-public:
-  RefCountTaskWrapper(GMPTask* aTask, RefCounted* aRefCounted)
-    : mTask(aTask)
-    , mRefCounted(aRefCounted)
-  {}
-  virtual void Run() override {
-    mTask->Run();
-  }
-  virtual void Destroy() override {
-    mTask->Destroy();
-    gmp_task_args_base::Destroy();
-  }
-private:
-  ~RefCountTaskWrapper() {}
-
-  GMPTask* mTask;
-  RefPtr<RefCounted> mRefCounted;
-};
-
-template<typename Type, typename Method, typename... Args>
-GMPTask*
-WrapTaskRefCounted(Type* aType, Method aMethod, Args&&... args)
-{
-  GMPTask* t = WrapTask(aType, aMethod, std::forward<Args>(args)...);
-  return new RefCountTaskWrapper(t, aType);
-}
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/gmp-task-utils.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Original author: ekr@rtfm.com
-
-#ifndef gmp_task_utils_h_
-#define gmp_task_utils_h_
-
-#include "gmp-api/gmp-platform.h"
-
-class gmp_task_args_base : public GMPTask {
-public:
-  virtual void Destroy() { delete this; }
-  virtual void Run() = 0;
-};
-
-// The generated file contains four major function templates
-// (in variants for arbitrary numbers of arguments up to 10,
-// which is why it is machine generated). The four templates
-// are:
-//
-// WrapTask(o, m, ...) -- wraps a member function m of an object ptr o
-// WrapTaskRet(o, m, ..., r) -- wraps a member function m of an object ptr o
-//                                  the function returns something that can
-//                                  be assigned to *r
-// WrapTaskNM(f, ...) -- wraps a function f
-// WrapTaskNMRet(f, ..., r) -- wraps a function f that returns something
-//                                 that can be assigned to *r
-//
-// All of these template functions return a GMPTask* which can be passed
-// to DispatchXX().
-#include "gmp-task-utils-generated.h"
-
-#endif // gmp_task_utils_h_
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/manifest.json.in
@@ -0,0 +1,13 @@
+{
+    "name": "clearkey",
+    "description": "ClearKey Gecko Media Plugin",
+    "version": "1",
+    "x-cdm-module-versions": "4",
+    "x-cdm-interface-versions": "8",
+    "x-cdm-host-versions": "8",
+#ifdef ENABLE_WMF
+    "x-cdm-codecs": "avc1"
+#else
+    "x-cdm-codecs": ""
+#endif
+}
\ No newline at end of file
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -3,60 +3,57 @@
 # 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/.
 
 SharedLibrary('clearkey')
 
 FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
 
-FINAL_TARGET_PP_FILES += ['clearkey.info.in']
+FINAL_TARGET_PP_FILES += ['manifest.json.in']
 
 UNIFIED_SOURCES += [
-    'ClearKeyAsyncShutdown.cpp',
     'ClearKeyBase64.cpp',
+    'ClearKeyCDM.cpp',
     'ClearKeyDecryptionManager.cpp',
     'ClearKeyPersistence.cpp',
     'ClearKeySession.cpp',
     'ClearKeySessionManager.cpp',
     'ClearKeyStorage.cpp',
     'ClearKeyUtils.cpp',
     'gmp-clearkey.cpp',
 ]
 
 SOURCES += [
     'openaes/oaes_lib.c',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     UNIFIED_SOURCES += [
-        'AnnexB.cpp',
         'VideoDecoder.cpp',
         'WMFH264Decoder.cpp',
     ]
 
     SOURCES += [
         'WMFUtils.cpp',
     ]
 
     OS_LIBS += [
         'mfuuid',
     ]
 
     DEFINES['ENABLE_WMF'] = True
 
+
+DEFINES['CDM_IMPLEMENTATION'] = True
+
 TEST_DIRS += [
     'gtest',
 ]
 
-
-LOCAL_INCLUDES += [
-    '/dom/media/gmp',
-]
-
 DISABLE_STL_WRAPPING = True
 DEFINES['MOZ_NO_MOZALLOC'] = True
 
 USE_LIBS += ['psshparser']
 
 # Suppress warnings in third-party code.
 if CONFIG['GNU_CXX']:
     CFLAGS += [
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -115,22 +115,24 @@ WebrtcVideoConduit::SendStreamStatistics
 {
       aOutDroppedFrames = mDroppedFrames;
 };
 
 void
 WebrtcVideoConduit::SendStreamStatistics::Update(
   const webrtc::VideoSendStream::Stats& aStats)
 {
-  CSFLogVerbose(logTag, "SendStreamStatistics::Update %s", __FUNCTION__);
   StreamStatistics::Update(aStats.encode_frame_rate, aStats.media_bitrate_bps);
   if (!aStats.substreams.empty()) {
     const webrtc::FrameCounts& fc =
       aStats.substreams.begin()->second.frame_counts;
-    mDroppedFrames = mSentFrames - fc.key_frames + fc.delta_frames;
+    CSFLogVerbose(logTag, "%s: framerate: %u, bitrate: %u, dropped frames delta: %u",
+                  __FUNCTION__, aStats.encode_frame_rate, aStats.media_bitrate_bps,
+                  (mSentFrames - (fc.key_frames + fc.delta_frames)) - mDroppedFrames);
+    mDroppedFrames = mSentFrames - (fc.key_frames + fc.delta_frames);
   } else {
     CSFLogVerbose(logTag, "%s aStats.substreams is empty", __FUNCTION__);
   }
 };
 
 void
 WebrtcVideoConduit::ReceiveStreamStatistics::DiscardedPackets(
   uint32_t& aOutDiscPackets) const
--- a/mobile/android/base/java/org/mozilla/gecko/db/SuggestedSites.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/SuggestedSites.java
@@ -38,16 +38,17 @@ import org.mozilla.gecko.annotation.Robo
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.Locales;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.restrictions.Restrictions;
 import org.mozilla.gecko.util.RawResource;
 import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.preferences.GeckoPreferences;
 
 /**
  * {@code SuggestedSites} provides API to get a list of locale-specific
  * suggested sites to be used in Fennec's top sites panel. It provides
  * only a single method to fetch the list as a {@code Cursor}. This cursor
  * will then be wrapped by {@code TopSitesCursorWrapper} to blend top,
  * pinned, and suggested sites in the UI. The returned {@code Cursor}
  * uses its own schema defined in {@code BrowserContract.SuggestedSites}
@@ -63,20 +64,22 @@ import org.mozilla.gecko.util.ThreadUtil
  *
  * Changes to the list of suggested sites are saved in SharedPreferences.
  */
 @RobocopTarget
 public class SuggestedSites {
     private static final String LOGTAG = "GeckoSuggestedSites";
 
     // SharedPreference key for suggested sites that should be hidden.
-    public static final String PREF_SUGGESTED_SITES_HIDDEN = "suggestedSites.hidden";
+    public static final String PREF_SUGGESTED_SITES_HIDDEN = GeckoPreferences.NON_PREF_PREFIX + "suggestedSites.hidden";
+    public static final String PREF_SUGGESTED_SITES_HIDDEN_OLD = "suggestedSites.hidden";
 
     // Locale used to generate the current suggested sites.
-    public static final String PREF_SUGGESTED_SITES_LOCALE = "suggestedSites.locale";
+    public static final String PREF_SUGGESTED_SITES_LOCALE = GeckoPreferences.NON_PREF_PREFIX + "suggestedSites.locale";
+    public static final String PREF_SUGGESTED_SITES_LOCALE_OLD = "suggestedSites.locale";
 
     // File in profile dir with the list of suggested sites.
     private static final String FILENAME = "suggestedsites.json";
 
     private static final String[] COLUMNS = new String[] {
         BrowserContract.SuggestedSites._ID,
         BrowserContract.SuggestedSites.URL,
         BrowserContract.SuggestedSites.TITLE,
@@ -177,17 +180,26 @@ public class SuggestedSites {
             cachedFile = GeckoProfile.get(context).getFile(FILENAME);
         }
         return cachedFile;
     }
 
     private static boolean isNewLocale(Context context, Locale requestedLocale) {
         final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
 
-        String locale = prefs.getString(PREF_SUGGESTED_SITES_LOCALE, null);
+        String locale = prefs.getString(PREF_SUGGESTED_SITES_LOCALE_OLD, null);
+        if (locale != null) {
+          // Migrate the old pref and remove it
+          final Editor editor = prefs.edit();
+          editor.remove(PREF_SUGGESTED_SITES_LOCALE_OLD);
+          editor.putString(PREF_SUGGESTED_SITES_LOCALE, locale);
+          editor.apply();
+        } else {
+          locale = prefs.getString(PREF_SUGGESTED_SITES_LOCALE, null);
+        }
         if (locale == null) {
             // Initialize config with the current locale
             updateSuggestedSitesLocale(context);
             return true;
         }
 
         return !TextUtils.equals(requestedLocale.toString(), locale);
     }
@@ -526,18 +538,27 @@ public class SuggestedSites {
         final Site site = getSiteForUrl(url);
         return (site != null ? site.bgColor : null);
     }
 
     private Set<String> loadBlacklist() {
         Log.d(LOGTAG, "Loading blacklisted suggested sites from SharedPreferences.");
         final Set<String> blacklist = new HashSet<String>();
 
-        final SharedPreferences preferences = GeckoSharedPrefs.forProfile(context);
-        final String sitesString = preferences.getString(PREF_SUGGESTED_SITES_HIDDEN, null);
+        final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
+        String sitesString = prefs.getString(PREF_SUGGESTED_SITES_HIDDEN_OLD, null);
+        if (sitesString != null) {
+          // Migrate the old pref and remove it
+          final Editor editor = prefs.edit();
+          editor.remove(PREF_SUGGESTED_SITES_HIDDEN_OLD);
+          editor.putString(PREF_SUGGESTED_SITES_HIDDEN, sitesString);
+          editor.apply();
+        } else {
+          sitesString = prefs.getString(PREF_SUGGESTED_SITES_HIDDEN, null);
+        }
 
         if (sitesString != null) {
             for (String site : sitesString.trim().split(" ")) {
                 blacklist.add(Uri.decode(site));
             }
         }
 
         return blacklist;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
@@ -1225,19 +1225,18 @@ final class GeckoEditable extends JNIObj
             if (indexInText < 0) {
                 // Text was changed from under us. We are forced to discard any new spans.
                 mText.currentReplace(start, oldEnd, text);
 
                 // Don't ignore the next selection change because we are forced to re-sync
                 // with Gecko here.
                 mIgnoreSelectionChange = false;
 
-            } else if (indexInText == 0 && text.length() == action.mSequence.length() &&
-                    oldEnd - start == action.mEnd - action.mStart) {
-                // The new change exactly matches our saved change, so do a direct replace.
+            } else if (indexInText == 0 && text.length() == action.mSequence.length()) {
+                // The new text exactly matches our sequence, so do a direct replace.
                 mText.currentReplace(start, oldEnd, action.mSequence);
 
                 // Ignore the next selection change because the selection change is a
                 // side-effect of the replace-text event we sent.
                 mIgnoreSelectionChange = true;
 
             } else {
                 // The sequence is embedded within the changed text, so we have to perform
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6712,17 +6712,20 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             stickyConn = mTransaction->GetConnectionReference();
         }
 
         mTransferSize = mTransaction->GetTransferSize();
 
         // If we are using the transaction to serve content, we also save the
         // time since async open in the cache entry so we can compare telemetry
         // between cache and net response.
-        if (request == mTransactionPump && mCacheEntry &&
+        // Do not store the time of conditional requests because even if we
+        // fetch the data from the server, the time includes loading of the old
+        // cache entry which would skew the network load time.
+        if (request == mTransactionPump && mCacheEntry && !mDidReval &&
             !mAsyncOpenTime.IsNull() && !mOnStartRequestTimestamp.IsNull()) {
             nsAutoCString onStartTime;
             onStartTime.AppendInt( (uint64_t) (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds());
             mCacheEntry->SetMetaDataElement("net-response-time-onstart", onStartTime.get());
 
             nsAutoCString responseTime;
             responseTime.AppendInt( (uint64_t) (TimeStamp::Now() - mAsyncOpenTime).ToMilliseconds());
             mCacheEntry->SetMetaDataElement("net-response-time-onstop", responseTime.get());
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1642,19 +1642,21 @@ toolbar#nav-bar {
 
     def buildProfile(self, options):
         """ create the profile and add optional chrome bits and files if requested """
         if options.flavor == 'browser' and options.timeout:
             options.extraPrefs.append(
                 "testing.browserTestHarness.timeout=%d" %
                 options.timeout)
         # browser-chrome tests use a fairly short default timeout of 45 seconds;
-        # this is sometimes too short on asan, where we expect reduced performance.
-        if mozinfo.info["asan"] and options.flavor == 'browser' and options.timeout is None:
-            self.log.info("Increasing default timeout to 90 seconds on ASAN")
+        # this is sometimes too short on asan and debug, where we expect reduced
+        # performance.
+        if (mozinfo.info["asan"] or mozinfo.info["debug"]) and \
+                options.flavor == 'browser' and options.timeout is None:
+            self.log.info("Increasing default timeout to 90 seconds")
             options.extraPrefs.append("testing.browserTestHarness.timeout=90")
 
         options.extraPrefs.append(
             "browser.tabs.remote.autostart=%s" %
             ('true' if options.e10s else 'false'))
         if options.strictContentSandbox:
             options.extraPrefs.append("security.sandbox.content.level=1")
         options.extraPrefs.append(
--- a/toolkit/components/reader/Readability.js
+++ b/toolkit/components/reader/Readability.js
@@ -77,22 +77,22 @@ function Readability(uri, doc, options) 
       var elDesc = "";
       if (e.id)
         elDesc = "(#" + e.id + classDesc + ")";
       else if (classDesc)
         elDesc = "(" + classDesc + ")";
       return rv + elDesc;
     };
     this.log = function () {
-      if (typeof dump !== undefined) {
+      if (typeof dump !== "undefined") {
         var msg = Array.prototype.map.call(arguments, function(x) {
           return (x && x.nodeName) ? logEl(x) : x;
         }).join(" ");
         dump("Reader: (Readability) " + msg + "\n");
-      } else if (typeof console !== undefined) {
+      } else if (typeof console !== "undefined") {
         var args = ["Reader: (Readability) "].concat(arguments);
         console.log.apply(console, args);
       }
     };
   } else {
     this.log = function () {};
   }
 }
@@ -114,17 +114,17 @@ Readability.prototype = {
   DEFAULT_MAX_PAGES: 5,
 
   // Element tags to score by default.
   DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),
 
   // All of the regular expressions in use within readability.
   // Defined up here so we don't instantiate them repeatedly in loops.
   REGEXPS: {
-    unlikelyCandidates: /banner|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|modal|related|remark|rss|shoutbox|sidebar|skyscraper|sponsor|ad-break|agegate|pagination|pager|popup|yom-remote/i,
+    unlikelyCandidates: /banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|modal|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|ad-break|agegate|pagination|pager|popup|yom-remote/i,
     okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
     positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,
     negative: /hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|modal|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,
     extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
     byline: /byline|author|dateline|writtenby|p-author/i,
     replaceFonts: /<(\/?)font[^>]*>/gi,
     normalize: /\s{2,}/g,
     videos: /\/\/(www\.)?(dailymotion|youtube|youtube-nocookie|player\.vimeo)\.com/i,
@@ -472,16 +472,17 @@ Readability.prototype = {
    * @param Element
    * @return void
    **/
   _prepArticle: function(articleContent) {
     this._cleanStyles(articleContent);
 
     // Clean out junk from the article content
     this._cleanConditionally(articleContent, "form");
+    this._cleanConditionally(articleContent, "fieldset");
     this._clean(articleContent, "object");
     this._clean(articleContent, "embed");
     this._clean(articleContent, "h1");
     this._clean(articleContent, "footer");
 
     // Clean out elements have "share" in their id/class combinations from final top candidates,
     // which means we don't remove the top candidates even they have "share".
     this._forEachNode(articleContent.children, function(topCandidate) {
@@ -489,16 +490,20 @@ Readability.prototype = {
     });
 
     // If there is only one h2, they are probably using it as a header
     // and not a subheader, so remove it since we already have a header.
     if (articleContent.getElementsByTagName('h2').length === 1)
       this._clean(articleContent, "h2");
 
     this._clean(articleContent, "iframe");
+    this._clean(articleContent, "input");
+    this._clean(articleContent, "textarea");
+    this._clean(articleContent, "select");
+    this._clean(articleContent, "button");
     this._cleanHeaders(articleContent);
 
     // Do these last as the previous stuff may have removed junk
     // that will affect these
     this._cleanConditionally(articleContent, "table");
     this._cleanConditionally(articleContent, "ul");
     this._cleanConditionally(articleContent, "div");
 
@@ -841,28 +846,59 @@ Readability.prototype = {
           this.log("Moving child out:", kids[0]);
           topCandidate.appendChild(kids[0]);
         }
 
         page.appendChild(topCandidate);
 
         this._initializeNode(topCandidate);
       } else if (topCandidate) {
+        // Find a better top candidate node if it contains (at least three) nodes which belong to `topCandidates` array
+        // and whose scores are quite closed with current `topCandidate` node.
+        var alternativeCandidateAncestors = [];
+        for (var i = 1; i < topCandidates.length; i++) {
+          if (topCandidates[i].readability.contentScore / topCandidate.readability.contentScore >= 0.75) {
+            alternativeCandidateAncestors.push(this._getNodeAncestors(topCandidates[i]));
+          }
+        }
+        var MINIMUM_TOPCANDIDATES = 3;
+        if (alternativeCandidateAncestors.length >= MINIMUM_TOPCANDIDATES) {
+          parentOfTopCandidate = topCandidate.parentNode;
+          while (parentOfTopCandidate.tagName !== "BODY") {
+            var listsContainingThisAncestor = 0;
+            for (var ancestorIndex = 0; ancestorIndex < alternativeCandidateAncestors.length && listsContainingThisAncestor < MINIMUM_TOPCANDIDATES; ancestorIndex++) {
+              listsContainingThisAncestor += Number(alternativeCandidateAncestors[ancestorIndex].includes(parentOfTopCandidate));
+            }
+            if (listsContainingThisAncestor >= MINIMUM_TOPCANDIDATES) {
+              topCandidate = parentOfTopCandidate;
+              break;
+            }
+            parentOfTopCandidate = parentOfTopCandidate.parentNode;
+          }
+        }
+        if (!topCandidate.readability) {
+          this._initializeNode(topCandidate);
+        }
+
         // Because of our bonus system, parents of candidates might have scores
         // themselves. They get half of the node. There won't be nodes with higher
         // scores than our topCandidate, but if we see the score going *up* in the first
         // few steps up the tree, that's a decent sign that there might be more content
         // lurking in other places that we want to unify in. The sibling stuff
         // below does some of that - but only if we've looked high enough up the DOM
         // tree.
         parentOfTopCandidate = topCandidate.parentNode;
         var lastScore = topCandidate.readability.contentScore;
         // The scores shouldn't get too low.
         var scoreThreshold = lastScore / 3;
-        while (parentOfTopCandidate && parentOfTopCandidate.readability) {
+        while (parentOfTopCandidate.tagName !== "BODY") {
+          if (!parentOfTopCandidate.readability) {
+            parentOfTopCandidate = parentOfTopCandidate.parentNode;
+            continue;
+          }
           var parentScore = parentOfTopCandidate.readability.contentScore;
           if (parentScore < scoreThreshold)
             break;
           if (parentScore > lastScore) {
             // Alright! We found a better parent to use.
             topCandidate = parentOfTopCandidate;
             break;
           }
@@ -1236,21 +1272,16 @@ Readability.prototype = {
       if (segment.indexOf(".") !== -1) {
         possibleType = segment.split(".")[1];
 
         // If the type isn't alpha-only, it's probably not actually a file extension.
         if (!possibleType.match(/[^a-zA-Z]/))
           segment = segment.split(".")[0];
       }
 
-      // EW-CMS specific segment replacement. Ugly.
-      // Example: http://www.ew.com/ew/article/0,,20313460_20369436,00.html
-      if (segment.indexOf(',00') !== -1)
-        segment = segment.replace(',00', '');
-
       // If our first or second segment has anything looking like a page number, remove it.
       if (segment.match(/((_|-)?p[a-z]*|(_|-))[0-9]{1,2}$/i) && ((i === 1) || (i === 0)))
         segment = segment.replace(/((_|-)?p[a-z]*|(_|-))[0-9]{1,2}$/i, "");
 
       var del = false;
 
       // If this is purely a number, and it's the first or second segment,
       // it's probably a page number. Remove it.
@@ -1708,21 +1739,20 @@ Readability.prototype = {
           if (!this.REGEXPS.videos.test(embeds[ei].src))
             embedCount += 1;
         }
 
         var linkDensity = this._getLinkDensity(node);
         var contentLength = this._getInnerText(node).length;
 
         var haveToRemove =
-          // Make an exception for elements with no p's and exactly 1 img.
-          (img > p && !this._hasAncestorTag(node, "figure")) ||
+          (img > 1 && img > p && !this._hasAncestorTag(node, "figure")) ||
           (!isList && li > p) ||
           (input > Math.floor(p/3)) ||
-          (!isList && contentLength < 25 && (img === 0 || img > 2)) ||
+          (!isList && contentLength < 25 && (img === 0 || img > 2) && !this._hasAncestorTag(node, "figure")) ||
           (!isList && weight < 25 && linkDensity > 0.2) ||
           (weight >= 25 && linkDensity > 0.5) ||
           ((embedCount === 1 && contentLength < 75) || embedCount > 1);
         return haveToRemove;
       }
       return false;
     });
   },
@@ -1907,8 +1937,12 @@ Readability.prototype = {
       dir: this._articleDir,
       content: articleContent.innerHTML,
       textContent: textContent,
       length: textContent.length,
       excerpt: metadata.excerpt,
     };
   }
 };
+
+if (typeof module === "object") {
+  module.exports = Readability;
+}
--- a/toolkit/components/telemetry/TelemetryComms.h
+++ b/toolkit/components/telemetry/TelemetryComms.h
@@ -133,17 +133,17 @@ ParamTraits<mozilla::Telemetry::ScalarAc
             MOZ_ASSERT(false, "Count Scalar unable to convert variant to bool from child process.");
             return;
           }
           WriteParam(aMsg, val);
           break;
         }
       case nsITelemetry::SCALAR_STRING:
         {
-          nsString val;
+          nsAutoString val;
           nsresult rv = aParam.mData->GetAsAString(val);
           if (NS_FAILED(rv)) {
             MOZ_ASSERT(false, "Conversion failed.");
             return;
           }
           WriteParam(aMsg, val);
           break;
         }
@@ -184,17 +184,17 @@ ParamTraits<mozilla::Telemetry::ScalarAc
           if (!ReadParam(aMsg, aIter, &data) ||
               NS_FAILED(outVar->SetAsUint32(data))) {
             return false;
           }
           break;
         }
       case nsITelemetry::SCALAR_STRING:
         {
-          nsString data;
+          nsAutoString data;
           // De-serialize the data.
           if (!ReadParam(aMsg, aIter, &data) ||
               NS_FAILED(outVar->SetAsAString(data))) {
             return false;
           }
           break;
         }
       case nsITelemetry::SCALAR_BOOLEAN:
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -42,16 +42,17 @@
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla::widget;
 using namespace mozilla;
 using mozilla::MutexAutoLock;
 
 nsTArray<GfxDriverInfo>* GfxInfoBase::mDriverInfo;
+nsTArray<dom::GfxInfoFeatureStatus>* GfxInfoBase::mFeatureStatus;
 bool GfxInfoBase::mDriverInfoObserverInitialized;
 bool GfxInfoBase::mShutdownOccurred;
 
 // Observes for shutdown so that the child GfxDriverInfo list is freed.
 class ShutdownObserver : public nsIObserver
 {
   virtual ~ShutdownObserver() {}
 
@@ -63,16 +64,19 @@ public:
   NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
                      const char16_t *aData) override
   {
     MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
 
     delete GfxInfoBase::mDriverInfo;
     GfxInfoBase::mDriverInfo = nullptr;
 
+    delete GfxInfoBase::mFeatureStatus;
+    GfxInfoBase::mFeatureStatus = nullptr;
+
     for (uint32_t i = 0; i < DeviceFamilyMax; i++)
       delete GfxDriverInfo::mDeviceFamilies[i];
 
     for (uint32_t i = 0; i < DeviceVendorMax; i++)
       delete GfxDriverInfo::mDeviceVendors[i];
 
     GfxInfoBase::mShutdownOccurred = true;
 
@@ -594,23 +598,28 @@ GfxInfoBase::GetFeatureStatus(int32_t aF
     return NS_OK;
   }
 
   if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
     return NS_OK;
   }
 
   if (XRE_IsContentProcess()) {
-      // Delegate to the parent process.
-      mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
-      bool success;
-      nsCString remoteFailureId;
-      cc->SendGetGraphicsFeatureStatus(aFeature, aStatus, &remoteFailureId, &success);
-      aFailureId = remoteFailureId;
-      return success ? NS_OK : NS_ERROR_FAILURE;
+    // Use the cached data received from the parent process.
+    MOZ_ASSERT(mFeatureStatus);
+    bool success = false;
+    for (const auto& fs : *mFeatureStatus) {
+      if (fs.feature() == aFeature) {
+        aFailureId = fs.failureId();
+        *aStatus = fs.status();
+        success = true;
+        break;
+      }
+    }
+    return success ? NS_OK : NS_ERROR_FAILURE;
   }
 
   nsString version;
   nsTArray<GfxDriverInfo> driverInfo;
   nsresult rv = GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
   return rv;
 }
 
@@ -842,16 +851,23 @@ GfxInfoBase::FindBlocklistedDeviceInList
                                       (info[i].mDriverVersion & 0x000000000000ffff));
     }
   }
 #endif
 
   return status;
 }
 
+void
+GfxInfoBase::SetFeatureStatus(const nsTArray<dom::GfxInfoFeatureStatus>& aFS)
+{
+  MOZ_ASSERT(!mFeatureStatus);
+  mFeatureStatus = new nsTArray<dom::GfxInfoFeatureStatus>(aFS);
+}
+
 nsresult
 GfxInfoBase::GetFeatureStatusImpl(int32_t aFeature,
                                   int32_t* aStatus,
                                   nsAString& aSuggestedVersion,
                                   const nsTArray<GfxDriverInfo>& aDriverInfo,
                                   nsACString& aFailureId,
                                   OperatingSystem* aOS /* = nullptr */)
 {