Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 08 Jan 2015 15:44:18 +0100
changeset 222722 f3059526fe916e080feb3567d1cc0cc7af1e9589
parent 222702 ffd5b05a531167aa59847e10ab40b21caad954d2 (current diff)
parent 222721 d480b3542cc24bf1b964469d790ed0ab69fda5cb (diff)
child 222723 50bd347938d50289a626952a907d978d91e28581
push id10716
push userkwierso@gmail.com
push dateFri, 09 Jan 2015 01:17:28 +0000
treeherderfx-team@0f98d51a4a49 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone37.0a1
Merge mozilla-central to b2g-inbound
image/src/FrameBlender.cpp
image/src/FrameBlender.h
js/src/jit/ParallelFunctions.cpp
js/src/jit/ParallelFunctions.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/ParallelSafetyAnalysis.h
security/pkix/include/pkix/enumclass.h
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -54,15 +54,20 @@ MOZ_TIME_MANAGER=1
 MOZ_PAY=1
 MOZ_TOOLKIT_SEARCH=
 MOZ_PLACES=
 MOZ_B2G=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_NUWA_PROCESS=1
 MOZ_B2G_LOADER=1
+# Warnings-as-errors cannot be enabled on gcc <= 4.4 due to bug 915555.
+if test "$GCC_MAJOR_VERSION" -gt 4 -o \
+   "$GCC_MAJOR_VERSION" -eq 4 -a "$GCC_MINOR_VERSION" -gt 4; then
+MOZ_ENABLE_WARNINGS_AS_ERRORS=1
+fi
 fi
 
 MOZ_JSDOWNLOADS=1
 
 MOZ_BUNDLED_FONTS=1
 
 export JS_GC_SMALL_CHUNK_SIZE=1
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -20,17 +20,17 @@
     <command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
     <command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
 
     <command id="cmd_newNavigatorTab" oncommand="BrowserOpenNewTabOrWindow(event);"/>
     <command id="Browser:OpenFile"  oncommand="BrowserOpenFileWindow();"/>
     <command id="Browser:SavePage" oncommand="saveDocument(gBrowser.selectedBrowser.contentDocumentAsCPOW);"/>
 
     <command id="Browser:SendLink"
-             oncommand="MailIntegration.sendLinkForWindow(window.content);"/>
+             oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
     <command id="cmd_print" oncommand="PrintUtils.print(window.gBrowser.selectedBrowser.contentWindowAsCPOW, window.gBrowser.selectedBrowser);"/>
     <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
     <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1733,17 +1733,17 @@ function HandleAppCommandEvent(evt) {
   case "Print":
     PrintUtils.print(gBrowser.selectedBrowser.contentWindowAsCPOW,
                      gBrowser.selectedBrowser);
     break;
   case "Save":
     saveDocument(gBrowser.selectedBrowser.contentDocumentAsCPOW);
     break;
   case "SendMail":
-    MailIntegration.sendLinkForWindow(window.content);
+    MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
     break;
   default:
     return;
   }
   evt.stopPropagation();
   evt.preventDefault();
 }
 
@@ -6519,19 +6519,18 @@ function warnAboutClosingWindow() {
   // closing multiple tabs.
   return isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
 #else
   return true;
 #endif
 }
 
 var MailIntegration = {
-  sendLinkForWindow: function (aWindow) {
-    this.sendMessage(aWindow.location.href,
-                     aWindow.document.title);
+  sendLinkForBrowser: function (aBrowser) {
+    this.sendMessage(aBrowser.currentURI.spec, aBrowser.contentTitle);
   },
 
   sendMessage: function (aBody, aSubject) {
     // generate a mailto url based on the url and the url's title
     var mailtoUrl = "mailto:";
     if (aBody) {
       mailtoUrl += "?body=" + encodeURIComponent(aBody);
       mailtoUrl += "&subject=" + encodeURIComponent(aSubject);
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -915,17 +915,17 @@ const CustomizableWidgets = [
         this.charsetInfo = CharsetMenu.getData();
       }
     }
   }, {
     id: "email-link-button",
     tooltiptext: "email-link-button.tooltiptext3",
     onCommand: function(aEvent) {
       let win = aEvent.view;
-      win.MailIntegration.sendLinkForWindow(win.content);
+      win.MailIntegration.sendLinkForBrowser(win.gBrowser.selectedBrowser)
     }
   }, {
     id: "loop-button",
     type: "custom",
     label: "loop-call-button3.label",
     tooltiptext: "loop-call-button3.tooltiptext",
     defaultArea: CustomizableUI.AREA_NAVBAR,
     introducedInVersion: 4,
--- a/browser/components/loop/standalone/content/libs/l10n-gaia-02ca67948fe8.js
+++ b/browser/components/loop/standalone/content/libs/l10n-gaia-02ca67948fe8.js
@@ -1236,21 +1236,29 @@
 
       return locales[code] = new Locale(code, this);
     };
 
 
     // Getting ready
 
     function negotiate(available, requested, defaultLocale) {
-      if (available.indexOf(requested[0]) === -1 ||
-          requested[0] === defaultLocale) {
+      var supportedLocale;
+      for (var i = 0; i < requested.length; ++i) {
+        var locale = requested[i];
+        if (available.indexOf(locale) !== -1) {
+          supportedLocale = locale;
+          break;
+        }
+      }
+      if (!supportedLocale ||
+          supportedLocale === defaultLocale) {
         return [defaultLocale];
       } else {
-        return [requested[0], defaultLocale];
+        return [supportedLocale, defaultLocale];
       }
     }
 
     function freeze(supported) {
       var locale = this.getLocale(supported[0]);
       if (locale.isReady) {
         setReady.call(this, supported);
       } else {
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -142,16 +142,24 @@ DEFINES += -DMOZ_JEMALLOC3
 endif
 DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
 ifdef CLANG_CL
 DEFINES += -DCLANG_CL
 endif
+ifeq (x86,$(CPU_ARCH))
+ifdef _MSC_VER
+ifndef CLANG_CL
+DEFINES += -DWOW_HELPER
+endif
+endif
+endif
+
 
 libs::
 	$(MAKE) -C $(DEPTH)/browser/locales langpack
 
 ifeq (WINNT,$(OS_ARCH))
 PKGCOMP_FIND_OPTS =
 else
 PKGCOMP_FIND_OPTS = -L
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -778,16 +778,19 @@
 @RESPATH@/components/pipboot.xpt
 @RESPATH@/components/pipnss.xpt
 @RESPATH@/components/pippki.xpt
 
 ; For process sandboxing
 #if defined(MOZ_SANDBOX)
 #if defined(XP_WIN)
 @BINPATH@/@DLL_PREFIX@sandboxbroker@DLL_SUFFIX@
+#if defined(WOW_HELPER)
+@BINPATH@/wow_helper.exe
+#endif
 #endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.so
--- a/build/stlport/moz.build
+++ b/build/stlport/moz.build
@@ -4,19 +4,16 @@
 # 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/.
 
 Library('stlport')
 # Keep the same name as the NDK-provided library, while using a shorter
 # name for the Library for convenience in moz.build.
 STATIC_LIBRARY_NAME = 'stlport_static'
 
-if not CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
-    OS_LIBS += ['-static-libstdc++']
-
 FORCE_STATIC_LIB = True
 
 SOURCES += [
     'src/allocators.cpp',
     'src/bitset.cpp',
     'src/codecvt.cpp',
     'src/collate.cpp',
     'src/complex.cpp',
--- a/docshell/base/LoadInfo.cpp
+++ b/docshell/base/LoadInfo.cpp
@@ -23,31 +23,48 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   : mLoadingPrincipal(aLoadingContext ?
                         aLoadingContext->NodePrincipal() : aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal ?
                            aTriggeringPrincipal : mLoadingPrincipal.get())
   , mLoadingContext(do_GetWeakReference(aLoadingContext))
   , mSecurityFlags(aSecurityFlags)
   , mContentPolicyType(aContentPolicyType)
   , mBaseURI(aBaseURI)
+  , mInnerWindowID(aLoadingContext ?
+                     aLoadingContext->OwnerDoc()->InnerWindowID() : 0)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   // if consumers pass both, aLoadingContext and aLoadingPrincipal
   // then the loadingPrincipal must be the same as the node's principal
   MOZ_ASSERT(!aLoadingContext || !aLoadingPrincipal ||
              aLoadingContext->NodePrincipal() == aLoadingPrincipal);
 
   // if the load is sandboxed, we can not also inherit the principal
   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
     mSecurityFlags ^= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 }
 
+LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
+                   nsIPrincipal* aTriggeringPrincipal,
+                   nsSecurityFlags aSecurityFlags,
+                   nsContentPolicyType aContentPolicyType,
+                   uint32_t aInnerWindowID)
+  : mLoadingPrincipal(aLoadingPrincipal)
+  , mTriggeringPrincipal(aTriggeringPrincipal)
+  , mSecurityFlags(aSecurityFlags)
+  , mContentPolicyType(aContentPolicyType)
+  , mInnerWindowID(aInnerWindowID)
+{
+  MOZ_ASSERT(mLoadingPrincipal);
+  MOZ_ASSERT(mTriggeringPrincipal);
+}
+
 LoadInfo::~LoadInfo()
 {
 }
 
 NS_IMPL_ISUPPORTS(LoadInfo, nsILoadInfo)
 
 NS_IMETHODIMP
 LoadInfo::GetLoadingPrincipal(nsIPrincipal** aLoadingPrincipal)
@@ -130,9 +147,16 @@ LoadInfo::GetBaseURI(nsIURI** aBaseURI)
 }
 
 nsIURI*
 LoadInfo::BaseURI()
 {
   return mBaseURI;
 }
 
+NS_IMETHODIMP
+LoadInfo::GetInnerWindowID(uint32_t* outInnerWindowID)
+{
+  *outInnerWindowID = mInnerWindowID;
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/docshell/base/LoadInfo.h
+++ b/docshell/base/LoadInfo.h
@@ -12,16 +12,21 @@
 #include "nsIPrincipal.h"
 #include "nsIWeakReferenceUtils.h" // for nsWeakPtr
 #include "nsIURI.h"
 
 class nsINode;
 
 namespace mozilla {
 
+namespace net {
+class HttpChannelParent;
+class FTPChannelParent;
+}
+
 /**
  * Class that provides an nsILoadInfo implementation.
  */
 class MOZ_EXPORT LoadInfo MOZ_FINAL : public nsILoadInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSILOADINFO
@@ -30,22 +35,35 @@ public:
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
            nsINode* aLoadingContext,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            nsIURI* aBaseURI = nullptr);
 
 private:
+  // private constructor that is only allowed to be called from within
+  // HttpChannelParent and FTPChannelParent declared as friends undeneath.
+  // In e10s we can not serialize nsINode, hence we store the innerWindowID.
+  LoadInfo(nsIPrincipal* aLoadingPrincipal,
+           nsIPrincipal* aTriggeringPrincipal,
+           nsSecurityFlags aSecurityFlags,
+           nsContentPolicyType aContentPolicyType,
+           uint32_t aInnerWindowID);
+
+  friend class net::HttpChannelParent;
+  friend class net::FTPChannelParent;
+
   ~LoadInfo();
 
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   nsWeakPtr              mLoadingContext;
   nsSecurityFlags        mSecurityFlags;
   nsContentPolicyType    mContentPolicyType;
   nsCOMPtr<nsIURI>       mBaseURI;
+  uint32_t               mInnerWindowID;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_LoadInfo_h
 
--- a/docshell/base/nsILoadInfo.idl
+++ b/docshell/base/nsILoadInfo.idl
@@ -175,9 +175,29 @@ interface nsILoadInfo : nsISupports
    */
   readonly attribute nsIURI baseURI;
 
   /**
    * A C++-friendly version of baseURI.
    */
   [noscript, notxpcom, nostdcall, binaryname(BaseURI)]
   nsIURI binaryBaseURI();
+
+  /**
+   * The innerWindowId of the loadingDocument, used to identify
+   * the loadingDocument in e10s where the loadingDocument is
+   * not available.
+   *
+   * Warning: If the loadingDocument is null, then the
+   * innerWindowId is 0.
+   */
+  readonly attribute unsigned long innerWindowID;
+
+%{ C++
+  inline uint32_t GetInnerWindowID()
+  {
+    uint32_t result;
+    mozilla::DebugOnly<nsresult> rv = GetInnerWindowID(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+%}
 };
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -225,19 +225,17 @@ NS_GetContentList(nsINode* aRootNode,
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gContentListHashTable.ops) {
 
     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
     // when the entry is already in the hashtable.
     entry = static_cast<ContentListHashEntry *>
-                       (PL_DHashTableOperate(&gContentListHashTable,
-                                             &hashKey,
-                                             PL_DHASH_ADD));
+                       (PL_DHashTableAdd(&gContentListHashTable, &hashKey));
     if (entry)
       list = entry->mContentList;
   }
 
   if (!list) {
     // We need to create a ContentList and add it to our new entry, if
     // we have an entry
     nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
@@ -341,19 +339,18 @@ GetFuncStringContentList(nsINode* aRootN
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable.ops) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
     // when the entry is already in the hashtable.
     entry = static_cast<FuncStringContentListHashEntry *>
-                       (PL_DHashTableOperate(&gFuncStringContentListHashTable,
-                                             &hashKey,
-                                             PL_DHASH_ADD));
+                       (PL_DHashTableAdd(&gFuncStringContentListHashTable,
+                                         &hashKey));
     if (entry) {
       list = entry->mContentList;
 #ifdef DEBUG
       MOZ_ASSERT_IF(list, list->mType == ListType::sType);
 #endif
     }
   }
 
@@ -984,19 +981,17 @@ nsContentList::RemoveFromHashtable()
   uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key);
   if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) {
     sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr;
   }
 
   if (!gContentListHashTable.ops)
     return;
 
-  PL_DHashTableOperate(&gContentListHashTable,
-                       &key,
-                       PL_DHASH_REMOVE);
+  PL_DHashTableRemove(&gContentListHashTable, &key);
 
   if (gContentListHashTable.EntryCount() == 0) {
     PL_DHashTableFinish(&gContentListHashTable);
     gContentListHashTable.ops = nullptr;
   }
 }
 
 void
@@ -1027,19 +1022,17 @@ nsCacheableFuncStringContentList::~nsCac
 void
 nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
 {
   if (!gFuncStringContentListHashTable.ops) {
     return;
   }
 
   nsFuncStringCacheKey key(mRootNode, mFunc, mString);
-  PL_DHashTableOperate(&gFuncStringContentListHashTable,
-                       &key,
-                       PL_DHASH_REMOVE);
+  PL_DHashTableRemove(&gFuncStringContentListHashTable, &key);
 
   if (gFuncStringContentListHashTable.EntryCount() == 0) {
     PL_DHashTableFinish(&gFuncStringContentListHashTable);
     gFuncStringContentListHashTable.ops = nullptr;
   }
 }
 
 #ifdef DEBUG_CONTENT_LIST
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3949,18 +3949,17 @@ nsContentUtils::TraverseListenerManager(
 {
   if (!sEventListenerManagersHash.ops) {
     // We're already shut down, just return.
     return;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-               (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
-                                        PL_DHASH_LOOKUP));
+               (PL_DHashTableLookup(&sEventListenerManagersHash, aNode));
   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
     CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
                              "[via hash] mListenerManager");
   }
 }
 
 EventListenerManager*
 nsContentUtils::GetListenerManagerForNode(nsINode *aNode)
@@ -3969,18 +3968,17 @@ nsContentUtils::GetListenerManagerForNod
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-               (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
-                                        PL_DHASH_ADD));
+               (PL_DHashTableAdd(&sEventListenerManagersHash, aNode));
 
   if (!entry) {
     return nullptr;
   }
 
   if (!entry->mListenerManager) {
     entry->mListenerManager = new EventListenerManager(aNode);
 
@@ -4001,34 +3999,32 @@ nsContentUtils::GetExistingListenerManag
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-               (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
-                                        PL_DHASH_LOOKUP));
+               (PL_DHashTableLookup(&sEventListenerManagersHash, aNode));
   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
     return entry->mListenerManager;
   }
 
   return nullptr;
 }
 
 /* static */
 void
 nsContentUtils::RemoveListenerManager(nsINode *aNode)
 {
   if (sEventListenerManagersHash.ops) {
     EventListenerManagerMapEntry *entry =
       static_cast<EventListenerManagerMapEntry *>
-                 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
-                                          PL_DHASH_LOOKUP));
+                 (PL_DHashTableLookup(&sEventListenerManagersHash, aNode));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       nsRefPtr<EventListenerManager> listenerManager;
       listenerManager.swap(entry->mListenerManager);
       // Remove the entry and *then* do operations that could cause further
       // modification of sEventListenerManagersHash.  See bug 334177.
       PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
       if (listenerManager) {
         listenerManager->Disconnect();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3277,16 +3277,25 @@ nsDocument::GetUndoManager()
   if (!mUndoManager) {
     mUndoManager = new UndoManager(rootElement);
   }
 
   nsRefPtr<UndoManager> undoManager = mUndoManager;
   return undoManager.forget();
 }
 
+bool
+nsDocument::IsWebAnimationsEnabled(JSContext* /*unused*/, JSObject* /*unused*/)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return nsContentUtils::IsCallerChrome() ||
+         Preferences::GetBool("dom.animations-api.core.enabled");
+}
+
 AnimationTimeline*
 nsDocument::Timeline()
 {
   if (!mAnimationTimeline) {
     mAnimationTimeline = new AnimationTimeline(this);
   }
 
   return mAnimationTimeline;
@@ -3946,18 +3955,17 @@ nsDocument::SetSubDocumentFor(Element* a
   NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
 
   if (!aSubDoc) {
     // aSubDoc is nullptr, remove the mapping
 
     if (mSubDocuments) {
       SubDocMapEntry *entry =
         static_cast<SubDocMapEntry*>
-                   (PL_DHashTableOperate(mSubDocuments, aElement,
-                                         PL_DHASH_LOOKUP));
+                   (PL_DHashTableLookup(mSubDocuments, aElement));
 
       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
         PL_DHashTableRawRemove(mSubDocuments, entry);
       }
     }
   } else {
     if (!mSubDocuments) {
       // Create a new hashtable
@@ -3979,18 +3987,17 @@ nsDocument::SetSubDocumentFor(Element* a
       if (!mSubDocuments) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
     }
 
     // Add a mapping to the hash table
     SubDocMapEntry *entry =
       static_cast<SubDocMapEntry*>
-                 (PL_DHashTableOperate(mSubDocuments, aElement,
-                                       PL_DHASH_ADD));
+                 (PL_DHashTableAdd(mSubDocuments, aElement));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (entry->mSubDocument) {
       entry->mSubDocument->SetParentDocument(nullptr);
 
@@ -4008,18 +4015,17 @@ nsDocument::SetSubDocumentFor(Element* a
 }
 
 nsIDocument*
 nsDocument::GetSubDocumentFor(nsIContent *aContent) const
 {
   if (mSubDocuments && aContent->IsElement()) {
     SubDocMapEntry *entry =
       static_cast<SubDocMapEntry*>
-                 (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(),
-                                       PL_DHASH_LOOKUP));
+                 (PL_DHashTableLookup(mSubDocuments, aContent->AsElement()));
 
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       return entry->mSubDocument;
     }
   }
 
   return nullptr;
 }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -777,16 +777,17 @@ public:
                                                      nsViewManager* aViewManager,
                                                      nsStyleSet* aStyleSet) MOZ_OVERRIDE;
   virtual void DeleteShell() MOZ_OVERRIDE;
 
   virtual nsresult GetAllowPlugins(bool* aAllowPlugins) MOZ_OVERRIDE;
 
   virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager() MOZ_OVERRIDE;
 
+  static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject);
   virtual mozilla::dom::AnimationTimeline* Timeline() MOZ_OVERRIDE;
 
   virtual nsresult SetSubDocumentFor(Element* aContent,
                                      nsIDocument* aSubDoc) MOZ_OVERRIDE;
   virtual nsIDocument* GetSubDocumentFor(nsIContent* aContent) const MOZ_OVERRIDE;
   virtual Element* FindContentForSubDocument(nsIDocument *aDocument) const MOZ_OVERRIDE;
   virtual Element* GetRootElementInternal() const MOZ_OVERRIDE;
 
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -90,18 +90,17 @@ nsPropertyTable::DeleteAllPropertiesFor(
 nsresult
 nsPropertyTable::TransferOrDeleteAllPropertiesFor(nsPropertyOwner aObject,
                                                   nsPropertyTable *aOtherTable)
 {
   nsresult rv = NS_OK;
   for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) {
     if (prop->mTransfer) {
       PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-                                               (PL_DHashTableOperate(&prop->mObjectValueMap, aObject,
-                               PL_DHASH_LOOKUP));
+                                               (PL_DHashTableLookup(&prop->mObjectValueMap, aObject));
       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
         rv = aOtherTable->SetProperty(aObject, prop->mName,
                                       entry->value, prop->mDtorFunc,
                                       prop->mDtorData, prop->mTransfer);
         if (NS_FAILED(rv)) {
           DeleteAllPropertiesFor(aObject);
           aOtherTable->DeleteAllPropertiesFor(aObject);
 
@@ -121,17 +120,17 @@ nsPropertyTable::TransferOrDeleteAllProp
 
 void
 nsPropertyTable::Enumerate(nsPropertyOwner aObject,
                            NSPropertyFunc aCallback, void *aData)
 {
   PropertyList* prop;
   for (prop = mPropertyList; prop; prop = prop->mNext) {
     PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-      (PL_DHashTableOperate(&prop->mObjectValueMap, aObject, PL_DHASH_LOOKUP));
+      (PL_DHashTableLookup(&prop->mObjectValueMap, aObject));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       aCallback(const_cast<void*>(aObject.get()), prop->mName, entry->value,
                  aData);
     }
   }
 }
 
 struct PropertyEnumeratorData
@@ -169,18 +168,17 @@ nsPropertyTable::GetPropertyInternal(nsP
 {
   NS_PRECONDITION(aPropertyName && aObject, "unexpected null param");
   nsresult rv = NS_PROPTABLE_PROP_NOT_THERE;
   void *propValue = nullptr;
 
   PropertyList* propertyList = GetPropertyListFor(aPropertyName);
   if (propertyList) {
     PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-                                             (PL_DHashTableOperate(&propertyList->mObjectValueMap, aObject,
-                             PL_DHASH_LOOKUP));
+                                             (PL_DHashTableLookup(&propertyList->mObjectValueMap, aObject));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       propValue = entry->value;
       if (aRemove) {
         // don't call propertyList->mDtorFunc.  That's the caller's job now.
         PL_DHashTableRawRemove(&propertyList->mObjectValueMap, entry);
       }
       rv = NS_OK;
     }
@@ -225,17 +223,17 @@ nsPropertyTable::SetPropertyInternal(nsP
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
   }
 
   // The current property value (if there is one) is replaced and the current
   // value is destroyed
   nsresult result = NS_OK;
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-                                           (PL_DHashTableOperate(&propertyList->mObjectValueMap, aObject, PL_DHASH_ADD));
+                                           (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject));
   if (!entry)
     return NS_ERROR_OUT_OF_MEMORY;
   // A nullptr entry->key is the sign that the entry has just been allocated
   // for us.  If it's non-nullptr then we have an existing entry.
   if (entry->key) {
     if (aOldValue)
       *aOldValue = entry->value;
     else if (propertyList->mDtorFunc)
@@ -324,17 +322,17 @@ nsPropertyTable::PropertyList::Destroy()
     PL_DHashTableEnumerate(&mObjectValueMap, DestroyPropertyEnumerator,
                            nullptr);
 }
 
 bool
 nsPropertyTable::PropertyList::DeletePropertyFor(nsPropertyOwner aObject)
 {
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-                                           (PL_DHashTableOperate(&mObjectValueMap, aObject, PL_DHASH_LOOKUP));
+                                           (PL_DHashTableLookup(&mObjectValueMap, aObject));
   if (!PL_DHASH_ENTRY_IS_BUSY(entry))
     return false;
 
   void* value = entry->value;
   PL_DHashTableRawRemove(&mObjectValueMap, entry);
 
   if (mDtorFunc)
     mDtorFunc(const_cast<void*>(aObject.get()), mName, value, mDtorData);
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -139,47 +139,46 @@ nsScriptNameSpaceManager::~nsScriptNameS
 }
 
 nsGlobalNameStruct *
 nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey,
                                     const char16_t **aClassName)
 {
   GlobalNameMapEntry *entry =
     static_cast<GlobalNameMapEntry *>
-               (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
+               (PL_DHashTableAdd(aTable, aKey));
 
   if (!entry) {
     return nullptr;
   }
 
   if (aClassName) {
     *aClassName = entry->mKey.get();
   }
 
   return &entry->mGlobalName;
 }
 
 void
 nsScriptNameSpaceManager::RemoveFromHash(PLDHashTable *aTable,
                                          const nsAString *aKey)
 {
-  PL_DHashTableOperate(aTable, aKey, PL_DHASH_REMOVE);
+  PL_DHashTableRemove(aTable, aKey);
 }
 
 nsGlobalNameStruct*
 nsScriptNameSpaceManager::GetConstructorProto(const nsGlobalNameStruct* aStruct)
 {
   NS_ASSERTION(aStruct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias,
                "This function only works on constructor aliases!");
   if (!aStruct->mAlias->mProto) {
     GlobalNameMapEntry *proto =
       static_cast<GlobalNameMapEntry *>
-                 (PL_DHashTableOperate(&mGlobalNames,
-                                          &aStruct->mAlias->mProtoName,
-                                          PL_DHASH_LOOKUP));
+                 (PL_DHashTableLookup(&mGlobalNames,
+                                      &aStruct->mAlias->mProtoName));
 
     if (PL_DHASH_ENTRY_IS_BUSY(proto)) {
       aStruct->mAlias->mProto = &proto->mGlobalName;
     }
   }
   return aStruct->mAlias->mProto;
 }
 
@@ -384,18 +383,17 @@ nsScriptNameSpaceManager::Init()
 }
 
 nsGlobalNameStruct*
 nsScriptNameSpaceManager::LookupNameInternal(const nsAString& aName,
                                              const char16_t **aClassName)
 {
   GlobalNameMapEntry *entry =
     static_cast<GlobalNameMapEntry *>
-               (PL_DHashTableOperate(&mGlobalNames, &aName,
-                                        PL_DHASH_LOOKUP));
+               (PL_DHashTableLookup(&mGlobalNames, &aName));
 
   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
     if (aClassName) {
       *aClassName = entry->mKey.get();
     }
     return &entry->mGlobalName;
   }
 
@@ -405,18 +403,17 @@ nsScriptNameSpaceManager::LookupNameInte
   return nullptr;
 }
 
 const nsGlobalNameStruct*
 nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName)
 {
   GlobalNameMapEntry *entry =
     static_cast<GlobalNameMapEntry *>
-               (PL_DHashTableOperate(&mNavigatorNames, &aName,
-                                     PL_DHASH_LOOKUP));
+               (PL_DHashTableLookup(&mNavigatorNames, &aName));
 
   if (!PL_DHASH_ENTRY_IS_BUSY(entry)) {
     return nullptr;
   }
 
   return &entry->mGlobalName;
 }
 
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -89,17 +89,17 @@ function BrowserElementChild() {
   // true.
   this._forcedVisible = true;
   this._ownerVisible = true;
 
   this._nextPaintHandler = null;
 
   this._isContentWindowCreated = false;
   this._pendingSetInputMethodActive = [];
-  this._forceDispatchSelectionStateChanged = false;
+  this._selectionStateChangedTarget = null;
 
   this._init();
 };
 
 BrowserElementChild.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
@@ -595,17 +595,17 @@ BrowserElementChild.prototype = {
   _selectionStateChangedHandler: function(e) {
     e.stopPropagation();
     let boundingClientRect = e.boundingClientRect;
 
     let isCollapsed = (e.selectedText.length == 0);
     let isMouseUp = (e.states.indexOf('mouseup') == 0);
     let canPaste = this._isCommandEnabled("paste");
 
-    if (!this._forceDispatchSelectionStateChanged) {
+    if (this._selectionStateChangedTarget != e.target) {
       // SelectionStateChanged events with the following states are not
       // necessary to trigger the text dialog, bypass these events
       // by default.
       //
       if(e.states.length == 0 ||
          e.states.indexOf('drag') == 0 ||
          e.states.indexOf('keypress') == 0 ||
          e.states.indexOf('mousedown') == 0) {
@@ -619,24 +619,25 @@ BrowserElementChild.prototype = {
         if (isMouseUp && canPaste) {
           //Dispatch this selection change event to support shortcut mode
         } else {
           return;
         }
       }
     }
 
-    // If we select something and selection range is visible, we set the
-    // forceDispatchSelectionStateChanged flag as true to dispatch the
-    // next SelectionStateChange event so that the parent side can
-    // hide the text dialog.
+    // If we select something and selection range is visible, we cache current
+    // event's target to selectionStateChangedTarget.
+    // And dispatch the next SelectionStateChagne event if target is matched, so
+    // that the parent side can hide the text dialog.
+    // We clear selectionStateChangedTarget if selection carets are invisible.
     if (e.visible && !isCollapsed) {
-      this._forceDispatchSelectionStateChanged = true;
+      this._selectionStateChangedTarget = e.target;
     } else {
-      this._forceDispatchSelectionStateChanged = false;
+      this._selectionStateChangedTarget = null;
     }
 
     let zoomFactor = content.screen.width / content.innerWidth;
 
     let detail = {
       rect: {
         width: boundingClientRect ? boundingClientRect.width : 0,
         height: boundingClientRect ? boundingClientRect.height : 0,
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -28,18 +28,18 @@ WebGLContext::GetExtensionString(WebGLEx
 
 #define WEBGL_EXTENSION_IDENTIFIER(x) \
         sExtensionNamesEnumeratedArray[WebGLExtensionID::x] = #x;
 
         WEBGL_EXTENSION_IDENTIFIER(ANGLE_instanced_arrays)
         WEBGL_EXTENSION_IDENTIFIER(EXT_blend_minmax)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
+        WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
         WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
-        WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
         WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
         WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
         WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
@@ -90,99 +90,107 @@ bool WebGLContext::IsExtensionSupported(
             // For warnings-as-errors.
             break;
         }
     }
 
     return IsExtensionSupported(ext);
 }
 
-bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
+bool
+WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
 {
     if (mDisableExtensions)
         return false;
 
+    // In alphabetical order
     switch (ext) {
-    case WebGLExtensionID::OES_vertex_array_object:
-    case WebGLExtensionID::WEBGL_lose_context:
-        // Always supported.
-        return true;
+    // ANGLE_
+    case WebGLExtensionID::ANGLE_instanced_arrays:
+        return WebGLExtensionInstancedArrays::IsSupported(this);
 
+    // EXT_
     case WebGLExtensionID::EXT_blend_minmax:
         return WebGLExtensionBlendMinMax::IsSupported(this);
+    case WebGLExtensionID::EXT_color_buffer_half_float:
+        return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
+    case WebGLExtensionID::EXT_frag_depth:
+        return WebGLExtensionFragDepth::IsSupported(this);
+    case WebGLExtensionID::EXT_shader_texture_lod:
+        return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod);
+    case WebGLExtensionID::EXT_sRGB:
+        return WebGLExtensionSRGB::IsSupported(this);
+    case WebGLExtensionID::EXT_texture_filter_anisotropic:
+        return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic);
+
+    // OES_
     case WebGLExtensionID::OES_element_index_uint:
         return gl->IsSupported(gl::GLFeature::element_index_uint);
     case WebGLExtensionID::OES_standard_derivatives:
         return gl->IsSupported(gl::GLFeature::standard_derivatives);
-
     case WebGLExtensionID::OES_texture_float:
         return gl->IsSupported(gl::GLFeature::texture_float);
     case WebGLExtensionID::OES_texture_float_linear:
         return gl->IsSupported(gl::GLFeature::texture_float_linear);
     case WebGLExtensionID::OES_texture_half_float:
         // If we have Feature::texture_half_float, we must not be on ES2
         // and need to translate HALF_FLOAT_OES -> HALF_FLOAT.  We do that
         // right before making the relevant calls.
         return gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float) ||
                gl->IsSupported(gl::GLFeature::texture_half_float);
 
     case WebGLExtensionID::OES_texture_half_float_linear:
         return gl->IsSupported(gl::GLFeature::texture_half_float_linear);
-    case WebGLExtensionID::EXT_texture_filter_anisotropic:
-        return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic);
+    case WebGLExtensionID::OES_vertex_array_object:
+        return true;
+
+    // WEBGL_
+    case WebGLExtensionID::WEBGL_color_buffer_float:
+        return WebGLExtensionColorBufferFloat::IsSupported(this);
+    case WebGLExtensionID::WEBGL_compressed_texture_atc:
+        return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture);
+    case WebGLExtensionID::WEBGL_compressed_texture_etc1:
+        return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture);
+    case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
+        return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc);
     case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
         if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc))
             return true;
 
         return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5);
-
-    case WebGLExtensionID::WEBGL_compressed_texture_atc:
-        return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture);
-    case WebGLExtensionID::WEBGL_compressed_texture_etc1:
-        return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture);
-    case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
-        return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc);
     case WebGLExtensionID::WEBGL_depth_texture:
         // WEBGL_depth_texture supports DEPTH_STENCIL textures
         if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil))
             return false;
 
         return gl->IsSupported(gl::GLFeature::depth_texture) ||
                gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture);
-
-    case WebGLExtensionID::ANGLE_instanced_arrays:
-        return WebGLExtensionInstancedArrays::IsSupported(this);
-    case WebGLExtensionID::EXT_sRGB:
-        return WebGLExtensionSRGB::IsSupported(this);
     case WebGLExtensionID::WEBGL_draw_buffers:
         return WebGLExtensionDrawBuffers::IsSupported(this);
-    case WebGLExtensionID::EXT_frag_depth:
-        return WebGLExtensionFragDepth::IsSupported(this);
-    case WebGLExtensionID::EXT_shader_texture_lod:
-        return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod);
+    case WebGLExtensionID::WEBGL_lose_context:
+        // We always support this extension.
+        return true;
 
     default:
         // For warnings-as-errors.
         break;
     }
 
     if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
         IsWebGL2())
     {
+        /* None for now.
         switch (ext) {
-        case WebGLExtensionID::EXT_color_buffer_half_float:
-            return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
-        case WebGLExtensionID::WEBGL_color_buffer_float:
-            return WebGLExtensionColorBufferFloat::IsSupported(this);
         default:
             // For warnings-as-errors.
             break;
         }
+        */
     }
 
     return false;
 }
 
 static bool
 CompareWebGLExtensionName(const nsACString& name, const char* other)
 {
@@ -287,91 +295,98 @@ WebGLContext::GetExtension(JSContext* cx
 
 void
 WebGLContext::EnableExtension(WebGLExtensionID ext)
 {
     MOZ_ASSERT(IsExtensionEnabled(ext) == false);
 
     WebGLExtensionBase* obj = nullptr;
     switch (ext) {
+    // ANGLE_
+    case WebGLExtensionID::ANGLE_instanced_arrays:
+        obj = new WebGLExtensionInstancedArrays(this);
+        break;
+
+    // EXT_
+    case WebGLExtensionID::EXT_blend_minmax:
+        obj = new WebGLExtensionBlendMinMax(this);
+        break;
+    case WebGLExtensionID::EXT_color_buffer_half_float:
+        obj = new WebGLExtensionColorBufferHalfFloat(this);
+        break;
+    case WebGLExtensionID::EXT_frag_depth:
+        obj = new WebGLExtensionFragDepth(this);
+        break;
+    case WebGLExtensionID::EXT_shader_texture_lod:
+        obj = new WebGLExtensionShaderTextureLod(this);
+        break;
+    case WebGLExtensionID::EXT_sRGB:
+        obj = new WebGLExtensionSRGB(this);
+        break;
+    case WebGLExtensionID::EXT_texture_filter_anisotropic:
+        obj = new WebGLExtensionTextureFilterAnisotropic(this);
+        break;
+
+    // OES_
     case WebGLExtensionID::OES_element_index_uint:
         obj = new WebGLExtensionElementIndexUint(this);
         break;
     case WebGLExtensionID::OES_standard_derivatives:
         obj = new WebGLExtensionStandardDerivatives(this);
         break;
-    case WebGLExtensionID::EXT_texture_filter_anisotropic:
-        obj = new WebGLExtensionTextureFilterAnisotropic(this);
-        break;
-    case WebGLExtensionID::WEBGL_lose_context:
-        obj = new WebGLExtensionLoseContext(this);
-        break;
-    case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
-        obj = new WebGLExtensionCompressedTextureS3TC(this);
-        break;
-    case WebGLExtensionID::WEBGL_compressed_texture_atc:
-        obj = new WebGLExtensionCompressedTextureATC(this);
-        break;
-    case WebGLExtensionID::WEBGL_compressed_texture_etc1:
-        obj = new WebGLExtensionCompressedTextureETC1(this);
-        break;
-    case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
-        obj = new WebGLExtensionCompressedTexturePVRTC(this);
-        break;
-    case WebGLExtensionID::WEBGL_debug_renderer_info:
-        obj = new WebGLExtensionDebugRendererInfo(this);
-        break;
-    case WebGLExtensionID::WEBGL_debug_shaders:
-        obj = new WebGLExtensionDebugShaders(this);
-        break;
-    case WebGLExtensionID::WEBGL_depth_texture:
-        obj = new WebGLExtensionDepthTexture(this);
-        break;
     case WebGLExtensionID::OES_texture_float:
         obj = new WebGLExtensionTextureFloat(this);
         break;
     case WebGLExtensionID::OES_texture_float_linear:
         obj = new WebGLExtensionTextureFloatLinear(this);
         break;
     case WebGLExtensionID::OES_texture_half_float:
         obj = new WebGLExtensionTextureHalfFloat(this);
         break;
     case WebGLExtensionID::OES_texture_half_float_linear:
         obj = new WebGLExtensionTextureHalfFloatLinear(this);
         break;
+    case WebGLExtensionID::OES_vertex_array_object:
+        obj = new WebGLExtensionVertexArray(this);
+        break;
+
+    // WEBGL_
     case WebGLExtensionID::WEBGL_color_buffer_float:
         obj = new WebGLExtensionColorBufferFloat(this);
         break;
-    case WebGLExtensionID::EXT_color_buffer_half_float:
-        obj = new WebGLExtensionColorBufferHalfFloat(this);
+    case WebGLExtensionID::WEBGL_compressed_texture_atc:
+        obj = new WebGLExtensionCompressedTextureATC(this);
+        break;
+    case WebGLExtensionID::WEBGL_compressed_texture_etc1:
+        obj = new WebGLExtensionCompressedTextureETC1(this);
+        break;
+    case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
+        obj = new WebGLExtensionCompressedTexturePVRTC(this);
+        break;
+    case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
+        obj = new WebGLExtensionCompressedTextureS3TC(this);
+        break;
+    case WebGLExtensionID::WEBGL_debug_renderer_info:
+        obj = new WebGLExtensionDebugRendererInfo(this);
+        break;
+    case WebGLExtensionID::WEBGL_debug_shaders:
+        obj = new WebGLExtensionDebugShaders(this);
+        break;
+    case WebGLExtensionID::WEBGL_depth_texture:
+        obj = new WebGLExtensionDepthTexture(this);
         break;
     case WebGLExtensionID::WEBGL_draw_buffers:
         obj = new WebGLExtensionDrawBuffers(this);
         break;
-    case WebGLExtensionID::OES_vertex_array_object:
-        obj = new WebGLExtensionVertexArray(this);
-        break;
-    case WebGLExtensionID::ANGLE_instanced_arrays:
-        obj = new WebGLExtensionInstancedArrays(this);
-        break;
-    case WebGLExtensionID::EXT_sRGB:
-        obj = new WebGLExtensionSRGB(this);
+    case WebGLExtensionID::WEBGL_lose_context:
+        obj = new WebGLExtensionLoseContext(this);
         break;
-    case WebGLExtensionID::EXT_frag_depth:
-        obj = new WebGLExtensionFragDepth(this);
-        break;
-    case WebGLExtensionID::EXT_blend_minmax:
-        obj = new WebGLExtensionBlendMinMax(this);
-        break;
-    case WebGLExtensionID::EXT_shader_texture_lod:
-        obj = new WebGLExtensionShaderTextureLod(this);
-        break;
+
     default:
         MOZ_ASSERT(false, "should not get there.");
-        break;
     }
 
     mExtensions[ext] = obj;
 }
 
 void
 WebGLContext::GetSupportedExtensions(JSContext* cx,
                                      Nullable< nsTArray<nsString> >& retval)
--- a/dom/canvas/test/_webgl-conformance.ini
+++ b/dom/canvas/test/_webgl-conformance.ini
@@ -494,17 +494,16 @@ support-files = webgl-conformance/../web
 [webgl-conformance/_wrappers/test_conformance__buffers__buffer-bind-test.html]
 [webgl-conformance/_wrappers/test_conformance__buffers__buffer-data-array-buffer.html]
 [webgl-conformance/_wrappers/test_conformance__buffers__index-validation-copies-indices.html]
 [webgl-conformance/_wrappers/test_conformance__buffers__index-validation-crash-with-buffer-sub-data.html]
 [webgl-conformance/_wrappers/test_conformance__buffers__index-validation-verifies-too-many-indices.html]
 [webgl-conformance/_wrappers/test_conformance__buffers__index-validation-with-resized-buffer.html]
 [webgl-conformance/_wrappers/test_conformance__buffers__index-validation.html]
 [webgl-conformance/_wrappers/test_conformance__canvas__buffer-offscreen-test.html]
-skip-if = os == 'android' # Bug 1102402 - It fails intermittently and causes a lot of retried jobs
 [webgl-conformance/_wrappers/test_conformance__canvas__buffer-preserve-test.html]
 [webgl-conformance/_wrappers/test_conformance__canvas__canvas-test.html]
 [webgl-conformance/_wrappers/test_conformance__canvas__canvas-zero-size.html]
 [webgl-conformance/_wrappers/test_conformance__canvas__drawingbuffer-static-canvas-test.html]
 skip-if = os == 'mac'
 [webgl-conformance/_wrappers/test_conformance__canvas__drawingbuffer-test.html]
 [webgl-conformance/_wrappers/test_conformance__canvas__viewport-unchanged-upon-resize.html]
 [webgl-conformance/_wrappers/test_conformance__context__constants.html]
@@ -515,16 +514,17 @@ skip-if = (os == 'b2g')
 [webgl-conformance/_wrappers/test_conformance__context__context-type-test.html]
 [webgl-conformance/_wrappers/test_conformance__context__incorrect-context-object-behaviour.html]
 [webgl-conformance/_wrappers/test_conformance__context__methods.html]
 [webgl-conformance/_wrappers/test_conformance__context__premultiplyalpha-test.html]
 [webgl-conformance/_wrappers/test_conformance__context__resource-sharing-test.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__oes-standard-derivatives.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__ext-texture-filter-anisotropic.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__oes-texture-float.html]
+fail-if = (os == 'linux')
 [webgl-conformance/_wrappers/test_conformance__extensions__oes-vertex-array-object.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-renderer-info.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-shaders.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-etc1.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__ext-sRGB.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__ext-shader-texture-lod.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function.html]
@@ -763,17 +763,17 @@ skip-if = (os == 'android') || (os == 'b
 [webgl-conformance/_wrappers/test_conformance__textures__tex-sub-image-2d.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texparameter-test.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-active-bind-2.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-active-bind.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-complete.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-formats-test.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-mips.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-npot-video.html]
-skip-if = os == 'win' || buildapp == 'mulet' # Mulet - bug 1089453 (crashes in libLLVM-3.0.so)
+skip-if = os == 'win'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-npot.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-size.html]
 skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-size-cube-maps.html]
 skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-transparent-pixels-initialized.html]
 [webgl-conformance/_wrappers/test_conformance__typedarrays__array-buffer-crash.html]
 [webgl-conformance/_wrappers/test_conformance__typedarrays__array-buffer-view-crash.html]
--- a/dom/canvas/test/webgl-conformance/generate-wrappers-and-manifest.py
+++ b/dom/canvas/test/webgl-conformance/generate-wrappers-and-manifest.py
@@ -26,16 +26,17 @@ EXTRA_SUPPORT_FILES = [
     'always-fail.html',
     'iframe-autoresize.js',
     'mochi-single.html',
     '../webgl-mochitest/driver-info.js',
 ]
 
 ACCEPTABLE_ERRATA_KEYS = set([
   'skip-if',
+  'fail-if',
 ])
 
 GENERATED_HEADER = '''
 # This is a GENERATED FILE. Do not edit it directly.
 # Regenerated it by using `python generate-wrapper-and-manifest.py`.
 # Mark skipped tests in mochitest-errata.ini.
 # Mark failing tests in mochi-single.html.
 '''.strip()
--- a/dom/canvas/test/webgl-conformance/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conformance/mochitest-errata.ini
@@ -73,16 +73,22 @@ skip-if = os == 'android'
 [_wrappers/test_conformance__context__context-attributes-alpha-depth-stencil-antialias.html]
 # Asserts on 'B2G ICS Emulator Debug'.
 skip-if = (os == 'b2g')
 [_wrappers/test_conformance__textures__tex-image-and-uniform-binding-bugs.html]
 # Intermittently asserts on 'B2G ICS Emulator Debug'.
 skip-if = (os == 'b2g')
 
 ########################################################################
+# Linux
+[_wrappers/test_conformance__extensions__oes-texture-float.html]
+# Failures after enabling color_buffer_[half_]float.
+fail-if = (os == 'linux')
+
+########################################################################
 # Mac
 [_wrappers/test_conformance__canvas__drawingbuffer-static-canvas-test.html]
 # Intermittent crash on OSX.
 skip-if = os == 'mac'
 
 ########################################################################
 # Win
 [_wrappers/test_conformance__textures__texture-npot-video.html]
--- a/dom/fmradio/FMRadioService.cpp
+++ b/dom/fmradio/FMRadioService.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/FMRadioChild.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsJSUtils.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
+#include "mozilla/DebugOnly.h"
 
 #define TUNE_THREAD_TIMEOUT_MS  5000
 
 #define BAND_87500_108000_kHz 1
 #define BAND_76000_108000_kHz 2
 #define BAND_76000_90000_kHz  3
 
 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
@@ -775,17 +776,17 @@ FMRadioService::CancelSeek(FMRadioReplyR
   NS_DispatchToMainThread(aReplyRunnable);
 }
 
 void
 FMRadioService::SetRDSGroupMask(uint32_t aRDSGroupMask)
 {
   mRDSGroupMask = aRDSGroupMask;
   if (IsFMRadioOn() && mRDSEnabled) {
-    bool enabled = hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
+    DebugOnly<bool> enabled = hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
     MOZ_ASSERT(enabled);
   }
 }
 
 void
 FMRadioService::EnableRDS(FMRadioReplyRunnable* aReplyRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
--- a/dom/icc/Icc.cpp
+++ b/dom/icc/Icc.cpp
@@ -30,19 +30,19 @@ IsPukCardLockType(IccLockType aLockType)
     case IccLockType::Nck1Puk:
     case IccLockType::Nck2Puk:
     case IccLockType::HnckPuk:
     case IccLockType::CckPuk:
     case IccLockType::SpckPuk:
     case IccLockType::RcckPuk:
     case IccLockType::RspckPuk:
       return true;
+    default:
+      return false;
   }
-
-  return false;
 }
 
 } // anonymous namespace
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(Icc, DOMEventTargetHelper, mIccInfo)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Icc)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -60,16 +60,19 @@ public:
   // Called by the decode thread to keep track of the number of bytes read
   // from the resource.
   virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) = 0;
 
   // Increments the parsed and decoded frame counters by the passed in counts.
   // Can be called on any thread.
   virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) = 0;
 
+  // For decoders with a notion of timestamp offset, returns the value in microseconds.
+  virtual int64_t GetTimestampOffset() const { return 0; }
+
   // Return the duration of the media in microseconds.
   virtual int64_t GetMediaDuration() = 0;
 
   // Set the duration of the media in microseconds.
   virtual void SetMediaDuration(int64_t aDuration) = 0;
 
   // Sets the duration of the media in microseconds. The MediaDecoder
   // fires a durationchange event to its owner (e.g., an HTML audio
--- a/dom/media/encoder/OmxTrackEncoder.cpp
+++ b/dom/media/encoder/OmxTrackEncoder.cpp
@@ -21,16 +21,23 @@
 
 using namespace android;
 
 namespace mozilla {
 
 #define ENCODER_CONFIG_FRAME_RATE 30 // fps
 #define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds
 
+OmxVideoTrackEncoder::OmxVideoTrackEncoder()
+  : VideoTrackEncoder()
+{}
+
+OmxVideoTrackEncoder::~OmxVideoTrackEncoder()
+{}
+
 nsresult
 OmxVideoTrackEncoder::Init(int aWidth, int aHeight, int aDisplayWidth,
                            int aDisplayHeight, TrackRate aTrackRate)
 {
   mFrameWidth = aWidth;
   mFrameHeight = aHeight;
   mTrackRate = aTrackRate;
   mDisplayWidth = aDisplayWidth;
@@ -157,16 +164,23 @@ OmxVideoTrackEncoder::GetEncodedTrack(En
   if (outFlags & OMXCodecWrapper::BUFFER_EOS) {
     mEncodingComplete = true;
     OMX_LOG("Done encoding video.");
   }
 
   return NS_OK;
 }
 
+OmxAudioTrackEncoder::OmxAudioTrackEncoder()
+  : AudioTrackEncoder()
+{}
+
+OmxAudioTrackEncoder::~OmxAudioTrackEncoder()
+{}
+
 nsresult
 OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
 {
   nsTArray<uint8_t> frameData;
   int outFlags = 0;
   int64_t outTimeUs = -1;
 
   nsresult rv = mEncoder->GetNextEncodedFrame(&frameData, &outTimeUs, &outFlags,
--- a/dom/media/encoder/OmxTrackEncoder.h
+++ b/dom/media/encoder/OmxTrackEncoder.h
@@ -21,19 +21,18 @@ class OMXAudioEncoder;
  * Bean platform.
  */
 
 namespace mozilla {
 
 class OmxVideoTrackEncoder: public VideoTrackEncoder
 {
 public:
-  OmxVideoTrackEncoder()
-    : VideoTrackEncoder()
-  {}
+  OmxVideoTrackEncoder();
+  ~OmxVideoTrackEncoder();
 
   already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
 
   nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE;
 
 protected:
   nsresult Init(int aWidth, int aHeight,
                 int aDisplayWidth, int aDisplayHeight,
@@ -41,19 +40,18 @@ protected:
 
 private:
   nsAutoPtr<android::OMXVideoEncoder> mEncoder;
 };
 
 class OmxAudioTrackEncoder : public AudioTrackEncoder
 {
 public:
-  OmxAudioTrackEncoder()
-    : AudioTrackEncoder()
-  {}
+  OmxAudioTrackEncoder();
+  ~OmxAudioTrackEncoder();
 
   already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE;
 
 protected:
   nsresult Init(int aChannels, int aSamplingRate) = 0;
 
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -152,17 +152,17 @@ MP4Reader::InitLayersBackendType()
 
 static bool sIsEMEEnabled = false;
 
 nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   PlatformDecoderModule::Init();
-  mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource(), &mDemuxerMonitor), &mDemuxerMonitor);
+  mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource(), &mDemuxerMonitor), GetDecoder()->GetTimestampOffset(), &mDemuxerMonitor);
 
   InitLayersBackendType();
 
   mAudio.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
 
   mVideo.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
--- a/dom/media/gtest/MockMediaResource.h
+++ b/dom/media/gtest/MockMediaResource.h
@@ -33,17 +33,17 @@ public:
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE {}
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) MOZ_OVERRIDE {}
   virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
     MOZ_OVERRIDE
   {
     return NS_OK;
   }
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
-                          uint32_t* aBytes);
+                          uint32_t* aBytes) MOZ_OVERRIDE;
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE
   {
     return NS_OK;
   }
   virtual int64_t Tell() MOZ_OVERRIDE { return 0; }
   virtual void Pin() MOZ_OVERRIDE {}
   virtual void Unpin() MOZ_OVERRIDE {}
   virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; }
@@ -52,17 +52,17 @@ public:
   virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE;
   virtual bool IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE
   {
     return false;
   }
   virtual bool IsSuspendedByCache() MOZ_OVERRIDE { return false; }
   virtual bool IsSuspended() MOZ_OVERRIDE { return false; }
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
-                                 uint32_t aCount)
+                                 uint32_t aCount) MOZ_OVERRIDE
   {
     return NS_OK;
   }
   virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
   virtual nsresult Open(nsIStreamListener** aStreamListener) MOZ_OVERRIDE;
   virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
     MOZ_OVERRIDE;
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE
--- a/dom/media/gtest/TestMP4Demuxer.cpp
+++ b/dom/media/gtest/TestMP4Demuxer.cpp
@@ -19,17 +19,17 @@ public:
 
   nsRefPtr<MockMediaResource> resource;
   Monitor mMonitor;
   nsAutoPtr<MP4Demuxer> demuxer;
 
   explicit MP4DemuxerBinding(const char* aFileName = "dash_dashinit.mp4")
     : resource(new MockMediaResource(aFileName))
     , mMonitor("TestMP4Demuxer monitor")
-    , demuxer(new MP4Demuxer(new MP4Stream(resource, &mMonitor), &mMonitor))
+    , demuxer(new MP4Demuxer(new MP4Stream(resource, &mMonitor), 0, &mMonitor))
   {
     EXPECT_EQ(NS_OK, resource->Open(nullptr));
   }
 
 private:
   virtual ~MP4DemuxerBinding()
   {
   }
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -232,29 +232,33 @@ public:
       return false;
     }
 
     uint32_t chunk_size = BigEndian::readUint32(aData);
     if (chunk_size < 8) {
       return false;
     }
 
-    return aData[4] == 'm' && aData[5] == 'o' && aData[6] == 'o' &&
-           aData[7] == 'f';
+    return (aData[4] == 'm' && aData[5] == 'o' && aData[6] == 'o' && aData[7] == 'f') ||
+           (aData[4] == 's' && aData[5] == 't' && aData[6] == 'y' && aData[7] == 'p');
   }
 
   bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
                                   int64_t& aStart, int64_t& aEnd)
   {
     MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
                                    // but mParser requires us to hold a monitor.
     bool initSegment = IsInitSegmentPresent(aData, aLength);
     if (initSegment) {
       mStream = new mp4_demuxer::BufferStream();
-      mParser = new mp4_demuxer::MoofParser(mStream, 0, &mMonitor);
+      // We use a timestampOffset of 0 for ContainerParser, and require
+      // consumers of ParseStartAndEndTimestamps to add their timestamp offset
+      // manually. This allows the ContainerParser to be shared across different
+      // timestampOffsets.
+      mParser = new mp4_demuxer::MoofParser(mStream, 0, 0, &mMonitor);
     } else if (!mStream || !mParser) {
       return false;
     }
 
     mStream->AppendBytes(aData, aLength);
     nsTArray<MediaByteRange> byteRanges;
     byteRanges.AppendElement(mStream->GetByteRange());
     mParser->RebuildFragmentedIndex(byteRanges);
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -124,20 +124,20 @@ MediaSourceDecoder::AttachMediaSource(do
 void
 MediaSourceDecoder::DetachMediaSource()
 {
   MOZ_ASSERT(mMediaSource && NS_IsMainThread());
   mMediaSource = nullptr;
 }
 
 already_AddRefed<SourceBufferDecoder>
-MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
+MediaSourceDecoder::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset)
 {
   MOZ_ASSERT(mReader);
-  return mReader->CreateSubDecoder(aType);
+  return mReader->CreateSubDecoder(aType, aTimestampOffset);
 }
 
 void
 MediaSourceDecoder::AddTrackBuffer(TrackBuffer* aTrackBuffer)
 {
   MOZ_ASSERT(mReader);
   mReader->AddTrackBuffer(aTrackBuffer);
 }
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -41,17 +41,18 @@ public:
 
   virtual void Shutdown() MOZ_OVERRIDE;
 
   static already_AddRefed<MediaResource> CreateResource(nsIPrincipal* aPrincipal = nullptr);
 
   void AttachMediaSource(dom::MediaSource* aMediaSource);
   void DetachMediaSource();
 
-  already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
+  already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType,
+                                                         int64_t aTimestampOffset /* microseconds */);
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   void Ended();
   bool IsExpectingMoreData() MOZ_OVERRIDE;
 
   void SetDecodedDuration(int64_t aDuration);
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -452,24 +452,24 @@ CreateReaderForType(const nsACString& aT
       MP4Decoder::IsEnabled()) {
     return new MP4Reader(aDecoder);
   }
 #endif
   return DecoderTraits::CreateReader(aType, aDecoder);
 }
 
 already_AddRefed<SourceBufferDecoder>
-MediaSourceReader::CreateSubDecoder(const nsACString& aType)
+MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset)
 {
   if (IsShutdown()) {
     return nullptr;
   }
   MOZ_ASSERT(GetTaskQueue());
   nsRefPtr<SourceBufferDecoder> decoder =
-    new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder);
+    new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder, aTimestampOffset);
   nsRefPtr<MediaDecoderReader> reader(CreateReaderForType(aType, decoder));
   if (!reader) {
     return nullptr;
   }
 
   // MSE uses a start time of 0 everywhere. Set that immediately on the
   // subreader to make sure that it's always in a state where we can invoke
   // GetBuffered on it.
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -94,17 +94,18 @@ public:
   void ReadUpdatedMetadata(MediaInfo* aInfo) MOZ_OVERRIDE;
   nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
        int64_t aCurrentTime) MOZ_OVERRIDE;
 
   // Acquires the decoder monitor, and is thus callable on any thread.
   nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
 
-  already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
+  already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType,
+                                                         int64_t aTimestampOffset /* microseconds */);
 
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
 
   virtual void BreakCycles();
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -330,17 +330,19 @@ void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
   if (!PrepareAppend(aRv)) {
     return;
   }
   StartUpdating();
 
-  if (!mTrackBuffer->AppendData(aData, aLength)) {
+  MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
+             "We don't handle timestampOffset for sequence mode yet");
+  if (!mTrackBuffer->AppendData(aData, aLength, mTimestampOffset * USECS_PER_S)) {
     Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
     ErrorResult dummy;
     mMediaSource->EndOfStream(decodeError, dummy);
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (mTrackBuffer->HasInitSegment()) {
--- a/dom/media/mediasource/SourceBufferDecoder.cpp
+++ b/dom/media/mediasource/SourceBufferDecoder.cpp
@@ -28,20 +28,22 @@ namespace layers {
 
 class ImageContainer;
 
 } // namespace layers
 
 NS_IMPL_ISUPPORTS0(SourceBufferDecoder)
 
 SourceBufferDecoder::SourceBufferDecoder(MediaResource* aResource,
-                                         AbstractMediaDecoder* aParentDecoder)
+                                         AbstractMediaDecoder* aParentDecoder,
+                                         int64_t aTimestampOffset)
   : mResource(aResource)
   , mParentDecoder(aParentDecoder)
   , mReader(nullptr)
+  , mTimestampOffset(aTimestampOffset)
   , mMediaDuration(-1)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(SourceBufferDecoder);
 }
 
 SourceBufferDecoder::~SourceBufferDecoder()
 {
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -27,25 +27,27 @@ class TimeRanges;
 
 } // namespace dom
 
 class SourceBufferDecoder MOZ_FINAL : public AbstractMediaDecoder
 {
 public:
   // This class holds a weak pointer to MediaResource.  It's the responsibility
   // of the caller to manage the memory of the MediaResource object.
-  SourceBufferDecoder(MediaResource* aResource, AbstractMediaDecoder* aParentDecoder);
+  SourceBufferDecoder(MediaResource* aResource, AbstractMediaDecoder* aParentDecoder,
+                      int64_t aTimestampOffset /* microseconds */);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
   virtual bool IsShutdown() const MOZ_FINAL MOZ_OVERRIDE;
   virtual bool IsTransportSeekable() MOZ_FINAL MOZ_OVERRIDE;
   virtual bool OnDecodeThread() const MOZ_FINAL MOZ_OVERRIDE;
   virtual bool OnStateMachineThread() const MOZ_FINAL MOZ_OVERRIDE;
+  virtual int64_t GetTimestampOffset() const MOZ_FINAL MOZ_OVERRIDE { return mTimestampOffset; }
   virtual int64_t GetMediaDuration() MOZ_FINAL MOZ_OVERRIDE;
   virtual layers::ImageContainer* GetImageContainer() MOZ_FINAL MOZ_OVERRIDE;
   virtual MediaDecoderOwner* GetOwner() MOZ_FINAL MOZ_OVERRIDE;
   virtual SourceBufferResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE;
   virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE;
   virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
   virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
   virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo) MOZ_FINAL MOZ_OVERRIDE;
@@ -124,16 +126,17 @@ private:
 
   // Our TrackBuffer's task queue, this is only non-null during initialization.
   RefPtr<MediaTaskQueue> mTaskQueue;
 
   nsRefPtr<MediaResource> mResource;
 
   AbstractMediaDecoder* mParentDecoder;
   nsRefPtr<MediaDecoderReader> mReader;
+  int64_t mTimestampOffset;
   int64_t mMediaDuration;
 
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> mCDMProxy;
 #endif
 };
 
 } // namespace mozilla
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -34,16 +34,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #endif
 
 namespace mozilla {
 
 TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
   : mParentDecoder(aParentDecoder)
   , mType(aType)
   , mLastStartTimestamp(0)
+  , mLastTimestampOffset(0)
   , mShutdown(false)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
   mParser = ContainerParser::CreateForMIMEType(aType);
   mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   aParentDecoder->AddTrackBuffer(this);
   mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
   MSE_DEBUG("TrackBuffer(%p) created for parent decoder %p", this, aParentDecoder);
@@ -80,19 +81,19 @@ public:
 
   ~DecodersToInitialize()
   {
     for (size_t i = 0; i < mDecoders.Length(); i++) {
       mOwner->QueueInitializeDecoder(mDecoders[i]);
     }
   }
 
-  bool NewDecoder()
+  bool NewDecoder(int64_t aTimestampOffset)
   {
-    nsRefPtr<SourceBufferDecoder> decoder = mOwner->NewDecoder();
+    nsRefPtr<SourceBufferDecoder> decoder = mOwner->NewDecoder(aTimestampOffset);
     if (!decoder) {
       return false;
     }
     mDecoders.AppendElement(decoder);
     return true;
   }
 
 private:
@@ -133,43 +134,46 @@ TrackBuffer::ContinueShutdown()
 
   mInitializedDecoders.Clear();
   mParentDecoder = nullptr;
 
   mShutdownPromise.Resolve(true, __func__);
 }
 
 bool
-TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
+TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DecodersToInitialize decoders(this);
   // TODO: Run more of the buffer append algorithm asynchronously.
   if (mParser->IsInitSegmentPresent(aData, aLength)) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
-    if (!decoders.NewDecoder()) {
+    if (!decoders.NewDecoder(aTimestampOffset)) {
       return false;
     }
   } else if (!mParser->HasInitData()) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
     return false;
   }
 
   int64_t start, end;
   if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
+    start += aTimestampOffset;
+    end += aTimestampOffset;
     if (mParser->IsMediaSegmentPresent(aData, aLength) &&
         mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
+         mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment)) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       // This data is earlier in the timeline than data we have already
       // processed, so we must create a new decoder to handle the decoding.
-      if (!decoders.NewDecoder()) {
+      if (!decoders.NewDecoder(aTimestampOffset)) {
         return false;
       }
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
       const nsTArray<uint8_t>& initData = mParser->InitData();
       AppendDataToCurrentResource(initData.Elements(), initData.Length());
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
@@ -326,33 +330,34 @@ TrackBuffer::Buffered(dom::TimeRanges* a
       aRanges->Union(r, double(mParser->GetRoundingError()) / USECS_PER_S);
     }
   }
 
   return highestEndTime;
 }
 
 already_AddRefed<SourceBufferDecoder>
-TrackBuffer::NewDecoder()
+TrackBuffer::NewDecoder(int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mParentDecoder);
 
   DiscardDecoder();
 
-  nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType);
+  nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset);
   if (!decoder) {
     return nullptr;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
 
   mLastStartTimestamp = 0;
   mLastEndTimestamp.reset();
+  mLastTimestampOffset = aTimestampOffset;
 
   decoder->SetTaskQueue(mTaskQueue);
   return decoder.forget();
 }
 
 bool
 TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
 {
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -34,17 +34,17 @@ public:
 
   TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
 
   nsRefPtr<ShutdownPromise> Shutdown();
 
   // Append data to the current decoder.  Also responsible for calling
   // NotifyDataArrived on the decoder to keep buffered range computation up
   // to date.  Returns false if the append failed.
-  bool AppendData(const uint8_t* aData, uint32_t aLength);
+  bool AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset /* microseconds */);
   bool EvictData(uint32_t aThreshold);
   void EvictBefore(double aTime);
 
   // Returns the highest end time of all of the buffered ranges in the
   // decoders managed by this TrackBuffer, and returns the union of the
   // decoders buffered ranges in aRanges. This may be called on any thread.
   double Buffered(dom::TimeRanges* aRanges);
 
@@ -87,17 +87,17 @@ private:
   friend class DecodersToInitialize;
   ~TrackBuffer();
 
   // Create a new decoder, set mCurrentDecoder to the new decoder and
   // returns it. The new decoder must be queued using QueueInitializeDecoder
   // for initialization.
   // The decoder is not considered initialized until it is added to
   // mInitializedDecoders.
-  already_AddRefed<SourceBufferDecoder> NewDecoder();
+  already_AddRefed<SourceBufferDecoder> NewDecoder(int64_t aTimestampOffset /* microseconds */);
 
   // Helper for AppendData, ensures NotifyDataArrived is called whenever
   // data is appended to the current decoder's SourceBufferResource.
   bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
 
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
 
@@ -151,16 +151,19 @@ private:
   nsRefPtr<MediaSourceDecoder> mParentDecoder;
   const nsCString mType;
 
   // The last start and end timestamps added to the TrackBuffer via
   // AppendData.  Accessed on the main thread only.
   int64_t mLastStartTimestamp;
   Maybe<int64_t> mLastEndTimestamp;
 
+  // The timestamp offset used by our current decoder, in microseconds.
+  int64_t mLastTimestampOffset;
+
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
   bool mDecoderPerSegment;
   bool mShutdown;
--- a/dom/media/omx/AudioOffloadPlayer.cpp
+++ b/dom/media/omx/AudioOffloadPlayer.cpp
@@ -50,28 +50,28 @@ PRLogModuleInfo* gAudioOffloadPlayerLog;
 #define AUDIO_OFFLOAD_LOG(type, msg)
 #endif
 
 // maximum time in paused state when offloading audio decompression.
 // When elapsed, the AudioSink is destroyed to allow the audio DSP to power down.
 static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
 
 AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
-  mObserver(aObserver),
-  mInputBuffer(nullptr),
-  mSampleRate(0),
-  mSeeking(false),
-  mSeekDuringPause(false),
-  mReachedEOS(false),
-  mSeekTimeUs(0),
-  mStartPosUs(0),
-  mPositionTimeMediaUs(-1),
   mStarted(false),
   mPlaying(false),
-  mIsElementVisible(true)
+  mSeeking(false),
+  mReachedEOS(false),
+  mSeekDuringPause(false),
+  mIsElementVisible(true),
+  mSampleRate(0),
+  mStartPosUs(0),
+  mSeekTimeUs(0),
+  mPositionTimeMediaUs(-1),
+  mInputBuffer(nullptr),
+  mObserver(aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
 #ifdef PR_LOGGING
   if (!gAudioOffloadPlayerLog) {
     gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer");
   }
 #endif
--- a/dom/media/omx/AudioOutput.cpp
+++ b/dom/media/omx/AudioOutput.cpp
@@ -28,21 +28,21 @@ extern PRLogModuleInfo* gAudioOffloadPla
   PR_LOG(gAudioOffloadPlayerLog, type, msg)
 #else
 #define AUDIO_OFFLOAD_LOG(type, msg)
 #endif
 
 using namespace android;
 
 AudioOutput::AudioOutput(int aSessionId, int aUid) :
+  mCallbackCookie(nullptr),
   mCallback(nullptr),
-  mCallbackCookie(nullptr),
   mCallbackData(nullptr),
-  mSessionId(aSessionId),
-  mUid(aUid)
+  mUid(aUid),
+  mSessionId(aSessionId)
 {
 #ifdef PR_LOGGING
   if (!gAudioOffloadPlayerLog) {
     gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer");
   }
 #endif
 }
 
--- a/dom/media/omx/AudioOutput.h
+++ b/dom/media/omx/AudioOutput.h
@@ -21,18 +21,16 @@
 #define AUDIOOUTPUT_H_
 
 #include <stagefright/foundation/ABase.h>
 #include <utils/Mutex.h>
 #include <AudioTrack.h>
 
 #include "AudioSink.h"
 
-#define LOG_TAG "AudioOffloadPlayer"
-
 namespace mozilla {
 
 /**
  * Stripped version of Android KK MediaPlayerService::AudioOutput class
  * Android MediaPlayer uses AudioOutput as a wrapper to handle
  * Android::AudioTrack
  * Similarly to ease handling offloaded tracks, part of AudioOutput is used here
  */
--- a/dom/media/omx/MediaCodecProxy.cpp
+++ b/dom/media/omx/MediaCodecProxy.cpp
@@ -7,19 +7,18 @@
 #include "MediaCodecProxy.h"
 #include <string.h>
 #include <binder/IPCThreadState.h>
 #include <stagefright/foundation/ABuffer.h>
 #include <stagefright/foundation/ADebug.h>
 #include <stagefright/MetaData.h>
 #include "stagefright/MediaErrors.h"
 
-#define LOG_TAG "MediaCodecProxy"
 #include <android/log.h>
-#define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define MCP_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "MediaCodecProxy", __VA_ARGS__)
 #define TIMEOUT_DEQUEUE_INPUTBUFFER_MS 1000000ll
 
 namespace android {
 
 // General Template: MediaCodec::getOutputGraphicBufferFromIndex(...)
 template <typename T, bool InterfaceSupported>
 struct OutputGraphicBufferStub
 {
@@ -178,17 +177,17 @@ MediaCodecProxy::releaseCodec()
   {
     // Write Lock for mCodec
     RWLock::AutoWLock awl(mCodecLock);
 
     codec = mCodec;
 
     // Release MediaCodec
     if (mCodec != nullptr) {
-      status_t err = mCodec->stop();
+      mCodec->stop();
       mCodec->release();
       mCodec = nullptr;
     }
   }
 
   while (codec.promote() != nullptr) {
     // this value come from stagefright's AwesomePlayer.
     usleep(1000);
@@ -489,86 +488,85 @@ MediaCodecProxy::resourceCanceled()
   if (listener != nullptr) {
     listener->codecCanceled();
   }
 }
 
 bool MediaCodecProxy::Prepare()
 {
 
-  status_t err;
   if (start() != OK) {
-    ALOG("Couldn't start MediaCodec");
+    MCP_LOG("Couldn't start MediaCodec");
     return false;
   }
   if (getInputBuffers(&mInputBuffers) != OK) {
-    ALOG("Couldn't get input buffers from MediaCodec");
+    MCP_LOG("Couldn't get input buffers from MediaCodec");
     return false;
   }
   if (getOutputBuffers(&mOutputBuffers) != OK) {
-    ALOG("Couldn't get output buffers from MediaCodec");
+    MCP_LOG("Couldn't get output buffers from MediaCodec");
     return false;
   }
 
   return true;
 }
 
 bool MediaCodecProxy::UpdateOutputBuffers()
 {
   if (mCodec == nullptr) {
-    ALOG("MediaCodec has not been inited from input!");
+    MCP_LOG("MediaCodec has not been inited from input!");
     return false;
   }
 
   status_t err = getOutputBuffers(&mOutputBuffers);
   if (err != OK){
-    ALOG("Couldn't update output buffers from MediaCodec");
+    MCP_LOG("Couldn't update output buffers from MediaCodec");
     return false;
   }
   return true;
 }
 
 status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
                                 int64_t aTimestampUsecs, uint64_t aflags)
 {
   if (mCodec == nullptr) {
-    ALOG("MediaCodec has not been inited from input!");
+    MCP_LOG("MediaCodec has not been inited from input!");
     return NO_INIT;
   }
 
   size_t index;
   status_t err = dequeueInputBuffer(&index, TIMEOUT_DEQUEUE_INPUTBUFFER_MS);
   if (err != OK) {
-    ALOG("dequeueInputBuffer returned %d", err);
+    MCP_LOG("dequeueInputBuffer returned %d", err);
     return err;
   }
 
   if (aData) {
     const sp<ABuffer> &dstBuffer = mInputBuffers.itemAt(index);
 
     CHECK_LE(aDataSize, dstBuffer->capacity());
     dstBuffer->setRange(0, aDataSize);
 
     memcpy(dstBuffer->data(), aData, aDataSize);
     err = queueInputBuffer(index, 0, dstBuffer->size(), aTimestampUsecs, aflags);
   } else {
     err = queueInputBuffer(index, 0, 0, 0ll, MediaCodec::BUFFER_FLAG_EOS);
   }
 
   if (err != OK) {
-    ALOG("queueInputBuffer returned %d", err);
+    MCP_LOG("queueInputBuffer returned %d", err);
     return err;
   }
   return err;
 }
 
 status_t MediaCodecProxy::Output(MediaBuffer** aBuffer, int64_t aTimeoutUs)
 {
   if (mCodec == nullptr) {
-    ALOG("MediaCodec has not been inited from output!");
+    MCP_LOG("MediaCodec has not been inited from output!");
     return NO_INIT;
   }
 
   size_t index = 0;
   size_t offset = 0;
   size_t size = 0;
   int64_t timeUs = 0;
   uint32_t flags = 0;
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -133,25 +133,25 @@ void MediaOmxReader::CancelProcessCached
   MOZ_ASSERT(NS_IsMainThread());
   MutexAutoLock lock(mMutex);
   mIsShutdown = true;
 }
 
 MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
   : MediaOmxCommonReader(aDecoder)
   , mMutex("MediaOmxReader.Data")
-  , mMP3FrameParser(-1)
   , mHasVideo(false)
   , mHasAudio(false)
   , mVideoSeekTimeUs(-1)
   , mAudioSeekTimeUs(-1)
+  , mLastParserDuration(-1)
   , mSkipCount(0)
   , mUseParserDuration(false)
-  , mLastParserDuration(-1)
   , mIsShutdown(false)
+  , mMP3FrameParser(-1)
   , mIsWaitingResources(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
 
--- a/dom/media/omx/OMXCodecDescriptorUtil.cpp
+++ b/dom/media/omx/OMXCodecDescriptorUtil.cpp
@@ -79,18 +79,18 @@ struct AVCParamSet {
     return mSize + 2; // 2 more bytes for length value.
   }
 
   // Append 2 bytes length value and NAL unit bitstream to aOutputBuf.
   void AppendTo(nsTArray<uint8_t>* aOutputBuf)
   {
     // 2 bytes length value.
     uint8_t size[] = {
-      (mSize & 0xFF00) >> 8, // MSB.
-      mSize & 0x00FF,        // LSB.
+      uint8_t((mSize & 0xFF00) >> 8), // MSB.
+      uint8_t(mSize & 0x00FF),        // LSB.
     };
     aOutputBuf->AppendElements(size, sizeof(size));
 
     aOutputBuf->AppendElements(mPtr, mSize);
   }
 
   const uint8_t* mPtr; // Pointer to NAL unit.
   const size_t mSize;  // NAL unit length in bytes.
@@ -184,17 +184,16 @@ GenerateAVCDescriptorBlob(sp<AMessage>& 
     }
   }
 
   MOZ_ASSERT(sps != nullptr && pps != nullptr);
   if (sps == nullptr || pps == nullptr) {
     return ERROR_MALFORMED;
   }
 
-  status_t result = OK;
   if (aFormat == OMXVideoEncoder::BlobFormat::AVC_NAL) {
     // SPS + PPS.
     aOutputBuf->AppendElements(sps->data(), sps->size());
     aOutputBuf->AppendElements(pps->data(), pps->size());
     return OK;
   } else {
     status_t result = ConvertParamSetsToDescriptorBlob(sps, pps, aOutputBuf);
     MOZ_ASSERT(result == OK);
--- a/dom/media/omx/OMXCodecProxy.cpp
+++ b/dom/media/omx/OMXCodecProxy.cpp
@@ -50,21 +50,21 @@ OMXCodecProxy::OMXCodecProxy(
         const sp<MetaData> &meta,
         bool createEncoder,
         const sp<MediaSource> &source,
         const char *matchComponentName,
         uint32_t flags,
         const sp<ANativeWindow> &nativeWindow)
     : mOMX(omx),
       mSrcMeta(meta),
+      mComponentName(nullptr),
       mIsEncoder(createEncoder),
-      mSource(source),
-      mComponentName(nullptr),
       mFlags(flags),
       mNativeWindow(nativeWindow),
+      mSource(source),
       mState(MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE)
 {
 }
 
 OMXCodecProxy::~OMXCodecProxy()
 {
   mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
 
--- a/dom/media/omx/OMXCodecWrapper.cpp
+++ b/dom/media/omx/OMXCodecWrapper.cpp
@@ -282,21 +282,21 @@ ConvertPlanarYCbCrToNV12(const PlanarYCb
              ySize.height % uvSize.height == 0);
   size_t uvWidth = ySize.width / 2;
   size_t uvHeight = ySize.height / 2;
   size_t horiSubsample = uvSize.width / uvWidth;
   size_t uPixStride = horiSubsample * (1 + aSource->mCbSkip);
   size_t vPixStride = horiSubsample * (1 + aSource->mCrSkip);
   size_t lineStride = uvSize.height / uvHeight * aSource->mCbCrStride;
 
-  for (int i = 0; i < uvHeight; i++) {
+  for (size_t i = 0; i < uvHeight; i++) {
     // 1st pixel per line.
     uint8_t* uSrc = u;
     uint8_t* vSrc = v;
-    for (int j = 0; j < uvWidth; j++) {
+    for (size_t j = 0; j < uvWidth; j++) {
       *aDestination++ = *uSrc;
       *aDestination++ = *vSrc;
       // Pick next source pixel.
       uSrc += uPixStride;
       vSrc += vPixStride;
     }
     // Pick next source line.
     u += lineStride;
@@ -490,20 +490,20 @@ OMXVideoEncoder::AppendFrame(nsTArray<ui
 
   if (mBlobFormat == BlobFormat::AVC_NAL) {
     // Append NAL format data without modification.
     aOutputBuf->AppendElements(aData, aSize);
     return;
   }
   // Replace start code with data length.
   uint8_t length[] = {
-    (aSize >> 24) & 0xFF,
-    (aSize >> 16) & 0xFF,
-    (aSize >> 8) & 0xFF,
-    aSize & 0xFF,
+    uint8_t((aSize >> 24) & 0xFF),
+    uint8_t((aSize >> 16) & 0xFF),
+    uint8_t((aSize >> 8) & 0xFF),
+    uint8_t(aSize & 0xFF),
   };
   aOutputBuf->AppendElements(length, sizeof(length));
   aOutputBuf->AppendElements(aData + sizeof(length), aSize);
 }
 
 // MediaCodec::setParameters() is available only after API level 18.
 #if ANDROID_VERSION >= 18
 nsresult
@@ -587,18 +587,18 @@ public:
   InputBufferHelper(sp<MediaCodec>& aCodec, Vector<sp<ABuffer> >& aBuffers,
                     OMXAudioEncoder& aEncoder, int aInputFlags)
     : mCodec(aCodec)
     , mBuffers(aBuffers)
     , mOMXAEncoder(aEncoder)
     , mInputFlags(aInputFlags)
     , mIndex(0)
     , mData(nullptr)
+    , mCapicity(0)
     , mOffset(0)
-    , mCapicity(0)
   {}
 
   ~InputBufferHelper()
   {
     // Unflushed data in buffer.
     MOZ_ASSERT(!mData);
   }
 
@@ -903,24 +903,24 @@ OMXAudioEncoder::AppendDecoderConfig(nsT
   NS_ENSURE_TRUE(csdSize == 2, ERROR_MALFORMED);
   // Encoder output must be consistent with kAACFrameDuration:
   // 14th bit (frame length flag) == 0 => 1024 (kAACFrameDuration) samples.
   NS_ENSURE_TRUE((aData->data()[1] & 0x04) == 0, ERROR_MALFORMED);
 
   // Decoder config descriptor
   const uint8_t decConfig[] = {
     0x04,                   // Decoder config descriptor tag.
-    15 + csdSize,           // Size: following bytes + csd size.
+    uint8_t(15 + csdSize),  // Size: following bytes + csd size.
     0x40,                   // Object type: MPEG-4 audio.
     0x15,                   // Stream type: audio, reserved: 1.
     0x00, 0x03, 0x00,       // Buffer size: 768 (kAACFrameSize).
     0x00, 0x01, 0x77, 0x00, // Max bitrate: 96000 (kAACBitrate).
     0x00, 0x01, 0x77, 0x00, // Avg bitrate: 96000 (kAACBitrate).
     0x05,                   // Decoder specific descriptor tag.
-    csdSize,                // Data size.
+    uint8_t(csdSize),       // Data size.
   };
   // SL config descriptor.
   const uint8_t slConfig[] = {
     0x06, // SL config descriptor tag.
     0x01, // Size.
     0x02, // Fixed value.
   };
 
--- a/dom/media/omx/OmxDecoder.cpp
+++ b/dom/media/omx/OmxDecoder.cpp
@@ -28,19 +28,18 @@
 #include "MPAPI.h"
 #include "prlog.h"
 
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
 #include "OMXCodecProxy.h"
 #include "OmxDecoder.h"
 
-#define LOG_TAG "OmxDecoder"
 #include <android/log.h>
-#define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define OD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "OmxDecoder", __VA_ARGS__)
 
 #ifdef PR_LOGGING
 PRLogModuleInfo *gOmxDecoderLog;
 #define LOG(type, msg...) PR_LOG(gOmxDecoderLog, type, (msg))
 #else
 #define LOG(x...)
 #endif
 
@@ -60,23 +59,23 @@ OmxDecoder::OmxDecoder(MediaResource *aR
   mVideoHeight(0),
   mVideoColorFormat(0),
   mVideoStride(0),
   mVideoSliceHeight(0),
   mVideoRotation(0),
   mAudioChannels(-1),
   mAudioSampleRate(-1),
   mDurationUs(-1),
+  mVideoLastFrameTime(-1),
   mVideoBuffer(nullptr),
   mAudioBuffer(nullptr),
   mIsVideoSeeking(false),
   mAudioMetadataRead(false),
   mAudioPaused(false),
-  mVideoPaused(false),
-  mVideoLastFrameTime(-1)
+  mVideoPaused(false)
 {
   mLooper = new ALooper;
   mLooper->setName("OmxDecoder");
 
   mReflector = new AHandlerReflector<OmxDecoder>(this);
   // Register AMessage handler to ALooper.
   mLooper->registerHandler(mReflector);
   // Start ALooper thread.
@@ -114,17 +113,16 @@ static sp<IOMX> GetOMX()
 
 bool OmxDecoder::Init(sp<MediaExtractor>& extractor) {
 #ifdef PR_LOGGING
   if (!gOmxDecoderLog) {
     gOmxDecoderLog = PR_NewLogModule("OmxDecoder");
   }
 #endif
 
-  const char* extractorMime;
   sp<MetaData> meta = extractor->getMetaData();
 
   ssize_t audioTrackIndex = -1;
   ssize_t videoTrackIndex = -1;
 
   for (size_t i = 0; i < extractor->countTracks(); ++i) {
     sp<MetaData> meta = extractor->getTrackMetaData(i);
 
@@ -172,17 +170,16 @@ bool OmxDecoder::EnsureMetadata() {
   int64_t totalDurationUs = 0;
   int64_t durationUs = 0;
   if (mVideoTrack.get() && mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
     if (durationUs > totalDurationUs)
       totalDurationUs = durationUs;
   }
   if (mAudioTrack.get()) {
     durationUs = -1;
-    const char* audioMime;
     sp<MetaData> meta = mAudioTrack->getFormat();
 
     if ((durationUs == -1) && meta->findInt64(kKeyDuration, &durationUs)) {
       if (durationUs > totalDurationUs) {
         totalDurationUs = durationUs;
       }
     }
   }
@@ -599,17 +596,17 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
         seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
         findNextBuffer = true;
         {
           Mutex::Autolock autoLock(mSeekLock);
           mIsVideoSeeking = true;
         }
         continue;
       } else if (err != OK) {
-        ALOG("Unexpected error when seeking to %lld", aTimeUs);
+        OD_LOG("Unexpected error when seeking to %lld", aTimeUs);
         break;
       }
       // For some codecs, the length of first decoded frame after seek is 0.
       // Need to ignore it and continue to find the next one
       if (mVideoBuffer->range_length() == 0) {
         ReleaseVideoBuffer();
         findNextBuffer = true;
       }
--- a/dom/media/omx/mediaresourcemanager/MediaResourceManagerService.cpp
+++ b/dom/media/omx/mediaresourcemanager/MediaResourceManagerService.cpp
@@ -143,17 +143,17 @@ void MediaResourceManagerService::onMess
     return;
   }
 
   // Exit if no request.
   if (!mResources.hasRequest(type)) {
     return;
   }
 
-  const sp<IBinder>& req = mResources.nextRequest(type);
+  sp<IBinder> req = mResources.nextRequest(type);
   mResources.aquireResource(req, type, found);
   // Notify resource assignment to the client.
   sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(req);
   client->statusChanged(MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED);
   mResources.dequeueRequest(type);
 }
 
 void MediaResourceManagerService::cancelClientLocked(const sp<IBinder>& binder,
@@ -287,17 +287,17 @@ uint32_t MediaResourceManagerService::Re
     // Unsupported type.
     return 0;
   }
 
   const Fifo& queue = mMap.valueAt(found).mRequestQueue;
   return queue.size();
 }
 
-const sp<IBinder>& MediaResourceManagerService::ResourceTable::nextRequest(ResourceType type)
+sp<IBinder> MediaResourceManagerService::ResourceTable::nextRequest(ResourceType type)
 {
   ssize_t found = mMap.indexOfKey(type);
   if (found == NAME_NOT_FOUND) {
     // Unsupported type.
     return nullptr;
   }
 
   const Fifo& queue = mMap.valueAt(found).mRequestQueue;
@@ -328,17 +328,17 @@ status_t MediaResourceManagerService::Re
   Fifo& queue = mMap.editValueAt(found).mRequestQueue;
   queue.erase(queue.begin());
   return OK;
 }
 
 status_t MediaResourceManagerService::ResourceTable::forgetClient(const sp<IBinder>& client)
 {
   // Traverse all resources.
-  for (int i = 0; i < mMap.size(); i++) {
+  for (size_t i = 0; i < mMap.size(); i++) {
     forgetClient(client, mMap.keyAt(i));
   }
   return OK;
 }
 
 status_t MediaResourceManagerService::ResourceTable::forgetClient(const sp<IBinder>& client, ResourceType type)
 {
   MOZ_ASSERT(supportsType(type));
@@ -353,17 +353,17 @@ status_t MediaResourceManagerService::Re
       queue.erase(it);
       break;
     }
     it++;
   }
 
   // Revoke ownership for given client.
   Slots& slots = resources.mSlots;
-  for (int i = 0; i < slots.size(); i++) {
+  for (size_t i = 0; i < slots.size(); i++) {
     ResourceSlot& slot = slots.editItemAt(i);
     if (client.get() == slot.mClient.get()) {
       slot.mClient = nullptr;
     }
   }
 
   return OK;
 }
--- a/dom/media/omx/mediaresourcemanager/MediaResourceManagerService.h
+++ b/dom/media/omx/mediaresourcemanager/MediaResourceManagerService.h
@@ -94,17 +94,17 @@ private:
     bool supportsType(ResourceType type);
     ssize_t findAvailableResource(ResourceType type, size_t number_needed = 1);
     bool isOwnedByClient(const sp<IBinder>& client, ResourceType type, size_t index);
     status_t aquireResource(const sp<IBinder>& client, ResourceType type, size_t index);
     ResourceSlot* resourceOfTypeAt(ResourceType type, size_t index);
     // Request operations.
     bool hasRequest(ResourceType type);
     uint32_t countRequests(ResourceType type);
-    const sp<IBinder>& nextRequest(ResourceType type);
+    sp<IBinder> nextRequest(ResourceType type);
     status_t enqueueRequest(const sp<IBinder>& client, ResourceType type);
     status_t dequeueRequest(ResourceType type);
     status_t forgetClient(const sp<IBinder>& client, ResourceType type);
     status_t forgetClient(const sp<IBinder>& client);
 
     friend class MediaResourceManagerService;
 
     // A map for all types of supported resources.
--- a/dom/media/omx/mediaresourcemanager/moz.build
+++ b/dom/media/omx/mediaresourcemanager/moz.build
@@ -19,16 +19,23 @@ SOURCES += [
     'IMediaResourceManagerService.cpp',
     'MediaResourceHandler.cpp',
     'MediaResourceManagerClient.cpp',
     'MediaResourceManagerService.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
+# Suppress some GCC/clang warnings being treated as errors:
+#  - about multi-character constants which are used in codec-related code
+if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+  CXXFLAGS += [
+    '-Wno-error=multichar'
+  ]
+
 CXXFLAGS += [
     '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
         'frameworks/base/include',
         'frameworks/base/include/binder',
         'frameworks/base/include/utils',
         'frameworks/base/include/media/',
         'frameworks/base/include/media/stagefright/openmax',
         'frameworks/base/media/libstagefright/include',
--- a/dom/media/omx/moz.build
+++ b/dom/media/omx/moz.build
@@ -74,16 +74,26 @@ if CONFIG['ANDROID_VERSION'] >= '18':
         'I420ColorConverterHelper.cpp',
         'MediaCodecDecoder.cpp',
         'MediaCodecProxy.cpp',
         'MediaCodecReader.cpp',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
+# Suppress some GCC/clang warnings being treated as errors:
+#  - about attributes on forward declarations for types that are already
+#    defined, which complains about an important MOZ_EXPORT for android::AString
+#  - about multi-character constants which are used in codec-related code
+if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+  CXXFLAGS += [
+    '-Wno-error=attributes',
+    '-Wno-error=multichar'
+  ]
+
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/html',
     '/ipc/chromium/src',
     'mediaresourcemanager',
 ]
 
--- a/dom/media/webrtc/GonkCameraImage.cpp
+++ b/dom/media/webrtc/GonkCameraImage.cpp
@@ -21,17 +21,17 @@ GonkCameraImage::GonkCameraImage()
 GonkCameraImage::~GonkCameraImage()
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
   // mMediaBuffer must be cleared before destructor.
   MOZ_ASSERT(mMediaBuffer == nullptr);
 }
 
 nsresult
-GonkCameraImage::GetBuffer(android::MediaBuffer** aBuffer)
+GonkCameraImage::GetMediaBuffer(android::MediaBuffer** aBuffer)
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
 
   if (!mMediaBuffer) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
@@ -45,30 +45,30 @@ GonkCameraImage::GetBuffer(android::Medi
 bool
 GonkCameraImage::HasMediaBuffer()
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
   return mMediaBuffer != nullptr;
 }
 
 nsresult
-GonkCameraImage::SetBuffer(android::MediaBuffer* aBuffer)
+GonkCameraImage::SetMediaBuffer(android::MediaBuffer* aBuffer)
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
   MOZ_ASSERT(!mMediaBuffer);
 
   mMediaBuffer = aBuffer;
   mMediaBuffer->add_ref();
   mThread = NS_GetCurrentThread();
 
   return NS_OK;
 }
 
 nsresult
-GonkCameraImage::ClearBuffer()
+GonkCameraImage::ClearMediaBuffer()
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
 
   if (mMediaBuffer) {
     MOZ_ASSERT(NS_GetCurrentThread() == mThread);
     mMediaBuffer->release();
     mMediaBuffer = nullptr;
     mThread = nullptr;
--- a/dom/media/webrtc/GonkCameraImage.h
+++ b/dom/media/webrtc/GonkCameraImage.h
@@ -19,57 +19,57 @@ namespace mozilla {
 
 /**
  * GonkCameraImage has two parts. One is preview image which will be saved in
  * GrallocImage, another kind is the MediaBuffer keeps in mMediaBuffer
  * which is from gonk camera recording callback. The data in MediaBuffer is Gonk
  * shared memory based on android binder (IMemory), the actual format in IMemory
  * is platform dependent.
  * This instance is created in MediaEngine when the preview image arrives.
- * The MediaBuffer is attached to the current created GonkCameraImage via SetBuffer().
- * After sending this image to MediaStreamGraph by AppendToTrack(), ClearBuffer()
+ * The MediaBuffer is attached to the current created GonkCameraImage via SetMediaBuffer().
+ * After sending this image to MediaStreamGraph by AppendToTrack(), ClearMediaBuffer()
  * must be called to clear MediaBuffer to avoid MediaBuffer be kept in MSG thread.
  * The reason to keep MediaBuffer be accessed from MSG thread is MediaBuffer is
  * limited resource and it could cause frame rate jitter if MediaBuffer stay too
  * long in other threads.
  * So there will be 3 threads to accessed this class. First is camera preview
  * thread which creates an instance of this class and initialize the preview
  * image in the base class GrallocImage. Second is the camera recording
  * thread which attaches MediaBuffer and sends this image to MediaStreamDirectListener.
  * Third is the MSG thread via NotifyPull, the image should have preview image
  * only in NotifyPull.
  *
- * Note: SetBuffer() and GetBuffer() should be called from the same thread. It
- *       is forbidden to call GetBuffer() from other threads.
+ * Note: SetMediaBuffer() and GetMediaBuffer() should be called from the same 
+ *       thread. It is forbidden to call GetMediaBuffer() from other threads.
  */
 class GonkCameraImage : public layers::GrallocImage
 {
 public:
   GonkCameraImage();
 
   // The returned aBuffer has called aBuffer->add_ref() already, so it is caller's
   // duty to release aBuffer. It should be called from the same thread which
-  // called SetBuffer().
-  nsresult GetBuffer(android::MediaBuffer** aBuffer);
+  // called SetMediaBuffer().
+  nsresult GetMediaBuffer(android::MediaBuffer** aBuffer);
 
-  // Set MediaBuffer to image. It is caller's responsibility to call ClearBuffer()
+  // Set MediaBuffer to image. It is caller's responsibility to call ClearMediaBuffer()
   // after the MediaBuffer is sent via MediaStreamGraph.
-  nsresult SetBuffer(android::MediaBuffer* aBuffer);
+  nsresult SetMediaBuffer(android::MediaBuffer* aBuffer);
 
-  // It should be called from the same thread which called SetBuffer().
-  nsresult ClearBuffer();
+  // It should be called from the same thread which called SetMediaBuffer().
+  nsresult ClearMediaBuffer();
 
   bool HasMediaBuffer();
 
 protected:
   virtual ~GonkCameraImage();
 
   // mMonitor protects mMediaBuffer and mThread.
   ReentrantMonitor mMonitor;
   android::MediaBuffer* mMediaBuffer;
-  // Check if current thread is the same one which called SetBuffer().
+  // Check if current thread is the same one which called SetMediaBuffer().
   // It doesn't need to hold reference count.
   DebugOnly<nsIThread*> mThread;
 };
 
 } // namespace mozilla
 
 #endif /* GONKCAMERAIMAGE_H */
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -1,13 +1,14 @@
 /* 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 "MediaEngineGonkVideoSource.h"
 
+#undef LOG_TAG
 #define LOG_TAG "MediaEngineGonkVideoSource"
 
 #include <utils/Log.h>
 
 #include "GrallocImages.h"
 #include "mozilla/layers/GrallocTextureClient.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "VideoUtils.h"
@@ -795,17 +796,17 @@ MediaEngineGonkVideoSource::OnNewMediaBu
   }
 
   MonitorAutoLock enter(mMonitor);
   if (mImage) {
     if (mImage->AsGrallocImage()) {
       // MediaEngineGonkVideoSource expects that GrallocImage is GonkCameraImage.
       // See Bug 938034.
       GonkCameraImage* cameraImage = static_cast<GonkCameraImage*>(mImage.get());
-      cameraImage->SetBuffer(aBuffer);
+      cameraImage->SetMediaBuffer(aBuffer);
     } else {
       LOG(("mImage is non-GrallocImage"));
     }
 
     uint32_t len = mSources.Length();
     for (uint32_t i = 0; i < len; i++) {
       if (mSources[i]) {
         // Duration is 1 here.
@@ -816,16 +817,16 @@ MediaEngineGonkVideoSource::OnNewMediaBu
         // queued in MSG longer and longer as time going by in device like Frame)
         AppendToTrack(mSources[i], mImage, mTrackID, 1);
       }
     }
     if (mImage->AsGrallocImage()) {
       GonkCameraImage* cameraImage = static_cast<GonkCameraImage*>(mImage.get());
       // Clear MediaBuffer immediately, it prevents MediaBuffer is kept in
       // MediaStreamGraph thread.
-      cameraImage->ClearBuffer();
+      cameraImage->ClearMediaBuffer();
     }
   }
 
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.h
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.h
@@ -92,17 +92,17 @@ public:
   nsresult TakePhoto(PhotoCallback* aCallback) MOZ_OVERRIDE;
 
   // It sets the correct photo orientation via camera parameter according to
   // current screen orientation.
   nsresult UpdatePhotoOrientation();
 
   // It adds aBuffer to current preview image and sends this image to MediaStreamDirectListener
   // via AppendToTrack(). Due to MediaBuffer is limited resource, it will clear
-  // image's MediaBuffer by calling GonkCameraImage::ClearBuffer() before leaving
+  // image's MediaBuffer by calling GonkCameraImage::ClearMediaBuffer() before leaving
   // this function.
   nsresult OnNewMediaBufferFrame(android::MediaBuffer* aBuffer);
 
 protected:
   ~MediaEngineGonkVideoSource()
   {
     Shutdown();
   }
--- a/dom/media/webrtc/MediaEngineTabVideoSource.h
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.h
@@ -13,37 +13,37 @@ namespace mozilla {
 
 class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventListener, nsITimerCallback {
   public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDOMEVENTLISTENER
     NS_DECL_NSITIMERCALLBACK
     MediaEngineTabVideoSource();
 
-    virtual void GetName(nsAString_internal&);
-    virtual void GetUUID(nsAString_internal&);
+    virtual void GetName(nsAString_internal&) MOZ_OVERRIDE;
+    virtual void GetUUID(nsAString_internal&) MOZ_OVERRIDE;
     virtual nsresult Allocate(const VideoTrackConstraintsN &,
-                              const mozilla::MediaEnginePrefs&);
-    virtual nsresult Deallocate();
-    virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID);
-    virtual void SetDirectListeners(bool aHasDirectListeners) {};
+                              const mozilla::MediaEnginePrefs&) MOZ_OVERRIDE;
+    virtual nsresult Deallocate() MOZ_OVERRIDE;
+    virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) MOZ_OVERRIDE;
+    virtual void SetDirectListeners(bool aHasDirectListeners) MOZ_OVERRIDE {};
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) MOZ_OVERRIDE;
-    virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
-    virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t);
-    virtual bool IsFake();
-    virtual const MediaSourceType GetMediaSource() {
+    virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) MOZ_OVERRIDE;
+    virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) MOZ_OVERRIDE;
+    virtual bool IsFake() MOZ_OVERRIDE;
+    virtual const MediaSourceType GetMediaSource() MOZ_OVERRIDE {
       return MediaSourceType::Browser;
     }
     virtual bool SatisfiesConstraintSets(
-      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets)
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) MOZ_OVERRIDE
     {
       return true;
     }
 
-    virtual nsresult TakePhoto(PhotoCallback* aCallback)
+    virtual nsresult TakePhoto(PhotoCallback* aCallback) MOZ_OVERRIDE
     {
       return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     void Draw();
 
     class StartRunnable : public nsRunnable {
     public:
--- a/dom/media/webrtc/moz.build
+++ b/dom/media/webrtc/moz.build
@@ -55,16 +55,25 @@ UNIFIED_SOURCES += [
 ]
 
 EXPORTS.mozilla += [
     'PeerIdentity.h',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
+# Suppress some GCC/clang warnings being treated as errors:
+#  - about attributes on forward declarations for types that are already
+#    defined, which complains about important MOZ_EXPORT attributes for
+#    android API types
+if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+  CXXFLAGS += [
+    '-Wno-error=attributes'
+  ]
+
 FINAL_LIBRARY = 'xul'
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
 
 
 if CONFIG['_MSC_VER']:
   CXXFLAGS += [
     '-wd4275', # non dll-interface class used as base for dll-interface class
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
@@ -77,21 +77,21 @@ public:
   NS_IMETHOD Setup(nsISpeechTaskCallback* aCallback,
                    uint32_t aChannels, uint32_t aRate, uint8_t argc) MOZ_OVERRIDE;
 
   NS_IMETHOD SendAudio(JS::Handle<JS::Value> aData, JS::Handle<JS::Value> aLandmarks,
                        JSContext* aCx) MOZ_OVERRIDE;
 
   NS_IMETHOD SendAudioNative(int16_t* aData, uint32_t aDataLen) MOZ_OVERRIDE;
 
-  virtual void Pause();
+  virtual void Pause() MOZ_OVERRIDE;
 
-  virtual void Resume();
+  virtual void Resume() MOZ_OVERRIDE;
 
-  virtual void Cancel();
+  virtual void Cancel() MOZ_OVERRIDE;
 
 private:
   SpeechSynthesisRequestChild* mActor;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -63,17 +63,17 @@ public:
       case EVENT_REMOVED:
         mSpeechTask = nullptr;
         break;
       default:
         break;
     }
   }
 
-  virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked)
+  virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) MOZ_OVERRIDE
   {
     if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
       mStarted = true;
       nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1745,17 +1745,17 @@ NPObjWrapper_Convert(JSContext *cx, JS::
 }
 
 static void
 NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   if (npobj) {
     if (sNPObjWrappers.ops) {
-      PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE);
+      PL_DHashTableRemove(&sNPObjWrappers, npobj);
     }
   }
 
   if (!sDelayedReleases)
     sDelayedReleases = new nsTArray<NPObject*>;
   sDelayedReleases->AppendElement(npobj);
 }
 
@@ -1774,17 +1774,17 @@ NPObjWrapper_ObjectMoved(JSObject *obj, 
     return;
   }
 
   // The hazard analysis thinks that PL_DHashTableOperate() can GC but this is
   // not possible if we pass PL_DHASH_LOOKUP.
   JS::AutoSuppressGCAnalysis nogc;
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
+    (PL_DHashTableLookup(&sNPObjWrappers, npobj));
   MOZ_ASSERT(PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj);
   MOZ_ASSERT(entry->mJSObj == old);
   entry->mJSObj = obj;
 }
 
 static bool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
 {
@@ -1827,17 +1827,17 @@ nsNPObjWrapper::OnDestroy(NPObject *npob
 
   if (!sNPObjWrappers.ops) {
     // No hash yet (or any more), no used wrappers available.
 
     return;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
+    (PL_DHashTableLookup(&sNPObjWrappers, npobj));
 
   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
     // Found a live NPObject wrapper, null out its JSObjects' private
     // data.
 
     ::JS_SetPrivate(entry->mJSObj, nullptr);
 
     // Remove the npobj from the hash now that it went away.
@@ -1878,17 +1878,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   if (!sNPObjWrappers.ops) {
     // No hash yet (or any more), initialize it.
     if (!CreateNPObjWrapperTable()) {
       return nullptr;
     }
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
 
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
@@ -1912,17 +1912,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, js::Jsvalify(&sNPObjectJSWrapperClass),
                                                JS::NullPtr(), JS::NullPtr()));
 
   if (generation != sNPObjWrappers.Generation()) {
       // Reload entry if the JS_NewObject call caused a GC and reallocated
       // the table (see bug 445229). This is guaranteed to succeed.
 
       entry = static_cast<NPObjWrapperHashEntry *>
-        (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
+        (PL_DHashTableLookup(&sNPObjWrappers, npobj));
       NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry),
                    "Hashtable didn't find what we just added?");
   }
 
   if (!obj) {
     // OOM? Remove the stale entry from the hash.
 
     PL_DHashTableRawRemove(&sNPObjWrappers, entry);
@@ -2044,17 +2044,17 @@ static NPP
 LookupNPP(NPObject *npobj)
 {
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
     return o->mNpp;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
 
   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
     return nullptr;
   }
 
   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
 
   return entry->mNpp;
--- a/dom/svg/nsSVGPathGeometryElement.cpp
+++ b/dom/svg/nsSVGPathGeometryElement.cpp
@@ -82,17 +82,19 @@ nsSVGPathGeometryElement::GetOrBuildPath
   bool cacheable  = aDrawTarget.GetBackendType() ==
                       gfxPlatform::GetPlatform()->GetContentBackend();
 
   // Checking for and returning mCachedPath before checking the pref means
   // that the pref is only live on page reload (or app restart for SVG in
   // chrome). The benefit is that we avoid causing a CPU memory cache miss by
   // looking at the global variable that the pref's stored in.
   if (cacheable && mCachedPath) {
-    return mCachedPath;
+    if (aDrawTarget.GetBackendType() == mCachedPath->GetBackendType()) {
+      return mCachedPath;
+    }
   }
   RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(aFillRule);
   RefPtr<Path> path = BuildPath(builder);
   if (cacheable && NS_SVGPathCachingEnabled()) {
     mCachedPath = path;
   }
   return path.forget();
 }
--- a/dom/system/gonk/VolumeManager.cpp
+++ b/dom/system/gonk/VolumeManager.cpp
@@ -175,17 +175,16 @@ void VolumeManager::InitConfig()
   int n = 0;
   char line[255];
   const char *filename = "/system/etc/volume.cfg";
   if (!(fp = fopen(filename, "r"))) {
     LOG("Unable to open volume configuration file '%s' - ignoring", filename);
     return;
   }
   while(fgets(line, sizeof(line), fp)) {
-    const char *delim = " \t\n";
     n++;
 
     if (line[0] == '#')
       continue;
 
     nsCString commandline(line);
     nsCWhitespaceTokenizer tokenizer(commandline);
     if (!tokenizer.hasMoreTokens()) {
--- a/dom/webidl/Animatable.webidl
+++ b/dom/webidl/Animatable.webidl
@@ -7,11 +7,11 @@
  * http://dev.w3.org/fxtf/web-animations/#the-animatable-interface
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [NoInterfaceObject]
 interface Animatable {
-  [Pref="dom.animations-api.core.enabled"]
+  [Func="nsDocument::IsWebAnimationsEnabled"]
   sequence<AnimationPlayer> getAnimationPlayers();
 };
--- a/dom/webidl/Animation.webidl
+++ b/dom/webidl/Animation.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * http://dev.w3.org/fxtf/web-animations/#the-animation-interface
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.animations-api.core.enabled"]
+[Func="nsDocument::IsWebAnimationsEnabled"]
 interface Animation {
   // FIXME: |effect| should have type (AnimationEffect or EffectCallback)?
   // but we haven't implemented EffectCallback yet.
   [Cached,Pure]
   readonly attribute AnimationEffect? effect;
   // FIXME: This should be writeable (bug 1067769)
   readonly attribute Element?         target;
 };
--- a/dom/webidl/AnimationEffect.webidl
+++ b/dom/webidl/AnimationEffect.webidl
@@ -5,12 +5,12 @@
  *
  * The origin of this IDL file is
  * http://dev.w3.org/fxtf/web-animations/#the-animationeffect-interface
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.animations-api.core.enabled"]
+[Func="nsDocument::IsWebAnimationsEnabled"]
 interface AnimationEffect {
   readonly attribute DOMString name;
 };
--- a/dom/webidl/AnimationPlayer.webidl
+++ b/dom/webidl/AnimationPlayer.webidl
@@ -7,17 +7,17 @@
  * http://dev.w3.org/fxtf/web-animations/#idl-def-AnimationPlayer
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
 
-[Pref="dom.animations-api.core.enabled"]
+[Func="nsDocument::IsWebAnimationsEnabled"]
 interface AnimationPlayer {
   // Bug 1049975
   //           attribute AnimationNode?     source;
   [Pure]
   readonly attribute Animation? source;
   readonly attribute AnimationTimeline timeline;
   [BinaryName="startTimeAsDouble"]
   readonly attribute double? startTime;
--- a/dom/webidl/AnimationTimeline.webidl
+++ b/dom/webidl/AnimationTimeline.webidl
@@ -5,16 +5,16 @@
  *
  * The origin of this IDL file is
  * http://dev.w3.org/fxtf/web-animations/#the-animationtimeline-interface
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.animations-api.core.enabled"]
+[Func="nsDocument::IsWebAnimationsEnabled"]
 interface AnimationTimeline {
   [BinaryName="currentTimeAsDouble"]
   readonly attribute double? currentTime;
   // Not yet implemented:
   // AnimationPlayer            play (optional TimedItem? source = null);
   // sequence<AnimationPlayer>  getAnimationPlayers ();
 };
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -282,17 +282,17 @@ partial interface Document {
   NodeList  querySelectorAll(DOMString selectors);
 
   //(Not implemented)Element?  find(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
   //(Not implemented)NodeList  findAll(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
 };
 
 // http://dev.w3.org/fxtf/web-animations/#extensions-to-the-document-interface
 partial interface Document {
-  [Pref="dom.animations-api.core.enabled"]
+  [Func="nsDocument::IsWebAnimationsEnabled"]
   readonly attribute AnimationTimeline timeline;
 };
 
 //  Mozilla extensions of various sorts
 partial interface Document {
   // nsIDOMDocumentXBL.  Wish we could make these [ChromeOnly], but
   // that would likely break bindings running with the page principal.
   [Func="IsChromeOrXBL"]
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -777,24 +777,22 @@ XULDocument::AddBroadcastListenerFor(Ele
         if (! mBroadcasterMap) {
             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
     }
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
-                   (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
-                                            PL_DHASH_LOOKUP));
+                   (PL_DHashTableLookup(mBroadcasterMap, &aBroadcaster));
 
     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
         entry =
             static_cast<BroadcasterMapEntry*>
-                       (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
-                                                PL_DHASH_ADD));
+                       (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster));
 
         if (! entry) {
             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
         entry->mBroadcaster = &aBroadcaster;
 
@@ -845,34 +843,32 @@ XULDocument::RemoveBroadcastListenerFor(
 {
     // If we haven't added any broadcast listeners, then there sure
     // aren't any to remove.
     if (! mBroadcasterMap)
         return;
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
-                   (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
-                                            PL_DHASH_LOOKUP));
+                   (PL_DHashTableLookup(mBroadcasterMap, &aBroadcaster));
 
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
         nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
         for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
             BroadcastListener* bl =
                 static_cast<BroadcastListener*>(entry->mListeners[i]);
 
             nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
 
             if (blListener == &aListener && bl->mAttribute == attr) {
                 entry->mListeners.RemoveElementAt(i);
                 delete bl;
 
                 if (entry->mListeners.Count() == 0)
-                    PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
-                                         PL_DHASH_REMOVE);
+                    PL_DHashTableRemove(mBroadcasterMap, &aBroadcaster);
 
                 break;
             }
         }
     }
 }
 
 nsresult
@@ -969,18 +965,17 @@ XULDocument::AttributeChanged(nsIDocumen
     
     nsresult rv;
 
     // Synchronize broadcast listeners
     if (mBroadcasterMap &&
         CanBroadcast(aNameSpaceID, aAttribute)) {
         BroadcasterMapEntry* entry =
             static_cast<BroadcasterMapEntry*>
-                       (PL_DHashTableOperate(mBroadcasterMap, aElement,
-                                                PL_DHASH_LOOKUP));
+                       (PL_DHashTableLookup(mBroadcasterMap, aElement));
 
         if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
             // We've got listeners: push the value.
             nsAutoString value;
             bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
 
             int32_t i;
             for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
@@ -4166,17 +4161,17 @@ XULDocument::BroadcastAttributeChangeFro
 
     if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute))
         return rv;
 
     if (!aNode->IsElement())
         return rv;
 
     BroadcasterMapEntry* entry = static_cast<BroadcasterMapEntry*>
-        (PL_DHashTableOperate(mBroadcasterMap, aNode->AsElement(), PL_DHASH_LOOKUP));
+        (PL_DHashTableLookup(mBroadcasterMap, aNode->AsElement()));
     if (!PL_DHASH_ENTRY_IS_BUSY(entry))
         return rv;
 
     // We've got listeners: push the value.
     int32_t i;
     for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
         BroadcastListener* bl = static_cast<BroadcastListener*>
             (entry->mListeners[i]);
--- a/dom/xul/templates/nsContentSupportMap.cpp
+++ b/dom/xul/templates/nsContentSupportMap.cpp
@@ -22,16 +22,16 @@ nsContentSupportMap::Finish()
 nsresult
 nsContentSupportMap::Remove(nsIContent* aElement)
 {
     if (!mMap.ops)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsIContent* child = aElement;
     do {
-        PL_DHashTableOperate(&mMap, child, PL_DHASH_REMOVE);
+        PL_DHashTableRemove(&mMap, child);
         child = child->GetNextNode(aElement);
     } while(child);
 
     return NS_OK;
 }
 
 
--- a/dom/xul/templates/nsContentSupportMap.h
+++ b/dom/xul/templates/nsContentSupportMap.h
@@ -23,31 +23,31 @@ class nsContentSupportMap {
 public:
     nsContentSupportMap() { Init(); }
     ~nsContentSupportMap() { Finish(); }
 
     nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch) {
         if (!mMap.ops)
             return NS_ERROR_NOT_INITIALIZED;
 
-        PLDHashEntryHdr* hdr = PL_DHashTableOperate(&mMap, aElement, PL_DHASH_ADD);
+        PLDHashEntryHdr* hdr = PL_DHashTableAdd(&mMap, aElement);
         if (!hdr)
             return NS_ERROR_OUT_OF_MEMORY;
 
         Entry* entry = reinterpret_cast<Entry*>(hdr);
         NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry");
         entry->mContent = aElement;
         entry->mMatch   = aMatch;
         return NS_OK; }
 
     bool Get(nsIContent* aElement, nsTemplateMatch** aMatch) {
         if (!mMap.ops)
             return false;
 
-        PLDHashEntryHdr* hdr = PL_DHashTableOperate(&mMap, aElement, PL_DHASH_LOOKUP);
+        PLDHashEntryHdr* hdr = PL_DHashTableLookup(&mMap, aElement);
         if (PL_DHASH_ENTRY_IS_FREE(hdr))
             return false;
 
         Entry* entry = reinterpret_cast<Entry*>(hdr);
         *aMatch = entry->mMatch;
         return true; }
 
     nsresult Remove(nsIContent* aElement);
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -31,44 +31,44 @@ protected:
 
 public:
     nsTemplateMap() { Init(); }
 
     ~nsTemplateMap() { Finish(); }
 
     void
     Put(nsIContent* aContent, nsIContent* aTemplate) {
-        NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(PL_DHashTableOperate(&mTable, aContent, PL_DHASH_LOOKUP)),
+        NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(PL_DHashTableLookup(&mTable, aContent)),
                      "aContent already in map");
 
         Entry* entry =
-            reinterpret_cast<Entry*>(PL_DHashTableOperate(&mTable, aContent, PL_DHASH_ADD));
+            reinterpret_cast<Entry*>(PL_DHashTableAdd(&mTable, aContent));
 
         if (entry) {
             entry->mContent = aContent;
             entry->mTemplate = aTemplate;
         }
     }
 
     void
     Remove(nsIContent* aContent) {
-        PL_DHashTableOperate(&mTable, aContent, PL_DHASH_REMOVE);
+        PL_DHashTableRemove(&mTable, aContent);
 
         for (nsIContent* child = aContent->GetFirstChild();
              child;
              child = child->GetNextSibling()) {
             Remove(child);
         }
     }
 
 
     void
     GetTemplateFor(nsIContent* aContent, nsIContent** aResult) {
         Entry* entry =
-            reinterpret_cast<Entry*>(PL_DHashTableOperate(&mTable, aContent, PL_DHASH_LOOKUP));
+            reinterpret_cast<Entry*>(PL_DHashTableLookup(&mTable, aContent));
 
         if (PL_DHASH_ENTRY_IS_BUSY(&entry->mHdr))
             NS_IF_ADDREF(*aResult = entry->mTemplate);
         else
             *aResult = nullptr;
     }
 
     void
--- a/embedding/components/commandhandler/nsCommandParams.cpp
+++ b/embedding/components/commandhandler/nsCommandParams.cpp
@@ -209,45 +209,42 @@ nsCommandParams::SetISupportsValue(const
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCommandParams::RemoveValue(const char* aName)
 {
   // PL_DHASH_REMOVE doesn't tell us if the entry was really removed, so we
   // return NS_OK unconditionally.
-  (void)PL_DHashTableOperate(&mValuesHash, (void *)aName, PL_DHASH_REMOVE);
+  (void)PL_DHashTableRemove(&mValuesHash, (void *)aName);
   return NS_OK;
 }
 
 nsCommandParams::HashEntry*
 nsCommandParams::GetNamedEntry(const char* aName)
 {
   HashEntry *foundEntry =
-    (HashEntry *)PL_DHashTableOperate(&mValuesHash, (void *)aName,
-                                      PL_DHASH_LOOKUP);
+    (HashEntry *)PL_DHashTableLookup(&mValuesHash, (void *)aName);
   if (PL_DHASH_ENTRY_IS_BUSY(foundEntry)) {
     return foundEntry;
   }
   return nullptr;
 }
 
 nsCommandParams::HashEntry*
 nsCommandParams::GetOrMakeEntry(const char* aName, uint8_t entryType)
 {
   HashEntry *foundEntry =
-    (HashEntry *)PL_DHashTableOperate(&mValuesHash, (void *)aName,
-                                      PL_DHASH_LOOKUP);
+    (HashEntry *)PL_DHashTableLookup(&mValuesHash, (void *)aName);
   if (PL_DHASH_ENTRY_IS_BUSY(foundEntry)) { // reuse existing entry
     foundEntry->Reset(entryType);
     return foundEntry;
   }
 
-  foundEntry = (HashEntry *)PL_DHashTableOperate(&mValuesHash, (void *)aName,
-                                                 PL_DHASH_ADD);
+  foundEntry = (HashEntry *)PL_DHashTableAdd(&mValuesHash, (void *)aName);
   if (!foundEntry) {
     return nullptr;
   }
 
   // Use placement new. Our ctor does not clobber keyHash, which is important.
   new (foundEntry) HashEntry(entryType, aName);
   return foundEntry;
 }
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1176,16 +1176,18 @@ public:
   /*
    * This creates a new tiled DrawTarget. When a tiled drawtarget is used the
    * drawing is distributed over number of tiles which may each hold an
    * individual offset. The tiles in the set must each have the same backend
    * and format.
    */
   static TemporaryRef<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
 
+  static bool DoesBackendSupportDataDrawtarget(BackendType aType);
+
 #ifdef XP_MACOSX
   static TemporaryRef<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize);
   static TemporaryRef<GlyphRenderingOptions>
     CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
 #endif
 
 #ifdef WIN32
   static TemporaryRef<DrawTarget> CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -1269,17 +1269,17 @@ DrawTargetD2D::CreatePathBuilder(FillRul
     gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hexa(hr);
     return nullptr;
   }
 
   if (aFillRule == FillRule::FILL_WINDING) {
     sink->SetFillMode(D2D1_FILL_MODE_WINDING);
   }
 
-  return new PathBuilderD2D(sink, path, aFillRule);
+  return new PathBuilderD2D(sink, path, aFillRule, BackendType::DIRECT2D);
 }
 
 TemporaryRef<GradientStops>
 DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
 {
   D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
 
   for (uint32_t i = 0; i < aNumStops; i++) {
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -406,17 +406,17 @@ DrawTargetD2D1::StrokeLine(const Point &
 }
 
 void
 DrawTargetD2D1::Stroke(const Path *aPath,
                        const Pattern &aPattern,
                        const StrokeOptions &aStrokeOptions,
                        const DrawOptions &aOptions)
 {
-  if (aPath->GetBackendType() != BackendType::DIRECT2D) {
+  if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
     gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
     return;
   }
   const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
 
   PrepareForDrawing(aOptions.mCompositionOp, aPattern);
 
   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
@@ -429,17 +429,17 @@ DrawTargetD2D1::Stroke(const Path *aPath
   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
 }
 
 void
 DrawTargetD2D1::Fill(const Path *aPath,
                      const Pattern &aPattern,
                      const DrawOptions &aOptions)
 {
-  if (aPath->GetBackendType() != BackendType::DIRECT2D) {
+  if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
     gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
     return;
   }
   const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
 
   PrepareForDrawing(aOptions.mCompositionOp, aPattern);
 
   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
@@ -555,17 +555,17 @@ DrawTargetD2D1::Mask(const Pattern &aSou
   mDC->PopLayer();
 
   FinalizeDrawing(aOptions.mCompositionOp, aSource);
 }
 
 void
 DrawTargetD2D1::PushClip(const Path *aPath)
 {
-  if (aPath->GetBackendType() != BackendType::DIRECT2D) {
+  if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
     gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
     return;
   }
 
   mCurrentClippedGeometry = nullptr;
 
   RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
 
@@ -689,17 +689,17 @@ DrawTargetD2D1::CreatePathBuilder(FillRu
     gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hexa(hr);
     return nullptr;
   }
 
   if (aFillRule == FillRule::FILL_WINDING) {
     sink->SetFillMode(D2D1_FILL_MODE_WINDING);
   }
 
-  return new PathBuilderD2D(sink, path, aFillRule);
+  return new PathBuilderD2D(sink, path, aFillRule, BackendType::DIRECT2D1_1);
 }
 
 TemporaryRef<GradientStops>
 DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
 {
   if (aNumStops == 0) {
     gfxWarning() << *this << ": Failed to create GradientStopCollection with no stops.";
     return nullptr;
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -425,16 +425,35 @@ Factory::CreateTiledDrawTarget(const Til
 
   if (!dt->Init(aTileSet)) {
     return nullptr;
   }
 
   return dt.forget();
 }
 
+bool
+Factory::DoesBackendSupportDataDrawtarget(BackendType aType)
+{
+  switch (aType) {
+  case BackendType::DIRECT2D:
+  case BackendType::DIRECT2D1_1:
+  case BackendType::RECORDING:
+  case BackendType::NONE:
+  case BackendType::COREGRAPHICS_ACCELERATED:
+    return false;
+  case BackendType::CAIRO:
+  case BackendType::COREGRAPHICS:
+  case BackendType::SKIA:
+    return true;
+  }
+
+  return false;
+}
+
 TemporaryRef<ScaledFont>
 Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
 {
   switch (aNativeFont.mType) {
 #ifdef WIN32
   case NativeFontType::DWRITE_FONT_FACE:
     {
       return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
@@ -625,16 +644,20 @@ Factory::SetDirect3D11Device(ID3D11Devic
 {
   mD3D11Device = aDevice;
 
   if (mD2D1Device) {
     mD2D1Device->Release();
     mD2D1Device = nullptr;
   }
 
+  if (!aDevice) {
+    return;
+  }
+
   RefPtr<ID2D1Factory1> factory = D2DFactory1();
 
   RefPtr<IDXGIDevice> device;
   aDevice->QueryInterface((IDXGIDevice**)byRef(device));
   factory->CreateDevice(device, &mD2D1Device);
 }
 
 ID3D11Device*
--- a/gfx/2d/PathD2D.cpp
+++ b/gfx/2d/PathD2D.cpp
@@ -298,17 +298,17 @@ PathBuilderD2D::Finish()
   }
 
   HRESULT hr = mSink->Close();
   if (FAILED(hr)) {
     gfxDebug() << "Failed to close PathSink. Code: " << hexa(hr);
     return nullptr;
   }
 
-  return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule);
+  return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule, mBackendType);
 }
 
 TemporaryRef<PathBuilder>
 PathD2D::CopyToBuilder(FillRule aFillRule) const
 {
   return TransformedCopyToBuilder(Matrix(), aFillRule);
 }
 
@@ -340,17 +340,17 @@ PathD2D::TransformedCopyToBuilder(const 
                         D2DMatrix(aTransform),
                         &wrapSink);
   } else {
     mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
                         D2DMatrix(aTransform),
                         sink);
   }
 
-  RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule);
+  RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule, mBackendType);
   
   pathBuilder->mCurrentPoint = aTransform * mEndPoint;
   
   if (mEndedActive) {
     pathBuilder->mFigureActive = true;
   }
 
   return pathBuilder.forget();
--- a/gfx/2d/PathD2D.h
+++ b/gfx/2d/PathD2D.h
@@ -14,21 +14,22 @@ namespace mozilla {
 namespace gfx {
 
 class PathD2D;
 
 class PathBuilderD2D : public PathBuilder
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderD2D)
-  PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule)
+  PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule, BackendType aBackendType)
     : mSink(aSink)
     , mGeometry(aGeom)
     , mFigureActive(false)
     , mFillRule(aFillRule)
+    , mBackendType(aBackendType)
   {
   }
   virtual ~PathBuilderD2D();
 
   virtual void MoveTo(const Point &aPoint);
   virtual void LineTo(const Point &aPoint);
   virtual void BezierTo(const Point &aCP1,
                         const Point &aCP2,
@@ -37,47 +38,49 @@ public:
                                  const Point &aCP2);
   virtual void Close();
   virtual void Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
                    Float aEndAngle, bool aAntiClockwise = false);
   virtual Point CurrentPoint() const;
 
   virtual TemporaryRef<Path> Finish();
 
-  virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
+  virtual BackendType GetBackendType() const { return mBackendType; }
 
   ID2D1GeometrySink *GetSink() { return mSink; }
 
 private:
   friend class PathD2D;
 
   void EnsureActive(const Point &aPoint);
 
   RefPtr<ID2D1GeometrySink> mSink;
   RefPtr<ID2D1PathGeometry> mGeometry;
 
   bool mFigureActive;
   Point mCurrentPoint;
   Point mBeginPoint;
   FillRule mFillRule;
+  BackendType mBackendType;
 };
 
 class PathD2D : public Path
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D)
   PathD2D(ID2D1PathGeometry *aGeometry, bool aEndedActive,
-          const Point &aEndPoint, FillRule aFillRule)
+          const Point &aEndPoint, FillRule aFillRule, BackendType aBackendType)
     : mGeometry(aGeometry)
     , mEndedActive(aEndedActive)
     , mEndPoint(aEndPoint)
     , mFillRule(aFillRule)
+    , mBackendType(aBackendType)
   {}
   
-  virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
+  virtual BackendType GetBackendType() const { return mBackendType; }
 
   virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
   virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
                                                              FillRule aFillRule = FillRule::FILL_WINDING) const;
 
   virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
 
   virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
@@ -98,14 +101,15 @@ public:
 private:
   friend class DrawTargetD2D;
   friend class DrawTargetD2D1;
 
   mutable RefPtr<ID2D1PathGeometry> mGeometry;
   bool mEndedActive;
   Point mEndPoint;
   FillRule mFillRule;
+  BackendType mBackendType;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_PATHD2D_H_ */
--- a/gfx/2d/SourceSurfaceD2D1.cpp
+++ b/gfx/2d/SourceSurfaceD2D1.cpp
@@ -103,17 +103,23 @@ SourceSurfaceD2D1::DrawTargetWillChange(
   RefPtr<ID2D1Bitmap1> oldBitmap = mRealizedBitmap;
 
   D2D1_BITMAP_PROPERTIES1 props;
   props.dpiX = 96;
   props.dpiY = 96;
   props.pixelFormat = D2DPixelFormat(mFormat);
   props.colorContext = nullptr;
   props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
-  mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap));
+  HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap));
+
+  if (FAILED(hr)) {
+    gfxCriticalError() << "Failed to create bitmap to make DrawTarget copy. Size: " << mSize << " Code: " << hexa(hr);
+    MarkIndependent();
+    return;
+  }
 
   D2D1_POINT_2U point = D2D1::Point2U(0, 0);
   D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
   mRealizedBitmap->CopyFromBitmap(&point, oldBitmap, &rect);
   mImage = mRealizedBitmap;
 
   DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat);
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1277,18 +1277,18 @@ ClientTiledLayerBuffer::ValidateTile(Til
 
   ctxt = nullptr;
   drawTarget = nullptr;
 
   nsIntRegion tileRegion =
     nsIntRect(aTileOrigin.x, aTileOrigin.y,
               GetScaledTileSize().width, GetScaledTileSize().height);
   // Intersect this area with the portion that's invalid.
-  tileRegion = tileRegion.Sub(tileRegion, GetValidRegion());
-  tileRegion = tileRegion.Sub(tileRegion, aDirtyRegion); // Has now been validated
+  tileRegion.SubOut(GetValidRegion());
+  tileRegion.SubOut(aDirtyRegion); // Has now been validated
 
   backBuffer->SetWaste(tileRegion.Area() * mResolution * mResolution);
   backBuffer->Unlock();
 
   if (createdTextureClient) {
     if (!mCompositableClient->AddTextureClient(backBuffer)) {
       NS_WARNING("Failed to add tile TextureClient.");
       aTile.DiscardFrontBuffer();
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1040,17 +1040,17 @@ CompositorD3D11::BeginFrame(const nsIntR
                             const Rect& aRenderBounds,
                             Rect* aClipRectOut,
                             Rect* aRenderBoundsOut)
 {
   // Don't composite if we are minimised. Other than for the sake of efficency,
   // this is important because resizing our buffers when mimised will fail and
   // cause a crash when we're restored.
   NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?");
-  if (::IsIconic(mHwnd)) {
+  if (::IsIconic(mHwnd) || gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     *aRenderBoundsOut = Rect();
     return;
   }
 
   UpdateRenderTarget();
 
   // Failed to create a render target or the view.
   if (!mDefaultRT || !mDefaultRT->mRTView ||
@@ -1175,55 +1175,55 @@ void
 CompositorD3D11::EnsureSize()
 {
   nsIntRect rect;
   mWidget->GetClientBounds(rect);
 
   mSize = rect.Size();
 }
 
-void
+bool
 CompositorD3D11::VerifyBufferSize()
 {
   DXGI_SWAP_CHAIN_DESC swapDesc;
   HRESULT hr;
 
   hr = mSwapChain->GetDesc(&swapDesc);
   if (Failed(hr)) {
-    return;
+    return false;
   }
 
   if ((swapDesc.BufferDesc.Width == mSize.width &&
        swapDesc.BufferDesc.Height == mSize.height) ||
       mSize.width <= 0 || mSize.height <= 0) {
-    return;
+    return true;
   }
 
   if (mDefaultRT) {
     // Make sure the texture, which belongs to the swapchain, is destroyed
     // before resizing the swapchain.
     if (mCurrentRT == mDefaultRT) {
       mCurrentRT = nullptr;
     }
     MOZ_ASSERT(mDefaultRT->hasOneRef());
     mDefaultRT = nullptr;
   }
 
   if (IsRunningInWindowsMetro()) {
     hr = mSwapChain->ResizeBuffers(2, mSize.width, mSize.height,
                                    DXGI_FORMAT_B8G8R8A8_UNORM,
                                    0);
-    HandleError(hr);
     mDisableSequenceForNextFrame = true;
   } else {
     hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
                                    DXGI_FORMAT_B8G8R8A8_UNORM,
                                    0);
-    HandleError(hr);
   }
+
+  return Succeeded(hr);
 }
 
 void
 CompositorD3D11::UpdateRenderTarget()
 {
   EnsureSize();
   VerifyBufferSize();
 
@@ -1417,17 +1417,17 @@ CompositorD3D11::PaintToTarget()
 void
 CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
 {
   if (SUCCEEDED(hr)) {
     return;
   }
   // XXX - It would be nice to use gfxCriticalError, but it needs to
   // be made to work off the main thread first.
-  MOZ_ASSERT(aSeverity != DebugAssert);
+  //MOZ_ASSERT(aSeverity != DebugAssert);
 
   if (aSeverity == Critical) {
     MOZ_CRASH("Unrecoverable D3D11 error");
   }
 
   if (mDevice && hr == DXGI_ERROR_DEVICE_REMOVED) {
     hr = mDevice->GetDeviceRemovedReason();
   }
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -154,17 +154,17 @@ private:
   };
 
   void HandleError(HRESULT hr, Severity aSeverity = DebugAssert);
   bool Failed(HRESULT hr, Severity aSeverity = DebugAssert);
   bool Succeeded(HRESULT hr, Severity aSeverity = DebugAssert);
 
   // ensure mSize is up to date with respect to mWidget
   void EnsureSize();
-  void VerifyBufferSize();
+  bool VerifyBufferSize();
   void UpdateRenderTarget();
   bool CreateShaders();
   bool UpdateConstantBuffers();
   void SetSamplerForFilter(gfx::Filter aFilter);
   void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
   void PaintToTarget();
 
   virtual gfx::IntSize GetWidgetSize() const MOZ_OVERRIDE { return gfx::ToIntSize(mSize); }
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -226,17 +226,17 @@ GrallocTextureHostOGL::GetRenderState()
 
   return LayerRenderState();
 }
 
 TemporaryRef<gfx::DataSourceSurface>
 GrallocTextureHostOGL::GetAsSurface() {
   android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
   uint8_t* grallocData;
-  int32_t rv = graphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&grallocData));
+  graphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&grallocData));
   RefPtr<gfx::DataSourceSurface> grallocTempSurf =
     gfx::Factory::CreateWrappingDataSourceSurface(grallocData,
                                                   graphicBuffer->getStride() * android::bytesPerPixel(graphicBuffer->getPixelFormat()),
                                                   GetSize(), GetFormat());
   RefPtr<gfx::DataSourceSurface> surf = CreateDataSourceSurfaceByCloning(grallocTempSurf);
 
   graphicBuffer->unlock();
 
--- a/gfx/thebes/VsyncSource.cpp
+++ b/gfx/thebes/VsyncSource.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 "VsyncSource.h"
 #include "gfxPlatform.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/VsyncDispatcher.h"
+#include "MainThreadUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 void
 VsyncSource::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -22,17 +22,17 @@
 #include "gfxPlatform.h"
 #include "gfxTeeSurface.h"
 #include "GeckoProfiler.h"
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/DrawTargetTiled.h"
 #include <algorithm>
 
-#if CAIRO_HAS_DWRITE_FONT
+#if XP_WIN
 #include "gfxWindowsPlatform.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 UserDataKey gfxContext::sDontUseAsSourceKey;
 
@@ -1304,17 +1304,21 @@ gfxContext::PushNewDT(gfxContentType con
     mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
                                  format);
 
   if (!newDT) {
     NS_WARNING("Failed to create DrawTarget of sufficient size.");
     newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format);
 
     if (!newDT) {
-      if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+      if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()
+#ifdef XP_WIN
+          && !(mDT->GetBackendType() == BackendType::DIRECT2D1_1 && !gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice())
+#endif
+          ) {
         // If even this fails.. we're most likely just out of memory!
         NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64);
       }
       newDT = CurrentState().drawTarget;
     }
   }
 
   Save();
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -687,17 +687,17 @@ public:
             beginning = end + 1;
             if (!(end = strchr(beginning, ';'))) {
                 break;
             }
             uint32_t filesize = strtoul(beginning, nullptr, 10);
 
             FNCMapEntry* mapEntry =
                 static_cast<FNCMapEntry*>
-                (PL_DHashTableOperate(&mMap, filename.get(), PL_DHASH_ADD));
+                (PL_DHashTableAdd(&mMap, filename.get()));
             if (mapEntry) {
                 mapEntry->mFilename.Assign(filename);
                 mapEntry->mTimestamp = timestamp;
                 mapEntry->mFilesize = filesize;
                 mapEntry->mFaces.Assign(faceList);
                 // entries from the startupcache are marked "non-existing"
                 // until we have confirmed that the file still exists
                 mapEntry->mFileExists = false;
@@ -714,17 +714,17 @@ public:
     virtual void
     GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
                    uint32_t *aTimestamp, uint32_t *aFilesize)
     {
         if (!mMap.ops) {
             return;
         }
         PLDHashEntryHdr *hdr =
-            PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_LOOKUP);
+            PL_DHashTableLookup(&mMap, aFileName.get());
         if (!hdr) {
             return;
         }
         FNCMapEntry* entry = static_cast<FNCMapEntry*>(hdr);
         if (entry && entry->mFilesize) {
             *aTimestamp = entry->mTimestamp;
             *aFilesize = entry->mFilesize;
             aFaceList.Assign(entry->mFaces);
@@ -739,17 +739,17 @@ public:
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
         if (!mMap.ops) {
             return;
         }
         FNCMapEntry* entry =
             static_cast<FNCMapEntry*>
-            (PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_ADD));
+            (PL_DHashTableAdd(&mMap, aFileName.get()));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
             entry->mFilesize = aFilesize;
             entry->mFaces.Assign(aFaceList);
             entry->mFileExists = true;
         }
         mWriteNeeded = true;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1177,26 +1177,26 @@ gfxPlatform::CreateOffscreenContentDrawT
   return CreateDrawTargetForBackend(mContentBackend, aSize, aFormat);
 }
 
 TemporaryRef<DrawTarget>
 gfxPlatform::CreateDrawTargetForData(unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat)
 {
   NS_ASSERTION(mContentBackend != BackendType::NONE, "No backend.");
 
-  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(mContentBackend,
+  BackendType backendType = mContentBackend;
+  
+  if (!Factory::DoesBackendSupportDataDrawtarget(mContentBackend)) {
+    backendType = BackendType::CAIRO;
+  }
+
+  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(backendType,
                                                            aData, aSize,
                                                            aStride, aFormat);
-  if (!dt) {
-    // Factory::CreateDrawTargetForData does not support mContentBackend; retry
-    // with BackendType::CAIRO:
-    dt = Factory::CreateDrawTargetForData(BackendType::CAIRO,
-                                          aData, aSize,
-                                          aStride, aFormat);
-  }
+
   return dt.forget();
 }
 
 /* static */ BackendType
 gfxPlatform::BackendTypeForName(const nsCString& aName)
 {
   if (aName.EqualsLiteral("cairo"))
     return BackendType::CAIRO;
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/WindowsVersion.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTArray.h"
 
 #include "nsIWindowsRegKey.h"
 #include "nsIFile.h"
 #include "plbase64.h"
 #include "nsIXULRuntime.h"
+#include "imgLoader.h"
 
 #include "nsIGfxInfo.h"
 #include "GfxDriverInfo.h"
 
 #include "gfxCrashReporterUtils.h"
 
 #include "gfxGDIFontList.h"
 #include "gfxGDIFont.h"
@@ -379,16 +380,27 @@ gfxWindowsPlatform::GetDPIScale()
 }
 
 void
 gfxWindowsPlatform::UpdateRenderMode()
 {
 /* Pick the default render mode for
  * desktop.
  */
+    if (DidRenderingDeviceReset()) {
+      mD3D11DeviceInitialized = false;
+      mD3D11Device = nullptr;
+      mD3D11ContentDevice = nullptr;
+      mAdapter = nullptr;
+
+      imgLoader::Singleton()->ClearCache(true);
+      imgLoader::Singleton()->ClearCache(false);
+      Factory::SetDirect3D11Device(nullptr);
+    }
+
     mRenderMode = RENDER_GDI;
 
     bool isVistaOrHigher = IsVistaOrLater();
 
     bool safeMode = false;
     nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
     if (xr)
       xr->GetInSafeMode(&safeMode);
@@ -1030,17 +1042,32 @@ gfxWindowsPlatform::IsFontFormatSupporte
 
     // no format hint set, need to look at data
     return true;
 }
 
 bool
 gfxWindowsPlatform::DidRenderingDeviceReset()
 {
-  return GetD3D10Device() && GetD3D10Device()->GetDeviceRemovedReason() != S_OK;
+  if (mD3D11Device) {
+    if (mD3D11Device->GetDeviceRemovedReason() != S_OK) {
+      return true;
+    }
+  }
+  if (mD3D11ContentDevice) {
+    if (mD3D11ContentDevice->GetDeviceRemovedReason() != S_OK) {
+      return true;
+    }
+  }
+  if (GetD3D10Device()) {
+    if (GetD3D10Device()->GetDeviceRemovedReason() != S_OK) {
+      return true;
+    }
+  }
+  return false;
 }
 
 gfxFontFamily *
 gfxWindowsPlatform::FindFontFamily(const nsAString& aName)
 {
     return gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
 }
 
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -43,16 +43,17 @@
 
 #include "base/message_loop.h"
 
 #include "Hal.h"
 #include "HalImpl.h"
 #include "HalLog.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/battery/Constants.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Preferences.h"
 #include "nsAlgorithm.h"
 #include "nsPrintfCString.h"
@@ -524,17 +525,17 @@ GetCurrentBatteryCharge(int* aCharge)
   #endif
 
   return (*aCharge >= 0) && (*aCharge <= 100);
 }
 
 static bool
 GetCurrentBatteryCharging(int* aCharging)
 {
-  static const int BATTERY_NOT_CHARGING = 0;
+  static const DebugOnly<int> BATTERY_NOT_CHARGING = 0;
   static const int BATTERY_CHARGING_USB = 1;
   static const int BATTERY_CHARGING_AC  = 2;
 
   // Generic device support
 
   int chargingSrc;
   bool success =
     ReadSysFile("/sys/class/power_supply/battery/charging_source", &chargingSrc);
@@ -1308,18 +1309,18 @@ EnsureKernelLowMemKillerParamsSet()
   // notify_trigger is a single integer.   If we set notify_trigger=Z, then
   // we'll get notified when there are fewer than Z pages of memory free.  (See
   // GonkMemoryPressureMonitoring.cpp.)
 
   // Build the adj and minfree strings.
   nsAutoCString adjParams;
   nsAutoCString minfreeParams;
 
-  int32_t lowerBoundOfNextOomScoreAdj = OOM_SCORE_ADJ_MIN - 1;
-  int32_t lowerBoundOfNextKillUnderKB = 0;
+  DebugOnly<int32_t> lowerBoundOfNextOomScoreAdj = OOM_SCORE_ADJ_MIN - 1;
+  DebugOnly<int32_t> lowerBoundOfNextKillUnderKB = 0;
   int32_t countOfLowmemorykillerParametersSets = 0;
 
   long page_size = sysconf(_SC_PAGESIZE);
 
   for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) {
     // The system doesn't function correctly if we're missing these prefs, so
     // crash loudly.
 
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -139,19 +139,19 @@ nsBMPDecoder::FinishInternal()
     // Send notifications if appropriate
     if (!IsSizeDecode() && HasSize()) {
 
         // Invalidate
         nsIntRect r(0, 0, mBIH.width, GetHeight());
         PostInvalidation(r);
 
         if (mUseAlphaData) {
-          PostFrameStop(FrameBlender::kFrameHasAlpha);
+          PostFrameStop(Opacity::SOME_TRANSPARENCY);
         } else {
-          PostFrameStop(FrameBlender::kFrameOpaque);
+          PostFrameStop(Opacity::OPAQUE);
         }
         PostDecodeDone();
     }
 }
 
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -211,17 +211,17 @@ nsGIFDecoder2::BeginImageFrame(uint16_t 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 }
 
 
 //******************************************************************************
 void
 nsGIFDecoder2::EndImageFrame()
 {
-  FrameBlender::FrameAlpha alpha = FrameBlender::kFrameHasAlpha;
+  Opacity opacity = Opacity::SOME_TRANSPARENCY;
 
   // First flush all pending image data
   if (!mGIFStruct.images_decoded) {
     // Only need to flush first frame
     FlushImageData();
 
     // If the first frame is smaller in height than the entire image, send an
     // invalidation for the area it does not have data for.
@@ -230,17 +230,17 @@ nsGIFDecoder2::EndImageFrame()
     if (realFrameHeight < mGIFStruct.screen_height) {
       nsIntRect r(0, realFrameHeight,
                   mGIFStruct.screen_width,
                   mGIFStruct.screen_height - realFrameHeight);
       PostInvalidation(r);
     }
     // This transparency check is only valid for first frame
     if (mGIFStruct.is_transparent && !mSawTransparency) {
-      alpha = FrameBlender::kFrameOpaque;
+      opacity = Opacity::OPAQUE;
     }
   }
   mCurrentRow = mLastFlushedRow = -1;
   mCurrentPass = mLastFlushedPass = 0;
 
   // Only add frame if we have any rows at all
   if (mGIFStruct.rows_remaining != mGIFStruct.height) {
     if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
@@ -254,18 +254,18 @@ nsGIFDecoder2::EndImageFrame()
 
   // Unconditionally increment images_decoded, because we unconditionally
   // append frames in BeginImageFrame(). This ensures that images_decoded
   // always refers to the frame in mImage we're currently decoding,
   // even if some of them weren't decoded properly and thus are blank.
   mGIFStruct.images_decoded++;
 
   // Tell the superclass we finished a frame
-  PostFrameStop(alpha,
-                FrameBlender::FrameDisposalMethod(mGIFStruct.disposal_method),
+  PostFrameStop(opacity,
+                DisposalMethod(mGIFStruct.disposal_method),
                 mGIFStruct.delay_time);
 
   // Reset the transparent pixel
   if (mOldColor) {
     mColormap[mGIFStruct.tpixel] = mOldColor;
     mOldColor = 0;
   }
 
@@ -825,20 +825,19 @@ nsGIFDecoder2::WriteInternal(const char*
       mGIFStruct.disposal_method = ((*q) >> 2) & 0x7;
       // Some specs say 3rd bit (value 4), other specs say value 3
       // Let's choose 3 (the more popular)
       if (mGIFStruct.disposal_method == 4) {
         mGIFStruct.disposal_method = 3;
       }
 
       {
-        int32_t method =
-          FrameBlender::FrameDisposalMethod(mGIFStruct.disposal_method);
-        if (method == FrameBlender::kDisposeClearAll ||
-            method == FrameBlender::kDisposeClear) {
+        DisposalMethod method = DisposalMethod(mGIFStruct.disposal_method);
+        if (method == DisposalMethod::CLEAR_ALL ||
+            method == DisposalMethod::CLEAR) {
           // We may have to display the background under this image during
           // animation playback, so we regard it as transparent.
           PostHasTransparency();
         }
       }
 
       mGIFStruct.delay_time = GETINT16(q + 1) * 10;
       GETN(1, gif_consume_block);
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -575,17 +575,17 @@ nsJPEGDecoder::ReadOrientationFromEXIF()
   EXIFData exif = EXIFParser::Parse(marker->data,
                                     static_cast<uint32_t>(marker->data_length));
   return exif.orientation;
 }
 
 void
 nsJPEGDecoder::NotifyDone()
 {
-  PostFrameStop(FrameBlender::kFrameOpaque);
+  PostFrameStop(Opacity::OPAQUE);
   PostDecodeDone();
 }
 
 void
 nsJPEGDecoder::OutputScanlines(bool* suspend)
 {
   *suspend = false;
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -49,25 +49,25 @@ GetPNGDecoderAccountingLog()
 #endif
 
 // For size decodes
 #define WIDTH_OFFSET 16
 #define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
 #define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
 
 nsPNGDecoder::AnimFrameInfo::AnimFrameInfo()
- : mDispose(FrameBlender::kDisposeKeep)
- , mBlend(FrameBlender::kBlendOver)
+ : mDispose(DisposalMethod::KEEP)
+ , mBlend(BlendMethod::OVER)
  , mTimeout(0)
 { }
 
 #ifdef PNG_APNG_SUPPORTED
 nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
- : mDispose(FrameBlender::kDisposeKeep)
- , mBlend(FrameBlender::kBlendOver)
+ : mDispose(DisposalMethod::KEEP)
+ , mBlend(BlendMethod::OVER)
  , mTimeout(0)
 {
   png_uint_16 delay_num, delay_den;
   // delay, in seconds is delay_num/delay_den
   png_byte dispose_op;
   png_byte blend_op;
   delay_num = png_get_next_frame_delay_num(aPNG, aInfo);
   delay_den = png_get_next_frame_delay_den(aPNG, aInfo);
@@ -83,27 +83,27 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameIn
 
     // Need to cast delay_num to float to have a proper division and
     // the result to int to avoid compiler warning
     mTimeout = static_cast<int32_t>(static_cast<double>(delay_num) *
                                     1000 / delay_den);
   }
 
   if (dispose_op == PNG_DISPOSE_OP_PREVIOUS) {
-    mDispose = FrameBlender::kDisposeRestorePrevious;
+    mDispose = DisposalMethod::RESTORE_PREVIOUS;
   } else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND) {
-    mDispose = FrameBlender::kDisposeClear;
+    mDispose = DisposalMethod::CLEAR;
   } else {
-    mDispose = FrameBlender::kDisposeKeep;
+    mDispose = DisposalMethod::KEEP;
   }
 
   if (blend_op == PNG_BLEND_OP_SOURCE) {
-    mBlend = FrameBlender::kBlendSource;
+    mBlend = BlendMethod::SOURCE;
   } else {
-    mBlend = FrameBlender::kBlendOver;
+    mBlend = BlendMethod::OVER;
   }
 }
 #endif
 
 // First 8 bytes of a PNG file
 const uint8_t
 nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
 
@@ -180,17 +180,17 @@ void nsPNGDecoder::CreateFrame(png_uint_
           "image frame with %dx%d pixels in container %p",
           width, height,
           &mImage));
 
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
     mAnimInfo = AnimFrameInfo(mPNG, mInfo);
 
-    if (mAnimInfo.mDispose == FrameBlender::kDisposeClear) {
+    if (mAnimInfo.mDispose == DisposalMethod::CLEAR) {
       // We may have to display the background under this image during
       // animation playback, so we regard it as transparent.
       PostHasTransparency();
     }
   }
 #endif
 }
 
@@ -199,33 +199,33 @@ void
 nsPNGDecoder::EndImageFrame()
 {
   if (mFrameIsHidden) {
     return;
   }
 
   mNumFrames++;
 
-  FrameBlender::FrameAlpha alpha;
+  Opacity opacity;
   if (mFrameHasNoAlpha) {
-    alpha = FrameBlender::kFrameOpaque;
+    opacity = Opacity::OPAQUE;
   } else {
-    alpha = FrameBlender::kFrameHasAlpha;
+    opacity = Opacity::SOME_TRANSPARENCY;
   }
 
 #ifdef PNG_APNG_SUPPORTED
   uint32_t numFrames = GetFrameCount();
 
   // We can't use mPNG->num_frames_read as it may be one ahead.
   if (numFrames > 1) {
     PostInvalidation(mFrameRect);
   }
 #endif
 
-  PostFrameStop(alpha, mAnimInfo.mDispose, mAnimInfo.mTimeout,
+  PostFrameStop(opacity, mAnimInfo.mDispose, mAnimInfo.mTimeout,
                 mAnimInfo.mBlend);
 }
 
 void
 nsPNGDecoder::InitInternal()
 {
   // For size decodes, we don't need to initialize the png decoder
   if (IsSizeDecode()) {
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -93,18 +93,18 @@ public:
 
   struct AnimFrameInfo
   {
     AnimFrameInfo();
 #ifdef PNG_APNG_SUPPORTED
     AnimFrameInfo(png_structp aPNG, png_infop aInfo);
 #endif
 
-    FrameBlender::FrameDisposalMethod mDispose;
-    FrameBlender::FrameBlendMethod mBlend;
+    DisposalMethod mDispose;
+    BlendMethod mBlend;
     int32_t mTimeout;
   };
 
   AnimFrameInfo mAnimInfo;
 
   // The number of frames we've finished.
   uint32_t mNumFrames;
 
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -328,34 +328,34 @@ Decoder::PostFrameStart()
   // Decoder implementations should only call this method if they successfully
   // appended the frame to the image. So mFrameCount should always match that
   // reported by the Image.
   MOZ_ASSERT(mFrameCount == mImage.GetNumFrames(),
              "Decoder frame count doesn't match image's!");
 }
 
 void
-Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha /* = FrameBlender::kFrameHasAlpha */,
-                       FrameBlender::FrameDisposalMethod aDisposalMethod /* = FrameBlender::kDisposeKeep */,
+Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
+                       DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
                        int32_t aTimeout /* = 0 */,
-                       FrameBlender::FrameBlendMethod aBlendMethod /* = FrameBlender::kBlendOver */)
+                       BlendMethod aBlendMethod /* = BlendMethod::OVER */)
 {
   // We should be mid-frame
   MOZ_ASSERT(!IsSizeDecode(), "Stopping frame during a size decode");
   MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
 
   // Update our state
   mInFrame = false;
 
-  if (aFrameAlpha == FrameBlender::kFrameOpaque) {
+  if (aFrameOpacity == Opacity::OPAQUE) {
     mCurrentFrame->SetHasNoAlpha();
   }
 
-  mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
+  mCurrentFrame->SetDisposalMethod(aDisposalMethod);
   mCurrentFrame->SetRawTimeout(aTimeout);
   mCurrentFrame->SetBlendMethod(aBlendMethod);
   mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
 
   mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
 }
 
 void
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_IMAGELIB_DECODER_H_
 #define MOZILLA_IMAGELIB_DECODER_H_
 
+#include "FrameAnimator.h"
 #include "RasterImage.h"
 #include "mozilla/RefPtr.h"
 #include "DecodePool.h"
 #include "ImageMetadata.h"
 #include "Orientation.h"
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
@@ -221,35 +222,35 @@ protected:
                 Orientation aOrientation = Orientation());
 
   // Called by decoders if they determine that the image has transparency.
   //
   // This should be fired as early as possible to allow observers to do things
   // that affect content, so it's necessarily pessimistic - if there's a
   // possibility that the image has transparency, for example because its header
   // specifies that it has an alpha channel, we fire PostHasTransparency
-  // immediately. PostFrameStop's aFrameAlpha argument, on the other hand, is
+  // immediately. PostFrameStop's aFrameOpacity argument, on the other hand, is
   // only used internally to ImageLib. Because PostFrameStop isn't delivered
   // until the entire frame has been decoded, decoders may take into account the
   // actual contents of the frame and give a more accurate result.
   void PostHasTransparency();
 
   // Called by decoders when they begin a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
   void PostFrameStart();
 
   // Called by decoders when they end a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
   // Specify whether this frame is opaque as an optimization.
   // For animated images, specify the disposal, blend method and timeout for
   // this frame.
-  void PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha = FrameBlender::kFrameHasAlpha,
-                     FrameBlender::FrameDisposalMethod aDisposalMethod = FrameBlender::kDisposeKeep,
+  void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
+                     DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
                      int32_t aTimeout = 0,
-                     FrameBlender::FrameBlendMethod aBlendMethod = FrameBlender::kBlendOver);
+                     BlendMethod aBlendMethod = BlendMethod::OVER);
 
   // Called by the decoders when they have a region to invalidate. We may not
   // actually pass these invalidations on right away.
   void PostInvalidation(nsIntRect& aRect);
 
   // Called by the decoders when they have successfully decoded the image. This
   // may occur as the result of the decoder getting to the appropriate point in
   // the stream, or by us calling FinishInternal().
--- a/image/src/FrameAnimator.cpp
+++ b/image/src/FrameAnimator.cpp
@@ -1,20 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrameAnimator.h"
-#include "FrameBlender.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "imgIContainer.h"
+#include "MainThreadUtils.h"
 #include "RasterImage.h"
 
-#include "imgIContainer.h"
+#include "pixman.h"
 
 namespace mozilla {
+
+using namespace gfx;
+
 namespace image {
 
 int32_t
 FrameAnimator::GetSingleLoopTime() const
 {
   // If we aren't done decoding, we don't know the image's full play time.
   if (!mDoneDecoding) {
     return -1;
@@ -22,17 +29,17 @@ FrameAnimator::GetSingleLoopTime() const
 
   // If we're not looping, a single loop time has no meaning
   if (mAnimationMode != imgIContainer::kNormalAnimMode) {
     return -1;
   }
 
   uint32_t looptime = 0;
   for (uint32_t i = 0; i < mImage->GetNumFrames(); ++i) {
-    int32_t timeout = mFrameBlender.GetTimeoutForFrame(i);
+    int32_t timeout = GetTimeoutForFrame(i);
     if (timeout >= 0) {
       looptime += static_cast<uint32_t>(timeout);
     } else {
       // If we have a frame that never times out, we're probably in an error
       // case, but let's handle it more gracefully.
       NS_WARNING("Negative frame timeout - how did this happen?");
       return -1;
     }
@@ -41,17 +48,17 @@ FrameAnimator::GetSingleLoopTime() const
   return looptime;
 }
 
 TimeStamp
 FrameAnimator::GetCurrentImgFrameEndTime() const
 {
   TimeStamp currentFrameTime = mCurrentAnimationFrameTime;
   int32_t timeout =
-    mFrameBlender.GetTimeoutForFrame(mCurrentAnimationFrameIndex);
+    GetTimeoutForFrame(mCurrentAnimationFrameIndex);
 
   if (timeout < 0) {
     // We need to return a sentinel value in this case, because our logic
     // doesn't work correctly if we have a negative timeout value. We use
     // one year in the future as the sentinel because it works with the loop
     // in RequestRefresh() below.
     // XXX(seth): It'd be preferable to make our logic work correctly with
     // negative timeouts.
@@ -72,17 +79,17 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
   NS_ASSERTION(aTime <= TimeStamp::Now(),
                "Given time appears to be in the future");
 
   uint32_t currentFrameIndex = mCurrentAnimationFrameIndex;
   uint32_t nextFrameIndex = currentFrameIndex + 1;
   int32_t timeout = 0;
 
   RefreshResult ret;
-  RawAccessFrameRef nextFrame = mFrameBlender.GetRawFrame(nextFrameIndex);
+  RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
 
   // If we're done decoding, we know we've got everything we're going to get.
   // If we aren't, we only display fully-downloaded frames; everything else
   // gets delayed.
   bool canDisplay = mDoneDecoding || (nextFrame && nextFrame->ImageComplete());
 
   if (!canDisplay) {
     // Uh oh, the frame we want to show is currently being decoded (partial)
@@ -91,56 +98,56 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
   }
 
   // If we're done decoding the next frame, go ahead and display it now and
   // reinit with the next frame's delay time.
   if (mImage->GetNumFrames() == nextFrameIndex) {
     // End of an animation loop...
 
     // If we are not looping forever, initialize the loop counter
-    if (mLoopCounter < 0 && mFrameBlender.LoopCount() >= 0) {
-      mLoopCounter = mFrameBlender.LoopCount();
+    if (mLoopRemainingCount < 0 && LoopCount() >= 0) {
+      mLoopRemainingCount = LoopCount();
     }
 
     // If animation mode is "loop once", or we're at end of loop counter,
-    // it's time to stop animating
+    // it's time to stop animating.
     if (mAnimationMode == imgIContainer::kLoopOnceAnimMode ||
-        mLoopCounter == 0) {
+        mLoopRemainingCount == 0) {
       ret.animationFinished = true;
     }
 
     nextFrameIndex = 0;
 
-    if (mLoopCounter > 0) {
-      mLoopCounter--;
+    if (mLoopRemainingCount > 0) {
+      mLoopRemainingCount--;
     }
 
     // If we're done, exit early.
     if (ret.animationFinished) {
       return ret;
     }
   }
 
-  timeout = mFrameBlender.GetTimeoutForFrame(nextFrameIndex);
+  timeout = GetTimeoutForFrame(nextFrameIndex);
 
   // Bad data
   if (timeout < 0) {
     ret.animationFinished = true;
     ret.error = true;
   }
 
   if (nextFrameIndex == 0) {
     ret.dirtyRect = mFirstFrameRefreshArea;
   } else {
     // Change frame
     if (nextFrameIndex != currentFrameIndex + 1) {
-      nextFrame = mFrameBlender.GetRawFrame(nextFrameIndex);
+      nextFrame = GetRawFrame(nextFrameIndex);
     }
 
-    if (!mFrameBlender.DoBlend(&ret.dirtyRect, currentFrameIndex,
+    if (!DoBlend(&ret.dirtyRect, currentFrameIndex,
                                nextFrameIndex)) {
       // something went wrong, move on to next
       NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
       nextFrame->SetCompositingFailed(true);
       mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
       mCurrentAnimationFrameIndex = nextFrameIndex;
 
       ret.error = true;
@@ -205,16 +212,17 @@ FrameAnimator::RequestRefresh(const Time
 
   return ret;
 }
 
 void
 FrameAnimator::ResetAnimation()
 {
   mCurrentAnimationFrameIndex = 0;
+  mLastCompositedFrameIndex = -1;
 }
 
 void
 FrameAnimator::SetDoneDecoding(bool aDone)
 {
   mDoneDecoding = aDone;
 }
 
@@ -257,10 +265,508 @@ FrameAnimator::GetCurrentAnimationFrameI
 }
 
 nsIntRect
 FrameAnimator::GetFirstFrameRefreshArea() const
 {
   return mFirstFrameRefreshArea;
 }
 
+DrawableFrameRef
+FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
+{
+  MOZ_ASSERT(aFrameNum != 0, "First frame is never composited");
+
+  // If we have a composited version of this frame, return that.
+  if (mLastCompositedFrameIndex == int32_t(aFrameNum)) {
+    return mCompositingFrame->DrawableRef();
+  }
+
+  // Otherwise return the raw frame. DoBlend is required to ensure that we only
+  // hit this case if the frame is not paletted and doesn't require compositing.
+  DrawableFrameRef ref =
+    SurfaceCache::Lookup(ImageKey(mImage),
+                         RasterSurfaceKey(mSize,
+                                          0,  // Default decode flags.
+                                          aFrameNum));
+  MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "About to return a paletted frame");
+  return ref;
+}
+
+int32_t
+FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
+{
+  RawAccessFrameRef frame = GetRawFrame(aFrameNum);
+  const int32_t timeout = frame->GetRawTimeout();
+
+  // Ensure a minimal time between updates so we don't throttle the UI thread.
+  // consider 0 == unspecified and make it fast but not too fast.  Unless we
+  // have a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug
+  // 207059. The behavior of recent IE and Opera versions seems to be:
+  // IE 6/Win:
+  //   10 - 50ms go 100ms
+  //   >50ms go correct speed
+  // Opera 7 final/Win:
+  //   10ms goes 100ms
+  //   >10ms go correct speed
+  // It seems that there are broken tools out there that set a 0ms or 10ms
+  // timeout when they really want a "default" one.  So munge values in that
+  // range.
+  if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) {
+    return 100;
+  }
+
+  return timeout;
+}
+
+size_t
+FrameAnimator::SizeOfCompositingFrames(gfxMemoryLocation aLocation,
+                                       MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = 0;
+
+  if (mCompositingFrame) {
+    n += mCompositingFrame->SizeOfExcludingThis(aLocation, aMallocSizeOf);
+  }
+  if (mCompositingPrevFrame) {
+    n += mCompositingPrevFrame->SizeOfExcludingThis(aLocation, aMallocSizeOf);
+  }
+
+  return n;
+}
+
+RawAccessFrameRef
+FrameAnimator::GetRawFrame(uint32_t aFrameNum) const
+{
+  DrawableFrameRef ref =
+    SurfaceCache::Lookup(ImageKey(mImage),
+                         RasterSurfaceKey(mSize,
+                                          0,  // Default decode flags.
+                                          aFrameNum));
+  return ref ? ref->RawAccessRef()
+             : RawAccessFrameRef();
+}
+
+//******************************************************************************
+// DoBlend gets called when the timer for animation get fired and we have to
+// update the composited frame of the animation.
+bool
+FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
+                       uint32_t aPrevFrameIndex,
+                       uint32_t aNextFrameIndex)
+{
+  RawAccessFrameRef prevFrame = GetRawFrame(aPrevFrameIndex);
+  RawAccessFrameRef nextFrame = GetRawFrame(aNextFrameIndex);
+
+  MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
+
+  DisposalMethod prevFrameDisposalMethod = prevFrame->GetDisposalMethod();
+  if (prevFrameDisposalMethod == DisposalMethod::RESTORE_PREVIOUS &&
+      !mCompositingPrevFrame) {
+    prevFrameDisposalMethod = DisposalMethod::CLEAR;
+  }
+
+  nsIntRect prevFrameRect = prevFrame->GetRect();
+  bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
+                          prevFrameRect.width == mSize.width &&
+                          prevFrameRect.height == mSize.height);
+
+  // Optimization: DisposeClearAll if the previous frame is the same size as
+  //               container and it's clearing itself
+  if (isFullPrevFrame &&
+      (prevFrameDisposalMethod == DisposalMethod::CLEAR)) {
+    prevFrameDisposalMethod = DisposalMethod::CLEAR_ALL;
+  }
+
+  DisposalMethod nextFrameDisposalMethod = nextFrame->GetDisposalMethod();
+  nsIntRect nextFrameRect = nextFrame->GetRect();
+  bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
+                          nextFrameRect.width == mSize.width &&
+                          nextFrameRect.height == mSize.height);
+
+  if (!nextFrame->GetIsPaletted()) {
+    // Optimization: Skip compositing if the previous frame wants to clear the
+    //               whole image
+    if (prevFrameDisposalMethod == DisposalMethod::CLEAR_ALL) {
+      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+      return true;
+    }
+
+    // Optimization: Skip compositing if this frame is the same size as the
+    //               container and it's fully drawing over prev frame (no alpha)
+    if (isFullNextFrame &&
+        (nextFrameDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) &&
+        !nextFrame->GetHasAlpha()) {
+      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+      return true;
+    }
+  }
+
+  // Calculate area that needs updating
+  switch (prevFrameDisposalMethod) {
+    default:
+    case DisposalMethod::NOT_SPECIFIED:
+    case DisposalMethod::KEEP:
+      *aDirtyRect = nextFrameRect;
+      break;
+
+    case DisposalMethod::CLEAR_ALL:
+      // Whole image container is cleared
+      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+      break;
+
+    case DisposalMethod::CLEAR:
+      // Calc area that needs to be redrawn (the combination of previous and
+      // this frame)
+      // XXX - This could be done with multiple framechanged calls
+      //       Having prevFrame way at the top of the image, and nextFrame
+      //       way at the bottom, and both frames being small, we'd be
+      //       telling framechanged to refresh the whole image when only two
+      //       small areas are needed.
+      aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
+      break;
+
+    case DisposalMethod::RESTORE_PREVIOUS:
+      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+      break;
+  }
+
+  // Optimization:
+  //   Skip compositing if the last composited frame is this frame
+  //   (Only one composited frame was made for this animation.  Example:
+  //    Only Frame 3 of a 10 frame image required us to build a composite frame
+  //    On the second loop, we do not need to rebuild the frame
+  //    since it's still sitting in compositingFrame)
+  if (mLastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
+    return true;
+  }
+
+  bool needToBlankComposite = false;
+
+  // Create the Compositing Frame
+  if (!mCompositingFrame) {
+    nsRefPtr<imgFrame> newFrame = new imgFrame;
+    nsresult rv = newFrame->InitForDecoder(ThebesIntSize(mSize),
+                                           SurfaceFormat::B8G8R8A8);
+    if (NS_FAILED(rv)) {
+      mCompositingFrame.reset();
+      return false;
+    }
+    mCompositingFrame = newFrame->RawAccessRef();
+    needToBlankComposite = true;
+  } else if (int32_t(aNextFrameIndex) != mLastCompositedFrameIndex+1) {
+
+    // If we are not drawing on top of last composited frame,
+    // then we are building a new composite frame, so let's clear it first.
+    needToBlankComposite = true;
+  }
+
+  // More optimizations possible when next frame is not transparent
+  // But if the next frame has DisposalMethod::RESTORE_PREVIOUS,
+  // this "no disposal" optimization is not possible,
+  // because the frame in "after disposal operation" state
+  // needs to be stored in compositingFrame, so it can be
+  // copied into compositingPrevFrame later.
+  bool doDisposal = true;
+  if (!nextFrame->GetHasAlpha() &&
+      nextFrameDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
+    if (isFullNextFrame) {
+      // Optimization: No need to dispose prev.frame when
+      // next frame is full frame and not transparent.
+      doDisposal = false;
+      // No need to blank the composite frame
+      needToBlankComposite = false;
+    } else {
+      if ((prevFrameRect.x >= nextFrameRect.x) &&
+          (prevFrameRect.y >= nextFrameRect.y) &&
+          (prevFrameRect.x + prevFrameRect.width <=
+              nextFrameRect.x + nextFrameRect.width) &&
+          (prevFrameRect.y + prevFrameRect.height <=
+              nextFrameRect.y + nextFrameRect.height)) {
+        // Optimization: No need to dispose prev.frame when
+        // next frame fully overlaps previous frame.
+        doDisposal = false;
+      }
+    }
+  }
+
+  if (doDisposal) {
+    // Dispose of previous: clear, restore, or keep (copy)
+    switch (prevFrameDisposalMethod) {
+      case DisposalMethod::CLEAR:
+        if (needToBlankComposite) {
+          // If we just created the composite, it could have anything in its
+          // buffer. Clear whole frame
+          ClearFrame(mCompositingFrame->GetRawData(),
+                     mCompositingFrame->GetRect());
+        } else {
+          // Only blank out previous frame area (both color & Mask/Alpha)
+          ClearFrame(mCompositingFrame->GetRawData(),
+                     mCompositingFrame->GetRect(),
+                     prevFrameRect);
+        }
+        break;
+
+      case DisposalMethod::CLEAR_ALL:
+        ClearFrame(mCompositingFrame->GetRawData(),
+                   mCompositingFrame->GetRect());
+        break;
+
+      case DisposalMethod::RESTORE_PREVIOUS:
+        // It would be better to copy only the area changed back to
+        // compositingFrame.
+        if (mCompositingPrevFrame) {
+          CopyFrameImage(mCompositingPrevFrame->GetRawData(),
+                         mCompositingPrevFrame->GetRect(),
+                         mCompositingFrame->GetRawData(),
+                         mCompositingFrame->GetRect());
+
+          // destroy only if we don't need it for this frame's disposal
+          if (nextFrameDisposalMethod !=
+              DisposalMethod::RESTORE_PREVIOUS) {
+            mCompositingPrevFrame.reset();
+          }
+        } else {
+          ClearFrame(mCompositingFrame->GetRawData(),
+                     mCompositingFrame->GetRect());
+        }
+        break;
+
+      default:
+        // Copy previous frame into compositingFrame before we put the new
+        // frame on top
+        // Assumes that the previous frame represents a full frame (it could be
+        // smaller in size than the container, as long as the frame before it
+        // erased itself)
+        // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1)
+        // will always be a valid frame number.
+        if (mLastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
+          if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
+            // Just copy the bits
+            CopyFrameImage(prevFrame->GetRawData(),
+                           prevFrame->GetRect(),
+                           mCompositingFrame->GetRawData(),
+                           mCompositingFrame->GetRect());
+          } else {
+            if (needToBlankComposite) {
+              // Only blank composite when prev is transparent or not full.
+              if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
+                ClearFrame(mCompositingFrame->GetRawData(),
+                           mCompositingFrame->GetRect());
+              }
+            }
+            DrawFrameTo(prevFrame->GetRawData(), prevFrameRect,
+                        prevFrame->PaletteDataLength(),
+                        prevFrame->GetHasAlpha(),
+                        mCompositingFrame->GetRawData(),
+                        mCompositingFrame->GetRect(),
+                        prevFrame->GetBlendMethod());
+          }
+        }
+    }
+  } else if (needToBlankComposite) {
+    // If we just created the composite, it could have anything in its
+    // buffers. Clear them
+    ClearFrame(mCompositingFrame->GetRawData(),
+               mCompositingFrame->GetRect());
+  }
+
+  // Check if the frame we are composing wants the previous image restored after
+  // it is done. Don't store it (again) if last frame wanted its image restored
+  // too
+  if ((nextFrameDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) &&
+      (prevFrameDisposalMethod != DisposalMethod::RESTORE_PREVIOUS)) {
+    // We are storing the whole image.
+    // It would be better if we just stored the area that nextFrame is going to
+    // overwrite.
+    if (!mCompositingPrevFrame) {
+      nsRefPtr<imgFrame> newFrame = new imgFrame;
+      nsresult rv = newFrame->InitForDecoder(ThebesIntSize(mSize),
+                                             SurfaceFormat::B8G8R8A8);
+      if (NS_FAILED(rv)) {
+        mCompositingPrevFrame.reset();
+        return false;
+      }
+
+      mCompositingPrevFrame = newFrame->RawAccessRef();
+    }
+
+    CopyFrameImage(mCompositingFrame->GetRawData(),
+                   mCompositingFrame->GetRect(),
+                   mCompositingPrevFrame->GetRawData(),
+                   mCompositingPrevFrame->GetRect());
+  }
+
+  // blit next frame into it's correct spot
+  DrawFrameTo(nextFrame->GetRawData(), nextFrameRect,
+              nextFrame->PaletteDataLength(),
+              nextFrame->GetHasAlpha(),
+              mCompositingFrame->GetRawData(),
+              mCompositingFrame->GetRect(),
+              nextFrame->GetBlendMethod());
+
+  // Set timeout of CompositeFrame to timeout of frame we just composed
+  // Bug 177948
+  int32_t timeout = nextFrame->GetRawTimeout();
+  mCompositingFrame->SetRawTimeout(timeout);
+
+  // Tell the image that it is fully 'downloaded'.
+  nsresult rv =
+    mCompositingFrame->ImageUpdated(mCompositingFrame->GetRect());
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  mLastCompositedFrameIndex = int32_t(aNextFrameIndex);
+
+  return true;
+}
+
+//******************************************************************************
+// Fill aFrame with black. Does also clears the mask.
+void
+FrameAnimator::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect)
+{
+  if (!aFrameData) {
+    return;
+  }
+
+  memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
+}
+
+//******************************************************************************
+void
+FrameAnimator::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect,
+                          const nsIntRect& aRectToClear)
+{
+  if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
+      aRectToClear.width <= 0 || aRectToClear.height <= 0) {
+    return;
+  }
+
+  nsIntRect toClear = aFrameRect.Intersect(aRectToClear);
+  if (toClear.IsEmpty()) {
+    return;
+  }
+
+  uint32_t bytesPerRow = aFrameRect.width * 4;
+  for (int row = toClear.y; row < toClear.y + toClear.height; ++row) {
+    memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0,
+           toClear.width * 4);
+  }
+}
+
+//******************************************************************************
+// Whether we succeed or fail will not cause a crash, and there's not much
+// we can do about a failure, so there we don't return a nsresult
+bool
+FrameAnimator::CopyFrameImage(const uint8_t* aDataSrc, const nsIntRect& aRectSrc,
+                              uint8_t* aDataDest, const nsIntRect& aRectDest)
+{
+  uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
+  uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
+
+  if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
+    return false;
+  }
+
+  memcpy(aDataDest, aDataSrc, dataLengthDest);
+
+  return true;
+}
+
+nsresult
+FrameAnimator::DrawFrameTo(const uint8_t* aSrcData, const nsIntRect& aSrcRect,
+                           uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
+                           uint8_t* aDstPixels, const nsIntRect& aDstRect,
+                           BlendMethod aBlendMethod)
+{
+  NS_ENSURE_ARG_POINTER(aSrcData);
+  NS_ENSURE_ARG_POINTER(aDstPixels);
+
+  // According to both AGIF and APNG specs, offsets are unsigned
+  if (aSrcRect.x < 0 || aSrcRect.y < 0) {
+    NS_WARNING("FrameAnimator::DrawFrameTo: negative offsets not allowed");
+    return NS_ERROR_FAILURE;
+  }
+  // Outside the destination frame, skip it
+  if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
+    return NS_OK;
+  }
+
+  if (aSrcPaletteLength) {
+    // Larger than the destination frame, clip it
+    int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
+    int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);
+
+    // The clipped image must now fully fit within destination image frame
+    NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
+                 (aSrcRect.x + width <= aDstRect.width) &&
+                 (aSrcRect.y + height <= aDstRect.height),
+                "FrameAnimator::DrawFrameTo: Invalid aSrcRect");
+
+    // clipped image size may be smaller than source, but not larger
+    NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
+                 "FrameAnimator::DrawFrameTo: source must be smaller than dest");
+
+    // Get pointers to image data
+    const uint8_t* srcPixels = aSrcData + aSrcPaletteLength;
+    uint32_t* dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
+    const uint32_t* colormap = reinterpret_cast<const uint32_t*>(aSrcData);
+
+    // Skip to the right offset
+    dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
+    if (!aSrcHasAlpha) {
+      for (int32_t r = height; r > 0; --r) {
+        for (int32_t c = 0; c < width; c++) {
+          dstPixels[c] = colormap[srcPixels[c]];
+        }
+        // Go to the next row in the source resp. destination image
+        srcPixels += aSrcRect.width;
+        dstPixels += aDstRect.width;
+      }
+    } else {
+      for (int32_t r = height; r > 0; --r) {
+        for (int32_t c = 0; c < width; c++) {
+          const uint32_t color = colormap[srcPixels[c]];
+          if (color) {
+            dstPixels[c] = color;
+          }
+        }
+        // Go to the next row in the source resp. destination image
+        srcPixels += aSrcRect.width;
+        dstPixels += aDstRect.width;
+      }
+    }
+  } else {
+    pixman_image_t* src =
+      pixman_image_create_bits(
+          aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
+          aSrcRect.width, aSrcRect.height,
+          reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
+          aSrcRect.width * 4);
+    pixman_image_t* dst =
+      pixman_image_create_bits(PIXMAN_a8r8g8b8,
+                               aDstRect.width,
+                               aDstRect.height,
+                               reinterpret_cast<uint32_t*>(aDstPixels),
+                               aDstRect.width * 4);
+
+    auto op = aBlendMethod == BlendMethod::SOURCE ? PIXMAN_OP_SRC
+                                                  : PIXMAN_OP_OVER;
+    pixman_image_composite32(op,
+                             src,
+                             nullptr,
+                             dst,
+                             0, 0,
+                             0, 0,
+                             aSrcRect.x, aSrcRect.y,
+                             aSrcRect.width, aSrcRect.height);
+
+    pixman_image_unref(src);
+    pixman_image_unref(dst);
+  }
+
+  return NS_OK;
+}
+
 } // namespace image
 } // namespace mozilla
--- a/image/src/FrameAnimator.h
+++ b/image/src/FrameAnimator.h
@@ -2,35 +2,42 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_src_FrameAnimator_h
 #define mozilla_image_src_FrameAnimator_h
 
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
+#include "gfx2DGlue.h"
+#include "gfxTypes.h"
+#include "imgFrame.h"
+#include "nsCOMPtr.h"
 #include "nsRect.h"
+#include "SurfaceCache.h"
 
 namespace mozilla {
 namespace image {
 
-class FrameBlender;
 class RasterImage;
 
 class FrameAnimator
 {
 public:
   FrameAnimator(RasterImage* aImage,
-                FrameBlender& aFrameBlender,
+                gfx::IntSize aSize,
                 uint16_t aAnimationMode)
-    : mCurrentAnimationFrameIndex(0)
-    , mLoopCounter(-1)
-    , mImage(aImage)
-    , mFrameBlender(aFrameBlender)
+    : mImage(aImage)
+    , mSize(aSize)
+    , mCurrentAnimationFrameIndex(0)
+    , mLoopRemainingCount(-1)
+    , mLastCompositedFrameIndex(-1)
+    , mLoopCount(-1)
     , mAnimationMode(aAnimationMode)
     , mDoneDecoding(false)
   { }
 
   /**
    * Return value from RequestRefresh. Tells callers what happened in that call
    * to RequestRefresh.
    */
@@ -119,16 +126,40 @@ public:
    */
   uint32_t GetCurrentAnimationFrameIndex() const;
 
   /**
    * Get the area we refresh when we loop around to the first frame.
    */
   nsIntRect GetFirstFrameRefreshArea() const;
 
+  /**
+   * If we have a composited frame for @aFrameNum, returns it. Otherwise, returns
+   * an empty DrawableFrameRef. It is an error to call this method with
+   * aFrameNum == 0, because the first frame is never composited.
+   */
+  DrawableFrameRef GetCompositedFrame(uint32_t aFrameNum);
+
+  /*
+   * Returns the frame's adjusted timeout. If the animation loops and the
+   * timeout falls in between a certain range then the timeout is adjusted so
+   * that it's never 0. If the animation does not loop then no adjustments are
+   * made.
+   */
+  int32_t GetTimeoutForFrame(uint32_t aFrameNum) const;
+
+  /*
+   * Set number of times to loop the image.
+   * @note -1 means loop forever.
+   */
+  void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
+  int32_t LoopCount() const { return mLoopCount; }
+
+  size_t SizeOfCompositingFrames(gfxMemoryLocation aLocation,
+                                 MallocSizeOf aMallocSizeOf) const;
 private: // methods
   /**
    * Gets the length of a single loop of this image, in milliseconds.
    *
    * If this image is not finished decoding, is not animated, or it is animated
    * but does not loop, returns -1. Can return 0 in the case of an animated
    * image that has a 0ms delay between its frames and does not loop.
    */
@@ -150,34 +181,105 @@ private: // methods
 
   /**
    * Get the time the frame we're currently displaying is supposed to end.
    *
    * In the error case, returns an "infinity" timestamp.
    */
   TimeStamp GetCurrentImgFrameEndTime() const;
 
+  bool DoBlend(nsIntRect* aDirtyRect, uint32_t aPrevFrameIndex,
+               uint32_t aNextFrameIndex);
+
+  /**
+   * Get the @aIndex-th frame in the frame index, ignoring results of blending.
+   */
+  RawAccessFrameRef GetRawFrame(uint32_t aFrameNum) const;
+
+  /** Clears an area of <aFrame> with transparent black.
+   *
+   * @param aFrameData Target Frame data
+   * @param aFrameRect The rectangle of the data pointed ot by aFrameData
+   *
+   * @note Does also clears the transparency mask
+   */
+  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect);
+
+  //! @overload
+  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect,
+                         const nsIntRect& aRectToClear);
+
+  //! Copy one frame's image and mask into another
+  static bool CopyFrameImage(const uint8_t* aDataSrc, const nsIntRect& aRectSrc,
+                             uint8_t* aDataDest, const nsIntRect& aRectDest);
+
+  /**
+   * Draws one frame's image to into another, at the position specified by
+   * aSrcRect.
+   *
+   * @aSrcData the raw data of the current frame being drawn
+   * @aSrcRect the size of the source frame, and the position of that frame in
+   *           the composition frame
+   * @aSrcPaletteLength the length (in bytes) of the palette at the beginning
+   *                    of the source data (0 if image is not paletted)
+   * @aSrcHasAlpha whether the source data represents an image with alpha
+   * @aDstPixels the raw data of the composition frame where the current frame
+   *             is drawn into (32-bit ARGB)
+   * @aDstRect the size of the composition frame
+   * @aBlendMethod the blend method for how to blend src on the composition
+   * frame.
+   */
+  static nsresult DrawFrameTo(const uint8_t* aSrcData,
+                              const nsIntRect& aSrcRect,
+                              uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
+                              uint8_t* aDstPixels, const nsIntRect& aDstRect,
+                              BlendMethod aBlendMethod);
+
 private: // data
+  //! A weak pointer to our owning image.
+  RasterImage* mImage;
+
+  //! The intrinsic size of the image.
+  gfx::IntSize mSize;
+
+  /** For managing blending of frames
+   *
+   * Some animations will use the compositingFrame to composite images
+   * and just hand this back to the caller when it is time to draw the frame.
+   * NOTE: When clearing compositingFrame, remember to set
+   *       lastCompositedFrameIndex to -1.  Code assume that if
+   *       lastCompositedFrameIndex >= 0 then compositingFrame exists.
+   */
+  RawAccessFrameRef mCompositingFrame;
+
+  /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
+   *
+   * The Previous Frame (all frames composited up to the current) needs to be
+   * stored in cases where the image specifies it wants the last frame back
+   * when it's done with the current frame.
+   */
+  RawAccessFrameRef mCompositingPrevFrame;
+
   //! Area of the first frame that needs to be redrawn on subsequent loops.
   nsIntRect mFirstFrameRefreshArea;
 
   //! the time that the animation advanced to the current frame
   TimeStamp mCurrentAnimationFrameTime;
 
   //! The current frame index we're on. 0 to (numFrames - 1).
   uint32_t mCurrentAnimationFrameIndex;
 
   //! number of loops remaining before animation stops (-1 no stop)
-  int32_t mLoopCounter;
+  int32_t mLoopRemainingCount;
 
-  //! A weak pointer to our owner.
-  RasterImage* mImage;
+  //! Track the last composited frame for Optimizations (See DoComposite code)
+  int32_t mLastCompositedFrameIndex;
 
-  //! All the frames of the image, shared with our owner
-  FrameBlender& mFrameBlender;
+  //! The total number of loops for the image.
+  int32_t mLoopCount;
 
   //! The animation mode of this image. Constants defined in imgIContainer.
   uint16_t mAnimationMode;
 
   //! Whether this image is done being decoded.
   bool mDoneDecoding;
 };
 
deleted file mode 100644
--- a/image/src/FrameBlender.cpp
+++ /dev/null
@@ -1,525 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "FrameBlender.h"
-
-#include "mozilla/MemoryReporting.h"
-#include "mozilla/Move.h"
-#include "MainThreadUtils.h"
-
-#include "pixman.h"
-
-namespace mozilla {
-
-using namespace gfx;
-
-namespace image {
-
-DrawableFrameRef
-FrameBlender::GetCompositedFrame(uint32_t aFrameNum)
-{
-  MOZ_ASSERT(aFrameNum != 0, "First frame is never composited");
-
-  // If we have a composited version of this frame, return that.
-  if (mLastCompositedFrameIndex == int32_t(aFrameNum)) {
-    return mCompositingFrame->DrawableRef();
-  }
-
-  // Otherwise return the raw frame. DoBlend is required to ensure that we only
-  // hit this case if the frame is not paletted and doesn't require compositing.
-  DrawableFrameRef ref =
-    SurfaceCache::Lookup(mImageKey,
-                         RasterSurfaceKey(mSize,
-                                          0,  // Default decode flags.
-                                          aFrameNum));
-  MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "About to return a paletted frame");
-  return ref;
-}
-
-RawAccessFrameRef
-FrameBlender::GetRawFrame(uint32_t aFrameNum)
-{
-  DrawableFrameRef ref =
-    SurfaceCache::Lookup(mImageKey,
-                         RasterSurfaceKey(mSize,
-                                          0,  // Default decode flags.
-                                          aFrameNum));
-  return ref ? ref->RawAccessRef()
-             : RawAccessFrameRef();
-}
-
-int32_t
-FrameBlender::GetTimeoutForFrame(uint32_t aFrameNum)
-{
-  RawAccessFrameRef frame = GetRawFrame(aFrameNum);
-  const int32_t timeout = frame->GetRawTimeout();
-
-  // Ensure a minimal time between updates so we don't throttle the UI thread.
-  // consider 0 == unspecified and make it fast but not too fast.  Unless we
-  // have a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug
-  // 207059. The behavior of recent IE and Opera versions seems to be:
-  // IE 6/Win:
-  //   10 - 50ms go 100ms
-  //   >50ms go correct speed
-  // Opera 7 final/Win:
-  //   10ms goes 100ms
-  //   >10ms go correct speed
-  // It seems that there are broken tools out there that set a 0ms or 10ms
-  // timeout when they really want a "default" one.  So munge values in that
-  // range.
-  if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) {
-    return 100;
-  }
-
-  return timeout;
-}
-
-//******************************************************************************
-// DoBlend gets called when the timer for animation get fired and we have to
-// update the composited frame of the animation.
-bool
-FrameBlender::DoBlend(nsIntRect* aDirtyRect,
-                      uint32_t aPrevFrameIndex,
-                      uint32_t aNextFrameIndex)
-{
-  RawAccessFrameRef prevFrame = GetRawFrame(aPrevFrameIndex);
-  RawAccessFrameRef nextFrame = GetRawFrame(aNextFrameIndex);
-
-  MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
-
-  int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod();
-  if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
-      !mCompositingPrevFrame) {
-    prevFrameDisposalMethod = FrameBlender::kDisposeClear;
-  }
-
-  nsIntRect prevFrameRect = prevFrame->GetRect();
-  bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
-                          prevFrameRect.width == mSize.width &&
-                          prevFrameRect.height == mSize.height);
-
-  // Optimization: DisposeClearAll if the previous frame is the same size as
-  //               container and it's clearing itself
-  if (isFullPrevFrame &&
-      (prevFrameDisposalMethod == FrameBlender::kDisposeClear)) {
-    prevFrameDisposalMethod = FrameBlender::kDisposeClearAll;
-  }
-
-  int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod();
-  nsIntRect nextFrameRect = nextFrame->GetRect();
-  bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
-                          nextFrameRect.width == mSize.width &&
-                          nextFrameRect.height == mSize.height);
-
-  if (!nextFrame->GetIsPaletted()) {
-    // Optimization: Skip compositing if the previous frame wants to clear the
-    //               whole image
-    if (prevFrameDisposalMethod == FrameBlender::kDisposeClearAll) {
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      return true;
-    }
-
-    // Optimization: Skip compositing if this frame is the same size as the
-    //               container and it's fully drawing over prev frame (no alpha)
-    if (isFullNextFrame &&
-        (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) &&
-        !nextFrame->GetHasAlpha()) {
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      return true;
-    }
-  }
-
-  // Calculate area that needs updating
-  switch (prevFrameDisposalMethod) {
-    default:
-    case FrameBlender::kDisposeNotSpecified:
-    case FrameBlender::kDisposeKeep:
-      *aDirtyRect = nextFrameRect;
-      break;
-
-    case FrameBlender::kDisposeClearAll:
-      // Whole image container is cleared
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      break;
-
-    case FrameBlender::kDisposeClear:
-      // Calc area that needs to be redrawn (the combination of previous and
-      // this frame)
-      // XXX - This could be done with multiple framechanged calls
-      //       Having prevFrame way at the top of the image, and nextFrame
-      //       way at the bottom, and both frames being small, we'd be
-      //       telling framechanged to refresh the whole image when only two
-      //       small areas are needed.
-      aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
-      break;
-
-    case FrameBlender::kDisposeRestorePrevious:
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      break;
-  }
-
-  // Optimization:
-  //   Skip compositing if the last composited frame is this frame
-  //   (Only one composited frame was made for this animation.  Example:
-  //    Only Frame 3 of a 10 frame image required us to build a composite frame
-  //    On the second loop, we do not need to rebuild the frame
-  //    since it's still sitting in compositingFrame)
-  if (mLastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
-    return true;
-  }
-
-  bool needToBlankComposite = false;
-
-  // Create the Compositing Frame
-  if (!mCompositingFrame) {
-    nsRefPtr<imgFrame> newFrame = new imgFrame;
-    nsresult rv = newFrame->InitForDecoder(ThebesIntSize(mSize),
-                                           SurfaceFormat::B8G8R8A8);
-    if (NS_FAILED(rv)) {
-      mCompositingFrame.reset();
-      return false;
-    }
-    mCompositingFrame = newFrame->RawAccessRef();
-    needToBlankComposite = true;
-  } else if (int32_t(aNextFrameIndex) != mLastCompositedFrameIndex+1) {
-
-    // If we are not drawing on top of last composited frame,
-    // then we are building a new composite frame, so let's clear it first.
-    needToBlankComposite = true;
-  }
-
-  // More optimizations possible when next frame is not transparent
-  // But if the next frame has FrameBlender::kDisposeRestorePrevious,
-  // this "no disposal" optimization is not possible,
-  // because the frame in "after disposal operation" state
-  // needs to be stored in compositingFrame, so it can be
-  // copied into compositingPrevFrame later.
-  bool doDisposal = true;
-  if (!nextFrame->GetHasAlpha() &&
-      nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) {
-    if (isFullNextFrame) {
-      // Optimization: No need to dispose prev.frame when
-      // next frame is full frame and not transparent.
-      doDisposal = false;
-      // No need to blank the composite frame
-      needToBlankComposite = false;
-    } else {
-      if ((prevFrameRect.x >= nextFrameRect.x) &&
-          (prevFrameRect.y >= nextFrameRect.y) &&
-          (prevFrameRect.x + prevFrameRect.width <=
-              nextFrameRect.x + nextFrameRect.width) &&
-          (prevFrameRect.y + prevFrameRect.height <=
-              nextFrameRect.y + nextFrameRect.height)) {
-        // Optimization: No need to dispose prev.frame when
-        // next frame fully overlaps previous frame.
-        doDisposal = false;
-      }
-    }
-  }
-
-  if (doDisposal) {
-    // Dispose of previous: clear, restore, or keep (copy)
-    switch (prevFrameDisposalMethod) {
-      case FrameBlender::kDisposeClear:
-        if (needToBlankComposite) {
-          // If we just created the composite, it could have anything in its
-          // buffer. Clear whole frame
-          ClearFrame(mCompositingFrame->GetRawData(),
-                     mCompositingFrame->GetRect());
-        } else {
-          // Only blank out previous frame area (both color & Mask/Alpha)
-          ClearFrame(mCompositingFrame->GetRawData(),
-                     mCompositingFrame->GetRect(),
-                     prevFrameRect);
-        }
-        break;
-
-      case FrameBlender::kDisposeClearAll:
-        ClearFrame(mCompositingFrame->GetRawData(),
-                   mCompositingFrame->GetRect());
-        break;
-
-      case FrameBlender::kDisposeRestorePrevious:
-        // It would be better to copy only the area changed back to
-        // compositingFrame.
-        if (mCompositingPrevFrame) {
-          CopyFrameImage(mCompositingPrevFrame->GetRawData(),
-                         mCompositingPrevFrame->GetRect(),
-                         mCompositingFrame->GetRawData(),
-                         mCompositingFrame->GetRect());
-
-          // destroy only if we don't need it for this frame's disposal
-          if (nextFrameDisposalMethod !=
-              FrameBlender::kDisposeRestorePrevious) {
-            mCompositingPrevFrame.reset();
-          }
-        } else {
-          ClearFrame(mCompositingFrame->GetRawData(),
-                     mCompositingFrame->GetRect());
-        }
-        break;
-
-      default:
-        // Copy previous frame into compositingFrame before we put the new
-        // frame on top
-        // Assumes that the previous frame represents a full frame (it could be
-        // smaller in size than the container, as long as the frame before it
-        // erased itself)
-        // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1)
-        // will always be a valid frame number.
-        if (mLastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
-          if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
-            // Just copy the bits
-            CopyFrameImage(prevFrame->GetRawData(),
-                           prevFrame->GetRect(),
-                           mCompositingFrame->GetRawData(),
-                           mCompositingFrame->GetRect());
-          } else {
-            if (needToBlankComposite) {
-              // Only blank composite when prev is transparent or not full.
-              if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
-                ClearFrame(mCompositingFrame->GetRawData(),
-                           mCompositingFrame->GetRect());
-              }
-            }
-            DrawFrameTo(prevFrame->GetRawData(), prevFrameRect,
-                        prevFrame->PaletteDataLength(),
-                        prevFrame->GetHasAlpha(),
-                        mCompositingFrame->GetRawData(),
-                        mCompositingFrame->GetRect(),
-                        FrameBlendMethod(prevFrame->GetBlendMethod()));
-          }
-        }
-    }
-  } else if (needToBlankComposite) {
-    // If we just created the composite, it could have anything in its
-    // buffers. Clear them
-    ClearFrame(mCompositingFrame->GetRawData(),
-               mCompositingFrame->GetRect());
-  }
-
-  // Check if the frame we are composing wants the previous image restored after
-  // it is done. Don't store it (again) if last frame wanted its image restored
-  // too
-  if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
-      (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
-    // We are storing the whole image.
-    // It would be better if we just stored the area that nextFrame is going to
-    // overwrite.
-    if (!mCompositingPrevFrame) {
-      nsRefPtr<imgFrame> newFrame = new imgFrame;
-      nsresult rv = newFrame->InitForDecoder(ThebesIntSize(mSize),
-                                             SurfaceFormat::B8G8R8A8);
-      if (NS_FAILED(rv)) {
-        mCompositingPrevFrame.reset();
-        return false;
-      }
-
-      mCompositingPrevFrame = newFrame->RawAccessRef();
-    }
-
-    CopyFrameImage(mCompositingFrame->GetRawData(),
-                   mCompositingFrame->GetRect(),
-                   mCompositingPrevFrame->GetRawData(),
-                   mCompositingPrevFrame->GetRect());
-  }
-
-  // blit next frame into it's correct spot
-  DrawFrameTo(nextFrame->GetRawData(), nextFrameRect,
-              nextFrame->PaletteDataLength(),
-              nextFrame->GetHasAlpha(),
-              mCompositingFrame->GetRawData(),
-              mCompositingFrame->GetRect(),
-              FrameBlendMethod(nextFrame->GetBlendMethod()));
-
-  // Set timeout of CompositeFrame to timeout of frame we just composed
-  // Bug 177948
-  int32_t timeout = nextFrame->GetRawTimeout();
-  mCompositingFrame->SetRawTimeout(timeout);
-
-  // Tell the image that it is fully 'downloaded'.
-  nsresult rv =
-    mCompositingFrame->ImageUpdated(mCompositingFrame->GetRect());
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  mLastCompositedFrameIndex = int32_t(aNextFrameIndex);
-
-  return true;
-}
-
-//******************************************************************************
-// Fill aFrame with black. Does also clears the mask.
-void
-FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect)
-{
-  if (!aFrameData) {
-    return;
-  }
-
-  memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
-}
-
-//******************************************************************************
-void
-FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect,
-                         const nsIntRect& aRectToClear)
-{
-  if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
-      aRectToClear.width <= 0 || aRectToClear.height <= 0) {
-    return;
-  }
-
-  nsIntRect toClear = aFrameRect.Intersect(aRectToClear);
-  if (toClear.IsEmpty()) {
-    return;
-  }
-
-  uint32_t bytesPerRow = aFrameRect.width * 4;
-  for (int row = toClear.y; row < toClear.y + toClear.height; ++row) {
-    memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0,
-           toClear.width * 4);
-  }
-}
-
-//******************************************************************************
-// Whether we succeed or fail will not cause a crash, and there's not much
-// we can do about a failure, so there we don't return a nsresult
-bool
-FrameBlender::CopyFrameImage(const uint8_t* aDataSrc, const nsIntRect& aRectSrc,
-                             uint8_t* aDataDest, const nsIntRect& aRectDest)
-{
-  uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
-  uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
-
-  if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
-    return false;
-  }
-
-  memcpy(aDataDest, aDataSrc, dataLengthDest);
-
-  return true;
-}
-
-nsresult
-FrameBlender::DrawFrameTo(const uint8_t* aSrcData, const nsIntRect& aSrcRect,
-                          uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
-                          uint8_t* aDstPixels, const nsIntRect& aDstRect,
-                          FrameBlender::FrameBlendMethod aBlendMethod)
-{
-  NS_ENSURE_ARG_POINTER(aSrcData);
-  NS_ENSURE_ARG_POINTER(aDstPixels);
-
-  // According to both AGIF and APNG specs, offsets are unsigned
-  if (aSrcRect.x < 0 || aSrcRect.y < 0) {
-    NS_WARNING("FrameBlender::DrawFrameTo: negative offsets not allowed");
-    return NS_ERROR_FAILURE;
-  }
-  // Outside the destination frame, skip it
-  if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
-    return NS_OK;
-  }
-
-  if (aSrcPaletteLength) {
-    // Larger than the destination frame, clip it
-    int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
-    int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);
-
-    // The clipped image must now fully fit within destination image frame
-    NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
-                 (aSrcRect.x + width <= aDstRect.width) &&
-                 (aSrcRect.y + height <= aDstRect.height),
-                "FrameBlender::DrawFrameTo: Invalid aSrcRect");
-
-    // clipped image size may be smaller than source, but not larger
-    NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
-                 "FrameBlender::DrawFrameTo: source must be smaller than dest");
-
-    // Get pointers to image data
-    const uint8_t* srcPixels = aSrcData + aSrcPaletteLength;
-    uint32_t* dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
-    const uint32_t* colormap = reinterpret_cast<const uint32_t*>(aSrcData);
-
-    // Skip to the right offset
-    dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
-    if (!aSrcHasAlpha) {
-      for (int32_t r = height; r > 0; --r) {
-        for (int32_t c = 0; c < width; c++) {
-          dstPixels[c] = colormap[srcPixels[c]];
-        }
-        // Go to the next row in the source resp. destination image
-        srcPixels += aSrcRect.width;
-        dstPixels += aDstRect.width;
-      }
-    } else {
-      for (int32_t r = height; r > 0; --r) {
-        for (int32_t c = 0; c < width; c++) {
-          const uint32_t color = colormap[srcPixels[c]];
-          if (color) {
-            dstPixels[c] = color;
-          }
-        }
-        // Go to the next row in the source resp. destination image
-        srcPixels += aSrcRect.width;
-        dstPixels += aDstRect.width;
-      }
-    }
-  } else {
-    pixman_image_t* src =
-      pixman_image_create_bits(
-          aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
-          aSrcRect.width, aSrcRect.height,
-          reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
-          aSrcRect.width * 4);
-    pixman_image_t* dst =
-      pixman_image_create_bits(PIXMAN_a8r8g8b8,
-                               aDstRect.width,
-                               aDstRect.height,
-                               reinterpret_cast<uint32_t*>(aDstPixels),
-                               aDstRect.width * 4);
-
-    auto op = aBlendMethod == FrameBlender::kBlendSource ? PIXMAN_OP_SRC
-                                                         : PIXMAN_OP_OVER;
-    pixman_image_composite32(op,
-                             src,
-                             nullptr,
-                             dst,
-                             0, 0,
-                             0, 0,
-                             aSrcRect.x, aSrcRect.y,
-                             aSrcRect.width, aSrcRect.height);
-
-    pixman_image_unref(src);
-    pixman_image_unref(dst);
-  }
-
-  return NS_OK;
-}
-
-size_t
-FrameBlender::SizeOfDecoded(gfxMemoryLocation aLocation,
-                            MallocSizeOf aMallocSizeOf) const
-{
-  size_t n = 0;
-
-  if (mCompositingFrame) {
-    n += mCompositingFrame->SizeOfExcludingThis(aLocation, aMallocSizeOf);
-  }
-  if (mCompositingPrevFrame) {
-    n += mCompositingPrevFrame->SizeOfExcludingThis(aLocation, aMallocSizeOf);
-  }
-
-  return n;
-}
-
-void
-FrameBlender::ResetAnimation()
-{
-  mLastCompositedFrameIndex = -1;
-}
-
-} // namespace image
-} // namespace mozilla
deleted file mode 100644
--- a/image/src/FrameBlender.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_imagelib_FrameBlender_h_
-#define mozilla_imagelib_FrameBlender_h_
-
-#include "mozilla/MemoryReporting.h"
-#include "gfx2DGlue.h"
-#include "gfxTypes.h"
-#include "imgFrame.h"
-#include "nsCOMPtr.h"
-#include "SurfaceCache.h"
-
-namespace mozilla {
-namespace image {
-
-/**
- * FrameBlender stores and gives access to imgFrames. It also knows how to
- * blend frames from previous to next, looping if necessary.
- *
- * All logic about when and whether to blend are external to FrameBlender.
- */
-class FrameBlender
-{
-public:
-  FrameBlender(ImageKey aImageKey, gfx::IntSize aSize)
-   : mImageKey(aImageKey)
-   , mSize(aSize)
-   , mLastCompositedFrameIndex(-1)
-   , mLoopCount(-1)
-  { }
-
-  bool DoBlend(nsIntRect* aDirtyRect, uint32_t aPrevFrameIndex,
-               uint32_t aNextFrameIndex);
-
-  /**
-   * If we have a composited frame for @aFrameNum, returns it. Otherwise, returns
-   * an empty DrawableFrameRef. It is an error to call this method with
-   * aFrameNum == 0, because the first frame is never composited.
-   */
-  DrawableFrameRef GetCompositedFrame(uint32_t aFrameNum);
-
-  /**
-   * Get the @aIndex-th frame in the frame index, ignoring results of blending.
-   */
-  RawAccessFrameRef GetRawFrame(uint32_t aFrameNum);
-
-  /*
-   * Returns the frame's adjusted timeout. If the animation loops and the
-   * timeout falls in between a certain range then the timeout is adjusted so
-   * that it's never 0. If the animation does not loop then no adjustments are
-   * made.
-   */
-  int32_t GetTimeoutForFrame(uint32_t aFrameNum);
-
-  /*
-   * Set number of times to loop the image.
-   * @note -1 means loop forever.
-   */
-  void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
-  int32_t LoopCount() const { return mLoopCount; }
-
-  size_t SizeOfDecoded(gfxMemoryLocation aLocation,
-                       MallocSizeOf aMallocSizeOf) const;
-
-  void ResetAnimation();
-
-  // "Blend" method indicates how the current image is combined with the
-  // previous image.
-  enum FrameBlendMethod {
-    // All color components of the frame, including alpha, overwrite the current
-    // contents of the frame's output buffer region
-    kBlendSource =  0,
-
-    // The frame should be composited onto the output buffer based on its alpha,
-    // using a simple OVER operation
-    kBlendOver
-  };
-
-  enum FrameDisposalMethod {
-    kDisposeClearAll         = -1, // Clear the whole image, revealing
-                                   // what was there before the gif displayed
-    kDisposeNotSpecified,   // Leave frame, let new frame draw on top
-    kDisposeKeep,           // Leave frame, let new frame draw on top
-    kDisposeClear,          // Clear the frame's area, revealing bg
-    kDisposeRestorePrevious // Restore the previous (composited) frame
-  };
-
-  // A hint as to whether an individual frame is entirely opaque, or requires
-  // alpha blending.
-  enum FrameAlpha {
-    kFrameHasAlpha,
-    kFrameOpaque
-  };
-
-private:
-  /** Clears an area of <aFrame> with transparent black.
-   *
-   * @param aFrameData Target Frame data
-   * @param aFrameRect The rectangle of the data pointed ot by aFrameData
-   *
-   * @note Does also clears the transparency mask
-   */
-  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect);
-
-  //! @overload
-  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect,
-                         const nsIntRect& aRectToClear);
-
-  //! Copy one frame's image and mask into another
-  static bool CopyFrameImage(const uint8_t* aDataSrc, const nsIntRect& aRectSrc,
-                             uint8_t* aDataDest, const nsIntRect& aRectDest);
-
-  /**
-   * Draws one frame's image to into another, at the position specified by
-   * aSrcRect.
-   *
-   * @aSrcData the raw data of the current frame being drawn
-   * @aSrcRect the size of the source frame, and the position of that frame in
-   *           the composition frame
-   * @aSrcPaletteLength the length (in bytes) of the palette at the beginning
-   *                    of the source data (0 if image is not paletted)
-   * @aSrcHasAlpha whether the source data represents an image with alpha
-   * @aDstPixels the raw data of the composition frame where the current frame
-   *             is drawn into (32-bit ARGB)
-   * @aDstRect the size of the composition frame
-   * @aBlendMethod the blend method for how to blend src on the composition
-   * frame.
-   */
-  static nsresult DrawFrameTo(const uint8_t* aSrcData,
-                              const nsIntRect& aSrcRect,
-                              uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
-                              uint8_t* aDstPixels, const nsIntRect& aDstRect,
-                              FrameBlendMethod aBlendMethod);
-
-private: // data
-  ImageKey mImageKey;
-  gfx::IntSize mSize;
-
-  //! Track the last composited frame for Optimizations (See DoComposite code)
-  int32_t mLastCompositedFrameIndex;
-
-  /** For managing blending of frames
-   *
-   * Some animations will use the compositingFrame to composite images
-   * and just hand this back to the caller when it is time to draw the frame.
-   * NOTE: When clearing compositingFrame, remember to set
-   *       lastCompositedFrameIndex to -1.  Code assume that if
-   *       lastCompositedFrameIndex >= 0 then compositingFrame exists.
-   */
-  RawAccessFrameRef mCompositingFrame;
-
-  /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
-   *
-   * The Previous Frame (all frames composited up to the current) needs to be
-   * stored in cases where the image specifies it wants the last frame back
-   * when it's done with the current frame.
-   */
-  RawAccessFrameRef mCompositingPrevFrame;
-
-  int32_t mLoopCount;
-};
-
-} // namespace image
-} // namespace mozilla
-
-#endif /* mozilla_imagelib_FrameBlender_h_ */
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -547,20 +547,19 @@ RasterImage::LookupFrameInternal(uint32_
 {
   if (!mAnim) {
     NS_ASSERTION(aFrameNum == 0,
                  "Don't ask for a frame > 0 if we're not animated!");
     aFrameNum = 0;
   }
 
   if (mAnim && aFrameNum > 0) {
-    MOZ_ASSERT(mFrameBlender, "mAnim but no mFrameBlender?");
     MOZ_ASSERT(DecodeFlags(aFlags) == DECODE_FLAGS_DEFAULT,
                "Can't composite frames with non-default decode flags");
-    return mFrameBlender->GetCompositedFrame(aFrameNum);
+    return mAnim->GetCompositedFrame(aFrameNum);
   }
 
   return SurfaceCache::Lookup(ImageKey(this),
                               RasterSurfaceKey(aSize.ToIntSize(),
                                                DecodeFlags(aFlags),
                                                aFrameNum));
 }
 
@@ -697,18 +696,18 @@ RasterImage::GetFirstFrameDelay()
 {
   if (mError)
     return -1;
 
   bool animated = false;
   if (NS_FAILED(GetAnimated(&animated)) || !animated)
     return -1;
 
-  MOZ_ASSERT(mFrameBlender, "Animated images should have a FrameBlender");
-  return mFrameBlender->GetTimeoutForFrame(0);
+  MOZ_ASSERT(mAnim, "Animated images should have a FrameAnimator");
+  return mAnim->GetTimeoutForFrame(0);
 }
 
 TemporaryRef<SourceSurface>
 RasterImage::CopyFrame(uint32_t aWhichFrame,
                        uint32_t aFlags,
                        bool aShouldSyncNotify /* = true */)
 {
   if (aWhichFrame > FRAME_MAX_VALUE)
@@ -920,18 +919,18 @@ RasterImage::SizeOfSourceWithComputedFal
 }
 
 size_t
 RasterImage::SizeOfDecoded(gfxMemoryLocation aLocation,
                            MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
   n += SurfaceCache::SizeOfSurfaces(ImageKey(this), aLocation, aMallocSizeOf);
-  if (mFrameBlender) {
-    n += mFrameBlender->SizeOfDecoded(aLocation, aMallocSizeOf);
+  if (mAnim) {
+    n += mAnim->SizeOfCompositingFrames(aLocation, aMallocSizeOf);
   }
   return n;
 }
 
 RawAccessFrameRef
 RasterImage::InternalAddFrame(uint32_t aFrameNum,
                               const nsIntRect& aFrameRect,
                               uint32_t aDecodeFlags,
@@ -979,20 +978,18 @@ RasterImage::InternalAddFrame(uint32_t a
                                           aFrameNum),
                          Lifetime::Persistent);
   if (!succeeded) {
     return RawAccessFrameRef();
   }
 
   if (aFrameNum == 1) {
     // We're becoming animated, so initialize animation stuff.
-    MOZ_ASSERT(!mFrameBlender, "Already have a FrameBlender?");
     MOZ_ASSERT(!mAnim, "Already have animation state?");
-    mFrameBlender.emplace(ImageKey(this), mSize.ToIntSize());
-    mAnim = MakeUnique<FrameAnimator>(this, *mFrameBlender, mAnimationMode);
+    mAnim = MakeUnique<FrameAnimator>(this, mSize.ToIntSize(), mAnimationMode);
 
     // We don't support discarding animated images (See bug 414259).
     // Lock the image and throw away the key.
     //
     // Note that this is inefficient, since we could get rid of the source data
     // too. However, doing this is actually hard, because we're probably
     // mid-decode, and thus we're decoding out of the source buffer. Since we're
     // going to fix this anyway later, and since we didn't kill the source data
@@ -1000,19 +997,20 @@ RasterImage::InternalAddFrame(uint32_t a
     LockImage();
 
     MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
     aPreviousFrame->SetRawAccessOnly();
 
     // If we dispose of the first frame by clearing it, then the first frame's
     // refresh area is all of itself.
     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
-    int32_t frameDisposalMethod = aPreviousFrame->GetFrameDisposalMethod();
-    if (frameDisposalMethod == FrameBlender::kDisposeClear ||
-        frameDisposalMethod == FrameBlender::kDisposeRestorePrevious) {
+    DisposalMethod disposalMethod = aPreviousFrame->GetDisposalMethod();
+    if (disposalMethod == DisposalMethod::CLEAR ||
+        disposalMethod == DisposalMethod::CLEAR_ALL ||
+        disposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
       mAnim->SetFirstFrameRefreshArea(aPreviousFrame->GetRect());
     }
 
     if (mPendingAnimation && ShouldAnimate()) {
       StartAnimation();
     }
   }
 
@@ -1092,17 +1090,17 @@ RasterImage::EnsureFrame(uint32_t aFrame
   // We're replacing a frame. It must be the first frame; there's no reason to
   // ever replace any other frame, since the first frame is the only one we
   // speculatively allocate without knowing what the decoder really needs.
   // XXX(seth): I'm not convinced there's any reason to support this at all. We
   // should figure out how to avoid triggering this and rip it out.
   MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?");
   MOZ_ASSERT(GetNumFrames() == 1, "Should have only one frame");
   MOZ_ASSERT(aPreviousFrame, "Need the previous frame to replace");
-  MOZ_ASSERT(!mFrameBlender && !mAnim, "Shouldn't be animated");
+  MOZ_ASSERT(!mAnim, "Shouldn't be animated");
   if (aFrameNum != 0 || !aPreviousFrame || GetNumFrames() != 1) {
     return RawAccessFrameRef();
   }
 
   MOZ_ASSERT(!aPreviousFrame->GetRect().IsEqualEdges(aFrameRect) ||
              aPreviousFrame->GetFormat() != aFormat ||
              aPreviousFrame->GetPaletteDepth() != aPaletteDepth,
              "Replacing first frame with the same kind of frame?");
@@ -1167,27 +1165,24 @@ RasterImage::StartAnimation()
   // mPendingAnimation will cause us to start animating as soon as we have a
   // second frame, which causes mAnim to be constructed.
   mPendingAnimation = !mAnim;
   if (mPendingAnimation) {
     return NS_OK;
   }
 
   // A timeout of -1 means we should display this frame forever.
-  MOZ_ASSERT(mFrameBlender, "Have an animation but no FrameBlender?");
-  if (mFrameBlender->GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) {
+  if (mAnim->GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) {
     mAnimationFinished = true;
     return NS_ERROR_ABORT;
   }
 
-  if (mAnim) {
-    // We need to set the time that this initial frame was first displayed, as
-    // this is used in AdvanceFrame().
-    mAnim->InitAnimationFrameTimeIfNecessary();
-  }
+  // We need to set the time that this initial frame was first displayed, as
+  // this is used in AdvanceFrame().
+  mAnim->InitAnimationFrameTimeIfNecessary();
 
   return NS_OK;
 }
 
 //******************************************************************************
 /* void stopAnimation (); */
 nsresult
 RasterImage::StopAnimation()
@@ -1220,18 +1215,17 @@ RasterImage::ResetAnimation()
     return NS_OK;
   }
 
   mAnimationFinished = false;
 
   if (mAnimating)
     StopAnimation();
 
-  MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender");
-  mFrameBlender->ResetAnimation();
+  MOZ_ASSERT(mAnim, "Should have a FrameAnimator");
   mAnim->ResetAnimation();
 
   UpdateImageContainer();
 
   // Note - We probably want to kick off a redecode somewhere around here when
   // we fix bug 500402.
 
   // Update display
@@ -1268,20 +1262,19 @@ RasterImage::GetFrameIndex(uint32_t aWhi
 }
 
 void
 RasterImage::SetLoopCount(int32_t aLoopCount)
 {
   if (mError)
     return;
 
+  // No need to set this if we're not an animation.
   if (mAnim) {
-    // No need to set this if we're not an animation
-    MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender");
-    mFrameBlender->SetLoopCount(aLoopCount);
+    mAnim->SetLoopCount(aLoopCount);
   }
 }
 
 NS_IMETHODIMP_(nsIntRect)
 RasterImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 {
   return aRect;
 }
@@ -1533,18 +1526,17 @@ RasterImage::Discard()
 
   // As soon as an image becomes animated, it becomes non-discardable and any
   // timers are cancelled.
   NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
 
   // For post-operation logging
   int old_frame_count = GetNumFrames();
 
-  // Delete all the decoded frames
-  mFrameBlender.reset();
+  // Delete all the decoded frames.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   // Flag that we no longer have decoded frames for this image
   mDecoded = false;
   mFrameCount = 0;
 
   // Notify that we discarded
   if (mProgressTracker) {
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -13,17 +13,16 @@
  * @author  Arron Mogge <paper@animecity.nu>
  * @author  Andrew Smith <asmith15@learn.senecac.on.ca>
  */
 
 #ifndef mozilla_imagelib_RasterImage_h_
 #define mozilla_imagelib_RasterImage_h_
 
 #include "Image.h"
-#include "FrameBlender.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsTArray.h"
 #include "imgFrame.h"
 #include "nsThreadUtils.h"
 #include "DecodePool.h"
 #include "Orientation.h"
@@ -343,21 +342,19 @@ private: // data
   // with the browser's needs for displaying the image to the user.
   // As such, we may need to redecode if we're being asked for
   // a frame with different flags.  0 indicates default flags.
   //
   // Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
   // and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION.
   uint32_t                   mFrameDecodeFlags;
 
-  //! All the frames of the image.
-  Maybe<FrameBlender>       mFrameBlender;
-
   nsCOMPtr<nsIProperties>   mProperties;
 
+  //! All the frames of the image.
   // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
   // that the frames actually exist (they may have been discarded to save memory, or
   // we maybe decoding on draw).
   UniquePtr<FrameAnimator> mAnim;
 
   // Image locking.
   uint32_t                   mLockCount;
 
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -128,19 +128,19 @@ static bool AllowedImageAndFrameDimensio
 }
 
 
 imgFrame::imgFrame() :
   mDecoded(0, 0, 0, 0),
   mDecodedMutex("imgFrame::mDecoded"),
   mPalettedImageData(nullptr),
   mTimeout(100),
-  mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
   mLockCount(0),
-  mBlendMethod(1), /* imgIContainer::kBlendOver */
+  mDisposalMethod(DisposalMethod::NOT_SPECIFIED),
+  mBlendMethod(BlendMethod::OVER),
   mSinglePixel(false),
   mCompositingFailed(false),
   mHasNoAlpha(false),
   mNonPremult(false),
   mOptimizable(false)
 {
   static bool hasCheckedOptimize = false;
   if (!hasCheckedOptimize) {
@@ -830,36 +830,16 @@ int32_t imgFrame::GetRawTimeout() const
   return mTimeout;
 }
 
 void imgFrame::SetRawTimeout(int32_t aTimeout)
 {
   mTimeout = aTimeout;
 }
 
-int32_t imgFrame::GetFrameDisposalMethod() const
-{
-  return mDisposalMethod;
-}
-
-void imgFrame::SetFrameDisposalMethod(int32_t aFrameDisposalMethod)
-{
-  mDisposalMethod = aFrameDisposalMethod;
-}
-
-int32_t imgFrame::GetBlendMethod() const
-{
-  return mBlendMethod;
-}
-
-void imgFrame::SetBlendMethod(int32_t aBlendMethod)
-{
-  mBlendMethod = (int8_t)aBlendMethod;
-}
-
 // This can be called from any thread.
 bool imgFrame::ImageComplete() const
 {
   MutexAutoLock lock(mDecodedMutex);
 
   return mDecoded.IsEqualInterior(nsIntRect(mOffset.x, mOffset.y,
                                             mSize.width, mSize.height));
 }
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -5,27 +5,51 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef imgFrame_h
 #define imgFrame_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/TypedEnum.h"
 #include "mozilla/VolatileBuffer.h"
 #include "gfxDrawable.h"
 #include "imgIContainer.h"
 
 namespace mozilla {
 namespace image {
 
 class ImageRegion;
 class DrawableFrameRef;
 class RawAccessFrameRef;
 
+MOZ_BEGIN_ENUM_CLASS(BlendMethod, int8_t)
+  // All color components of the frame, including alpha, overwrite the current
+  // contents of the frame's output buffer region.
+  SOURCE,
+
+  // The frame should be composited onto the output buffer based on its alpha,
+  // using a simple OVER operation.
+  OVER
+MOZ_END_ENUM_CLASS(BlendMethod)
+
+MOZ_BEGIN_ENUM_CLASS(DisposalMethod, int8_t)
+  CLEAR_ALL = -1,  // Clear the whole image, revealing what's underneath.
+  NOT_SPECIFIED,   // Leave the frame and let the new frame draw on top.
+  KEEP,            // Leave the frame and let the new frame draw on top.
+  CLEAR,           // Clear the frame's area, revealing what's underneath.
+  RESTORE_PREVIOUS // Restore the previous (composited) frame.
+MOZ_END_ENUM_CLASS(DisposalMethod)
+
+MOZ_BEGIN_ENUM_CLASS(Opacity, uint8_t)
+  OPAQUE,
+  SOME_TRANSPARENCY
+MOZ_END_ENUM_CLASS(Opacity)
+
 class imgFrame
 {
   typedef gfx::Color Color;
   typedef gfx::DataSourceSurface DataSourceSurface;
   typedef gfx::DrawTarget DrawTarget;
   typedef gfx::IntSize IntSize;
   typedef gfx::SourceSurface SourceSurface;
   typedef gfx::SurfaceFormat SurfaceFormat;
@@ -108,20 +132,25 @@ public:
   uint8_t* GetImageData() const;
   void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
   uint32_t* GetPaletteData() const;
   uint8_t* GetRawData() const;
 
   int32_t GetRawTimeout() const;
   void SetRawTimeout(int32_t aTimeout);
 
-  int32_t GetFrameDisposalMethod() const;
-  void SetFrameDisposalMethod(int32_t aFrameDisposalMethod);
-  int32_t GetBlendMethod() const;
-  void SetBlendMethod(int32_t aBlendMethod);
+  DisposalMethod GetDisposalMethod() const { return mDisposalMethod; }
+  void SetDisposalMethod(DisposalMethod aDisposalMethod)
+  {
+    mDisposalMethod = aDisposalMethod;
+  }
+
+  BlendMethod GetBlendMethod() const { return mBlendMethod; }
+  void SetBlendMethod(BlendMethod aBlendMethod) { mBlendMethod = aBlendMethod; }
+
   bool ImageComplete() const;
 
   void SetHasNoAlpha();
   void SetAsNonPremult(bool aIsNonPremult);
 
   bool GetCompositingFailed() const;
   void SetCompositingFailed(bool val);
 
@@ -197,27 +226,27 @@ private: // data
   // The paletted data comes first, then the image data itself.
   // Total length is PaletteDataLength() + GetImageDataLength().
   uint8_t*     mPalettedImageData;
 
   // Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
   Color        mSinglePixelColor;
 
   int32_t      mTimeout; // -1 means display forever
-  int32_t      mDisposalMethod;
 
   /** Indicates how many readers currently have locked this frame */
   int32_t mLockCount;
 
   RefPtr<VolatileBuffer> mVBuf;
   VolatileBufferPtr<uint8_t> mVBufPtr;
 
-  SurfaceFormat mFormat;
-  uint8_t      mPaletteDepth;
-  int8_t       mBlendMethod;
+  SurfaceFormat  mFormat;
+  uint8_t        mPaletteDepth;
+  DisposalMethod mDisposalMethod;
+  BlendMethod    mBlendMethod;
   bool mSinglePixel;
   bool mCompositingFailed;
   bool mHasNoAlpha;
   bool mNonPremult;
   bool mOptimizable;
 
   friend class DrawableFrameRef;
   friend class RawAccessFrameRef;
--- a/image/src/moz.build
+++ b/image/src/moz.build
@@ -16,17 +16,16 @@ EXPORTS += [
 ]
 
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
     'DecodePool.cpp',
     'Decoder.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
-    'FrameBlender.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
     'ImageFactory.cpp',
     'ImageMetadata.cpp',
     'ImageOps.cpp',
     'ImageWrapper.cpp',
     'imgFrame.cpp',
     'imgTools.cpp',
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -8487,17 +8487,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee);
 
     // 2.3. Save callee
     masm.storePtr(callee, Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 2.4. Load callee executable entry point
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
-    masm.loadBaselineOrIonNoArgCheck(callee, callee, SequentialExecution, nullptr);
+    masm.loadBaselineOrIonNoArgCheck(callee, callee, nullptr);
 
     // 3. Argc
     unsigned argc = exit.sig().args().length();
     masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 4. |this| value
     masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset));
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -180,18 +180,17 @@ Zone::discardJitCode(FreeOp *fop)
         /* Mark baseline scripts on the stack as active. */
         jit::MarkActiveBaselineScripts(this);
 
         /* Only mark OSI points if code is being discarded. */
         jit::InvalidateAll(fop, this);
 
         for (ZoneCellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
-            jit::FinishInvalidation<SequentialExecution>(fop, script);
-            jit::FinishInvalidation<ParallelExecution>(fop, script);
+            jit::FinishInvalidation(fop, script);
 
             /*
              * Discard baseline script if it's not marked as active. Note that
              * this also resets the active flag.
              */
             jit::FinishDiscardBaselineScript(fop, script);
 
             /*
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/execution-observability-03.js
@@ -0,0 +1,17 @@
+// Tests that bare callVMs (in the delprop below) are patched correctly.
+
+var o = {};
+var global = this;
+var p = new Proxy(o, {
+  "deleteProperty": function (target, key) {
+    var g = newGlobal();
+    g.parent = global;
+    g.eval("var dbg = new Debugger(parent); dbg.onEnterFrame = function(frame) {};");
+    return true;
+  }
+});
+function test() {
+  for (var i=0; i<100; i++) {}
+  assertEq(delete p.foo, true);
+}
+test();
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -546,17 +546,17 @@ BaselineCompiler::emitStackCheck(bool ea
     pushArg(R1.scratchReg());
 
     CallVMPhase phase = POST_INITIALIZE;
     if (earlyCheck)
         phase = PRE_INITIALIZE;
     else if (needsEarlyStackCheck())
         phase = CHECK_OVER_RECURSED;
 
-    if (!callVM(CheckOverRecursedWithExtraInfo, phase))
+    if (!callVMNonOp(CheckOverRecursedWithExtraInfo, phase))
         return false;
 
     masm.bind(&skipCall);
     return true;
 }
 
 typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *);
 static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::DebugPrologue);
@@ -621,31 +621,31 @@ BaselineCompiler::initScopeChain()
 
         if (fun->isHeavyweight()) {
             // Call into the VM to create a new call object.
             prepareVMCall();
 
             masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
             pushArg(R0.scratchReg());
 
-            if (!callVM(HeavyweightFunPrologueInfo, phase))
+            if (!callVMNonOp(HeavyweightFunPrologueInfo, phase))
                 return false;
         }
     } else {
         // ScopeChain pointer in BaselineFrame has already been initialized
         // in prologue.
 
         if (script->isForEval() && script->strict()) {
             // Strict eval needs its own call object.
             prepareVMCall();
 
             masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
             pushArg(R0.scratchReg());
 
-            if (!callVM(StrictEvalPrologueInfo, phase))
+            if (!callVMNonOp(StrictEvalPrologueInfo, phase))
                 return false;
         }
     }
 
     return true;
 }
 
 typedef bool (*InterruptCheckFn)(JSContext *);
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -345,18 +345,17 @@ PatchBaselineFramesForDebugMode(JSContex
     // Recompile Patching Overview
     //
     // When toggling debug mode with live baseline scripts on the stack, we
     // could have entered the VM via the following ways from the baseline
     // script.
     //
     // Off to On:
     //  A. From a "can call" stub.
-    //  B. From a VM call (interrupt handler, debugger statement handler,
-    //                     throw).
+    //  B. From a VM call.
     //  H. From inside HandleExceptionBaseline.
     //
     // On to Off:
     //  - All the ways above.
     //  C. From the debug trap handler.
     //  D. From the debug prologue.
     //  E. From the debug epilogue.
     //
@@ -468,35 +467,37 @@ PatchBaselineFramesForDebugMode(JSContex
             }
 
             // The RecompileInfo must already be allocated so that this
             // function may be infallible.
             BaselineDebugModeOSRInfo *recompInfo = entry.takeRecompInfo();
 
             bool popFrameReg;
             switch (kind) {
-              case ICEntry::Kind_CallVM:
+              case ICEntry::Kind_CallVM: {
                 // Case B above.
                 //
-                // Patching returns from an interrupt handler or the debugger
-                // statement handler is similar in that we can resume at the
-                // next op.
+                // Patching returns from a VM call. After fixing up the the
+                // continuation for unsynced values (the frame register is
+                // popped by the callVM trampoline), we resume at the
+                // return-from-callVM address. The assumption here is that all
+                // callVMs which can trigger debug mode OSR are the *only*
+                // callVMs generated for their respective pc locations in the
+                // baseline JIT code.
                 //
-                // Throws are treated differently, as patching a throw means
-                // we are recompiling on-stack scripts from inside an
-                // onExceptionUnwind invocation. The resume address must be
-                // settled on the throwing pc and not its successor, so that
-                // Debugger.Frame may report the correct offset. Note we never
-                // actually resume execution there, and it is set for the sake
-                // of frame iterators.
-                if (!iter.baselineFrame()->isDebuggerHandlingException())
-                    pc += GetBytecodeLength(pc);
-                recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
-                popFrameReg = true;
+                // Get the slot info for the next pc, but ignore the code
+                // address. We get the code address from the
+                // return-from-callVM entry instead.
+                (void) bl->maybeNativeCodeForPC(script, pc + GetBytecodeLength(pc),
+                                                &recompInfo->slotInfo);
+                ICEntry &callVMEntry = bl->callVMEntryFromPCOffset(pcOffset);
+                recompInfo->resumeAddr = bl->returnAddressForIC(callVMEntry);
+                popFrameReg = false;
                 break;
+              }
 
               case ICEntry::Kind_DebugTrap:
                 // Case C above.
                 //
                 // Debug traps are emitted before each op, so we resume at the
                 // same op. Calling debug trap handlers is done via a toggled
                 // call to a thunk (DebugTrapHandler) that takes care tearing
                 // down its own stub frame so we don't need to worry about
@@ -901,22 +902,19 @@ HasForcedReturn(BaselineDebugModeOSRInfo
     ICEntry::Kind kind = info->frameKind;
 
     // The debug epilogue always checks its resumption value, so we don't need
     // to check rv.
     if (kind == ICEntry::Kind_DebugEpilogue)
         return true;
 
     // |rv| is the value in ReturnReg. If true, in the case of the prologue,
-    // debug trap, and debugger statement handler, it means a forced return.
-    if (kind == ICEntry::Kind_DebugPrologue ||
-        (kind == ICEntry::Kind_CallVM && JSOp(*info->pc) == JSOP_DEBUGGER))
-    {
+    // it means a forced return.
+    if (kind == ICEntry::Kind_DebugPrologue)
         return rv;
-    }
 
     // N.B. The debug trap handler handles its own forced return, so no
     // need to deal with it here.
     return false;
 }
 
 static void
 SyncBaselineDebugModeOSRInfo(BaselineFrame *frame, Value *vp, bool rv)
@@ -929,23 +927,29 @@ SyncBaselineDebugModeOSRInfo(BaselineFra
         // Load the frame's rval and overwrite the resume address to go to the
         // epilogue.
         MOZ_ASSERT(R0 == JSReturnOperand);
         info->valueR0 = frame->returnValue();
         info->resumeAddr = frame->script()->baselineScript()->epilogueEntryAddr();
         return;
     }
 
-    // Read stack values and make sure R0 and R1 have the right values.
-    unsigned numUnsynced = info->slotInfo.numUnsynced();
-    MOZ_ASSERT(numUnsynced <= 2);
-    if (numUnsynced > 0)
-        info->popValueInto(info->slotInfo.topSlotLocation(), vp);
-    if (numUnsynced > 1)
-        info->popValueInto(info->slotInfo.nextSlotLocation(), vp);
+    // Read stack values and make sure R0 and R1 have the right values if we
+    // aren't returning from a callVM.
+    //
+    // In the case of returning from a callVM, we don't need to restore R0 and
+    // R1 ourself since we'll return into code that does it if needed.
+    if (info->frameKind != ICEntry::Kind_CallVM) {
+        unsigned numUnsynced = info->slotInfo.numUnsynced();
+        MOZ_ASSERT(numUnsynced <= 2);
+        if (numUnsynced > 0)
+            info->popValueInto(info->slotInfo.topSlotLocation(), vp);
+        if (numUnsynced > 1)
+            info->popValueInto(info->slotInfo.nextSlotLocation(), vp);
+    }
 
     // Scale stackAdjust.
     info->stackAdjust *= sizeof(Value);
 }
 
 static void
 FinishBaselineDebugModeOSR(BaselineFrame *frame)
 {
@@ -980,16 +984,63 @@ JitRuntime::getBaselineDebugModeOSRHandl
 {
     if (!getBaselineDebugModeOSRHandler(cx))
         return nullptr;
     return popFrameReg
            ? baselineDebugModeOSRHandler_->raw()
            : baselineDebugModeOSRHandlerNoFrameRegPopAddr_;
 }
 
+static void
+EmitBaselineDebugModeOSRHandlerTail(MacroAssembler &masm, Register temp, bool returnFromCallVM)
+{
+    // Save real return address on the stack temporarily.
+    //
+    // If we're returning from a callVM, we don't need to worry about R0 and
+    // R1 but do need to propagate the original ReturnReg value. Otherwise we
+    // need to worry about R0 and R1 but can clobber ReturnReg. Indeed, on
+    // x86, R1 contains ReturnReg.
+    if (returnFromCallVM) {
+        masm.push(ReturnReg);
+    } else {
+        masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
+        masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
+    }
+    masm.push(BaselineFrameReg);
+    masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
+
+    // Call a stub to free the allocated info.
+    masm.setupUnalignedABICall(1, temp);
+    masm.loadBaselineFramePtr(BaselineFrameReg, temp);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBaselineDebugModeOSR));
+
+    // Restore saved values.
+    GeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
+    if (returnFromCallVM) {
+        jumpRegs.take(ReturnReg);
+    } else {
+        jumpRegs.take(R0);
+        jumpRegs.take(R1);
+    }
+    jumpRegs.take(BaselineFrameReg);
+    Register target = jumpRegs.takeAny();
+
+    masm.pop(target);
+    masm.pop(BaselineFrameReg);
+    if (returnFromCallVM) {
+        masm.pop(ReturnReg);
+    } else {
+        masm.popValue(R1);
+        masm.popValue(R0);
+    }
+
+    masm.jump(target);
+}
+
 JitCode *
 JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut)
 {
     MacroAssembler masm(cx);
 
     GeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(BaselineFrameReg);
     regs.take(ReturnReg);
@@ -1000,58 +1051,49 @@ JitRuntime::generateBaselineDebugModeOSR
     masm.pop(BaselineFrameReg);
 
     // Not all patched baseline frames are returning from a situation where
     // the frame reg is already fixed up.
     CodeOffsetLabel noFrameRegPopOffset(masm.currentOffset());
 
     // Record the stack pointer for syncing.
     masm.movePtr(StackPointer, syncedStackStart);
+    masm.push(ReturnReg);
     masm.push(BaselineFrameReg);
 
     // Call a stub to fully initialize the info.
     masm.setupUnalignedABICall(3, temp);
     masm.loadBaselineFramePtr(BaselineFrameReg, temp);
     masm.passABIArg(temp);
     masm.passABIArg(syncedStackStart);
     masm.passABIArg(ReturnReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, SyncBaselineDebugModeOSRInfo));
 
     // Discard stack values depending on how many were unsynced, as we always
-    // have a fully synced stack in the recompile handler. See assert in
-    // DebugModeOSREntry constructor.
+    // have a fully synced stack in the recompile handler. We arrive here via
+    // a callVM, and prepareCallVM in BaselineCompiler always fully syncs the
+    // stack.
     masm.pop(BaselineFrameReg);
+    masm.pop(ReturnReg);
     masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp);
     masm.addPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)), StackPointer);
 
-    // Save real return address on the stack temporarily.
-    masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
-    masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
-    masm.push(BaselineFrameReg);
-    masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
-
-    // Call a stub to free the allocated info.
-    masm.setupUnalignedABICall(1, temp);
-    masm.loadBaselineFramePtr(BaselineFrameReg, temp);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBaselineDebugModeOSR));
+    // Emit two tails for the case of returning from a callVM and all other
+    // cases, as the state we need to restore differs depending on the case.
+    Label returnFromCallVM, end;
+    masm.branch32(MacroAssembler::Equal,
+                  Address(temp, offsetof(BaselineDebugModeOSRInfo, frameKind)),
+                  Imm32(ICEntry::Kind_CallVM),
+                  &returnFromCallVM);
 
-    // Restore saved values.
-    GeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
-    jumpRegs.take(R0);
-    jumpRegs.take(R1);
-    jumpRegs.take(BaselineFrameReg);
-    Register target = jumpRegs.takeAny();
-
-    masm.pop(target);
-    masm.pop(BaselineFrameReg);
-    masm.popValue(R1);
-    masm.popValue(R0);
-
-    masm.jump(target);
+    EmitBaselineDebugModeOSRHandlerTail(masm, temp, /* returnFromCallVM = */ false);
+    masm.jump(&end);
+    masm.bind(&returnFromCallVM);
+    EmitBaselineDebugModeOSRHandlerTail(masm, temp, /* returnFromCallVM = */ true);
+    masm.bind(&end);
 
     Linker linker(masm);
     AutoFlushICache afc("BaselineDebugModeOSRHandler");
     JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
     noFrameRegPopOffset.fixup(&masm);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4256,30 +4256,30 @@ ICGetElemNativeCompiler::emitCallScripte
         masm.Push(Imm32(0));  // ActualArgc is 0
         masm.Push(callee);
         masm.Push(callScratch);
         regs.add(callScratch);
     }
 
     Register code = regs.takeAnyExcluding(ArgumentsRectifierReg);
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
-    masm.loadBaselineOrIonRaw(code, code, SequentialExecution, nullptr);
+    masm.loadBaselineOrIonRaw(code, code, nullptr);
 
     Register scratch = regs.takeAny();
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
     masm.branch32(Assembler::Equal, scratch, Imm32(0), &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != code);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
         masm.mov(ImmWord(0), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
 
@@ -4489,18 +4489,17 @@ ICGetElemNativeCompiler::generateStubCod
         } else {
             MOZ_ASSERT(acctype_ == ICGetElemNativeStub::ScriptedGetter);
 
             // Load function in scratchReg and ensure that it has a jit script.
             masm.loadPtr(Address(BaselineStubReg, ICGetElemNativeGetterStub::offsetOfGetter()),
                          scratchReg);
             masm.branchIfFunctionHasNoScript(scratchReg, popR1 ? &failurePopR1 : &failure);
             masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
-            masm.loadBaselineOrIonRaw(scratchReg, scratchReg, SequentialExecution,
-                                      popR1 ? &failurePopR1 : &failure);
+            masm.loadBaselineOrIonRaw(scratchReg, scratchReg, popR1 ? &failurePopR1 : &failure);
 
             // At this point, we are guaranteed to successfully complete.
             if (popR1)
                 masm.addPtr(Imm32(sizeof(size_t)), BaselineStackReg);
 
             emitCallScripted(masm, objReg);
         }
     }
@@ -7416,17 +7415,17 @@ ICGetProp_CallScripted::Compiler::genera
         regs.take(callee);
     } else {
         callee = regs.takeAny();
     }
     Register code = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallScripted::offsetOfGetter()), callee);
     masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame);
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
-    masm.loadBaselineOrIonRaw(code, code, SequentialExecution, &failureLeaveStubFrame);
+    masm.loadBaselineOrIonRaw(code, code, &failureLeaveStubFrame);
 
     // Getter is called with 0 arguments, just |obj| as thisv.
     // Note that we use Push, not push, so that callJit will align the stack
     // properly on ARM.
     masm.Push(R0);
     EmitCreateStubFrameDescriptor(masm, scratch);
     masm.Push(Imm32(0));  // ActualArgc is 0
     masm.Push(callee);
@@ -7436,17 +7435,17 @@ ICGetProp_CallScripted::Compiler::genera
     Label noUnderflow;
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
     masm.branch32(Assembler::Equal, scratch, Imm32(0), &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != code);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
         masm.mov(ImmWord(0), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
 
@@ -8788,17 +8787,17 @@ ICSetProp_CallScripted::Compiler::genera
         regs.take(callee);
     } else {
         callee = regs.takeAny();
     }
     Register code = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfSetter()), callee);
     masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame);
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
-    masm.loadBaselineOrIonRaw(code, code, SequentialExecution, &failureLeaveStubFrame);
+    masm.loadBaselineOrIonRaw(code, code, &failureLeaveStubFrame);
 
     // Setter is called with the new value as the only argument, and |obj| as thisv.
     // Note that we use Push, not push, so that callJit will align the stack
     // properly on ARM.
 
     // To Push R1, read it off of the stowed values on stack.
     // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.. ]
     masm.movePtr(BaselineStackReg, scratch);
@@ -8813,17 +8812,17 @@ ICSetProp_CallScripted::Compiler::genera
     Label noUnderflow;
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
     masm.branch32(Assembler::BelowOrEqual, scratch, Imm32(1), &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != code);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
         masm.mov(ImmWord(1), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
 
@@ -9755,17 +9754,17 @@ ICCallStubCompiler::guardFunApply(MacroA
                             failure);
 
     if (checkNative) {
         masm.branchIfInterpreted(target, failure);
     } else {
         masm.branchIfFunctionHasNoScript(target, failure);
         Register temp = regs.takeAny();
         masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), temp);
-        masm.loadBaselineOrIonRaw(temp, temp, SequentialExecution, failure);
+        masm.loadBaselineOrIonRaw(temp, temp, failure);
         regs.add(temp);
     }
     return target;
 }
 
 void
 ICCallStubCompiler::pushCallerArguments(MacroAssembler &masm, GeneralRegisterSet regs)
 {
@@ -9990,17 +9989,17 @@ ICCallScriptedCompiler::generateStubCode
             masm.branchIfFunctionHasNoScript(callee, &failure);
         masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
     }
 
     // Load the start of the target JitCode.
     Register code;
     if (!isConstructing_) {
         code = regs.takeAny();
-        masm.loadBaselineOrIonRaw(callee, code, SequentialExecution, &failure);
+        masm.loadBaselineOrIonRaw(callee, code, &failure);
     } else {
         Address scriptCode(callee, JSScript::offsetOfBaselineOrIonRaw());
         masm.branchPtr(Assembler::Equal, scriptCode, ImmPtr(nullptr), &failure);
     }
 
     // We no longer need R1.
     regs.add(R1);
 
@@ -10074,17 +10073,17 @@ ICCallScriptedCompiler::generateStubCode
             masm.loadValue(calleeSlot3, R0);
         }
         callee = masm.extractObject(R0, ExtractTemp0);
         regs.add(R0);
         regs.takeUnchecked(callee);
         masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
 
         code = regs.takeAny();
-        masm.loadBaselineOrIonRaw(callee, code, SequentialExecution, &failureLeaveStubFrame);
+        masm.loadBaselineOrIonRaw(callee, code, &failureLeaveStubFrame);
 
         // Release callee register, but don't add ExtractTemp0 back into the pool
         // ExtractTemp0 is used later, and if it's allocated to some other register at that
         // point, it will get clobbered when used.
         if (callee != ExtractTemp0)
             regs.add(callee);
 
         if (canUseTailCallReg)
@@ -10118,17 +10117,17 @@ ICCallScriptedCompiler::generateStubCode
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), callee);
     masm.branch32(Assembler::AboveOrEqual, argcReg, callee, &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != code);
         MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
         masm.mov(argcReg, ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
 
@@ -10595,28 +10594,28 @@ ICCall_ScriptedApplyArray::Compiler::gen
 
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
     masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), target);
-    masm.loadBaselineOrIonRaw(target, target, SequentialExecution, nullptr);
+    masm.loadBaselineOrIonRaw(target, target, nullptr);
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != target);
         MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), target);
         masm.loadPtr(Address(target, JitCode::offsetOfCode()), target);
         masm.mov(argcReg, ArgumentsRectifierReg);
     }
     masm.bind(&noUnderflow);
     regs.add(argcReg);
 
@@ -10696,28 +10695,28 @@ ICCall_ScriptedApplyArguments::Compiler:
     masm.loadPtr(Address(argcReg, BaselineFrame::offsetOfNumActualArgs()), argcReg);
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
     masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), target);
-    masm.loadBaselineOrIonRaw(target, target, SequentialExecution, nullptr);
+    masm.loadBaselineOrIonRaw(target, target, nullptr);
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != target);
         MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), target);
         masm.loadPtr(Address(target, JitCode::offsetOfCode()), target);
         masm.mov(argcReg, ArgumentsRectifierReg);
     }
     masm.bind(&noUnderflow);
     regs.add(argcReg);
 
@@ -10776,17 +10775,17 @@ ICCall_ScriptedFunCall::Compiler::genera
 
     masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
                             &failure);
     masm.branchIfFunctionHasNoScript(callee, &failure);
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
 
     // Load the start of the target JitCode.
     Register code = regs.takeAny();
-    masm.loadBaselineOrIonRaw(callee, code, SequentialExecution, &failure);
+    masm.loadBaselineOrIonRaw(callee, code, &failure);
 
     // We no longer need R1.
     regs.add(R1);
 
     // Push a stub frame so that we can perform a non-tail call.
     enterStubFrame(masm, regs.getAny());
     if (canUseTailCallReg)
         regs.add(BaselineTailCallReg);
@@ -10829,17 +10828,17 @@ ICCall_ScriptedFunCall::Compiler::genera
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), callee);
     masm.branch32(Assembler::AboveOrEqual, argcReg, callee, &noUnderflow);
     {
         // Call the arguments rectifier.
         MOZ_ASSERT(ArgumentsRectifierReg != code);
         MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
 
         JitCode *argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
+            cx->runtime()->jitRuntime()->getArgumentsRectifier();
 
         masm.movePtr(ImmGCPtr(argumentsRectifier), code);
         masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
         masm.mov(argcReg, ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -217,19 +217,23 @@ class ICEntry
   public:
     enum Kind {
         // A for-op IC entry.
         Kind_Op = 0,
 
         // A non-op IC entry.
         Kind_NonOp,
 
-        // A fake IC entry for returning from a callVM.
+        // A fake IC entry for returning from a callVM for an op.
         Kind_CallVM,
 
+        // A fake IC entry for returning from a callVM not for an op (e.g., in
+        // the prologue).
+        Kind_NonOpCallVM,
+
         // A fake IC entry for returning from DebugTrapHandler.
         Kind_DebugTrap,
 
         // A fake IC entry for returning from a callVM to
         // Debug{Prologue,Epilogue}.
         Kind_DebugPrologue,
         Kind_DebugEpilogue,
 
@@ -294,16 +298,20 @@ class ICEntry
     void setForDebugPrologue() {
         MOZ_ASSERT(kind() == Kind_CallVM);
         setKind(Kind_DebugPrologue);
     }
     void setForDebugEpilogue() {
         MOZ_ASSERT(kind() == Kind_CallVM);
         setKind(Kind_DebugEpilogue);
     }
+    void setForNonOpCallVM() {
+        MOZ_ASSERT(kind() == Kind_CallVM);
+        setKind(Kind_NonOpCallVM);
+    }
 
     bool hasStub() const {
         return firstStub_ != nullptr;
     }
     ICStub *firstStub() const {
         MOZ_ASSERT(hasStub());
         return firstStub_;
     }
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -558,34 +558,41 @@ BaselineScript::icEntryFromReturnOffset(
 }
 
 uint8_t *
 BaselineScript::returnAddressForIC(const ICEntry &ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
-ICEntry &
-BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
+static inline size_t
+ComputeBinarySearchMid(BaselineScript *baseline, uint32_t pcOffset)
 {
-    // Multiple IC entries can have the same PC offset, but this method only looks for
-    // those which have isForOp() set.
     size_t bottom = 0;
-    size_t top = numICEntries();
+    size_t top = baseline->numICEntries();
     size_t mid = bottom + (top - bottom) / 2;
     while (mid < top) {
-        ICEntry &midEntry = icEntry(mid);
+        ICEntry &midEntry = baseline->icEntry(mid);
         if (midEntry.pcOffset() < pcOffset)
             bottom = mid + 1;
         else if (midEntry.pcOffset() > pcOffset)
             top = mid;
         else
             break;
         mid = bottom + (top - bottom) / 2;
     }
+    return mid;
+}
+
+ICEntry &
+BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
+{
+    // Multiple IC entries can have the same PC offset, but this method only looks for
+    // those which have isForOp() set.
+    size_t mid = ComputeBinarySearchMid(this, pcOffset);
 
     // Found an IC entry with a matching PC offset.  Search backward, and then
     // forward from this IC entry, looking for one with the same PC offset which
     // has isForOp() set.
     for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) {
         if (icEntry(i).isForOp())
             return icEntry(i);
     }
@@ -614,16 +621,34 @@ BaselineScript::icEntryFromPCOffset(uint
         }
         MOZ_ASSERT(curEntry->pcOffset() == pcOffset && curEntry->isForOp());
         return *curEntry;
     }
 
     return icEntryFromPCOffset(pcOffset);
 }
 
+ICEntry &
+BaselineScript::callVMEntryFromPCOffset(uint32_t pcOffset)
+{
+    // Like icEntryFromPCOffset, but only looks for the fake ICEntries
+    // inserted by VM calls.
+    size_t mid = ComputeBinarySearchMid(this, pcOffset);
+
+    for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) {
+        if (icEntry(i).kind() == ICEntry::Kind_CallVM)
+            return icEntry(i);
+    }
+    for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
+        if (icEntry(i).kind() == ICEntry::Kind_CallVM)
+            return icEntry(i);
+    }
+    MOZ_CRASH("Invalid PC offset for callVM entry.");
+}
+
 ICEntry *
 BaselineScript::maybeICEntryFromReturnAddress(uint8_t *returnAddr)
 {
     MOZ_ASSERT(returnAddr > method_->raw());
     MOZ_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
     CodeOffsetLabel offset(returnAddr - method_->raw());
     return maybeICEntryFromReturnOffset(offset);
 }
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -341,16 +341,17 @@ struct BaselineScript
         return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
     }
 
     ICEntry &icEntry(size_t index);
     ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry);
+    ICEntry &callVMEntryFromPCOffset(uint32_t pcOffset);
     ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr);
     ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
     uint8_t *returnAddressForIC(const ICEntry &ent);
 
     size_t numICEntries() const {
         return icEntries_;
     }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -26,20 +26,17 @@
 #include "jit/IonCaches.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
-#include "jit/ParallelFunctions.h"
-#include "jit/ParallelSafetyAnalysis.h"
 #include "jit/RangeAnalysis.h"
-#include "vm/ForkJoin.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/ExecutionMode-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
@@ -162,20 +159,17 @@ CodeGenerator::CodeGenerator(MIRGenerato
 
 CodeGenerator::~CodeGenerator()
 {
     MOZ_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
     js_delete(scriptCounts_);
 }
 
 typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
-typedef bool (*StringToNumberParFn)(ForkJoinContext *, JSString *, double *);
-static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
-    FunctionInfo<StringToNumberFn>(StringToNumber),
-    FunctionInfo<StringToNumberParFn>(StringToNumberPar));
+static const VMFunction StringToNumberInfo = FunctionInfo<StringToNumberFn>(StringToNumber);
 
 void
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
     Register output = ToRegister(lir->output());
     FloatRegister temp = ToFloatRegister(lir->tempFloat());
 
@@ -816,40 +810,34 @@ CodeGenerator::emitIntToString(Register 
     masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool);
 
     // Fast path for small integers.
     masm.movePtr(ImmPtr(&GetJitContext()->runtime->staticStrings().intStaticTable), output);
     masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
 }
 
 typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int);
-typedef JSFlatString *(*IntToStringParFn)(ForkJoinContext *, int);
-static const VMFunctionsModal IntToStringInfo = VMFunctionsModal(
-    FunctionInfo<IntToStringFn>(Int32ToString<CanGC>),
-    FunctionInfo<IntToStringParFn>(IntToStringPar));
+static const VMFunction IntToStringInfo = FunctionInfo<IntToStringFn>(Int32ToString<CanGC>);
 
 void
 CodeGenerator::visitIntToString(LIntToString *lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
 
     OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input),
                                    StoreRegisterTo(output));
 
     emitIntToString(input, output, ool->entry());
 
     masm.bind(ool->rejoin());
 }
 
 typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double);
-typedef JSString *(*DoubleToStringParFn)(ForkJoinContext *, double);
-static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal(
-    FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>),
-    FunctionInfo<DoubleToStringParFn>(DoubleToStringPar));
+static const VMFunction DoubleToStringInfo = FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>);
 
 void
 CodeGenerator::visitDoubleToString(LDoubleToString *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     Register temp = ToRegister(lir->tempInt());
     Register output = ToRegister(lir->output());
 
@@ -859,20 +847,17 @@ CodeGenerator::visitDoubleToString(LDoub
     // Try double to integer conversion and run integer to string code.
     masm.convertDoubleToInt32(input, temp, ool->entry(), true);
     emitIntToString(temp, output, ool->entry());
 
     masm.bind(ool->rejoin());
 }
 
 typedef JSString *(*PrimitiveToStringFn)(JSContext *, HandleValue);
-typedef JSString *(*PrimitiveToStringParFn)(ForkJoinContext *, HandleValue);
-static const VMFunctionsModal PrimitiveToStringInfo = VMFunctionsModal(
-    FunctionInfo<PrimitiveToStringFn>(ToStringSlow),
-    FunctionInfo<PrimitiveToStringParFn>(PrimitiveToStringPar));
+static const VMFunction PrimitiveToStringInfo = FunctionInfo<PrimitiveToStringFn>(ToStringSlow);
 
 void
 CodeGenerator::visitValueToString(LValueToString *lir)
 {
     ValueOperand input = ToValue(lir, LValueToString::Input);
     Register output = ToRegister(lir->output());
 
     OutOfLineCode *ool = oolCallVM(PrimitiveToStringInfo, lir, (ArgList(), input),
@@ -1761,32 +1746,16 @@ CodeGenerator::emitLambdaInit(Register o
     masm.store32(Imm32(u.word), Address(output, JSFunction::offsetOfNargs()));
     masm.storePtr(ImmGCPtr(info.scriptOrLazyScript),
                   Address(output, JSFunction::offsetOfNativeOrScript()));
     masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
     masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
 }
 
 void
-CodeGenerator::visitLambdaPar(LLambdaPar *lir)
-{
-    Register resultReg = ToRegister(lir->output());
-    Register cxReg = ToRegister(lir->forkJoinContext());
-    Register scopeChainReg = ToRegister(lir->scopeChain());
-    Register tempReg1 = ToRegister(lir->getTemp0());
-    Register tempReg2 = ToRegister(lir->getTemp1());
-    const LambdaFunctionInfo &info = lir->mir()->info();
-
-    MOZ_ASSERT(scopeChainReg != resultReg);
-
-    emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, info.fun);
-    emitLambdaInit(resultReg, scopeChainReg, info);
-}
-
-void
 CodeGenerator::visitLabel(LLabel *lir)
 {
 }
 
 void
 CodeGenerator::visitNop(LNop *lir)
 {
 }
@@ -1848,20 +1817,17 @@ class OutOfLineInterruptCheckImplicit : 
     { }
 
     void accept(CodeGenerator *codegen) {
         codegen->visitOutOfLineInterruptCheckImplicit(this);
     }
 };
 
 typedef bool (*InterruptCheckFn)(JSContext *);
-typedef bool (*InterruptCheckParFn)(ForkJoinContext *);
-static const VMFunctionsModal InterruptCheckInfo = VMFunctionsModal(
-    FunctionInfo<InterruptCheckFn>(InterruptCheck),
-    FunctionInfo<InterruptCheckParFn>(InterruptCheckPar));
+static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
 
 void
 CodeGenerator::visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ool)
 {
 #ifdef CHECK_OSIPOINT_REGISTERS
     // This is path is entered from the patched back-edge of the loop. This
     // means that the JitAtivation flags used for checking the validity of the
     // OSI points are not reseted by the path generated by generateBody, so we
@@ -2016,20 +1982,18 @@ CodeGenerator::visitReturn(LReturn *lir)
 void
 CodeGenerator::visitOsrEntry(LOsrEntry *lir)
 {
     // Remember the OSR entry offset into the code buffer.
     masm.flushBuffer();
     setOsrEntryOffset(masm.size());
 
 #ifdef JS_TRACE_LOGGING
-    if (gen->info().executionMode() == SequentialExecution) {
-        emitTracelogStopEvent(TraceLogger_Baseline);
-        emitTracelogStartEvent(TraceLogger_IonMonkey);
-    }
+    emitTracelogStopEvent(TraceLogger_Baseline);
+    emitTracelogStartEvent(TraceLogger_IonMonkey);
 #endif
 
     // Allocate the full frame for this function
     // Note we have a new entry here. So we reset MacroAssembler::framePushed()
     // to 0, before reserving the stack.
     MOZ_ASSERT(masm.framePushed() == frameSize());
     masm.setFramePushed(0);
     masm.reserveStack(frameSize());
@@ -2448,40 +2412,16 @@ CodeGenerator::visitMaybeCopyElementsFor
 void
 CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment *lir)
 {
     Address environment(ToRegister(lir->function()), JSFunction::offsetOfEnvironment());
     masm.loadPtr(environment, ToRegister(lir->output()));
 }
 
 void
-CodeGenerator::visitForkJoinContext(LForkJoinContext *lir)
-{
-    const Register tempReg = ToRegister(lir->getTempReg());
-
-    masm.setupUnalignedABICall(0, tempReg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinContextPar));
-    MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
-}
-
-void
-CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
-{
-    MOZ_ASSERT(gen->info().executionMode() == ParallelExecution);
-
-    const Register tempReg = ToRegister(lir->getTempReg());
-    masm.setupUnalignedABICall(2, tempReg);
-    masm.passABIArg(ToRegister(lir->forkJoinContext()));
-    masm.passABIArg(ToRegister(lir->object()));
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
-
-    bailoutIfFalseBool(ReturnReg, lir->snapshot());
-}
-
-void
 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity *guard)
 {
     Register obj = ToRegister(guard->input());
 
     Assembler::Condition cond =
         guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
     bailoutCmpPtr(cond, obj, ImmGCPtr(guard->mir()->singleObject()), guard->snapshot());
 }
@@ -2664,71 +2604,51 @@ CodeGenerator::visitCallNative(LCallNati
 
     // Misc. temporary registers.
     const Register tempReg = ToRegister(call->getTempReg());
 
     DebugOnly<uint32_t> initialStack = masm.framePushed();
 
     masm.checkStackAlignment();
 
-    // Sequential native functions have the signature:
+    // Native functions have the signature:
     //  bool (*)(JSContext *, unsigned, Value *vp)
-    // and parallel native functions have the signature:
-    //  ParallelResult (*)(ForkJoinContext *, unsigned, Value *vp)
     // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
     // are the function arguments.
 
     // Allocate space for the outparam, moving the StackPointer to what will be &vp[1].
     masm.adjustStack(unusedStack);
 
     // Push a Value containing the callee object: natives are allowed to access their callee before
     // setitng the return value. The StackPointer is moved to &vp[0].
     masm.Push(ObjectValue(*target));
 
     // Preload arguments into registers.
-    //
-    // Note that for parallel execution, loadContext does an ABI call, so we
-    // need to do this before we load the other argument registers, otherwise
-    // we'll hose them.
-    ExecutionMode executionMode = gen->info().executionMode();
-    masm.loadContext(argContextReg, tempReg, executionMode);
+    masm.loadJSContext(argContextReg);
     masm.move32(Imm32(call->numStackArgs()), argUintNReg);
     masm.movePtr(StackPointer, argVpReg);
 
     masm.Push(argUintNReg);
 
     // Construct native exit frame.
     uint32_t safepointOffset;
     masm.buildFakeExitFrame(tempReg, &safepointOffset);
-    masm.enterFakeExitFrame(argContextReg, tempReg, executionMode,
-                            NativeExitFrameLayout::Token());
+    masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
 
     markSafepointAt(safepointOffset, call);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(3, tempReg);
     masm.passABIArg(argContextReg);
     masm.passABIArg(argUintNReg);
     masm.passABIArg(argVpReg);
-
-    switch (executionMode) {
-      case SequentialExecution:
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
-        break;
-
-      case ParallelExecution:
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->parallelNative()));
-        break;
-
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
 
     // Test for failure.
-    masm.branchIfFalseBool(ReturnReg, masm.failureLabel(executionMode));
+    masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
 
     // Load the outparam vp[0] into output register(s).
     masm.loadValue(Address(StackPointer, NativeExitFrameLayout::offsetOfResult()), JSReturnOperand);
 
     // The next instruction is removing the footer of the exit frame, so there
     // is no need for leaveFakeExitFrame.
 
     // Move the StackPointer back to its original location, unwinding the native exit frame.
@@ -2903,24 +2823,23 @@ CodeGenerator::emitCallInvokeFunction(LI
 
 void
 CodeGenerator::visitCallGeneric(LCallGeneric *call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     Register nargsreg  = ToRegister(call->getNargsReg());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
-    ExecutionMode executionMode = gen->info().executionMode();
     Label invoke, thunk, makeCall, end;
 
     // Known-target case is handled by LCallKnown.
     MOZ_ASSERT(!call->hasSingleTarget());
 
     // Generate an ArgumentsRectifier.
-    JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode);
+    JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier();
 
     masm.checkStackAlignment();
 
     // Guard that calleereg is actually a function object.
     masm.loadObjClass(calleereg, nargsreg);
     masm.branchPtr(Assembler::NotEqual, nargsreg, ImmPtr(&JSFunction::class_), &invoke);
 
     // Guard that calleereg is an interpreted function with a JSScript.
@@ -2929,17 +2848,17 @@ CodeGenerator::visitCallGeneric(LCallGen
         masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
     else
         masm.branchIfFunctionHasNoScript(calleereg, &invoke);
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
 
     // Load script jitcode.
-    masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
+    masm.loadBaselineOrIonRaw(objreg, objreg, &invoke);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
     masm.Push(Imm32(call->numActualArgs()));
     masm.PushCalleeToken(calleereg, call->mir()->isConstructing());
@@ -2967,65 +2886,39 @@ CodeGenerator::visitCallGeneric(LCallGen
     // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
     // The return address has already been removed from the Ion frame.
     int prefixGarbage = sizeof(JitFrameLayout) - sizeof(void *);
     masm.adjustStack(prefixGarbage - unusedStack);
     masm.jump(&end);
 
     // Handle uncompiled or native functions.
     masm.bind(&invoke);
-    switch (executionMode) {
-      case SequentialExecution:
-        emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack);
-        break;
-
-      case ParallelExecution:
-        emitCallToUncompiledScriptPar(call, calleereg);
-        break;
-
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack);
 
     masm.bind(&end);
 
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
     dropArguments(call->numStackArgs() + 1);
 }
 
-typedef bool (*CallToUncompiledScriptParFn)(ForkJoinContext *, JSObject *);
-static const VMFunction CallToUncompiledScriptParInfo =
-    FunctionInfo<CallToUncompiledScriptParFn>(CallToUncompiledScriptPar);
-
-// Generates a call to CallToUncompiledScriptPar() and then bails out.
-// |calleeReg| should contain the JSFunction*.
-void
-CodeGenerator::emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg)
-{
-    pushArg(calleeReg);
-    callVM(CallToUncompiledScriptParInfo, lir);
-    masm.assumeUnreachable("CallToUncompiledScriptParInfo always returns false.");
-}
-
 void
 CodeGenerator::visitCallKnown(LCallKnown *call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
     DebugOnly<JSFunction *> target = call->getSingleTarget();
-    ExecutionMode executionMode = gen->info().executionMode();
     Label end, uncompiled;
 
     // Native single targets are handled by LCallNative.
     MOZ_ASSERT(!target->isNative());
     // Missing arguments must have been explicitly appended by the IonBuilder.
     MOZ_ASSERT(target->nargs() <= call->numStackArgs());
 
     MOZ_ASSERT_IF(call->mir()->isConstructing(), target->isInterpretedConstructor());
@@ -3036,19 +2929,19 @@ CodeGenerator::visitCallKnown(LCallKnown
     // a LazyScript instead of a JSScript.
     masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
 
     // Load script jitcode.
     if (call->mir()->needsArgCheck())
-        masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
+        masm.loadBaselineOrIonRaw(objreg, objreg, &uncompiled);
     else
-        masm.loadBaselineOrIonNoArgCheck(objreg, objreg, executionMode, &uncompiled);
+        masm.loadBaselineOrIonNoArgCheck(objreg, objreg, &uncompiled);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
     masm.Push(Imm32(call->numActualArgs()));
     masm.PushCalleeToken(calleereg, call->mir()->isConstructing());
@@ -3061,28 +2954,17 @@ CodeGenerator::visitCallKnown(LCallKnown
     // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
     // The return address has already been removed from the Ion frame.
     int prefixGarbage = sizeof(JitFrameLayout) - sizeof(void *);
     masm.adjustStack(prefixGarbage - unusedStack);
     masm.jump(&end);
 
     // Handle uncompiled functions.
     masm.bind(&uncompiled);
-    switch (executionMode) {
-      case SequentialExecution:
-        emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack);
-        break;
-
-      case ParallelExecution:
-        emitCallToUncompiledScriptPar(call, calleereg);
-        break;
-
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack);
 
     masm.bind(&end);
 
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
@@ -3195,17 +3077,16 @@ CodeGenerator::visitApplyArgsGeneric(LAp
     }
 
     // Copy the arguments of the current function.
     emitPushArguments(apply, copyreg);
 
     masm.checkStackAlignment();
 
     // If the function is known to be uncompilable, only emit the call to InvokeFunction.
-    ExecutionMode executionMode = gen->info().executionMode();
     if (apply->hasSingleTarget()) {
         JSFunction *target = apply->getSingleTarget();
         if (target->isNative()) {
             emitCallInvokeFunction(apply, copyreg);
             emitPopArguments(apply, copyreg);
             return;
         }
     }
@@ -3219,17 +3100,17 @@ CodeGenerator::visitApplyArgsGeneric(LAp
         // Native single targets are handled by LCallNative.
         MOZ_ASSERT(!apply->getSingleTarget()->isNative());
     }
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
 
     // Load script jitcode.
-    masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
+    masm.loadBaselineOrIonRaw(objreg, objreg, &invoke);
 
     // Call with an Ion frame or a rectifier frame.
     {
         // Create the frame descriptor.
         unsigned pushed = masm.framePushed();
         masm.addPtr(Imm32(pushed), copyreg);
         masm.makeFrameDescriptor(copyreg, JitFrame_IonJS);
 
@@ -3252,17 +3133,17 @@ CodeGenerator::visitApplyArgsGeneric(LAp
         // underflow.
         masm.jump(&rejoin);
 
         // Argument fixup needed. Get ready to call the argumentsRectifier.
         {
             masm.bind(&underflow);
 
             // Hardcode the address of the argumentsRectifier code.
-            JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode);
+            JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier();
 
             MOZ_ASSERT(ArgumentsRectifierReg != objreg);
             masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
             masm.loadPtr(Address(objreg, JitCode::offsetOfCode()), objreg);
             masm.movePtr(argcreg, ArgumentsRectifierReg);
         }
 
         masm.bind(&rejoin);
@@ -3548,20 +3429,18 @@ CodeGenerator::visitDefFun(LDefFun *lir)
     pushArg(ImmGCPtr(lir->mir()->fun()));
     pushArg(scopeChain);
     pushArg(ImmGCPtr(current->mir()->info().script()));
 
     callVM(DefFunOperationInfo, lir);
 }
 
 typedef bool (*CheckOverRecursedFn)(JSContext *);
-typedef bool (*CheckOverRecursedParFn)(ForkJoinContext *);
-static const VMFunctionsModal CheckOverRecursedInfo = VMFunctionsModal(
-    FunctionInfo<CheckOverRecursedFn>(CheckOverRecursed),
-    FunctionInfo<CheckOverRecursedParFn>(CheckOverRecursedPar));
+static const VMFunction CheckOverRecursedInfo =
+    FunctionInfo<CheckOverRecursedFn>(CheckOverRecursed);
 
 void
 CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
 {
     // The OOL path is hit if the recursion depth has been exceeded.
     // Throw an InternalError for over-recursion.
 
     // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
@@ -3570,52 +3449,16 @@ CodeGenerator::visitCheckOverRecursedFai
     saveLive(ool->lir());
 
     callVM(CheckOverRecursedInfo, ool->lir());
 
     restoreLive(ool->lir());
     masm.jump(ool->rejoin());
 }
 
-void
-CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
-{
-    // See above: unlike visitCheckOverRecursed(), this code runs in
-    // parallel mode and hence uses the jitStackLimit from the current
-    // thread state.  Also, we must check the interrupt flags because
-    // on interrupt or abort, only the stack limit for the main thread
-    // is reset, not the worker threads.  See comment in vm/ForkJoin.h
-    // for more details.
-
-    Register cxReg = ToRegister(lir->forkJoinContext());
-    Register tempReg = ToRegister(lir->getTempReg());
-
-    masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
-    masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg);
-
-    // Conditional forward (unlikely) branch to failure.
-    CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
-    addOutOfLineCode(ool, lir->mir());
-
-    masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
-    masm.checkInterruptFlagPar(tempReg, ool->entry());
-    masm.bind(ool->rejoin());
-}
-
-void
-CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
-{
-    // First check for cx->shared->interrupt_.
-    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
-
-    Register tempReg = ToRegister(lir->getTempReg());
-    masm.checkInterruptFlagPar(tempReg, ool->entry());
-    masm.bind(ool->rejoin());
-}
-
 IonScriptCounts *
 CodeGenerator::maybeCreateScriptCounts()
 {
     // If scripts are being profiled, create a new IonScriptCounts for the
     // profiling data, which will be attached to the associated JSScript or
     // AsmJS module after code generation finishes.
     if (!GetJitContext()->runtime->profilingScripts())
         return nullptr;
@@ -3768,44 +3611,42 @@ CodeGenerator::emitObjectOrStringResultC
 
         masm.bind(&miss);
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
-    if (gen->info().executionMode() != ParallelExecution) {
-        saveVolatile();
-        masm.setupUnalignedABICall(2, temp);
-        masm.loadJSContext(temp);
-        masm.passABIArg(temp);
-        masm.passABIArg(output);
-
-        void *callee;
-        switch (mir->type()) {
-          case MIRType_Object:
-            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr);
-            break;
-          case MIRType_ObjectOrNull:
-            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectOrNullPtr);
-            break;
-          case MIRType_String:
-            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr);
-            break;
-          case MIRType_Symbol:
-            callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidSymbolPtr);
-            break;
-          default:
-            MOZ_CRASH();
-        }
-
-        masm.callWithABINoProfiling(callee);
-        restoreVolatile();
-    }
+    saveVolatile();
+    masm.setupUnalignedABICall(2, temp);
+    masm.loadJSContext(temp);
+    masm.passABIArg(temp);
+    masm.passABIArg(output);
+
+    void *callee;
+    switch (mir->type()) {
+      case MIRType_Object:
+        callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr);
+        break;
+      case MIRType_ObjectOrNull:
+        callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectOrNullPtr);
+        break;
+      case MIRType_String:
+        callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr);
+        break;
+      case MIRType_Symbol:
+        callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidSymbolPtr);
+        break;
+      default:
+        MOZ_CRASH();
+    }
+
+    masm.callWithABINoProfiling(callee);
+    restoreVolatile();
 
     masm.bind(&done);
     masm.pop(temp);
 }
 
 void
 CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
 {
@@ -3839,30 +3680,28 @@ CodeGenerator::emitValueResultChecks(LIn
 
         masm.bind(&miss);
         masm.assumeUnreachable("MIR instruction returned value with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
-    if (gen->info().executionMode() != ParallelExecution) {
-        saveVolatile();
-
-        masm.pushValue(output);
-        masm.movePtr(StackPointer, temp1);
-
-        masm.setupUnalignedABICall(2, temp2);
-        masm.loadJSContext(temp2);
-        masm.passABIArg(temp2);
-        masm.passABIArg(temp1);
-        masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue));
-        masm.popValue(output);
-        restoreVolatile();
-    }
+    saveVolatile();
+
+    masm.pushValue(output);
+    masm.movePtr(StackPointer, temp1);
+
+    masm.setupUnalignedABICall(2, temp2);
+    masm.loadJSContext(temp2);
+    masm.passABIArg(temp2);
+    masm.passABIArg(temp1);
+    masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue));
+    masm.popValue(output);
+    restoreVolatile();
 
     masm.bind(&done);
     masm.pop(temp2);
     masm.pop(temp1);
 }
 
 void
 CodeGenerator::emitDebugResultChecks(LInstruction *ins)
@@ -4008,18 +3847,16 @@ class OutOfLineNewArray : public OutOfLi
 
 typedef ArrayObject *(*NewDenseArrayFn)(ExclusiveContext *, uint32_t, HandleTypeObject,
                                         AllocatingBehaviour);
 static const VMFunction NewDenseArrayInfo = FunctionInfo<NewDenseArrayFn>(NewDenseArray);
 
 void
 CodeGenerator::visitNewArrayCallVM(LNewArray *lir)
 {
-    MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
-
     Register objReg = ToRegister(lir->output());
 
     MOZ_ASSERT(!lir->isCall());
     saveLive(lir);
 
     JSObject *templateObject = lir->mir()->templateObject();
     types::TypeObject *type =
         templateObject->hasSingletonType() ? nullptr : templateObject->type();
@@ -4041,19 +3878,16 @@ typedef JSObject *(*NewDerivedTypedObjec
                                              HandleObject owner,
                                              int32_t offset);
 static const VMFunction CreateDerivedTypedObjInfo =
     FunctionInfo<NewDerivedTypedObjectFn>(CreateDerivedTypedObj);
 
 void
 CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir)
 {
-    // Not yet made safe for par exec:
-    MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
-
     pushArg(ToRegister(lir->offset()));
     pushArg(ToRegister(lir->owner()));
     pushArg(ToRegister(lir->type()));
     callVM(CreateDerivedTypedObjInfo, lir);
 }
 
 void
 CodeGenerator::visitAtan2D(LAtan2D *lir)
@@ -4083,17 +3917,16 @@ CodeGenerator::visitHypot(LHypot *lir)
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitNewArray(LNewArray *lir)
 {
-    MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
     ArrayObject *templateObject = lir->mir()->templateObject();
     DebugOnly<uint32_t> count = lir->mir()->count();
 
     MOZ_ASSERT(count < NativeObject::NELEMENTS_LIMIT);
 
     if (lir->mir()->shouldUseVM()) {
@@ -4199,18 +4032,16 @@ static const VMFunction NewInitObjectInf
 
 typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandlePlainObject);
 static const VMFunction NewInitObjectWithClassPrototypeInfo =
     FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
 
 void
 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
 {
-    MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
-
     Register objReg = ToRegister(lir->output());
 
     MOZ_ASSERT(!lir->isCall());
     saveLive(lir);
 
     pushArg(ImmGCPtr(lir->mir()->templateObject()));
 
     // If we're making a new object with a class prototype (that is, an object
@@ -4310,17 +4141,16 @@ ShouldInitFixedSlots(LInstruction *lir, 
     }
 
     MOZ_CRASH("Shouldn't get here");
 }
 
 void
 CodeGenerator::visitNewObject(LNewObject *lir)
 {
-    MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
     PlainObject *templateObject = lir->mir()->templateObject();
 
     if (lir->mir()->shouldUseVM()) {
         visitNewObjectVMCall(lir);
         return;
     }
@@ -4437,55 +4267,16 @@ CodeGenerator::visitNewSingletonCallObje
 
     // Objects can only be given singleton types in VM calls.  We make the call
     // out of line to not bloat inline code, even if (naively) this seems like
     // extra work.
     masm.jump(ool->entry());
     masm.bind(ool->rejoin());
 }
 
-void
-CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
-{
-    Register resultReg = ToRegister(lir->output());
-    Register cxReg = ToRegister(lir->forkJoinContext());
-    Register tempReg1 = ToRegister(lir->getTemp0());
-    Register tempReg2 = ToRegister(lir->getTemp1());
-    CallObject *templateObj = lir->mir()->templateObj();
-
-    emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
-}
-
-typedef ArrayObject *(*ExtendArrayParFn)(ForkJoinContext*, ArrayObject*, uint32_t);
-static const VMFunction ExtendArrayParInfo =
-    FunctionInfo<ExtendArrayParFn>(ExtendArrayPar);
-
-void
-CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
-{
-    Register cxReg = ToRegister(lir->forkJoinContext());
-    Register lengthReg = ToRegister(lir->length());
-    Register tempReg0 = ToRegister(lir->getTemp0());
-    Register tempReg1 = ToRegister(lir->getTemp1());
-    Register tempReg2 = ToRegister(lir->getTemp2());
-    ArrayObject *templateObj = lir->mir()->templateObject();
-
-    emitAllocateGCThingPar(lir, tempReg2, cxReg, tempReg0, tempReg1, templateObj);
-
-    // Invoke a C helper to allocate the elements.  The helper returns
-    // nullptr on allocation error or the array object.
-
-    saveLive(lir);
-    pushArg(lengthReg);
-    pushArg(tempReg2);
-    callVM(ExtendArrayParInfo, lir);
-    storeResultTo(ToRegister(lir->output()));
-    restoreLive(lir);
-}
-
 typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString);
 static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject);
 
 void
 CodeGenerator::visitNewStringObject(LNewStringObject *lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
@@ -4501,86 +4292,16 @@ CodeGenerator::visitNewStringObject(LNew
     masm.loadStringLength(input, temp);
 
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
     masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
 
     masm.bind(ool->rejoin());
 }
 
-void
-CodeGenerator::visitNewPar(LNewPar *lir)
-{
-    Register objReg = ToRegister(lir->output());
-    Register cxReg = ToRegister(lir->forkJoinContext());
-    Register tempReg1 = ToRegister(lir->getTemp0());
-    Register tempReg2 = ToRegister(lir->getTemp1());
-    NativeObject *templateObject = lir->mir()->templateObject();
-    emitAllocateGCThingPar(lir, objReg, cxReg, tempReg1, tempReg2, templateObject);
-}
-
-class OutOfLineNewGCThingPar : public OutOfLineCodeBase<CodeGenerator>
-{
-public:
-    LInstruction *lir;
-    gc::AllocKind allocKind;
-    Register objReg;
-    Register cxReg;
-
-    OutOfLineNewGCThingPar(LInstruction *lir, gc::AllocKind allocKind, Register objReg,
-                           Register cxReg)
-      : lir(lir), allocKind(allocKind), objReg(objReg), cxReg(cxReg)
-    {}
-
-    void accept(CodeGenerator *codegen) {
-        codegen->visitOutOfLineNewGCThingPar(this);
-    }
-};
-
-typedef JSObject *(*NewGCThingParFn)(ForkJoinContext *, js::gc::AllocKind allocKind);
-static const VMFunction NewGCThingParInfo =
-    FunctionInfo<NewGCThingParFn>(NewGCThingPar);
-
-void
-CodeGenerator::emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
-                                      Register tempReg1, Register tempReg2, NativeObject *templateObj)
-{
-    MOZ_ASSERT(lir->mirRaw());
-    MOZ_ASSERT(lir->mirRaw()->isInstruction());
-
-    gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
-    OutOfLineNewGCThingPar *ool = new(alloc()) OutOfLineNewGCThingPar(lir, allocKind, objReg, cxReg);
-    addOutOfLineCode(ool, lir->mirRaw()->toInstruction());
-
-    masm.newGCThingPar(objReg, cxReg, tempReg1, tempReg2, templateObj, ool->entry());
-    masm.bind(ool->rejoin());
-    masm.initGCThing(objReg, tempReg1, templateObj);
-}
-
-void
-CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
-{
-    // As a fallback for allocation in par. exec. mode, we invoke the
-    // C helper NewGCThingPar(), which calls into the GC code.  If it
-    // returns nullptr, we bail.  If returns non-nullptr, we rejoin the
-    // original instruction.
-    Register out = ool->objReg;
-
-    saveVolatile(out);
-    masm.setupUnalignedABICall(2, out);
-    masm.passABIArg(ool->cxReg);
-    masm.move32(Imm32(ool->allocKind), out);
-    masm.passABIArg(out);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewGCThingPar));
-    masm.storeCallResult(out);
-    restoreVolatile(out);
-
-    bailoutTestPtr(Assembler::Zero, out, out, ool->lir->snapshot());
-}
-
 typedef bool(*InitElemFn)(JSContext *cx, HandleObject obj,
                           HandleValue id, HandleValue value);
 static const VMFunction InitElemInfo =
     FunctionInfo<InitElemFn>(InitElemOperation);
 
 void
 CodeGenerator::visitInitElem(LInitElem *lir)
 {
@@ -5182,26 +4903,23 @@ CodeGenerator::visitModD(LModD *ins)
 
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE);
 }
 
 typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, MutableHandleValue);
-typedef bool (*BinaryParFn)(ForkJoinContext *, HandleValue, HandleValue, MutableHandleValue);
 
 static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues);
 static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues);
 static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues);
 static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues);
 static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues);
-static const VMFunctionsModal UrshInfo = VMFunctionsModal(
-    FunctionInfo<BinaryFn>(js::UrshValues),
-    FunctionInfo<BinaryParFn>(UrshValuesPar));
+static const VMFunction UrshInfo = FunctionInfo<BinaryFn>(js::UrshValues);
 
 void
 CodeGenerator::visitBinaryV(LBinaryV *lir)
 {
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
 
     switch (lir->jsop()) {
@@ -5230,23 +4948,20 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         break;
 
       default:
         MOZ_CRASH("Unexpected binary op");
     }
 }
 
 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, bool *);
-typedef bool (*StringCompareParFn)(ForkJoinContext *, HandleString, HandleString, bool *);
-static const VMFunctionsModal StringsEqualInfo = VMFunctionsModal(
-    FunctionInfo<StringCompareFn>(jit::StringsEqual<true>),
-    FunctionInfo<StringCompareParFn>(jit::StringsEqualPar));
-static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal(
-    FunctionInfo<StringCompareFn>(jit::StringsEqual<false>),
-    FunctionInfo<StringCompareParFn>(jit::StringsUnequalPar));
+static const VMFunction StringsEqualInfo =
+    FunctionInfo<StringCompareFn>(jit::StringsEqual<true>);
+static const VMFunction StringsNotEqualInfo =
+    FunctionInfo<StringCompareFn>(jit::StringsEqual<false>);
 
 void
 CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
                             Register output)
 {
     MOZ_ASSERT(lir->isCompareS() || lir->isCompareStrictS());
 
     OutOfLineCode *ool = nullptr;
@@ -5294,41 +5009,24 @@ CodeGenerator::visitCompareS(LCompareS *
     Register left = ToRegister(lir->left());
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
 
     emitCompareS(lir, op, left, right, output);
 }
 
 typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *);
-typedef bool (*CompareParFn)(ForkJoinContext *, MutableHandleValue, MutableHandleValue, bool *);
-static const VMFunctionsModal EqInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::LooselyEqual<true>),
-    FunctionInfo<CompareParFn>(jit::LooselyEqualPar));
-static const VMFunctionsModal NeInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::LooselyEqual<false>),
-    FunctionInfo<CompareParFn>(jit::LooselyUnequalPar));
-static const VMFunctionsModal StrictEqInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::StrictlyEqual<true>),
-    FunctionInfo<CompareParFn>(jit::StrictlyEqualPar));
-static const VMFunctionsModal StrictNeInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::StrictlyEqual<false>),
-    FunctionInfo<CompareParFn>(jit::StrictlyUnequalPar));
-static const VMFunctionsModal LtInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::LessThan),
-    FunctionInfo<CompareParFn>(jit::LessThanPar));
-static const VMFunctionsModal LeInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::LessThanOrEqual),
-    FunctionInfo<CompareParFn>(jit::LessThanOrEqualPar));
-static const VMFunctionsModal GtInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::GreaterThan),
-    FunctionInfo<CompareParFn>(jit::GreaterThanPar));
-static const VMFunctionsModal GeInfo = VMFunctionsModal(
-    FunctionInfo<CompareFn>(jit::GreaterThanOrEqual),
-    FunctionInfo<CompareParFn>(jit::GreaterThanOrEqualPar));
+static const VMFunction EqInfo = FunctionInfo<CompareFn>(jit::LooselyEqual<true>);
+static const VMFunction NeInfo = FunctionInfo<CompareFn>(jit::LooselyEqual<false>);
+static const VMFunction StrictEqInfo = FunctionInfo<CompareFn>(jit::StrictlyEqual<true>);
+static const VMFunction StrictNeInfo = FunctionInfo<CompareFn>(jit::StrictlyEqual<false>);
+static const VMFunction LtInfo = FunctionInfo<CompareFn>(jit::LessThan);
+static const VMFunction LeInfo = FunctionInfo<CompareFn>(jit::LessThanOrEqual);
+static const VMFunction GtInfo = FunctionInfo<CompareFn>(jit::GreaterThan);
+static const VMFunction GeInfo = FunctionInfo<CompareFn>(jit::GreaterThanOrEqual);
 
 void
 CodeGenerator::visitCompareVM(LCompareVM *lir)
 {
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
 
     switch (lir->mir()->jsop()) {
@@ -5579,29 +5277,25 @@ CodeGenerator::visitEmulatesUndefinedAnd
     }
 
     Register objreg = ToRegister(lir->input());
 
     testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool);
 }
 
 typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
-typedef JSString *(*ConcatStringsParFn)(ForkJoinContext *, HandleString, HandleString);
-static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal(
-    FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>),
-    FunctionInfo<ConcatStringsParFn>(ConcatStringsPar));
+static const VMFunction ConcatStringsInfo = FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>);
 
 void
 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
 {
     OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
                                    StoreRegisterTo(output));
 
-    ExecutionMode mode = gen->info().executionMode();
-    JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStubNoBarrier(mode);
+    JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStubNoBarrier();
     masm.call(stringConcatStub);
     masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
 
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitConcat(LConcat *lir)
@@ -5618,36 +5312,16 @@ CodeGenerator::visitConcat(LConcat *lir)
     MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
     MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
     MOZ_ASSERT(ToRegister(lir->temp5()) == CallTempReg4);
     MOZ_ASSERT(output == CallTempReg5);
 
     emitConcat(lir, lhs, rhs, output);
 }
 
-void
-CodeGenerator::visitConcatPar(LConcatPar *lir)
-{
-    DebugOnly<Register> cx = ToRegister(lir->forkJoinContext());
-    Register lhs = ToRegister(lir->lhs());
-    Register rhs = ToRegister(lir->rhs());
-    Register output = ToRegister(lir->output());
-
-    MOZ_ASSERT(lhs == CallTempReg0);
-    MOZ_ASSERT(rhs == CallTempReg1);
-    MOZ_ASSERT((Register)cx == CallTempReg4);
-    MOZ_ASSERT(ToRegister(lir->temp1()) == CallTempReg0);
-    MOZ_ASSERT(ToRegister(lir->temp2()) == CallTempReg1);
-    MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
-    MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
-    MOZ_ASSERT(output == CallTempReg5);
-
-    emitConcat(lir, lhs, rhs, output);
-}
-
 static void
 CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
                 Register byteOpScratch, size_t fromWidth, size_t toWidth)
 {
     // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
     // (checked below in debug builds), and when done |to| must point to the
     // next available char.
 
@@ -5698,58 +5372,39 @@ CopyStringCharsMaybeInflate(MacroAssembl
         masm.loadStringChars(input, input);
         CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char), sizeof(char16_t));
     }
     masm.bind(&done);
 }
 
 static void
 ConcatFatInlineString(MacroAssembler &masm, Register lhs, Register rhs, Register output,
-                      Register temp1, Register temp2, Register temp3, Register forkJoinContext,
-                      ExecutionMode mode, Label *failure, Label *failurePopTemps, bool isTwoByte)
+                      Register temp1, Register temp2, Register temp3,
+                      Label *failure, Label *failurePopTemps, bool isTwoByte)
 {
     // State: result length in temp2.
 
     // Ensure both strings are linear.
     masm.branchIfRope(lhs, failure);
     masm.branchIfRope(rhs, failure);
 
     // Allocate a JSFatInlineString.
-    switch (mode) {
-      case SequentialExecution:
-        masm.newGCFatInlineString(output, temp1, failure);
-        break;
-      case ParallelExecution:
-        masm.push(temp1);
-        masm.push(temp2);
-        masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, failurePopTemps);
-        masm.pop(temp2);
-        masm.pop(temp1);
-        break;
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    masm.newGCFatInlineString(output, temp1, failure);
 
     // Store length and flags.
     uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
     if (!isTwoByte)
         flags |= JSString::LATIN1_CHARS_BIT;
     masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
     masm.store32(temp2, Address(output, JSString::offsetOfLength()));
 
     // Load chars pointer in temp2.
     masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
 
     {
-        // We use temp3 in this block, which in parallel execution also holds
-        // a live ForkJoinContext pointer. If we are compiling for parallel
-        // execution, be sure to save and restore the ForkJoinContext.
-        if (mode == ParallelExecution)
-            masm.push(temp3);
-
         // Copy lhs chars. Note that this advances temp2 to point to the next
         // char. This also clobbers the lhs register.
         if (isTwoByte) {
             CopyStringCharsMaybeInflate(masm, lhs, temp2, temp1, temp3);
         } else {
             masm.loadStringLength(lhs, temp3);
             masm.loadStringChars(lhs, lhs);
             CopyStringChars(masm, temp2, lhs, temp3, temp1, sizeof(char), sizeof(char));
@@ -5764,19 +5419,16 @@ ConcatFatInlineString(MacroAssembler &ma
             CopyStringChars(masm, temp2, rhs, temp3, temp1, sizeof(char), sizeof(char));
         }
 
         // Null-terminate.
         if (isTwoByte)
             masm.store16(Imm32(0), Address(temp2, 0));
         else
             masm.store8(Imm32(0), Address(temp2, 0));
-
-        if (mode == ParallelExecution)
-            masm.pop(temp3);
     }
 
     masm.ret();
 }
 
 typedef JSString *(*SubstringKernelFn)(JSContext *cx, HandleString str, int32_t begin, int32_t len);
 static const VMFunction SubstringKernelInfo =
     FunctionInfo<SubstringKernelFn>(SubstringKernel);
@@ -5891,32 +5543,27 @@ CodeGenerator::visitSubstr(LSubstr *lir)
         masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
         masm.jump(done);
     }
 
     masm.bind(done);
 }
 
 JitCode *
-JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
+JitCompartment::generateStringConcatStub(JSContext *cx)
 {
     MacroAssembler masm(cx);
 
     Register lhs = CallTempReg0;
     Register rhs = CallTempReg1;
     Register temp1 = CallTempReg2;
     Register temp2 = CallTempReg3;
     Register temp3 = CallTempReg4;
     Register output = CallTempReg5;
 
-    // In parallel execution, we pass in the ForkJoinContext in CallTempReg4, as
-    // by the time we need to use the temp3 we no longer have need of the
-    // cx.
-    Register forkJoinContext = CallTempReg4;
-
     Label failure, failurePopTemps;
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
     // If lhs is empty, return rhs.
     Label leftEmpty;
     masm.loadStringLength(lhs, temp1);
     masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
@@ -5949,30 +5596,17 @@ JitCompartment::generateStringConcatStub
     masm.bind(&notInline);
 
     // Keep AND'ed flags in temp1.
 
     // Ensure result length <= JSString::MAX_LENGTH.
     masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
 
     // Allocate a new rope.
-    switch (mode) {
-      case SequentialExecution:
-        masm.newGCString(output, temp3, &failure);
-        break;
-      case ParallelExecution:
-        masm.push(temp1);
-        masm.push(temp2);
-        masm.newGCStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
-        masm.pop(temp2);
-        masm.pop(temp1);
-        break;
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    masm.newGCString(output, temp3, &failure);
 
     // Store rope length and flags. temp1 still holds the result of AND'ing the
     // lhs and rhs flags, so we just have to clear the other flags to get our
     // rope flags (Latin1 if both lhs and rhs are Latin1).
     static_assert(JSString::ROPE_FLAGS == 0, "Rope flags must be 0");
     masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
     masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
     masm.store32(temp2, Address(output, JSString::offsetOfLength()));
@@ -5986,22 +5620,22 @@ JitCompartment::generateStringConcatStub
     masm.mov(rhs, output);
     masm.ret();
 
     masm.bind(&rightEmpty);
     masm.mov(lhs, output);
     masm.ret();
 
     masm.bind(&isFatInlineTwoByte);
-    ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, forkJoinContext,
-                          mode, &failure, &failurePopTemps, true);
+    ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
+                          &failure, &failurePopTemps, true);
 
     masm.bind(&isFatInlineLatin1);
-    ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, forkJoinContext,
-                          mode, &failure, &failurePopTemps, false);
+    ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
+                          &failure, &failurePopTemps, false);
 
     masm.bind(&failurePopTemps);
     masm.pop(temp2);
     masm.pop(temp1);
 
     masm.bind(&failure);
     masm.movePtr(ImmPtr(nullptr), output);
     masm.ret();
@@ -6520,20 +6154,17 @@ CodeGenerator::visitStoreElementHoleV(LS
     else
         masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
 
     masm.bind(ool->rejoin());
 }
 
 typedef bool (*SetDenseElementFn)(JSContext *, HandleNativeObject, int32_t, HandleValue,
                                   bool strict);
-typedef bool (*SetDenseElementParFn)(ForkJoinContext *, HandleObject, int32_t, HandleValue, bool);
-static const VMFunctionsModal SetDenseElementInfo = VMFunctionsModal(
-    FunctionInfo<SetDenseElementFn>(SetDenseElement),
-    FunctionInfo<SetDenseElementParFn>(SetDenseElementPar));
+static const VMFunction SetDenseElementInfo = FunctionInfo<SetDenseElementFn>(SetDenseElement);
 
 void
 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
 {
     Register object, elements;
     LInstruction *ins = ool->ins();
     const LAllocation *index;
     MIRType valueType;
@@ -7151,21 +6782,18 @@ void
 CodeGenerator::visitRunOncePrologue(LRunOncePrologue *lir)
 {
     pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
     callVM(RunOnceScriptPrologueInfo, lir);
 }
 
 typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
                                          HandleObject);
-typedef JSObject *(*InitRestParameterParFn)(ForkJoinContext *, uint32_t, Value *,
-                                            HandleObject, HandleArrayObject);
-static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal(
-    FunctionInfo<InitRestParameterFn>(InitRestParameter),
-    FunctionInfo<InitRestParameterParFn>(InitRestParameterPar));
+static const VMFunction InitRestParameterInfo =
+    FunctionInfo<InitRestParameterFn>(InitRestParameter);
 
 void
 CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
                         Register temp0, Register temp1, unsigned numFormals,
                         JSObject *templateObject, bool saveAndRestore, Register resultreg)
 {
     // Compute actuals() + numFormals.
     size_t actualsOffset = frameSize() + JitFrameLayout::offsetOfActualArgs();
@@ -7217,34 +6845,16 @@ CodeGenerator::visitRest(LRest *lir)
         masm.bind(&failAlloc);
         masm.movePtr(ImmPtr(nullptr), temp2);
     }
     masm.bind(&joinAlloc);
 
     emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject, false, ToRegister(lir->output()));
 }
 
-// LRestPar cannot derive from LCallInstructionHelper because emitAllocateGCThingPar may
-// itself contain a VM call.  Thus there's some manual work here and in emitRest().
-
-void
-CodeGenerator::visitRestPar(LRestPar *lir)
-{
-    Register numActuals = ToRegister(lir->numActuals());
-    Register cx = ToRegister(lir->forkJoinContext());
-    Register temp0 = ToRegister(lir->getTemp(0));
-    Register temp1 = ToRegister(lir->getTemp(1));
-    Register temp2 = ToRegister(lir->getTemp(2));
-    unsigned numFormals = lir->mir()->numFormals();
-    ArrayObject *templateObject = lir->mir()->templateObject();
-
-    emitAllocateGCThingPar(lir, temp2, cx, temp0, temp1, templateObject);
-    emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject, true, ToRegister(lir->output()));
-}
-
 bool
 CodeGenerator::generateAsmJS(AsmJSFunctionLabels *labels)
 {
     JitSpew(JitSpew_Codegen, "# Emitting asm.js code");
 
     // AsmJS doesn't do SPS instrumentation.
     sps_.disable();
 
@@ -7334,17 +6944,17 @@ CodeGenerator::generate()
     setSkipArgCheckEntryOffset(masm.size());
     masm.setFramePushed(0);
     if (!generatePrologue())
         return false;
 
     masm.bind(&skipPrologue);
 
 #ifdef JS_TRACE_LOGGING
-    if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
+    if (!gen->compilingAsmJS()) {
         emitTracelogScriptStart();
         emitTracelogStartEvent(TraceLogger_IonMonkey);
     }
 #endif
 
 #ifdef DEBUG
     // Assert that the argument types are correct.
     generateArgumentsChecks(/* bailout = */ false);
@@ -7418,29 +7028,24 @@ struct AutoDiscardIonCode
 
 bool
 CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
 {
     RootedScript script(cx, gen->info().script());
     ExecutionMode executionMode = gen->info().executionMode();
     OptimizationLevel optimizationLevel = gen->optimizationInfo().level();
 
-    MOZ_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution);
-
     // We finished the new IonScript. Invalidate the current active IonScript,
     // so we can replace it with this new (probably higher optimized) version.
     if (HasIonScript(script, executionMode)) {
         MOZ_ASSERT(GetIonScript(script, executionMode)->isRecompiling());
         // Do a normal invalidate, except don't cancel offThread compilations,
         // since that will cancel this compilation too.
-        if (!Invalidate(cx, script, SequentialExecution,
-                        /* resetUses */ false, /* cancelOffThread*/ false))
-        {
+        if (!Invalidate(cx, script, /* resetUses */ false, /* cancelOffThread*/ false))
             return false;
-        }
     }
 
     if (scriptCounts_ && !script->hasScriptCounts() && !script->initScriptCounts(cx))
         return false;
 
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to jit::Compile() and return Method_Skipped.
     uint32_t warmUpCount = script->getWarmUpCount();
@@ -7457,45 +7062,36 @@ CodeGenerator::link(JSContext *cx, types
 
     uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
 
-    // List of possible scripts that this graph may call. Currently this is
-    // only tracked when compiling for parallel execution.
-    CallTargetVector callTargets(alloc());
-    if (executionMode == ParallelExecution)
-        AddPossibleCallees(cx, graph.mir(), callTargets);
-
     AutoDiscardIonCode discardIonCode(cx, &recompileInfo);
 
     IonScript *ionScript =
       IonScript::New(cx, recompileInfo,
                      graph.totalSlotCount(), scriptFrameSize,
                      snapshots_.listSize(), snapshots_.RVATableSize(),
                      recovers_.size(), bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
-                     safepoints_.size(), callTargets.length(),
-                     patchableBackedges_.length(), optimizationLevel);
+                     safepoints_.size(), patchableBackedges_.length(), optimizationLevel);
     if (!ionScript)
         return false;
     discardIonCode.ionScript = ionScript;
 
     // Also, note that creating the code here during an incremental GC will
     // trace the code and mark all GC things it refers to. This captures any
     // read barriers which were skipped while compiling the script off thread.
     Linker linker(masm);
     AutoFlushICache afc("IonLink");
-    JitCode *code = (executionMode == SequentialExecution)
-                    ? linker.newCodeForIonScript(cx)
-                    : linker.newCode<CanGC>(cx, ION_CODE);
+    JitCode *code = linker.newCodeForIonScript(cx);
     if (!code)
         return false;
 
     // Encode native to bytecode map if profiling is enabled.
     if (isNativeToBytecodeMapEnabled()) {
         // Generate native-to-bytecode main table.
         if (!generateCompactNativeToBytecodeMap(cx, code))
             return false;
@@ -7545,22 +7141,16 @@ CodeGenerator::link(JSContext *cx, types
     ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
 
     // If SPS is enabled, mark IonScript as having been instrumented with SPS
     if (sps_.enabled())
         ionScript->setHasSPSInstrumentation();
 
     SetIonScript(cx, script, executionMode, ionScript);
 
-    // In parallel execution mode, when we first compile a script, we
-    // don't know that its potential callees are compiled, so set a
-    // flag warning that the callees may not be fully compiled.
-    if (!callTargets.empty())
-        ionScript->setHasUncompiledCallTarget();
-
     invalidateEpilogueData_.fixup(&masm);
     Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
                                        ImmPtr(ionScript),
                                        ImmPtr((void*)-1));
 
     JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)",
             (void *) ionScript, (void *) code->raw());
 
@@ -7603,18 +7193,16 @@ CodeGenerator::link(JSContext *cx, types
         ionScript->copyOsiIndices(&osiIndices_[0], masm);
     if (snapshots_.listSize())
         ionScript->copySnapshots(&snapshots_);
     MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size());
     if (recovers_.size())
         ionScript->copyRecovers(&recovers_);
     if (graph.numConstants())
         ionScript->copyConstants(graph.constantPool());
-    if (callTargets.length() > 0)
-        ionScript->copyCallTargetEntries(callTargets.begin());
     if (patchableBackedges_.length() > 0)
         ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin(), masm);
 
 #ifdef JS_TRACE_LOGGING
     TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
     for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
         patchableTraceLoggers_[i].fixup(&masm);
         Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
@@ -7631,31 +7219,21 @@ CodeGenerator::link(JSContext *cx, types
             patchableTLScripts_[i].fixup(&masm);
             Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
                                                ImmPtr((void *) uintptr_t(textId)),
                                                ImmPtr((void *)0));
         }
     }
 #endif
 
-    switch (executionMode) {
-      case SequentialExecution:
-        // The correct state for prebarriers is unknown until the end of compilation,
-        // since a GC can occur during code generation. All barriers are emitted
-        // off-by-default, and are toggled on here if necessary.
-        if (cx->zone()->needsIncrementalBarrier())
-            ionScript->toggleBarriers(true);
-        break;
-      case ParallelExecution:
-        // We don't run incremental GC during parallel execution; no need to
-        // turn on barriers.
-        break;
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    // The correct state for prebarriers is unknown until the end of compilation,
+    // since a GC can occur during code generation. All barriers are emitted
+    // off-by-default, and are toggled on here if necessary.
+    if (cx->zone()->needsIncrementalBarrier())
+        ionScript->toggleBarriers(true);
 
     // Attach any generated script counts to the script.
     if (IonScriptCounts *counts = extractScriptCounts())
         script->addIonCounts(counts);
 
     // Make sure that AutoDiscardIonCode does not free the relevant info.
     discardIonCode.keepIonCode();
 
@@ -7748,20 +7326,17 @@ CodeGenerator::visitCallGetElement(LCall
     } else {
         MOZ_ASSERT(op == JSOP_CALLELEM);
         callVM(CallElementInfo, lir);
     }
 }
 
 typedef bool (*SetObjectElementFn)(JSContext *, HandleObject, HandleValue, HandleValue,
                                    bool strict);
-typedef bool (*SetElementParFn)(ForkJoinContext *, HandleObject, HandleValue, HandleValue, bool);
-static const VMFunctionsModal SetObjectElementInfo = VMFunctionsModal(
-    FunctionInfo<SetObjectElementFn>(SetObjectElement),
-    FunctionInfo<SetElementParFn>(SetElementPar));
+static const VMFunction SetObjectElementInfo = FunctionInfo<SetObjectElementFn>(SetObjectElement);
 
 void
 CodeGenerator::visitCallSetElement(LCallSetElement *lir)
 {
     pushArg(Imm32(lir->mir()->strict()));
     pushArg(ToValue(lir, LCallSetElement::Value));
     pushArg(ToValue(lir, LCallSetElement::Index));
     pushArg(ToRegister(lir->getOperand(0)));
@@ -7899,82 +7474,42 @@ CodeGenerator::visitNameIC(OutOfLineUpda
     masm.jump(ool->rejoin());
 }
 
 void
 CodeGenerator::addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                                    PropertyName *name, TypedOrValueRegister output,
                                    bool monitoredResult, jsbytecode *profilerLeavePc)
 {
-    switch (gen->info().executionMode()) {
-      case SequentialExecution: {
-        GetPropertyIC cache(liveRegs, objReg, name, output, monitoredResult);
-        cache.setProfilerLeavePC(profilerLeavePc);
-        addCache(ins, allocateCache(cache));
-        break;
-      }
-      case ParallelExecution: {
-        GetPropertyParIC cache(objReg, name, output);
-        cache.setProfilerLeavePC(profilerLeavePc);
-        addCache(ins, allocateCache(cache));
-        break;
-      }
-      default:
-        MOZ_CRASH("Bad execution mode");
-    }
+    GetPropertyIC cache(liveRegs, objReg, name, output, monitoredResult);
+    cache.setProfilerLeavePC(profilerLeavePc);
+    addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                                    PropertyName *name, ConstantOrRegister value, bool strict,
                                    bool needsTypeBarrier, jsbytecode *profilerLeavePc)
 {
-    switch (gen->info().executionMode()) {
-      case SequentialExecution: {
-          SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier);
-            cache.setProfilerLeavePC(profilerLeavePc);
-          addCache(ins, allocateCache(cache));
-          break;
-      }
-      case ParallelExecution: {
-          SetPropertyParIC cache(objReg, name, value, strict, needsTypeBarrier);
-            cache.setProfilerLeavePC(profilerLeavePc);
-          addCache(ins, allocateCache(cache));
-          break;
-      }
-      default:
-        MOZ_CRASH("Bad execution mode");
-    }
+    SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier);
+    cache.setProfilerLeavePC(profilerLeavePc);
+    addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
                                   Register temp, FloatRegister tempDouble,
                                   FloatRegister tempFloat32, ValueOperand index,
                                   ConstantOrRegister value, bool strict, bool guardHoles,
                                   jsbytecode *profilerLeavePc)
 {
-    switch (gen->info().executionMode()) {
-      case SequentialExecution: {
-        SetElementIC cache(obj, unboxIndex, temp, tempDouble, tempFloat32, index, value, strict,
-                           guardHoles);
-        cache.setProfilerLeavePC(profilerLeavePc);
-        addCache(ins, allocateCache(cache));
-        break;
-      }
-      case ParallelExecution: {
-        SetElementParIC cache(obj, unboxIndex, temp, tempDouble, tempFloat32, index, value, strict,
-                              guardHoles);
-        cache.setProfilerLeavePC(profilerLeavePc);
-        addCache(ins, allocateCache(cache));
-        break;
-      }
-      default:
-        MOZ_CRASH("Bad execution mode");
-    }
+    SetElementIC cache(obj, unboxIndex, temp, tempDouble, tempFloat32, index, value, strict,
+                       guardHoles);
+    cache.setProfilerLeavePC(profilerLeavePc);
+    addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     PropertyName *name = ins->mir()->name();
@@ -8020,57 +7555,25 @@ CodeGenerator::visitGetPropertyIC(OutOfL
     pushArg(Imm32(ool->getCacheIndex()));
     callVM(GetPropertyIC::UpdateInfo, lir);
     StoreValueTo(ic->output()).generate(this);
     restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
 
     masm.jump(ool->rejoin());
 }
 
-typedef bool (*GetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, MutableHandleValue);
-const VMFunction GetPropertyParIC::UpdateInfo =
-    FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update);
-
-void
-CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic)
-{
-    LInstruction *lir = ool->lir();
-    saveLive(lir);
-
-    pushArg(ic->object());
-    pushArg(Imm32(ool->getCacheIndex()));
-    callVM(GetPropertyParIC::UpdateInfo, lir);
-    StoreValueTo(ic->output()).generate(this);
-    restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
-
-    masm.jump(ool->rejoin());
-}
-
 void
 CodeGenerator::addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                                   TypedOrValueRegister output, bool monitoredResult,
                                   bool allowDoubleResult, jsbytecode *profilerLeavePc)
 {
-    switch (gen->info().executionMode()) {
-      case SequentialExecution: {
-        RegisterSet liveRegs = ins->safepoint()->liveRegs();
-        GetElementIC cache(liveRegs, obj, index, output, monitoredResult, allowDoubleResult);
-        cache.setProfilerLeavePC(profilerLeavePc);
-        addCache(ins, allocateCache(cache));
-        break;
-      }
-      case ParallelExecution: {
-        GetElementParIC cache(obj, index, output, monitoredResult, allowDoubleResult);
-        cache.setProfilerLeavePC(profilerLeavePc);
-        addCache(ins, allocateCache(cache));
-        break;
-      }
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    RegisterSet liveRegs = ins->safepoint()->liveRegs();
+    GetElementIC cache(liveRegs, obj, index, output, monitoredResult, allowDoubleResult);
+    cache.setProfilerLeavePC(profilerLeavePc);
+    addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
 {
     Register obj = ToRegister(ins->object());
     ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
     TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
@@ -8164,57 +7667,16 @@ CodeGenerator::visitSetElementIC(OutOfLi
     pushArg(ic->object());
     pushArg(Imm32(ool->getCacheIndex()));
     callVM(SetElementIC::UpdateInfo, lir);
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
 }
 
-typedef bool (*SetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue, HandleValue);
-const VMFunction SetElementParIC::UpdateInfo =
-    FunctionInfo<SetElementParICFn>(SetElementParIC::update);
-
-void
-CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic)
-{
-    LInstruction *lir = ool->lir();
-    saveLive(lir);
-
-    pushArg(ic->value());
-    pushArg(ic->index());
-    pushArg(ic->object());
-    pushArg(Imm32(ool->getCacheIndex()));
-    callVM(SetElementParIC::UpdateInfo, lir);
-    restoreLive(lir);
-
-    masm.jump(ool->rejoin());
-}
-
-typedef bool (*GetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue,
-                                  MutableHandleValue);
-const VMFunction GetElementParIC::UpdateInfo =
-    FunctionInfo<GetElementParICFn>(GetElementParIC::update);
-
-void
-CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic)
-{
-    LInstruction *lir = ool->lir();
-    saveLive(lir);
-
-    pushArg(ic->index());
-    pushArg(ic->object());
-    pushArg(Imm32(ool->getCacheIndex()));
-    callVM(GetElementParIC::UpdateInfo, lir);
-    StoreValueTo(ic->output()).generate(this);
-    restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
-
-    masm.jump(ool->rejoin());
-}
-
 void
 CodeGenerator::visitBindNameCache(LBindNameCache *ins)
 {
     Register scopeChain = ToRegister(ins->scopeChain());
     Register output = ToRegister(ins->output());
     BindNameIC cache(scopeChain, ins->mir()->name(), output);
     cache.setProfilerLeavePC(ins->mir()->profilerLeavePc());
 
@@ -8237,21 +7699,17 @@ CodeGenerator::visitBindNameIC(OutOfLine
     StoreRegisterTo(ic->outputReg()).generate(this);
     restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
 }
 
 typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
                               HandlePropertyName, const HandleValue, bool, jsbytecode *);
-typedef bool (*SetPropertyParFn)(ForkJoinContext *, HandleObject,
-                                 HandlePropertyName, const HandleValue, bool, jsbytecode *);
-static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal(
-    FunctionInfo<SetPropertyFn>(SetProperty),
-    FunctionInfo<SetPropertyParFn>(SetPropertyPar));
+static const VMFunction SetPropertyInfo = FunctionInfo<SetPropertyFn>(SetProperty);
 
 void
 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
 {
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
 
     const Register objReg = ToRegister(ins->getOperand(0));
 
@@ -8344,75 +7802,42 @@ CodeGenerator::visitSetPropertyIC(OutOfL
     pushArg(ic->object());
     pushArg(Imm32(ool->getCacheIndex()));
     callVM(SetPropertyIC::UpdateInfo, lir);
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
 }
 
-typedef bool (*SetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue);
-const VMFunction SetPropertyParIC::UpdateInfo =
-    FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update);
-
-void
-CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic)
-{
-    LInstruction *lir = ool->lir();
-    saveLive(lir);
-
-    pushArg(ic->value());
-    pushArg(ic->object());
-    pushArg(Imm32(ool->getCacheIndex()));
-    callVM(SetPropertyParIC::UpdateInfo, lir);
-    restoreLive(lir);
-
-    masm.jump(ool->rejoin());
-}
-
 typedef bool (*ThrowFn)(JSContext *, HandleValue);
 static const VMFunction ThrowInfoCodeGen = FunctionInfo<ThrowFn>(js::Throw);
 
 void
 CodeGenerator::visitThrow(LThrow *lir)
 {
     pushArg(ToValue(lir, LThrow::Value));
     callVM(ThrowInfoCodeGen, lir);
 }
 
 typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p);
-typedef bool (*BitNotParFn)(ForkJoinContext *, HandleValue, int32_t *);
-static const VMFunctionsModal BitNotInfo = VMFunctionsModal(
-    FunctionInfo<BitNotFn>(BitNot),
-    FunctionInfo<BitNotParFn>(BitNotPar));
+static const VMFunction BitNotInfo = FunctionInfo<BitNotFn>(BitNot);
 
 void
 CodeGenerator::visitBitNotV(LBitNotV *lir)
 {
     pushArg(ToValue(lir, LBitNotV::Input));
     callVM(BitNotInfo, lir);
 }
 
 typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p);
-typedef bool (*BitopParFn)(ForkJoinContext *, HandleValue, HandleValue, int32_t *);
-static const VMFunctionsModal BitAndInfo = VMFunctionsModal(
-    FunctionInfo<BitopFn>(BitAnd),
-    FunctionInfo<BitopParFn>(BitAndPar));
-static const VMFunctionsModal BitOrInfo = VMFunctionsModal(
-    FunctionInfo<BitopFn>(BitOr),
-    FunctionInfo<BitopParFn>(BitOrPar));
-static const VMFunctionsModal BitXorInfo = VMFunctionsModal(
-    FunctionInfo<BitopFn>(BitXor),
-    FunctionInfo<BitopParFn>(BitXorPar));
-static const VMFunctionsModal BitLhsInfo = VMFunctionsModal(
-    FunctionInfo<BitopFn>(BitLsh),
-    FunctionInfo<BitopParFn>(BitLshPar));
-static const VMFunctionsModal BitRhsInfo = VMFunctionsModal(
-    FunctionInfo<BitopFn>(BitRsh),
-    FunctionInfo<BitopParFn>(BitRshPar));
+static const VMFunction BitAndInfo = FunctionInfo<BitopFn>(BitAnd);
+static const VMFunction BitOrInfo = FunctionInfo<BitopFn>(BitOr);
+static const VMFunction BitXorInfo = FunctionInfo<BitopFn>(BitXor);
+static const VMFunction BitLhsInfo = FunctionInfo<BitopFn>(BitLsh);
+static const VMFunction BitRhsInfo = FunctionInfo<BitopFn>(BitRsh);
 
 void
 CodeGenerator::visitBitOpV(LBitOpV *lir)
 {
     pushArg(ToValue(lir, LBitOpV::RhsInput));
     pushArg(ToValue(lir, LBitOpV::LhsInput));
 
     switch (lir->jsop()) {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -28,24 +28,21 @@
 
 namespace js {
 namespace jit {
 
 class OutOfLineTestObject;
 class OutOfLineNewArray;
 class OutOfLineNewObject;
 class CheckOverRecursedFailure;
-class CheckOverRecursedFailurePar;
-class OutOfLineInterruptCheckPar;
 class OutOfLineInterruptCheckImplicit;
 class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineLoadTypedArray;
-class OutOfLineNewGCThingPar;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineIsCallable;
 class OutOfLineRegExpExec;
 class OutOfLineRegExpTest;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
@@ -108,17 +105,16 @@ class CodeGenerator : public CodeGenerat
     void visitOutOfLineRegExpExec(OutOfLineRegExpExec *ool);
     void visitRegExpTest(LRegExpTest *lir);
     void visitOutOfLineRegExpTest(OutOfLineRegExpTest *ool);
     void visitRegExpReplace(LRegExpReplace *lir);
     void visitStringReplace(LStringReplace *lir);
     void visitLambda(LLambda *lir);
     void visitLambdaArrow(LLambdaArrow *lir);
     void visitLambdaForSingleton(LLambdaForSingleton *lir);
-    void visitLambdaPar(LLambdaPar *lir);
     void visitPointer(LPointer *lir);
     void visitSlots(LSlots *lir);
     void visitLoadSlotT(LLoadSlotT *lir);
     void visitLoadSlotV(LLoadSlotV *lir);
     void visitStoreSlotT(LStoreSlotT *lir);
     void visitStoreSlotV(LStoreSlotV *lir);
     void visitElements(LElements *lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles *lir);
@@ -157,20 +153,17 @@ class CodeGenerator : public CodeGenerat
     void visitNewArrayDynamicLength(LNewArrayDynamicLength *lir);
     void visitNewObjectVMCall(LNewObject *lir);
     void visitNewObject(LNewObject *lir);
     void visitOutOfLineNewObject(OutOfLineNewObject *ool);
     void visitNewTypedObject(LNewTypedObject *lir);
     void visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     void visitNewCallObject(LNewCallObject *lir);
     void visitNewSingletonCallObject(LNewSingletonCallObject *lir);
-    void visitNewCallObjectPar(LNewCallObjectPar *lir);
     void visitNewStringObject(LNewStringObject *lir);
-    void visitNewPar(LNewPar *lir);
-    void visitNewDenseArrayPar(LNewDenseArrayPar *lir);
     void visitNewDerivedTypedObject(LNewDerivedTypedObject *lir);
     void visitInitElem(LInitElem *lir);
     void visitInitElemGetterSetter(LInitElemGetterSetter *lir);
     void visitMutateProto(LMutateProto *lir);
     void visitInitProp(LInitProp *lir);
     void visitInitPropGetterSetter(LInitPropGetterSetter *lir);
     void visitCreateThis(LCreateThis *lir);
     void visitCreateThisWithProto(LCreateThisWithProto *lir);
@@ -226,23 +219,20 @@ class CodeGenerator : public CodeGenerat
     void visitCompareStrictS(LCompareStrictS *lir);
     void visitCompareVM(LCompareVM *lir);
     void visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
     void visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);
     void visitEmulatesUndefined(LEmulatesUndefined *lir);
     void visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
     void emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output);
     void visitConcat(LConcat *lir);
-    void visitConcatPar(LConcatPar *lir);
     void visitCharCodeAt(LCharCodeAt *lir);
     void visitFromCharCode(LFromCharCode *lir);
     void visitStringSplit(LStringSplit *lir);
     void visitFunctionEnvironment(LFunctionEnvironment *lir);
-    void visitForkJoinContext(LForkJoinContext *lir);
-    void visitGuardThreadExclusive(LGuardThreadExclusive *lir);
     void visitCallGetProperty(LCallGetProperty *lir);
     void visitCallGetElement(LCallGetElement *lir);
     void visitCallSetElement(LCallSetElement *lir);
     void visitCallInitElementArray(LCallInitElementArray *lir);
     void visitThrow(LThrow *lir);
     void visitTypeOfV(LTypeOfV *lir);
     void visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool);
     void visitToIdV(LToIdV *lir);
@@ -286,17 +276,16 @@ class CodeGenerator : public CodeGenerat
     void visitSetFrameArgumentT(LSetFrameArgumentT *lir);
     void visitSetFrameArgumentC(LSetFrameArgumentC *lir);
     void visitSetFrameArgumentV(LSetFrameArgumentV *lir);
     void visitRunOncePrologue(LRunOncePrologue *lir);
     void emitRest(LInstruction *lir, Register array, Register numActuals,
                   Register temp0, Register temp1, unsigned numFormals,
                   JSObject *templateObject, bool saveAndRestore, Register resultreg);
     void visitRest(LRest *lir);
-    void visitRestPar(LRestPar *lir);
     void visitCallSetProperty(LCallSetProperty *ins);
     void visitCallDeleteProperty(LCallDeleteProperty *lir);
     void visitCallDeleteElement(LCallDeleteElement *lir);
     void visitBitNotV(LBitNotV *lir);
     void visitBitOpV(LBitOpV *lir);
     void emitInstanceOf(LInstruction *ins, JSObject *prototypeObject);
     void visitIn(LIn *ins);
     void visitInArray(LInArray *ins);
@@ -320,29 +309,23 @@ class CodeGenerator : public CodeGenerat
     void visitAsmJSVoidReturn(LAsmJSVoidReturn *ret);
     void visitLexicalCheck(LLexicalCheck *ins);
     void visitThrowUninitializedLexical(LThrowUninitializedLexical *ins);
     void visitDebugger(LDebugger *ins);
 
     void visitCheckOverRecursed(LCheckOverRecursed *lir);
     void visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
-    void visitCheckOverRecursedPar(LCheckOverRecursedPar *lir);
-
-    void visitInterruptCheckPar(LInterruptCheckPar *lir);
-    void visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool);
-
     void visitInterruptCheckImplicit(LInterruptCheckImplicit *ins);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ins);
 
     void visitUnboxFloatingPoint(LUnboxFloatingPoint *lir);
     void visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint *ool);
     void visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool);
 
-    void visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool);
     void loadJSScriptForBlock(MBasicBlock *block, Register reg);
     void loadOutermostJSScript(Register reg);
 
     // Inline caches visitors.
     void visitOutOfLineCache(OutOfLineUpdateCache *ool);
 
     void visitGetPropertyCacheV(LGetPropertyCacheV *ins);
     void visitGetPropertyCacheT(LGetPropertyCacheT *ins);
@@ -353,23 +336,19 @@ class CodeGenerator : public CodeGenerat
     void visitBindNameCache(LBindNameCache *ins);
     void visitCallSetProperty(LInstruction *ins);
     void visitSetPropertyCacheV(LSetPropertyCacheV *ins);
     void visitSetPropertyCacheT(LSetPropertyCacheT *ins);
     void visitGetNameCache(LGetNameCache *ins);
     void visitCallsiteCloneCache(LCallsiteCloneCache *ins);
 
     void visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic);
-    void visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic);
     void visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic);
-    void visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic);
     void visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic);
-    void visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic);
     void visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic);
-    void visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic);
     void visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic);
     void visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic);
     void visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic);
 
     void visitAssertRangeI(LAssertRangeI *ins);
     void visitAssertRangeD(LAssertRangeD *ins);
     void visitAssertRangeF(LAssertRangeF *ins);
     void visitAssertRangeV(LAssertRangeV *ins);
@@ -396,22 +375,16 @@ class CodeGenerator : public CodeGenerat
                              bool needsTypeBarrier, jsbytecode *profilerLeavePc);
     void addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
                             FloatRegister tempDouble, FloatRegister tempFloat32,
                             ValueOperand index, ConstantOrRegister value,
                             bool strict, bool guardHoles, jsbytecode *profilerLeavePc);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
-    void emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
-                                Register tempReg1, Register tempReg2,
-                                NativeObject *templateObj);
-
-    void emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg);
-
     void emitLambdaInit(Register resultReg, Register scopeChainReg,
                         const LambdaFunctionInfo &info);
 
     void emitFilterArgumentsOrEval(LInstruction *lir, Register string, Register temp1,
                                    Register temp2);
 
     IonScriptCounts *maybeCreateScriptCounts();
 
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -426,20 +426,16 @@ class CompileInfo
     ExecutionMode executionMode() const {
         return executionMode_;
     }
 
     bool executionModeIsAnalysis() const {
         return executionMode_ == DefinitePropertiesAnalysis || executionMode_ == ArgumentsUsageAnalysis;
     }
 
-    bool isParallelExecution() const {
-        return executionMode_ == ParallelExecution;
-    }
-
     // Returns true if a slot can be observed out-side the current frame while
     // the frame is active on the stack.  This implies that these definitions
     // would have to be executed and that they cannot be removed even if they
     // are unused.
     bool isObservableSlot(uint32_t slot) const {
         if (isObservableFrameSlot(slot))
             return true;
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -28,24 +28,22 @@
 #include "jit/JitCommon.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitSpewer.h"
 #include "jit/LICM.h"
 #include "jit/LinearScan.h"
 #include "jit/LIR.h"
 #include "jit/LoopUnroller.h"
 #include "jit/Lowering.h"
-#include "jit/ParallelSafetyAnalysis.h"
 #include "jit/PerfSpewer.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/ScalarReplacement.h"
 #include "jit/Sink.h"
 #include "jit/StupidAllocator.h"
 #include "jit/ValueNumbering.h"
-#include "vm/ForkJoin.h"
 #include "vm/HelperThreads.h"
 #include "vm/TraceLogging.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
@@ -149,26 +147,23 @@ jit::InitializeIon()
     CheckPerf();
     return true;
 }
 
 JitRuntime::JitRuntime()
   : execAlloc_(nullptr),
     ionAlloc_(nullptr),
     exceptionTail_(nullptr),
-    exceptionTailParallel_(nullptr),
     bailoutTail_(nullptr),
     enterJIT_(nullptr),
     bailoutHandler_(nullptr),
     argumentsRectifier_(nullptr),
     argumentsRectifierReturnAddr_(nullptr),
-    parallelArgumentsRectifier_(nullptr),
     invalidator_(nullptr),
     debugTrapHandler_(nullptr),
-    forkJoinGetSliceStub_(nullptr),
     baselineDebugModeOSRHandler_(nullptr),
     functionWrappers_(nullptr),
     osrTempData_(nullptr),
     mutatingBackedgeList_(false),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     jitcodeGlobalTable_(nullptr)
 {
 }
@@ -205,26 +200,21 @@ JitRuntime::initialize(JSContext *cx)
 
     functionWrappers_ = cx->new_<VMWrapperMap>(cx);
     if (!functionWrappers_ || !functionWrappers_->init())
         return false;
 
     JitSpew(JitSpew_Codegen, "# Emitting exception tail stub");
 
     void *handler = JS_FUNC_TO_DATA_PTR(void *, jit::HandleException);
-    void *handlerParallel = JS_FUNC_TO_DATA_PTR(void *, jit::HandleParallelFailure);
 
     exceptionTail_ = generateExceptionTailStub(cx, handler);
     if (!exceptionTail_)
         return false;
 
-    exceptionTailParallel_ = generateExceptionTailStub(cx, handlerParallel);
-    if (!exceptionTailParallel_)
-        return false;
-
     JitSpew(JitSpew_Codegen, "# Emitting bailout tail stub");
     bailoutTail_ = generateBailoutTailStub(cx);
     if (!bailoutTail_)
         return false;
 
     if (cx->runtime()->jitSupportsFloatingPoint) {
         JitSpew(JitSpew_Codegen, "# Emitting bailout tables");
 
@@ -238,41 +228,31 @@ JitRuntime::initialize(JSContext *cx)
                 break;
             bailoutTables_.infallibleAppend((JitCode *)nullptr);
             bailoutTables_[id] = generateBailoutTable(cx, id);
             if (!bailoutTables_[id])
                 return false;
         }
 
         JitSpew(JitSpew_Codegen, "# Emitting bailout handler");
-        bailoutHandler_ = generateBailoutHandler(cx, SequentialExecution);
+        bailoutHandler_ = generateBailoutHandler(cx);
         if (!bailoutHandler_)
             return false;
 
-        JitSpew(JitSpew_Codegen, "# Emitting parallel bailout handler");
-        parallelBailoutHandler_ = generateBailoutHandler(cx, ParallelExecution);
-        if (!parallelBailoutHandler_)
-            return false;
-
         JitSpew(JitSpew_Codegen, "# Emitting invalidator");
         invalidator_ = generateInvalidator(cx);
         if (!invalidator_)
             return false;
     }
 
     JitSpew(JitSpew_Codegen, "# Emitting sequential arguments rectifier");
-    argumentsRectifier_ = generateArgumentsRectifier(cx, SequentialExecution, &argumentsRectifierReturnAddr_);
+    argumentsRectifier_ = generateArgumentsRectifier(cx, &argumentsRectifierReturnAddr_);
     if (!argumentsRectifier_)
         return false;
 
-    JitSpew(JitSpew_Codegen, "# Emitting parallel arguments rectifier");
-    parallelArgumentsRectifier_ = generateArgumentsRectifier(cx, ParallelExecution, nullptr);
-    if (!parallelArgumentsRectifier_)
-        return false;
-
     JitSpew(JitSpew_Codegen, "# Emitting EnterJIT sequence");
     enterJIT_ = generateEnterJIT(cx, EnterJitOptimized);
     if (!enterJIT_)
         return false;
 
     JitSpew(JitSpew_Codegen, "# Emitting EnterBaselineJIT sequence");
     enterBaselineJIT_ = generateEnterJIT(cx, EnterJitBaseline);
     if (!enterBaselineJIT_)
@@ -339,28 +319,16 @@ JitRuntime::debugTrapHandler(JSContext *
         // be allocated in the atoms compartment.
         AutoLockForExclusiveAccess lock(cx);
         AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
         debugTrapHandler_ = generateDebugTrapHandler(cx);
     }
     return debugTrapHandler_;
 }
 
-bool
-JitRuntime::ensureForkJoinGetSliceStubExists(JSContext *cx)
-{
-    if (!forkJoinGetSliceStub_) {
-        JitSpew(JitSpew_Codegen, "# Emitting ForkJoinGetSlice stub");
-        AutoLockForExclusiveAccess lock(cx);
-        AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
-        forkJoinGetSliceStub_ = generateForkJoinGetSliceStub(cx);
-    }
-    return !!forkJoinGetSliceStub_;
-}
-
 uint8_t *
 JitRuntime::allocateOsrTempData(size_t size)
 {
     osrTempData_ = (uint8_t *)js_realloc(osrTempData_, size);
     return osrTempData_;
 }
 
 void
@@ -400,17 +368,16 @@ JitRuntime::patchIonBackedges(JSRuntime 
 }
 
 JitCompartment::JitCompartment()
   : stubCodes_(nullptr),
     baselineCallReturnAddr_(nullptr),
     baselineGetPropReturnAddr_(nullptr),
     baselineSetPropReturnAddr_(nullptr),
     stringConcatStub_(nullptr),
-    parallelStringConcatStub_(nullptr),
     regExpExecStub_(nullptr),
     regExpTestStub_(nullptr),
     activeParallelEntryScripts_(nullptr)
 {
 }
 
 JitCompartment::~JitCompartment()
 {
@@ -427,27 +394,21 @@ JitCompartment::initialize(JSContext *cx
 
     return true;
 }
 
 bool
 JitCompartment::ensureIonStubsExist(JSContext *cx)
 {
     if (!stringConcatStub_) {
-        stringConcatStub_ = generateStringConcatStub(cx, SequentialExecution);
+        stringConcatStub_ = generateStringConcatStub(cx);
         if (!stringConcatStub_)
             return false;
     }
 
-    if (!parallelStringConcatStub_) {
-        parallelStringConcatStub_ = generateStringConcatStub(cx, ParallelExecution);
-        if (!parallelStringConcatStub_)
-            return false;
-    }
-
     return true;
 }
 
 bool
 JitCompartment::notifyOfActiveParallelEntryScript(JSContext *cx, HandleScript script)
 {
     // Fast path. The isParallelEntryScript bit guarantees that the script is
     // already in the set.
@@ -485,17 +446,17 @@ jit::FinishOffThreadBuilder(JSContext *c
     // Clean the references to the pending IonBuilder, if we just finished it.
     if (builder->script()->hasIonScript() && builder->script()->pendingIonBuilder() == builder)
         builder->script()->setPendingIonBuilder(cx, nullptr);
     if (builder->isInList())
         builder->remove();
 
     // Clear the recompiling flag of the old ionScript, since we continue to
     // use the old ionScript if recompiling fails.
-    if (executionMode == SequentialExecution && builder->script()->hasIonScript())
+    if (builder->script()->hasIonScript())
         builder->script()->ionScript()->clearRecompiling();
 
     // Clean up if compilation did not succeed.
     if (CompilingOffThread(builder->script(), executionMode)) {
         SetIonScript(cx, builder->script(), executionMode,
                      builder->abortReason() == AbortReason_Disable
                      ? ION_DISABLED_SCRIPT
                      : nullptr);
@@ -645,19 +606,16 @@ JitCompartment::sweep(FreeOp *fop, JSCom
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::GetProp_Fallback)))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::SetProp_Fallback)))
         baselineSetPropReturnAddr_ = nullptr;
 
     if (stringConcatStub_ && !IsJitCodeMarked(&stringConcatStub_))
         stringConcatStub_ = nullptr;
 
-    if (parallelStringConcatStub_ && !IsJitCodeMarked(&parallelStringConcatStub_))
-        parallelStringConcatStub_ = nullptr;
-
     if (regExpExecStub_ && !IsJitCodeMarked(&regExpExecStub_))
         regExpExecStub_ = nullptr;
 
     if (regExpTestStub_ && !IsJitCodeMarked(&regExpTestStub_))
         regExpTestStub_ = nullptr;
 
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
@@ -840,18 +798,16 @@ IonScript::IonScript()
     bailoutEntries_(0),
     osiIndexOffset_(0),
     osiIndexEntries_(0),
     snapshots_(0),
     snapshotsListSize_(0),
     snapshotsRVATableSize_(0),
     constantTable_(0),
     constantEntries_(0),
-    callTargetList_(0),
-    callTargetEntries_(0),
     backedgeList_(0),
     backedgeEntries_(0),
     invalidationCount_(0),
     parallelAge_(0),
     recompileInfo_(),
     osrPcMismatchCounter_(0),
     pendingBuilder_(nullptr)
 {
@@ -860,18 +816,17 @@ IonScript::IonScript()
 IonScript *
 IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
                uint32_t frameSlots, uint32_t frameSize,
                size_t snapshotsListSize, size_t snapshotsRVATableSize,
                size_t recoversSize, size_t bailoutEntries,
                size_t constants, size_t safepointIndices,
                size_t osiIndices, size_t cacheEntries,
                size_t runtimeSize,  size_t safepointsSize,
-               size_t callTargetEntries, size_t backedgeEntries,
-               OptimizationLevel optimizationLevel)
+               size_t backedgeEntries, OptimizationLevel optimizationLevel)
 {
     static const int DataAlignment = sizeof(void *);
 
     if (snapshotsListSize >= MAX_BUFFER_SIZE ||
         (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t)))
     {
         js_ReportOutOfMemory(cx);
         return nullptr;
@@ -884,28 +839,26 @@ IonScript::New(JSContext *cx, types::Rec
     size_t paddedRecoversSize = AlignBytes(recoversSize, DataAlignment);
     size_t paddedBailoutSize = AlignBytes(bailoutEntries * sizeof(uint32_t), DataAlignment);
     size_t paddedConstantsSize = AlignBytes(constants * sizeof(Value), DataAlignment);
     size_t paddedSafepointIndicesSize = AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment);
     size_t paddedOsiIndicesSize = AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment);
     size_t paddedCacheEntriesSize = AlignBytes(cacheEntries * sizeof(uint32_t), DataAlignment);
     size_t paddedRuntimeSize = AlignBytes(runtimeSize, DataAlignment);
     size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
-    size_t paddedCallTargetSize = AlignBytes(callTargetEntries * sizeof(JSScript *), DataAlignment);
     size_t paddedBackedgeSize = AlignBytes(backedgeEntries * sizeof(PatchableBackedge), DataAlignment);
     size_t bytes = paddedSnapshotsSize +
                    paddedRecoversSize +
                    paddedBailoutSize +
                    paddedConstantsSize +
                    paddedSafepointIndicesSize+
                    paddedOsiIndicesSize +
                    paddedCacheEntriesSize +
                    paddedRuntimeSize +
                    paddedSafepointSize +
-                   paddedCallTargetSize +
                    paddedBackedgeSize;
     IonScript *script = cx->zone()->pod_malloc_with_extra<IonScript, uint8_t>(bytes);
     if (!script)
         return nullptr;
     new (script) IonScript();
 
     uint32_t offsetCursor = sizeof(IonScript);
 
@@ -941,20 +894,16 @@ IonScript::New(JSContext *cx, types::Rec
     script->recovers_ = offsetCursor;
     script->recoversSize_ = recoversSize;
     offsetCursor += paddedRecoversSize;
 
     script->constantTable_ = offsetCursor;
     script->constantEntries_ = constants;
     offsetCursor += paddedConstantsSize;
 
-    script->callTargetList_ = offsetCursor;
-    script->callTargetEntries_ = callTargetEntries;
-    offsetCursor += paddedCallTargetSize;
-
     script->backedgeList_ = offsetCursor;
     script->backedgeEntries_ = backedgeEntries;
     offsetCursor += paddedBackedgeSize;
 
     script->frameSlots_ = frameSlots;
     script->frameSize_ = frameSize;
 
     script->recompileInfo_ = recompileInfo;
@@ -969,21 +918,16 @@ IonScript::trace(JSTracer *trc)
     if (method_)
         MarkJitCode(trc, &method_, "method");
 
     if (deoptTable_)
         MarkJitCode(trc, &deoptTable_, "deoptimizationTable");
 
     for (size_t i = 0; i < numConstants(); i++)
         gc::MarkValue(trc, &getConstant(i), "constant");
-
-    // No write barrier is needed for the call target list, as it's attached
-    // at compilation time and is read only.
-    for (size_t i = 0; i < callTargetEntries(); i++)
-        gc::MarkScriptUnbarriered(trc, &callTargetList()[i], "callTarget");
 }
 
 /* static */ void
 IonScript::writeBarrierPre(Zone *zone, IonScript *ionScript)
 {
     if (zone->needsIncrementalBarrier())
         ionScript->trace(zone->barrierTracer());
 }
@@ -1024,23 +968,16 @@ IonScript::copyBailoutTable(const Snapsh
 void
 IonScript::copyConstants(const Value *vp)
 {
     for (size_t i = 0; i < constantEntries_; i++)
         constants()[i].init(vp[i]);
 }
 
 void
-IonScript::copyCallTargetEntries(JSScript **callTargets)
-{
-    for (size_t i = 0; i < callTargetEntries_; i++)
-        callTargetList()[i] = callTargets[i];
-}
-
-void
 IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code,
                                   PatchableBackedgeInfo *backedges,
                                   MacroAssembler &masm)
 {
     JitRuntime *jrt = cx->runtime()->jitRuntime();
     JitRuntime::AutoMutateBackedges amb(jrt);
 
     for (size_t i = 0; i < backedgeEntries_; i++) {
@@ -1375,31 +1312,16 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
         IonSpewPass("Apply types");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Apply types"))
             return false;
     }
 
-    // Parallel Safety Analysis. Note that this may delete blocks containing
-    // instructions pointed to by the dependency() field of instructions which
-    // are not deleted, leaving them dangling. This is ok, since we'll rerun
-    // AliasAnalysis, which recomputes them, before they're needed.
-    if (graph.entryBlock()->info().executionMode() == ParallelExecution) {
-        AutoTraceLog log(logger, TraceLogger_ParallelSafetyAnalysis);
-        ParallelSafetyAnalysis analysis(mir, graph);
-        if (!analysis.analyze())
-            return false;
-        IonSpewPass("Parallel Safety Analysis");
-        AssertExtendedGraphCoherency(graph);
-        if (mir->shouldCancel("Parallel Safety Analysis"))
-            return false;
-    }
-
     ValueNumberer gvn(mir, graph);
     if (!gvn.init())
         return false;
 
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
     if (mir->optimizationInfo().licmEnabled() ||
         mir->optimizationInfo().gvnEnabled())
@@ -1762,19 +1684,17 @@ AttachFinishedCompilations(JSContext *cx
                 break;
             }
         }
         if (!builder)
             break;
 
         // Try to defer linking if the script is on the stack, to postpone
         // invalidating them.
-        if (builder->info().executionMode() == SequentialExecution &&
-            builder->script()->hasIonScript())
-        {
+        if (builder->script()->hasIonScript()) {
             bool onStack = false;
             for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
                 for (JitFrameIterator it(iter); !it.done(); ++it) {
                     if (!it.isIonJS())
                         continue;
                     if (it.checkInvalidation())
                         continue;
 
@@ -1870,18 +1790,17 @@ TrackPropertiesForSingletonScopes(JSCont
         if (scope->is<CallObject>() && scope->hasSingletonType())
             TrackAllProperties(cx, scope);
     }
 }
 
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
            BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
-           ExecutionMode executionMode, bool recompile,
-           OptimizationLevel optimizationLevel)
+           bool recompile, OptimizationLevel optimizationLevel)
 {
     TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
     TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
     AutoTraceLog logScript(logger, event);
     AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
 
     MOZ_ASSERT(optimizationLevel > Optimization_DontCompile);
 
@@ -1906,33 +1825,26 @@ IonCompile(JSContext *cx, JSScript *scri
     types::AutoEnterAnalysis enter(cx);
 
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
         return AbortReason_Alloc;
 
     if (!cx->compartment()->jitCompartment()->ensureIonStubsExist(cx))
         return AbortReason_Alloc;
 
-    if (executionMode == ParallelExecution &&
-        LIRGenerator::allowInlineForkJoinGetSlice() &&
-        !cx->runtime()->jitRuntime()->ensureForkJoinGetSliceStubExists(cx))
-    {
-        return AbortReason_Alloc;
-    }
-
     MIRGraph *graph = alloc->new_<MIRGraph>(temp);
     if (!graph)
         return AbortReason_Alloc;
 
     InlineScriptTree *inlineScriptTree = InlineScriptTree::New(temp, nullptr, nullptr, script);
     if (!inlineScriptTree)
         return AbortReason_Alloc;
 
     CompileInfo *info = alloc->new_<CompileInfo>(script, script->functionNonDelazifying(), osrPc,
-                                                 constructing, executionMode,
+                                                 constructing, SequentialExecution,
                                                  script->needsArgsObj(), inlineScriptTree);
     if (!info)
         return AbortReason_Alloc;
 
     BaselineInspector *inspector = alloc->new_<BaselineInspector>(script);
     if (!inspector)
         return AbortReason_Alloc;
 
@@ -1953,25 +1865,23 @@ IonCompile(JSContext *cx, JSScript *scri
     IonBuilder *builder = alloc->new_<IonBuilder>((JSContext *) nullptr,
                                                   CompileCompartment::get(cx->compartment()),
                                                   options, temp, graph, constraints,
                                                   inspector, info, optimizationInfo,
                                                   baselineFrameInspector);
     if (!builder)
         return AbortReason_Alloc;
 
-    MOZ_ASSERT(recompile == HasIonScript(builder->script(), executionMode));
-    MOZ_ASSERT(CanIonCompile(builder->script(), executionMode));
+    MOZ_ASSERT(recompile == builder->script()->hasIonScript());
+    MOZ_ASSERT(CanIonCompile(builder->script(), SequentialExecution));
 
     RootedScript builderScript(cx, builder->script());
 
-    if (recompile) {
-        MOZ_ASSERT(executionMode == SequentialExecution);
+    if (recompile)
         builderScript->ionScript()->setRecompiling();
-    }
 
 #ifdef DEBUG
     IonSpewFunction ionSpewFunction(graph, builderScript);
 #endif
 
     bool succeeded = builder->build();
     builder->clearForBackEnd();
 
@@ -1988,17 +1898,17 @@ IonCompile(JSContext *cx, JSScript *scri
             }
         }
         return reason;
     }
 
     // If possible, compile the script off thread.
     if (OffThreadCompilationAvailable(cx)) {
         if (!recompile)
-            SetIonScript(cx, builderScript, executionMode, ION_COMPILING_SCRIPT);
+            builderScript->setIonScript(cx, ION_COMPILING_SCRIPT);
 
         JitSpew(JitSpew_IonLogs, "Can't log script %s:%d. (Compiled on background thread.)",
                 builderScript->filename(), builderScript->lineno());
 
         if (!StartOffThreadIonCompile(cx, builder)) {
             JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation.");
             return AbortReason_Alloc;
         }
@@ -2100,35 +2010,28 @@ CanIonCompileScript(JSContext *cx, JSScr
 {
     if (!script->canIonCompile() || !CheckScript(cx, script, osr))
         return false;
 
     return CheckScriptSize(cx, script) == Method_Compiled;
 }
 
 static OptimizationLevel
-GetOptimizationLevel(HandleScript script, jsbytecode *pc, ExecutionMode executionMode)
+GetOptimizationLevel(HandleScript script, jsbytecode *pc)
 {
-    if (executionMode == ParallelExecution)
-        return Optimization_Normal;
-
-    MOZ_ASSERT(executionMode == SequentialExecution);
-
     return js_IonOptimizations.levelForScript(script, pc);
 }
 
 static MethodStatus
 Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
-        bool constructing, ExecutionMode executionMode, bool forceRecompile = false)
+        bool constructing, bool forceRecompile = false)
 {
     MOZ_ASSERT(jit::IsIonEnabled(cx));
     MOZ_ASSERT(jit::IsBaselineEnabled(cx));
     MOZ_ASSERT_IF(osrPc != nullptr, LoopEntryCanIonOsr(osrPc));
-    MOZ_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPc);
-    MOZ_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode));
 
     if (!script->hasBaselineScript())
         return Method_Skipped;
 
     if (script->isDebuggee() || (osrFrame && osrFrame->isDebuggee())) {
         JitSpew(JitSpew_IonAbort, "debugging");
         return Method_Skipped;
     }
@@ -2140,21 +2043,21 @@ Compile(JSContext *cx, HandleScript scri
 
     MethodStatus status = CheckScriptSize(cx, script);
     if (status != Method_Compiled) {
         JitSpew(JitSpew_IonAbort, "Aborted compilation of %s:%d", script->filename(), script->lineno());
         return status;
     }
 
     bool recompile = false;
-    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc, executionMode);
+    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc);
     if (optimizationLevel == Optimization_DontCompile)
         return Method_Skipped;
 
-    IonScript *scriptIon = GetIonScript(script, executionMode);
+    IonScript *scriptIon = script->maybeIonScript();
     if (scriptIon) {
         if (!scriptIon->method())
             return Method_CantCompile;
 
         // Don't recompile/overwrite higher optimized code,
         // with a lower optimization level.
         if (optimizationLevel <= scriptIon->optimizationLevel() && !forceRecompile)
             return Method_Compiled;
@@ -2164,31 +2067,31 @@ Compile(JSContext *cx, HandleScript scri
             return Method_Compiled;
 
         if (osrPc)
             scriptIon->resetOsrPcMismatchCounter();
 
         recompile = true;
     }
 
-    AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode,
+    AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing,
                                     recompile, optimizationLevel);
     if (reason == AbortReason_Error)
         return Method_Error;
 
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     if (reason == AbortReason_Alloc) {
         js_ReportOutOfMemory(cx);
         return Method_Error;
     }
 
     // Compilation succeeded or we invalidated right away or an inlining/alloc abort
-    if (HasIonScript(script, executionMode))
+    if (script->hasIonScript())
         return Method_Compiled;
     return Method_Skipped;
 }
 
 } // namespace jit
 } // namespace js
 
 // Decide if a transition from interpreter execution to Ion code should occur.
@@ -2234,17 +2137,17 @@ jit::CanEnterAtBranch(JSContext *cx, JSS
 
     // Attempt compilation.
     // - Returns Method_Compiled if the right ionscript is present
     //   (Meaning it was present or a sequantial compile finished)
     // - Returns Method_Skipped if pc doesn't match
     //   (This means a background thread compilation with that pc could have started or not.)
     RootedScript rscript(cx, script);
     MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(),
-                                  SequentialExecution, force);
+                                  force);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     // Return the compilation was skipped when the osr pc wasn't adjusted.
     // This can happen when there was still an IonScript available and a
@@ -2304,18 +2207,17 @@ jit::CanEnter(JSContext *cx, RunState &s
     if (js_JitOptions.eagerCompilation && !rscript->hasBaselineScript()) {
         MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
             return status;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
     bool constructing = state.isInvoke() && state.asInvoke()->constructing();
-    MethodStatus status =
-        Compile(cx, rscript, nullptr, nullptr, constructing, SequentialExecution);
+    MethodStatus status = Compile(cx, rscript, nullptr, nullptr, constructing);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, rscript);
         return status;
     }
 
     return Method_Compiled;
 }
@@ -2331,18 +2233,17 @@ jit::CompileFunctionForBaseline(JSContex
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(frame)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    MethodStatus status =
-        Compile(cx, script, frame, nullptr, frame->isConstructing(), SequentialExecution);
+    MethodStatus status = Compile(cx, script, frame, nullptr, frame->isConstructing());
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
@@ -2350,71 +2251,27 @@ jit::CompileFunctionForBaseline(JSContex
 MethodStatus
 jit::Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
                bool constructing, bool force)
 {
     MOZ_ASSERT(script->hasIonScript());
     if (script->ionScript()->isRecompiling())
         return Method_Compiled;
 
-    MethodStatus status =
-        Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution, force);
+    MethodStatus status = Compile(cx, script, osrFrame, osrPc, constructing, force);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
 
 MethodStatus
-jit::CanEnterInParallel(JSContext *cx, HandleScript script)
-{
-    // Skip if the script has been disabled.
-    //
-    // Note: We return Method_Skipped in this case because the other
-    // CanEnter() methods do so. However, ForkJoin.cpp detects this
-    // condition differently treats it more like an error.
-    if (!script->canParallelIonCompile())
-        return Method_Skipped;
-
-    // Skip if the script is being compiled off thread.
-    if (script->isParallelIonCompilingOffThread())
-        return Method_Skipped;
-
-    MethodStatus status = Compile(cx, script, nullptr, nullptr, false, ParallelExecution);
-    if (status != Method_Compiled) {
-        if (status == Method_CantCompile)
-            ForbidCompilation(cx, script, ParallelExecution);
-        return status;
-    }
-
-    // This can GC, so afterward, script->parallelIon is
-    // not guaranteed to be valid.
-    if (!cx->runtime()->jitRuntime()->enterIon())
-        return Method_Error;
-
-    // Subtle: it is possible for GC to occur during
-    // compilation of one of the invoked functions, which
-    // would cause the earlier functions (such as the
-    // kernel itself) to be collected.  In this event, we
-    // give up and fallback to sequential for now.
-    if (!script->hasParallelIonScript()) {
-        parallel::Spew(
-            parallel::SpewCompile,
-            "Script %p:%s:%u was garbage-collected or invalidated",
-            script.get(), script->filename(), script->lineno());
-        return Method_Skipped;
-    }
-
-    return Method_Compiled;
-}
-
-MethodStatus
 jit::CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs)
 {
     MOZ_ASSERT(jit::IsIonEnabled(cx));
 
     // Skip if the code is expected to result in a bailout.
     if (!script->hasIonScript() || script->ionScript()->bailoutExpected())
         return Method_Skipped;
 
@@ -2800,27 +2657,19 @@ jit::Invalidate(types::TypeZone &types, 
             continue;
 
         SetIonScript(nullptr, script, executionMode, nullptr);
         ionScript->decrementInvalidationCount(fop);
         co->invalidate();
         numInvalidations--;
 
         // Wait for the scripts to get warm again before doing another
-        // compile, unless either:
-        // (1) we are recompiling *because* a script got hot;
-        //     (resetUses is false); or,
-        // (2) we are invalidating a parallel script.  This is because
-        //     the warmUpCounter only applies to sequential uses.  Parallel
-        //     execution *requires* ion, and so we don't limit it to
-        //     methods with a high usage count (though we do check that
-        //     the warmUpCount is at least 1 when compiling the transitive
-        //     closure of potential callees, to avoid compiling things
-        //     that are never run at all).
-        if (resetUses && executionMode != ParallelExecution)
+        // compile, unless we are recompiling *because* a script got hot
+        // (resetUses is false).
+        if (resetUses)
             script->resetWarmUpCounter();
     }
 
     // Make sure we didn't leak references by invalidating the same IonScript
     // multiple times in the above loop.
     MOZ_ASSERT(!numInvalidations);
 }
 
@@ -2839,18 +2688,17 @@ jit::IonScript::invalidate(JSContext *cx
     types::RecompileInfoVector list;
     if (!list.append(recompileInfo()))
         return false;
     Invalidate(cx, list, resetUses, true);
     return true;
 }
 
 bool
-jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses,
-                bool cancelOffThread)
+jit::Invalidate(JSContext *cx, JSScript *script, bool resetUses, bool cancelOffThread)
 {
     MOZ_ASSERT(script->hasIonScript());
 
     if (cx->runtime()->spsProfiler.enabled()) {
         // Register invalidation with profiler.
         // Format of event payload string:
         //      "<filename>:<lineno>"
 
@@ -2866,135 +2714,70 @@ jit::Invalidate(JSContext *cx, JSScript 
 
         // Construct the descriptive string.
         JS_snprintf(buf, len, "Invalidate %s:%u", filename, (unsigned int)script->lineno());
         cx->runtime()->spsProfiler.markEvent(buf);
         js_free(buf);
     }
 
     types::RecompileInfoVector scripts;
-
-    switch (mode) {
-      case SequentialExecution:
-        MOZ_ASSERT(script->hasIonScript());
-        if (!scripts.append(script->ionScript()->recompileInfo()))
-            return false;
-        break;
-      case ParallelExecution:
-        MOZ_ASSERT(script->hasParallelIonScript());
-        if (!scripts.append(script->parallelIonScript()->recompileInfo()))
-            return false;
-        break;
-      default:
-        MOZ_CRASH("No such execution mode");
-    }
+    MOZ_ASSERT(script->hasIonScript());
+    if (!scripts.append(script->ionScript()->recompileInfo()))
+        return false;
 
     Invalidate(cx, scripts, resetUses, cancelOffThread);
     return true;
 }
 
-bool
-jit::Invalidate(JSContext *cx, JSScript *script, bool resetUses, bool cancelOffThread)
-{
-    return Invalidate(cx, script, SequentialExecution, resetUses, cancelOffThread);
-}
-
 static void
 FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript)
 {
     types::TypeZone &types = script->zone()->types;
 
     // Note: If the script is about to be swept, the compiler output may have
     // already been destroyed.
     if (types::CompilerOutput *output = ionScript->recompileInfo().compilerOutput(types))
         output->invalidate();
 
     // If this script has Ion code on the stack, invalidated() will return
     // true. In this case we have to wait until destroying it.
     if (!ionScript->invalidated())
         jit::IonScript::Destroy(fop, ionScript);
 }
 
-template <ExecutionMode mode>
 void
 jit::FinishInvalidation(FreeOp *fop, JSScript *script)
 {
-    // In all cases, nullptr out script->ion or script->parallelIon to avoid
-    // re-entry.
-    switch (mode) {
-      case SequentialExecution:
-        if (script->hasIonScript()) {
-            IonScript *ion = script->ionScript();
-            script->setIonScript(nullptr, nullptr);
-            FinishInvalidationOf(fop, script, ion);
-        }
-        return;
-
-      case ParallelExecution:
-        if (script->hasParallelIonScript()) {
-            IonScript *parallelIon = script->parallelIonScript();
-            script->setParallelIonScript(nullptr);
-            FinishInvalidationOf(fop, script, parallelIon);
-        }
-        return;
-
-      default:
-        MOZ_CRASH("bad execution mode");
+    // In all cases, nullptr out script->ion to avoid re-entry.
+    if (script->hasIonScript()) {
+        IonScript *ion = script->ionScript();
+        script->setIonScript(nullptr, nullptr);
+        FinishInvalidationOf(fop, script, ion);
     }
 }
 
-template void
-jit::FinishInvalidation<SequentialExecution>(FreeOp *fop, JSScript *script);
-
-template void
-jit::FinishInvalidation<ParallelExecution>(FreeOp *fop, JSScript *script);
-
 void
 jit::ForbidCompilation(JSContext *cx, JSScript *script)
 {
-    ForbidCompilation(cx, script, SequentialExecution);
-}
-
-void
-jit::ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode)
-{
-    JitSpew(JitSpew_IonAbort, "Disabling Ion mode %d compilation of script %s:%d",
-            mode, script->filename(), script->lineno());
+    JitSpew(JitSpew_IonAbort, "Disabling Ion compilation of script %s:%d",
+            script->filename(), script->lineno());
 
     CancelOffThreadIonCompile(cx->compartment(), script);
 
-    switch (mode) {
-      case SequentialExecution:
-        if (script->hasIonScript()) {
-            // It is only safe to modify script->ion if the script is not currently
-            // running, because JitFrameIterator needs to tell what ionScript to
-            // use (either the one on the JSScript, or the one hidden in the
-            // breadcrumbs Invalidation() leaves). Therefore, if invalidation
-            // fails, we cannot disable the script.
-            if (!Invalidate(cx, script, mode, false))
-                return;
-        }
-
-        script->setIonScript(cx, ION_DISABLED_SCRIPT);
-        return;
-
-      case ParallelExecution:
-        if (script->hasParallelIonScript()) {
-            if (!Invalidate(cx, script, mode, false))
-                return;
-        }
-
-        script->setParallelIonScript(ION_DISABLED_SCRIPT);
-        return;
-
-      default:
-        MOZ_CRASH("No such execution mode");
+    if (script->hasIonScript()) {
+        // It is only safe to modify script->ion if the script is not currently
+        // running, because JitFrameIterator needs to tell what ionScript to
+        // use (either the one on the JSScript, or the one hidden in the
+        // breadcrumbs Invalidation() leaves). Therefore, if invalidation
+        // fails, we cannot disable the script.
+        if (!Invalidate(cx, script, false))
+            return;
     }
 
-    MOZ_CRASH("No such execution mode");
+    script->setIonScript(cx, ION_DISABLED_SCRIPT);
 }
 
 AutoFlushICache *
 PerThreadData::autoFlushICache() const
 {
     return autoFlushICache_;
 }
 
@@ -3134,57 +2917,45 @@ AutoFlushICache::~AutoFlushICache()
 #endif
 }
 
 void
 jit::PurgeCaches(JSScript *script)
 {
     if (script->hasIonScript())
         script->ionScript()->purgeCaches();
-
-    if (script->hasParallelIonScript())
-        script->parallelIonScript()->purgeCaches();
 }
 
 size_t
 jit::SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf)
 {
     size_t result = 0;
 
     if (script->hasIonScript())
         result += script->ionScript()->sizeOfIncludingThis(mallocSizeOf);
 
-    if (script->hasParallelIonScript())
-        result += script->parallelIonScript()->sizeOfIncludingThis(mallocSizeOf);
-
     return result;
 }
 
 void
 jit::DestroyJitScripts(FreeOp *fop, JSScript *script)
 {
     if (script->hasIonScript())
         jit::IonScript::Destroy(fop, script->ionScript());
 
-    if (script->hasParallelIonScript())
-        jit::IonScript::Destroy(fop, script->parallelIonScript());
-
     if (script->hasBaselineScript())
         jit::BaselineScript::Destroy(fop, script->baselineScript());
 }
 
 void
 jit::TraceJitScripts(JSTracer* trc, JSScript *script)
 {
     if (script->hasIonScript())
         jit::IonScript::Trace(trc, scrip