merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 20 Apr 2017 11:13:07 +0200
changeset 354003 27311156637f9b5d4504373967e01c4241902ae7
parent 353979 20325547884af3bd8c66ef543358692b5cccf5b0 (current diff)
parent 354002 e2fccaa1a87a6cb8ed7c79925c3cd3c64eb9efd6 (diff)
child 354053 83f60253d9654b3305f2c33061f301fe30addee8
child 354111 06f5f1145845aa44bd2796c57d02dca39bbe5c70
child 355155 fd86020ac35ec57f249afd2db9831d533cc541ae
push id31684
push usercbook@mozilla.com
push dateThu, 20 Apr 2017 09:13:26 +0000
treeherdermozilla-central@27311156637f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
27311156637f / 55.0a1 / 20170420100256 / files
nightly linux64
27311156637f / 55.0a1 / 20170420100256 / files
nightly mac
27311156637f / 55.0a1 / 20170420030346 / files
nightly win32
27311156637f / 55.0a1 / 20170420030346 / files
nightly win64
27311156637f / 55.0a1 / 20170420030346 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
gfx/gl/TextureGarbageBin.cpp
gfx/gl/TextureGarbageBin.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8786,16 +8786,69 @@ nsContentUtils::StorageAllowedForWindow(
 // static, public
 nsContentUtils::StorageAccess
 nsContentUtils::StorageAllowedForPrincipal(nsIPrincipal* aPrincipal)
 {
   return InternalStorageAllowedForPrincipal(aPrincipal, nullptr);
 }
 
 // static, private
+void
+nsContentUtils::GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
+                                              uint32_t* aLifetimePolicy,
+                                              uint32_t* aBehavior)
+{
+  *aLifetimePolicy = sCookiesLifetimePolicy;
+  *aBehavior = sCookiesBehavior;
+
+  // Any permissions set for the given principal will override our default
+  // settings from preferences.
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+    services::GetPermissionManager();
+  if (!permissionManager) {
+    return;
+  }
+
+  uint32_t perm;
+  permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
+  switch (perm) {
+    case nsICookiePermission::ACCESS_ALLOW:
+      *aBehavior = nsICookieService::BEHAVIOR_ACCEPT;
+      *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+      break;
+    case nsICookiePermission::ACCESS_DENY:
+      *aBehavior = nsICookieService::BEHAVIOR_REJECT;
+      *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+      break;
+    case nsICookiePermission::ACCESS_SESSION:
+      *aBehavior = nsICookieService::BEHAVIOR_ACCEPT;
+      *aLifetimePolicy = nsICookieService::ACCEPT_SESSION;
+      break;
+    case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
+      *aBehavior = nsICookieService::BEHAVIOR_REJECT_FOREIGN;
+      // NOTE: The decision was made here to override the lifetime policy to be
+      // ACCEPT_NORMALLY for consistency with ACCESS_ALLOW, but this does
+      // prevent us from expressing BEHAVIOR_REJECT_FOREIGN/ACCEPT_SESSION for a
+      // specific domain. As BEHAVIOR_REJECT_FOREIGN isn't visible in our UI,
+      // this is probably not an issue.
+      *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+      break;
+    case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
+      *aBehavior = nsICookieService::BEHAVIOR_LIMIT_FOREIGN;
+      // NOTE: The decision was made here to override the lifetime policy to be
+      // ACCEPT_NORMALLY for consistency with ACCESS_ALLOW, but this does
+      // prevent us from expressing BEHAVIOR_REJECT_FOREIGN/ACCEPT_SESSION for a
+      // specific domain. As BEHAVIOR_LIMIT_FOREIGN isn't visible in our UI,
+      // this is probably not an issue.
+      *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+      break;
+  }
+}
+
+// static, private
 nsContentUtils::StorageAccess
 nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
                                                    nsPIDOMWindowInner* aWindow)
 {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(!aWindow || aWindow->IsInnerWindow());
 
   StorageAccess access = StorageAccess::eAllow;
@@ -8814,38 +8867,22 @@ nsContentUtils::InternalStorageAllowedFo
     }
 
     // Check if we are in private browsing, and record that fact
     if (IsInPrivateBrowsing(document)) {
       access = StorageAccess::ePrivateBrowsing;
     }
   }
 
-  nsCOMPtr<nsIPermissionManager> permissionManager =
-    services::GetPermissionManager();
-  if (!permissionManager) {
-    return StorageAccess::eDeny;
-  }
-
-  // check the permission manager for any allow or deny permissions
-  // for cookies for the window.
-  uint32_t perm;
-  permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
-  if (perm == nsIPermissionManager::DENY_ACTION) {
-    return StorageAccess::eDeny;
-  }
-  if (perm == nsICookiePermission::ACCESS_SESSION) {
-    return std::min(access, StorageAccess::eSessionScoped);
-  }
-  if (perm == nsIPermissionManager::ALLOW_ACTION) {
-    return access;
-  }
+  uint32_t lifetimePolicy;
+  uint32_t behavior;
+  GetCookieBehaviorForPrincipal(aPrincipal, &lifetimePolicy, &behavior);
 
   // Check if we should only allow storage for the session, and record that fact
-  if (sCookiesLifetimePolicy == nsICookieService::ACCEPT_SESSION) {
+  if (lifetimePolicy == nsICookieService::ACCEPT_SESSION) {
     // Storage could be StorageAccess::ePrivateBrowsing or StorageAccess::eAllow
     // so perform a std::min comparison to make sure we preserve ePrivateBrowsing
     // if it has been set.
     access = std::min(StorageAccess::eSessionScoped, access);
   }
 
   // About URIs are allowed to access storage, even if they don't have chrome
   // privileges. If this is not desired, than the consumer will have to
@@ -8876,23 +8913,23 @@ nsContentUtils::InternalStorageAllowedFo
     bool isAbout = false;
     MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("about", &isAbout));
     if (isAbout) {
       return access;
     }
   }
 
   // We don't want to prompt for every attempt to access permissions.
-  if (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT) {
+  if (behavior == nsICookieService::BEHAVIOR_REJECT) {
     return StorageAccess::eDeny;
   }
 
   // In the absense of a window, we assume that we are first-party.
-  if (aWindow && (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
-                  sCookiesBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN)) {
+  if (aWindow && (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
+                  behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN)) {
     nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
       do_GetService(THIRDPARTYUTIL_CONTRACTID);
     MOZ_ASSERT(thirdPartyUtil);
 
     bool thirdPartyWindow = false;
     if (NS_SUCCEEDED(thirdPartyUtil->IsThirdPartyWindow(
           aWindow->GetOuterWindow(), nullptr, &thirdPartyWindow)) && thirdPartyWindow) {
       // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2899,16 +2899,26 @@ private:
   // Fills in aInfo with the tokens from the supplied autocomplete attribute.
   static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
                                                                       mozilla::dom::AutocompleteInfo& aInfo);
 
   static bool CallOnAllRemoteChildren(nsIMessageBroadcaster* aManager,
                                       CallOnRemoteChildFunction aCallback,
                                       void* aArg);
 
+  /**
+   * Gets the current cookie lifetime policy and cookie behavior for a given
+   * principal by checking with preferences and the permission manager.
+   *
+   * Used in the implementation of InternalStorageAllowedForPrincipal.
+   */
+  static void GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
+                                            uint32_t* aLifetimePolicy,
+                                            uint32_t* aBehavior);
+
   /*
    * Checks if storage for a given principal is permitted by the user's
    * preferences. If aWindow is non-null, its principal must be passed as
    * aPrincipal, and the third-party iframe and sandboxing status of the window
    * are also checked.
    *
    * Used in the implementation of StorageAllowedForWindow and
    * StorageAllowedForPrincipal.
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1371,18 +1371,16 @@ nsIDocument::nsIDocument()
     mInSyncOperationCount(0),
     mBlockDOMContentLoaded(0),
     mUseCounters(0),
     mChildDocumentUseCounters(0),
     mNotifiedPageForUseCounter(0),
     mUserHasInteracted(false)
 {
   SetIsInDocument();
-
-  PR_INIT_CLIST(&mDOMMediaQueryLists);
 }
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
   , mIsTopLevelContentDocument(false)
   , mIsContentDocument(false)
   , mSubDocuments(nullptr)
   , mFlashClassification(FlashClassification::Unclassified)
@@ -1452,17 +1450,17 @@ nsDocument::ClearAllBoxObjects()
     }
     delete mBoxObjectTable;
     mBoxObjectTable = nullptr;
   }
 }
 
 nsIDocument::~nsIDocument()
 {
-  MOZ_ASSERT(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
+  MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
              "must not have media query lists left");
 
   if (mNodeInfoManager) {
     mNodeInfoManager->DropDocumentReference();
   }
 
   if (mDocGroup) {
     mDocGroup->RemoveDocument(this);
@@ -1853,19 +1851,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     }
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
-  for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
-       l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
-    MediaQueryList *mql = static_cast<MediaQueryList*>(l);
+  for (auto mql : tmp->mDOMMediaQueryLists) {
     if (mql->HasListeners()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
       cb.NoteXPCOMChild(mql);
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
@@ -1969,22 +1965,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   if (tmp->mCSSLoader) {
     tmp->mCSSLoader->DropDocumentReference();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
   }
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
-  for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
-       l != &tmp->mDOMMediaQueryLists; ) {
-    PRCList *next = PR_NEXT_LINK(l);
-    MediaQueryList *mql = static_cast<MediaQueryList*>(l);
+  for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
+    MediaQueryList* next = mql->getNext();
     mql->Disconnect();
-    l = next;
+    mql = next;
   }
 
   tmp->mInUnlinkOrDeletion = false;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 nsresult
 nsDocument::Init()
 {
@@ -7177,18 +7171,17 @@ nsDocument::ClearBoxObjectFor(nsIContent
   }
 }
 
 already_AddRefed<MediaQueryList>
 nsIDocument::MatchMedia(const nsAString& aMediaQueryList)
 {
   RefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
 
-  // Insert the new item at the end of the linked list.
-  PR_INSERT_BEFORE(result, &mDOMMediaQueryLists);
+  mDOMMediaQueryLists.insertBack(result);
 
   return result.forget();
 }
 
 void
 nsDocument::FlushSkinBindings()
 {
   BindingManager()->FlushSkinBindings();
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -26,17 +26,16 @@
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
-#include "prclist.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/dom/DispatcherTrait.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/SegmentedVector.h"
 #include "mozilla/StyleBackendType.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
@@ -1870,18 +1869,18 @@ public:
 
   /**
    * Support for window.matchMedia()
    */
 
   already_AddRefed<mozilla::dom::MediaQueryList>
     MatchMedia(const nsAString& aMediaQueryList);
 
-  const PRCList* MediaQueryLists() const {
-    return &mDOMMediaQueryLists;
+  mozilla::LinkedList<mozilla::dom::MediaQueryList>& MediaQueryLists() {
+    return mDOMMediaQueryLists;
   }
 
   /**
    * Get the compatibility mode for this document
    */
   nsCompatibility GetCompatibilityMode() const {
     return mCompatMode;
   }
@@ -3370,17 +3369,17 @@ protected:
 
   RefPtr<mozilla::dom::XPathEvaluator> mXPathEvaluator;
 
   nsTArray<RefPtr<mozilla::dom::AnonymousContent>> mAnonymousContents;
 
   uint32_t mBlockDOMContentLoaded;
 
   // Our live MediaQueryLists
-  PRCList mDOMMediaQueryLists;
+  mozilla::LinkedList<mozilla::dom::MediaQueryList> mDOMMediaQueryLists;
 
   // Flags for use counters used directly by this document.
   std::bitset<mozilla::eUseCounter_Count> mUseCounters;
   // Flags for use counters used by any child documents of this document.
   std::bitset<mozilla::eUseCounter_Count> mChildDocumentUseCounters;
   // Flags for whether we've notified our top-level "page" of a use counter
   // for this child document.
   std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -914,20 +914,16 @@ nsCSPHashSrc::allows(enum CSPKeyword aKe
 
   rv = hasher->Update((uint8_t *)utf8_hash.get(), utf8_hash.Length());
   NS_ENSURE_SUCCESS(rv, false);
 
   nsAutoCString hash;
   rv = hasher->Finish(true, hash);
   NS_ENSURE_SUCCESS(rv, false);
 
-  // The NSS Base64 encoder automatically adds linebreaks "\r\n" every 64
-  // characters. We need to remove these so we can properly validate longer
-  // (SHA-512) base64-encoded hashes
-  hash.StripChars("\r\n");
   return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
 }
 
 bool
 nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const
 {
   return aVisitor->visitHashSrc(*this);
 }
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/file_tryAccessSessionStorage.html
@@ -0,0 +1,10 @@
+<script>
+  try {
+    sessionStorage.setItem("am_i_blocked", "nope");
+    window.parent.postMessage('sessionStorage=true', '*');
+    document.body.innerHTML += 'yes';
+  } catch (ex) {
+    window.parent.postMessage('sessionStorage=false', '*');
+    document.body.innerHTML += 'no';
+  }
+</script>
--- a/dom/tests/mochitest/localstorage/mochitest.ini
+++ b/dom/tests/mochitest/localstorage/mochitest.ini
@@ -11,16 +11,17 @@ support-files =
   frameReplace.html
   frameSlaveEqual.html
   frameSlaveNotEqual.html
   interOriginFrame.js
   interOriginTest.js
   interOriginTest2.js
   localStorageCommon.js
   frameLocalStorageSessionOnly.html
+  file_tryAccessSessionStorage.html
 
 [test_brokenUTF-16.html]
 [test_bug600307-DBOps.html]
 [test_bug746272-1.html]
 [test_bug746272-2.html]
 skip-if = os == "android" # bug 962029
 [test_cookieBlock.html]
 [test_cookieSession.html]
@@ -45,8 +46,9 @@ skip-if = toolkit == 'android' #TIMED_OU
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_localStorageQuotaSessionOnly2.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_localStorageReplace.html]
 skip-if = toolkit == 'android'
 [test_lowDeviceStorage.html]
 [test_storageConstructor.html]
 [test_localStorageSessionPrefOverride.html]
+[test_firstPartyOnlyPermission.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/test_firstPartyOnlyPermission.html
@@ -0,0 +1,62 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>first party storage permission test</title>
+
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+  TRY_ACCESS_SESSION_STORAGE =
+    'http://example.com/tests/dom/tests/mochitest/localstorage/file_tryAccessSessionStorage.html';
+
+  add_task(function*() {
+    yield SpecialPowers.pushPrefEnv({
+      set: [['network.cookie.cookieBehavior', SpecialPowers.Ci.nsICookieService.BEHAVIOR_REJECT]],
+    });
+
+    try {
+      sessionStorage.setItem("blocked", "blocked");
+      ok(false, "Shouldn't be avaliable yet");
+    } catch (ex) {
+      ok(true, "Shouldn't be avaliable yet");
+    }
+
+    yield new Promise(resolve => SpecialPowers.pushPermissions([{
+      type: 'cookie',
+      allow: SpecialPowers.Ci.nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY,
+      context: document,
+    }], resolve));
+
+    // With the permission set to ACCESS_ALLOW_FIRST_PARTY_ONLY, we should be
+    // able to run it from this iframe (as we are first party with the test
+    // runner parent document).
+    try {
+      sessionStorage.setItem("blocked", "blocked");
+      ok(true, "Should be avaliable");
+    } catch (ex) {
+      ok(false, "Should be avaliable");
+    }
+
+    // A third party iframe should not have access however.
+    yield new Promise(resolve => {
+      window.onmessage = evt => {
+        window.onmessage = null;
+        is(evt.data, "sessionStorage=false");
+        resolve();
+      };
+
+      let iframe = document.createElement('iframe');
+      iframe.setAttribute('src', TRY_ACCESS_SESSION_STORAGE);
+      document.body.appendChild(iframe);
+    });
+  });
+
+</script>
+
+</head>
+
+<body>
+
+</body>
+</html>
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -28,17 +28,16 @@
 #include "GLTextureImage.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "prlink.h"
 #include "ScopedGLHelpers.h"
 #include "SharedSurfaceGL.h"
 #include "GfxTexturesReporter.h"
-#include "TextureGarbageBin.h"
 #include "gfx2DGlue.h"
 #include "gfxPrefs.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/gfx/Logging.h"
 
 #include "OGLShaderProgram.h" // for ShaderProgramType
 
 #include "mozilla/DebugOnly.h"
@@ -942,18 +941,16 @@ GLContext::InitWithPrefixImpl(const char
 
     // TODO: Remove SurfaceCaps::any.
     if (mCaps.any) {
         mCaps.any = false;
         mCaps.color = true;
         mCaps.alpha = false;
     }
 
-    mTexGarbageBin = new TextureGarbageBin(this);
-
     MOZ_ASSERT(IsCurrent());
 
     if (ShouldSpew() && IsExtensionSupported(KHR_debug)) {
         fEnable(LOCAL_GL_DEBUG_OUTPUT);
         fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
         fDebugMessageCallback(&StaticDebugCallback, (void*)this);
         fDebugMessageControl(LOCAL_GL_DONT_CARE,
                              LOCAL_GL_DONT_CARE,
@@ -2101,19 +2098,17 @@ GLContext::MarkDestroyed()
         return;
 
     // Null these before they're naturally nulled after dtor, as we want GLContext to
     // still be alive in *their* dtors.
     mScreen = nullptr;
     mBlitHelper = nullptr;
     mReadTexImageHelper = nullptr;
 
-    if (MakeCurrent()) {
-        mTexGarbageBin->GLContextTeardown();
-    } else {
+    if (!MakeCurrent()) {
         NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
     }
 
     mSymbols.Zero();
 }
 
 #ifdef MOZ_GL_DEBUG
 /* static */ void
@@ -2383,22 +2378,16 @@ GLContext::CleanDirtyScreen()
 {
     ScopedBindFramebuffer autoFB(0);
 
     BeforeGLReadCall();
     // no-op; we just want to make sure the Read FBO is updated if it needs to be
     AfterGLReadCall();
 }
 
-void
-GLContext::EmptyTexGarbageBin()
-{
-    TexGarbageBin()->EmptyGarbage();
-}
-
 bool
 GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const
 {
     int32_t biggerDimension = std::max(aSize.width, aSize.height);
     int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize);
     return biggerDimension <= maxAllowed;
 }
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -58,17 +58,16 @@ namespace mozilla {
     namespace gl {
         class GLBlitHelper;
         class GLBlitTextureImageHelper;
         class GLContext;
         class GLLibraryEGL;
         class GLReadTexImageHelper;
         class GLScreenBuffer;
         class SharedSurface;
-        class TextureGarbageBin;
         struct SurfaceCaps;
     } // namespace gl
 
     namespace layers {
         class ColorTextureLayerProgram;
     } // namespace layers
 
     namespace widget {
@@ -3452,27 +3451,16 @@ public:
      * Useful for resizing offscreen buffers.
      */
     void ClearSafely();
 
     bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
 
     bool IsDrawingToDefaultFramebuffer();
 
-protected:
-    RefPtr<TextureGarbageBin> mTexGarbageBin;
-
-public:
-    TextureGarbageBin* TexGarbageBin() {
-        MOZ_ASSERT(mTexGarbageBin);
-        return mTexGarbageBin;
-    }
-
-    void EmptyTexGarbageBin();
-
     bool IsOffscreenSizeAllowed(const gfx::IntSize& aSize) const;
 
 protected:
     bool InitWithPrefix(const char* prefix, bool trygl);
 
 private:
     bool InitWithPrefixImpl(const char* prefix, bool trygl);
     void LoadMoreSymbols(const char* prefix, bool trygl);
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -6,17 +6,16 @@
 #include "SharedSurfaceEGL.h"
 
 #include "GLBlitHelper.h"
 #include "GLContextEGL.h"
 #include "GLLibraryEGL.h"
 #include "GLReadTexImageHelper.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "SharedSurface.h"
-#include "TextureGarbageBin.h"
 
 namespace mozilla {
 namespace gl {
 
 /*static*/ UniquePtr<SharedSurface_EGLImage>
 SharedSurface_EGLImage::Create(GLContext* prodGL,
                                const GLFormats& formats,
                                const gfx::IntSize& size,
--- a/gfx/gl/SharedSurfaceEGL.h
+++ b/gfx/gl/SharedSurfaceEGL.h
@@ -10,17 +10,16 @@
 #include "mozilla/Mutex.h"
 #include "SharedSurface.h"
 
 namespace mozilla {
 namespace gl {
 
 class GLContext;
 class GLLibraryEGL;
-class TextureGarbageBin;
 
 class SharedSurface_EGLImage
     : public SharedSurface
 {
 public:
     static UniquePtr<SharedSurface_EGLImage> Create(GLContext* prodGL,
                                                     const GLFormats& formats,
                                                     const gfx::IntSize& size,
deleted file mode 100644
--- a/gfx/gl/TextureGarbageBin.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=8 sts=4 et sw=4 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "TextureGarbageBin.h"
-#include "GLContext.h"
-
-using namespace mozilla;
-using namespace mozilla::gl;
-
-void
-TextureGarbageBin::GLContextTeardown()
-{
-    EmptyGarbage();
-
-    MutexAutoLock lock(mMutex);
-    mGL = nullptr;
-}
-
-void
-TextureGarbageBin::Trash(GLuint tex)
-{
-    MutexAutoLock lock(mMutex);
-    if (!mGL)
-        return;
-
-    mGarbageTextures.push(tex);
-}
-
-void
-TextureGarbageBin::EmptyGarbage()
-{
-    MutexAutoLock lock(mMutex);
-    if (!mGL)
-        return;
-
-    MOZ_RELEASE_ASSERT(mGL->IsCurrent(), "GFX: GL context not current.");
-    while (!mGarbageTextures.empty()) {
-        GLuint tex = mGarbageTextures.top();
-        mGarbageTextures.pop();
-        mGL->fDeleteTextures(1, &tex);
-    }
-}
deleted file mode 100644
--- a/gfx/gl/TextureGarbageBin.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=8 sts=4 et sw=4 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef TEXTUREGARBAGEBIN_H_
-#define TEXTUREGARBAGEBIN_H_
-
-#include <stack>
-
-#include "mozilla/Mutex.h"
-#include "nsISupportsImpl.h"
-
-#include "GLContextTypes.h"
-
-namespace mozilla {
-namespace gl {
-
-class TextureGarbageBin final {
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureGarbageBin)
-
-private:
-    // Private destructor, to discourage deletion outside of Release():
-    ~TextureGarbageBin()
-    {
-    }
-
-    GLContext* mGL;
-    Mutex mMutex;
-    std::stack<GLuint> mGarbageTextures;
-
-public:
-    explicit TextureGarbageBin(GLContext* gl)
-        : mGL(gl)
-        , mMutex("TextureGarbageBin mutex")
-    {}
-
-    void GLContextTeardown();
-    void Trash(GLuint tex);
-    void EmptyGarbage();
-};
-
-} // namespace gl
-} // namespace mozilla
-
-#endif // TEXTUREGARBAGEBIN_H_
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -46,17 +46,16 @@ EXPORTS += [
     'GLTypes.h',
     'GLUploadHelpers.h',
     'HeapCopyOfStackArray.h',
     'ScopedGLHelpers.h',
     'SharedSurface.h',
     'SharedSurfaceEGL.h',
     'SharedSurfaceGL.h',
     'SurfaceTypes.h',
-    'TextureGarbageBin.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS += [
         'GLContextGLX.h',
         'GLXLibrary.h',
     ]
 
@@ -135,17 +134,16 @@ UNIFIED_SOURCES += [
     'GLScreenBuffer.cpp',
     'GLTextureImage.cpp',
     'GLUploadHelpers.cpp',
     'ScopedGLHelpers.cpp',
     'SharedSurface.cpp',
     'SharedSurfaceEGL.cpp',
     'SharedSurfaceGL.cpp',
     'SurfaceTypes.cpp',
-    'TextureGarbageBin.cpp',
     'TextureImageEGL.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_D3DCOMPILER_VISTA_DLL']:
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -850,17 +850,17 @@ static const uint32_t JSCLASS_FOREGROUND
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
 static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
-    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46;
+    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 37;
 
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                              \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -232,22 +232,16 @@ DataViewObject::constructSameCompartment
 // Our DataViewObject implementation doesn't support a DataView in
 // compartment A backed by an ArrayBuffer in compartment B. So in this case,
 // we create the DataView in B (!) and return a cross-compartment wrapper.
 //
 // Extra twist: the spec says the new DataView's [[Prototype]] must be
 // A's DataView.prototype. So even though we're creating the DataView in B,
 // its [[Prototype]] must be (a cross-compartment wrapper for) the
 // DataView.prototype in A.
-//
-// As if this were not confusing enough, the way we actually do this is also
-// tricky. We call compartment A's createDataViewForThis method, passing it
-// bufobj as `this`. That calls ArrayBufferObject::createDataViewForThis(),
-// which uses CallNonGenericMethod to switch to compartment B so that
-// the new DataView is created there.
 bool
 DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args)
 {
     MOZ_ASSERT(args.isConstructing());
     MOZ_ASSERT(bufobj->is<WrapperObject>());
 
     RootedObject unwrapped(cx, CheckedUnwrap(bufobj));
     if (!unwrapped) {
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -52,17 +52,16 @@ class MOZ_STACK_CLASS BytecodeCompiler
 
     ScriptSourceObject* sourceObjectPtr() const;
     SourceCompressionTask* sourceCompressionTask() const;
 
   private:
     JSScript* compileScript(HandleObject environment, SharedContext* sc);
     bool checkLength();
     bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
-    bool enqueueOffThreadSourceCompression();
     bool canLazilyParse();
     bool createParser();
     bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing());
 
     // If toString{Start,End} are not explicitly passed, assume the script's
     // offsets in the source used to parse it are the same as what should be
     // used to compute its Function.prototype.toString() value.
     bool createScript();
@@ -78,17 +77,16 @@ class MOZ_STACK_CLASS BytecodeCompiler
     LifoAlloc& alloc;
     const ReadOnlyCompileOptions& options;
     SourceBufferHolder& sourceBuffer;
 
     RootedScope enclosingScope;
 
     RootedScriptSource sourceObject;
     ScriptSource* scriptSource;
-    SourceCompressionTask* sourceCompressionTask_;
 
     Maybe<UsedNameTracker> usedNames;
     Maybe<Parser<SyntaxParseHandler>> syntaxParser;
     Maybe<Parser<FullParseHandler>> parser;
 
     Directives directives;
     TokenStream::Position startPosition;
 
@@ -168,17 +166,16 @@ BytecodeCompiler::BytecodeCompiler(JSCon
   : keepAtoms(cx),
     cx(cx),
     alloc(alloc),
     options(options),
     sourceBuffer(sourceBuffer),
     enclosingScope(cx, enclosingScope),
     sourceObject(cx),
     scriptSource(nullptr),
-    sourceCompressionTask_(nullptr),
     directives(options.strictOption),
     startPosition(keepAtoms),
     script(cx)
 {
     MOZ_ASSERT(sourceBuffer.get());
 }
 
 bool
@@ -215,50 +212,16 @@ BytecodeCompiler::createScriptSource(con
             return false;
         }
     }
 
     return true;
 }
 
 bool
-BytecodeCompiler::enqueueOffThreadSourceCompression()
-{
-    // There are several cases where source compression is not a good idea:
-    //  - If the script is tiny, then compression will save little or no space.
-    //  - If there is only one core, then compression will contend with JS
-    //    execution (which hurts benchmarketing).
-    //
-    // Otherwise, enqueue a compression task to be processed when a major
-    // GC is requested.
-
-    if (!scriptSource->hasUncompressedSource())
-        return true;
-
-    bool canCompressOffThread =
-        HelperThreadState().cpuCount > 1 &&
-        HelperThreadState().threadCount >= 2 &&
-        CanUseExtraThreads();
-    const size_t TINY_SCRIPT = 256;
-    if (TINY_SCRIPT <= sourceBuffer.length() && canCompressOffThread) {
-        // Heap allocate the task. It will be freed upon compression
-        // completing in AttachFinishedCompressedSources.
-        SourceCompressionTask* task = cx->new_<SourceCompressionTask>(cx->runtime(),
-                                                                      scriptSource);
-        if (!task)
-            return false;
-        if (!EnqueueOffThreadCompression(cx, task))
-            return false;
-        sourceCompressionTask_ = task;
-    }
-
-    return true;
-}
-
-bool
 BytecodeCompiler::canLazilyParse()
 {
     return options.canLazilyParse &&
            !(enclosingScope && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) &&
            !cx->compartment()->behaviors().disableLazyParsing() &&
            !cx->compartment()->behaviors().discardSource() &&
            !options.sourceIsLazy &&
            !cx->lcovEnabled();
@@ -411,17 +374,17 @@ BytecodeCompiler::compileScript(HandleOb
         usedNames->reset();
     }
 
     // We have just finished parsing the source. Inform the source so that we
     // can compute statistics (e.g. how much time our functions remain lazy).
     script->scriptSource()->recordParseEnded();
 
     // Enqueue an off-thread source compression task after finishing parsing.
-    if (!enqueueOffThreadSourceCompression())
+    if (!scriptSource->tryCompressOffThread(cx))
         return nullptr;
 
     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
 
     return script;
 }
 
 JSScript*
@@ -476,17 +439,17 @@ BytecodeCompiler::compileModule()
 
     RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module));
     if (!env)
         return nullptr;
 
     module->setInitialEnvironment(env);
 
     // Enqueue an off-thread source compression task after finishing parsing.
-    if (!enqueueOffThreadSourceCompression())
+    if (!scriptSource->tryCompressOffThread(cx))
         return nullptr;
 
     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
     return module;
 }
 
 // Compile a standalone JS function, which might appear as the value of an
 // event handler attribute in an HTML <INPUT> tag, or in a Function()
@@ -532,34 +495,28 @@ BytecodeCompiler::compileStandaloneFunct
         fun.set(fn->pn_funbox->function());
         MOZ_ASSERT(IsAsmJSModule(fun));
     }
 
     if (!NameFunctions(cx, fn))
         return false;
 
     // Enqueue an off-thread source compression task after finishing parsing.
-    if (!enqueueOffThreadSourceCompression())
+    if (!scriptSource->tryCompressOffThread(cx))
         return false;
 
     return true;
 }
 
 ScriptSourceObject*
 BytecodeCompiler::sourceObjectPtr() const
 {
     return sourceObject.get();
 }
 
-SourceCompressionTask*
-BytecodeCompiler::sourceCompressionTask() const
-{
-    return sourceCompressionTask_;
-}
-
 ScriptSourceObject*
 frontend::CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
                                    const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
 {
     ScriptSource* ss = cx->new_<ScriptSource>();
     if (!ss)
         return nullptr;
     ScriptSourceHolder ssHolder(ss);
@@ -601,78 +558,70 @@ frontend::CreateScriptSourceObject(JSCon
 // Whatever happens to the top-level script compilation (even if it fails and
 // returns null), we must finish initializing the SSO.  This is because there
 // may be valid inner scripts observable by the debugger which reference the
 // partially-initialized SSO.
 class MOZ_STACK_CLASS AutoInitializeSourceObject
 {
     BytecodeCompiler& compiler_;
     ScriptSourceObject** sourceObjectOut_;
-    SourceCompressionTask** sourceCompressionTaskOut_;
 
   public:
     AutoInitializeSourceObject(BytecodeCompiler& compiler,
-                               ScriptSourceObject** sourceObjectOut,
-                               SourceCompressionTask** sourceCompressionTaskOut)
+                               ScriptSourceObject** sourceObjectOut)
       : compiler_(compiler),
-        sourceObjectOut_(sourceObjectOut),
-        sourceCompressionTaskOut_(sourceCompressionTaskOut)
+        sourceObjectOut_(sourceObjectOut)
     { }
 
     ~AutoInitializeSourceObject() {
         if (sourceObjectOut_)
             *sourceObjectOut_ = compiler_.sourceObjectPtr();
-        if (sourceCompressionTaskOut_)
-            *sourceCompressionTaskOut_ = compiler_.sourceCompressionTask();
     }
 };
 
 JSScript*
 frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
                               const ReadOnlyCompileOptions& options,
                               SourceBufferHolder& srcBuf,
-                              ScriptSourceObject** sourceObjectOut,
-                              SourceCompressionTask** sourceCompressionTaskOut)
+                              ScriptSourceObject** sourceObjectOut)
 {
     MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
-    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
+    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     return compiler.compileGlobalScript(scopeKind);
 }
 
 JSScript*
 frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
                             HandleObject environment, HandleScope enclosingScope,
                             const ReadOnlyCompileOptions& options,
                             SourceBufferHolder& srcBuf,
-                            ScriptSourceObject** sourceObjectOut,
-                            SourceCompressionTask** sourceCompressionTaskOut)
+                            ScriptSourceObject** sourceObjectOut)
 {
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
-    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
+    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     return compiler.compileEvalScript(environment, enclosingScope);
 }
 
 ModuleObject*
 frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
                         SourceBufferHolder& srcBuf, LifoAlloc& alloc,
-                        ScriptSourceObject** sourceObjectOut,
-                        SourceCompressionTask** sourceCompressionTaskOut)
+                        ScriptSourceObject** sourceObjectOut)
 {
     MOZ_ASSERT(srcBuf.get());
     MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
 
     CompileOptions options(cx, optionsInput);
     options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
     options.setIsRunOnce(true);
     options.allowHTMLComments = false;
 
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
-    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
+    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     return compiler.compileModule();
 }
 
 ModuleObject*
 frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                         SourceBufferHolder& srcBuf)
 {
     if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -18,48 +18,44 @@
 class JSLinearString;
 
 namespace js {
 
 class LazyScript;
 class LifoAlloc;
 class ModuleObject;
 class ScriptSourceObject;
-class SourceCompressionTask;
 
 namespace frontend {
 
 class TokenStream;
 class FunctionBox;
 class ParseNode;
 
 JSScript*
 CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
                     const ReadOnlyCompileOptions& options,
                     SourceBufferHolder& srcBuf,
-                    ScriptSourceObject** sourceObjectOut = nullptr,
-                    SourceCompressionTask** sourceCompressionTaskOut = nullptr);
+                    ScriptSourceObject** sourceObjectOut = nullptr);
 
 JSScript*
 CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
                   HandleObject scopeChain, HandleScope enclosingScope,
                   const ReadOnlyCompileOptions& options,
                   SourceBufferHolder& srcBuf,
-                  ScriptSourceObject** sourceObjectOut = nullptr,
-                  SourceCompressionTask** sourceCompressionTaskOut = nullptr);
+                  ScriptSourceObject** sourceObjectOut = nullptr);
 
 ModuleObject*
 CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf);
 
 ModuleObject*
 CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, LifoAlloc& alloc,
-              ScriptSourceObject** sourceObjectOut = nullptr,
-              SourceCompressionTask** sourceCompressionTaskOut = nullptr);
+              ScriptSourceObject** sourceObjectOut = nullptr);
 
 MOZ_MUST_USE bool
 CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
 
 //
 // Compile a single function. The source in srcBuf must match the ECMA-262
 // FunctionExpression production.
 //
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -5048,32 +5048,28 @@ SweepMiscTask::run()
 /* virtual */ void
 SweepCompressionTasksTask::run()
 {
     AutoLockHelperThreadState lock;
 
     // Attach finished compression tasks.
     auto& finished = HelperThreadState().compressionFinishedList(lock);
     for (size_t i = 0; i < finished.length(); i++) {
-        SourceCompressionTask* task = finished[i];
-        if (task->runtimeMatches(runtime())) {
+        if (finished[i]->runtimeMatches(runtime())) {
+            UniquePtr<SourceCompressionTask> task(Move(finished[i]));
             HelperThreadState().remove(finished, &i);
             task->complete();
-            js_delete(task);
         }
     }
 
     // Sweep pending tasks that are holding onto should-be-dead ScriptSources.
     auto& pending = HelperThreadState().compressionPendingList(lock);
     for (size_t i = 0; i < pending.length(); i++) {
-        SourceCompressionTask* task = pending[i];
-        if (task->shouldCancel()) {
+        if (pending[i]->shouldCancel())
             HelperThreadState().remove(pending, &i);
-            js_delete(task);
-        }
     }
 }
 
 void
 GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
 {
     if (!task.startWithLockHeld(locked)) {
         AutoUnlockHelperThreadState unlock(locked);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1816,16 +1816,60 @@ ScriptSource::setSource(JSContext* cx,
 
 void
 ScriptSource::setSource(SharedImmutableTwoByteString&& string)
 {
     MOZ_ASSERT(data.is<Missing>());
     data = SourceType(Uncompressed(mozilla::Move(string)));
 }
 
+bool
+ScriptSource::tryCompressOffThread(JSContext* cx)
+{
+    if (!data.is<Uncompressed>())
+        return true;
+
+    // There are several cases where source compression is not a good idea:
+    //  - If the script is tiny, then compression will save little or no space.
+    //  - If there is only one core, then compression will contend with JS
+    //    execution (which hurts benchmarketing).
+    //
+    // Otherwise, enqueue a compression task to be processed when a major
+    // GC is requested.
+
+    bool canCompressOffThread =
+        HelperThreadState().cpuCount > 1 &&
+        HelperThreadState().threadCount >= 2 &&
+        CanUseExtraThreads();
+    const size_t TINY_SCRIPT = 256;
+    if (TINY_SCRIPT > length() || !canCompressOffThread)
+        return true;
+
+    // The SourceCompressionTask needs to record the major GC number for
+    // scheduling. If we're parsing off thread, this number is not safe to
+    // access.
+    //
+    // When parsing on the main thread, the attempts made to compress off
+    // thread in BytecodeCompiler will succeed.
+    //
+    // When parsing off-thread, the above attempts will fail and the attempt
+    // made in ParseTask::finish will succeed.
+    if (!CurrentThreadCanAccessRuntime(cx->runtime()))
+        return true;
+
+    // Heap allocate the task. It will be freed upon compression
+    // completing in AttachFinishedCompressedSources.
+    auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), this);
+    if (!task) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+    return EnqueueOffThreadCompression(cx, Move(task));
+}
+
 MOZ_MUST_USE bool
 ScriptSource::setCompressedSource(JSContext* cx,
                                   mozilla::UniquePtr<char[], JS::FreePolicy>&& raw,
                                   size_t rawLength,
                                   size_t sourceLength)
 {
     MOZ_ASSERT(raw);
     auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -568,16 +568,18 @@ class ScriptSource
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 JS::ScriptSourceInfo* info) const;
 
     MOZ_MUST_USE bool setSource(JSContext* cx,
                                 UniqueTwoByteChars&& source,
                                 size_t length);
     void setSource(SharedImmutableTwoByteString&& string);
 
+    MOZ_MUST_USE bool tryCompressOffThread(JSContext* cx);
+
     MOZ_MUST_USE bool setCompressedSource(JSContext* cx,
                                           UniqueChars&& raw,
                                           size_t rawLength,
                                           size_t sourceLength);
     void setCompressedSource(SharedImmutableString&& raw, size_t sourceLength);
 
     // XDR handling
     template <XDRMode mode>
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/constructor-buffer-sequence.js
@@ -0,0 +1,230 @@
+// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
+
+// Ensure the various error conditions are tested in the correct order.
+
+const otherGlobal = newGlobal();
+
+function* createBuffers(lengths = [0, 8]) {
+    for (let length of lengths) {
+        let buffer = new ArrayBuffer(length);
+        yield {buffer, detach: () => detachArrayBuffer(buffer)};
+    }
+
+    for (let length of lengths) {
+        let buffer = new otherGlobal.ArrayBuffer(length);
+        yield {buffer, detach: () => otherGlobal.detachArrayBuffer(buffer)};
+    }
+}
+
+const poisonedValue = new Proxy({}, new Proxy({}, {
+    get() {
+        // Throws an exception when any proxy trap is invoked.
+        throw new Error("Poisoned Value");
+    }
+}));
+
+class ExpectedError extends Error { }
+
+function ConstructorWithThrowingPrototype(detach) {
+    return Object.defineProperty(function(){}.bind(null), "prototype", {
+        get() {
+            if (detach)
+                detach();
+            throw new ExpectedError();
+        }
+    });
+}
+
+function ValueThrowing(detach) {
+    return {
+        valueOf() {
+            if (detach)
+                detach();
+            throw new ExpectedError();
+        }
+    };
+}
+
+function ValueReturning(value, detach) {
+    return {
+        valueOf() {
+            if (detach)
+                detach();
+            return value;
+        }
+    };
+}
+
+// Ensure step 4 |AllocateTypedArray| is executed before step 6 |ToIndex(byteOffset)|.
+for (let {buffer} of createBuffers()) {
+    let constructor = ConstructorWithThrowingPrototype();
+
+    assertThrowsInstanceOf(() =>
+        Reflect.construct(Int32Array, [buffer, poisonedValue, 0], constructor), ExpectedError);
+}
+
+// Ensure step 4 |AllocateTypedArray| is executed before step 9 |IsDetachedBuffer(buffer)|.
+for (let {buffer, detach} of createBuffers()) {
+    let constructor = ConstructorWithThrowingPrototype();
+
+    detach();
+    assertThrowsInstanceOf(() =>
+        Reflect.construct(Int32Array, [buffer, 0, 0], constructor), ExpectedError);
+}
+
+// Ensure step 4 |AllocateTypedArray| is executed before step 9 |IsDetachedBuffer(buffer)|.
+// - Variant: Detach buffer dynamically.
+for (let {buffer, detach} of createBuffers()) {
+    let constructor = ConstructorWithThrowingPrototype(detach);
+
+    assertThrowsInstanceOf(() =>
+        Reflect.construct(Int32Array, [buffer, 0, 0], constructor), ExpectedError);
+}
+
+// Ensure step 4 |AllocateTypedArray| is executed before step 8.a |ToIndex(length)|.
+for (let {buffer} of createBuffers()) {
+    let constructor = ConstructorWithThrowingPrototype();
+
+    assertThrowsInstanceOf(() =>
+        Reflect.construct(Int32Array, [buffer, 0, poisonedValue], constructor), ExpectedError);
+}
+
+// Ensure step 6 |ToIndex(byteOffset)| is executed before step 9 |IsDetachedBuffer(buffer)|.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = ValueThrowing();
+
+    detach();
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), ExpectedError);
+}
+
+// Ensure step 6 |ToIndex(byteOffset)| is executed before step 9 |IsDetachedBuffer(buffer)|.
+// - Variant: Detach buffer dynamically.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = ValueThrowing(detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), ExpectedError);
+}
+
+// Ensure step 6 |ToIndex(byteOffset)| is executed before step 8.a |ToIndex(length)|.
+for (let {buffer} of createBuffers()) {
+    let byteOffset = ValueThrowing();
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, poisonedValue), ExpectedError);
+}
+
+// Ensure step 7 |offset modulo elementSize ≠ 0| is executed before step 9 |IsDetachedBuffer(buffer)|.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 1;
+
+    detach();
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), RangeError);
+}
+
+// Ensure step 7 |offset modulo elementSize ≠ 0| is executed before step 9 |IsDetachedBuffer(buffer)|.
+// - Variant: Detach buffer dynamically.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = ValueReturning(1, detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), RangeError);
+}
+
+// Ensure step 7 |offset modulo elementSize ≠ 0| is executed before step 8.a |ToIndex(length)|.
+for (let {buffer} of createBuffers()) {
+    assertThrowsInstanceOf(() => new Int32Array(buffer, 1, poisonedValue), RangeError);
+}
+
+// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 0;
+    let length = ValueThrowing();
+
+    detach();
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError);
+}
+
+// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|.
+// - Variant: Detach buffer dynamically (1).
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = ValueReturning(0, detach);
+    let length = ValueThrowing();
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError);
+}
+
+// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|.
+// - Variant: Detach buffer dynamically (2).
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 0;
+    let length = ValueThrowing(detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError);
+}
+
+// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.a |bufferByteLength modulo elementSize ≠ 0|.
+for (let {buffer, detach} of createBuffers([1, 9])) {
+    let byteOffset = 0;
+
+    detach();
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
+}
+
+// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.a |bufferByteLength modulo elementSize ≠ 0|.
+// - Variant: Detach buffer dynamically.
+for (let {buffer, detach} of createBuffers([1, 9])) {
+    let byteOffset = ValueReturning(0, detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
+}
+
+// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.c |newByteLength < 0|.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 64;
+
+    detach();
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
+}
+
+// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.c |newByteLength < 0|.
+// - Variant: Detach buffer dynamically.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = ValueReturning(64, detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
+}
+
+// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 12.b |offset+newByteLength > bufferByteLength|.
+// - Case A: The given byteOffset is too large.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 64;
+    let length = ValueReturning(0, detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
+}
+
+// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 12.b |offset+newByteLength > bufferByteLength|.
+// - Case B: The given length is too large.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 0;
+    let length = ValueReturning(64, detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
+}
+
+// Ensure we handle the case when ToIndex(byteOffset) detaches the array buffer.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = ValueReturning(0, detach);
+    let length = 0;
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
+}
+
+// Ensure we handle the case when ToIndex(length) detaches the array buffer.
+for (let {buffer, detach} of createBuffers()) {
+    let byteOffset = 0;
+    let length = ValueReturning(0, detach);
+
+    assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -249,21 +249,16 @@ class ArrayBufferObject : public ArrayBu
     static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
                                      HandleObject proto = nullptr,
                                      NewObjectKind newKind = GenericObject);
 
     // Create an ArrayBufferObject that is safely finalizable and can later be
     // initialize()d to become a real, content-visible ArrayBufferObject.
     static ArrayBufferObject* createEmpty(JSContext* cx);
 
-    template<typename T>
-    static bool createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args);
-    template<typename T>
-    static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp);
-
     static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
                          Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
                          uint32_t count);
 
     static void trace(JSTracer* trc, JSObject* obj);
     static void objectMoved(JSObject* obj, const JSObject* old);
 
     static BufferContents externalizeContents(JSContext* cx,
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -67,30 +67,16 @@ class GlobalObject : public NativeObject
      */
     static const unsigned STANDARD_CLASS_SLOTS = JSProto_LIMIT * 2;
 
     enum : unsigned {
         /* Various function values needed by the engine. */
         EVAL = APPLICATION_SLOTS + STANDARD_CLASS_SLOTS,
         THROWTYPEERROR,
 
-        /*
-         * Instances of the internal createArrayFromBuffer function used by the
-         * typed array code, one per typed array element type.
-         */
-        FROM_BUFFER_UINT8,
-        FROM_BUFFER_INT8,
-        FROM_BUFFER_UINT16,
-        FROM_BUFFER_INT16,
-        FROM_BUFFER_UINT32,
-        FROM_BUFFER_INT32,
-        FROM_BUFFER_FLOAT32,
-        FROM_BUFFER_FLOAT64,
-        FROM_BUFFER_UINT8CLAMPED,
-
         /* One-off properties stored after slots for built-ins. */
         LEXICAL_ENVIRONMENT,
         EMPTY_GLOBAL_SCOPE,
         ITERATOR_PROTO,
         ARRAY_ITERATOR_PROTO,
         STRING_ITERATOR_PROTO,
         LEGACY_GENERATOR_OBJECT_PROTO,
         STAR_GENERATOR_OBJECT_PROTO,
@@ -255,31 +241,16 @@ class GlobalObject : public NativeObject
     }
     bool errorClassesInitialized() const {
         return classIsInitialized(JSProto_Error);
     }
     bool dataViewClassInitialized() const {
         return classIsInitialized(JSProto_DataView);
     }
 
-    Value createArrayFromBufferHelper(uint32_t slot) const {
-        MOZ_ASSERT(FROM_BUFFER_UINT8 <= slot && slot <= FROM_BUFFER_UINT8CLAMPED);
-        return getSlot(slot);
-    }
-
-    void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun) {
-        MOZ_ASSERT(getSlotRef(slot).isUndefined());
-        setSlot(slot, ObjectValue(*fun));
-    }
-
-  public:
-    template<typename T>
-    inline void setCreateArrayFromBuffer(Handle<JSFunction*> fun);
-
-  private:
     // Disallow use of unqualified JSObject::create in GlobalObject.
     static GlobalObject* create(...) = delete;
 
     friend struct ::JSRuntime;
     static GlobalObject* createInternal(JSContext* cx, const Class* clasp);
 
   public:
     static GlobalObject*
@@ -774,19 +745,16 @@ class GlobalObject : public NativeObject
 
     JSObject* getThrowTypeError() const {
         const Value v = getReservedSlot(THROWTYPEERROR);
         MOZ_ASSERT(v.isObject(),
                    "attempting to access [[ThrowTypeError]] too early");
         return &v.toObject();
     }
 
-    template<typename T>
-    inline Value createArrayFromBuffer() const;
-
     static bool isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global);
 
     // Warn about use of the deprecated watch/unwatch functions in the global
     // in which |obj| was created, if no prior warning was given.
     static bool warnOnceAboutWatch(JSContext* cx, HandleObject obj) {
         // Temporarily disabled until we've provided a watch/unwatch workaround for
         // debuggers like Firebug (bug 934669).
         //return warnOnceAbout(cx, obj, WARN_WATCH_DEPRECATED, JSMSG_OBJECT_WATCH_DEPRECATED);
@@ -884,142 +852,16 @@ class GlobalObject : public NativeObject
     }
 
     // Returns either this global's star-generator function prototype, or null
     // if that object was never created.  Dodgy; for use only in also-dodgy
     // GlobalHelperThreadState::mergeParseTaskCompartment().
     JSObject* getStarGeneratorFunctionPrototype();
 };
 
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<uint8_t>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_UINT8);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<int8_t>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_INT8);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<uint16_t>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_UINT16);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<int16_t>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_INT16);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<uint32_t>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_UINT32);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<int32_t>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_INT32);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<float>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_FLOAT32);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<double>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_FLOAT64);
-}
-
-template<>
-inline Value
-GlobalObject::createArrayFromBuffer<uint8_clamped>() const
-{
-    return createArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED);
-}
-
 /*
  * Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
  * non-configurable, and non-writable; and define proto.constructor = ctor as
  * non-enumerable but configurable and writable.
  */
 extern bool
 LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto,
                             unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY,
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -296,29 +296,29 @@ static const JSClass parseTaskGlobalClas
 
 ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
                      const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : kind(kind), options(cx), chars(chars), length(length),
     alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     parseGlobal(parseGlobal),
     callback(callback), callbackData(callbackData),
-    script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr),
+    script(nullptr), sourceObject(nullptr),
     overRecursed(false), outOfMemory(false)
 {
 }
 
 ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
                      JS::TranscodeBuffer& buffer, size_t cursor,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : kind(kind), options(cx), buffer(&buffer), cursor(cursor),
     alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     parseGlobal(parseGlobal),
     callback(callback), callbackData(callbackData),
-    script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr),
+    script(nullptr), sourceObject(nullptr),
     overRecursed(false), outOfMemory(false)
 {
 }
 
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
 {
     if (!this->options.copy(cx, options))
@@ -335,18 +335,18 @@ ParseTask::activate(JSRuntime* rt)
 
 bool
 ParseTask::finish(JSContext* cx)
 {
     if (sourceObject) {
         RootedScriptSource sso(cx, sourceObject);
         if (!ScriptSourceObject::initFromOptions(cx, sso, options))
             return false;
-        if (sourceCompressionTask)
-            sourceCompressionTask->fixupMajorGCNumber(cx->runtime());
+        if (!sso->source()->tryCompressOffThread(cx))
+            return false;
     }
 
     return true;
 }
 
 ParseTask::~ParseTask()
 {
     for (size_t i = 0; i < errors.length(); i++)
@@ -380,34 +380,32 @@ ScriptParseTask::ScriptParseTask(JSConte
 }
 
 void
 ScriptParseTask::parse(JSContext* cx)
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
     script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
                                            options, srcBuf,
-                                           /* sourceObjectOut = */ &sourceObject,
-                                           /* sourceCompressionTaskOut = */ &sourceCompressionTask);
+                                           /* sourceObjectOut = */ &sourceObject);
 }
 
 ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
                                  const char16_t* chars, size_t length,
                                  JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::Module, cx, parseGlobal, chars, length, callback,
               callbackData)
 {
 }
 
 void
 ModuleParseTask::parse(JSContext* cx)
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
-    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject,
-                                                   &sourceCompressionTask);
+    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
     if (module)
         script = module->script();
 }
 
 ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
                                    JS::TranscodeBuffer& buffer, size_t cursor,
                                    JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::ScriptDecode, cx, parseGlobal,
@@ -1118,23 +1116,23 @@ GlobalHelperThreadState::startHandlingCo
         notifyOne(PRODUCER, lock);
 }
 
 void
 GlobalHelperThreadState::scheduleCompressionTasks(const AutoLockHelperThreadState& lock)
 {
     auto& pending = compressionPendingList(lock);
     auto& worklist = compressionWorklist(lock);
-    MOZ_ASSERT(worklist.capacity() >= pending.length());
 
     for (size_t i = 0; i < pending.length(); i++) {
-        SourceCompressionTask* task = pending[i];
-        if (task->shouldStart()) {
+        if (pending[i]->shouldStart()) {
+            // OOMing during appending results in the task not being scheduled
+            // and deleted.
+            Unused << worklist.append(Move(pending[i]));
             remove(pending, &i);
-            worklist.infallibleAppend(task);
         }
     }
 }
 
 bool
 GlobalHelperThreadState::canStartGCHelperTask(const AutoLockHelperThreadState& lock)
 {
     return !gcHelperWorklist(lock).empty() &&
@@ -1725,73 +1723,67 @@ HelperThread::handleParseWorkload(AutoLo
 }
 
 void
 HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
     MOZ_ASSERT(idle());
 
-    currentTask.emplace(HelperThreadState().compressionWorklist(locked).popCopy());
-    SourceCompressionTask* task = compressionTask();
+    UniquePtr<SourceCompressionTask> task;
+    {
+        auto& worklist = HelperThreadState().compressionWorklist(locked);
+        task = Move(worklist.back());
+        worklist.popBack();
+        currentTask.emplace(task.get());
+    }
 
     {
         AutoUnlockHelperThreadState unlock(locked);
 
         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
         AutoTraceLog logCompile(logger, TraceLogger_CompressSource);
 
         task->work();
     }
 
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!HelperThreadState().compressionFinishedList(locked).append(task))
+        if (!HelperThreadState().compressionFinishedList(locked).append(Move(task)))
             oomUnsafe.crash("handleCompressionWorkload");
     }
 
     currentTask.reset();
 
     // Notify the active thread in case it is waiting for the compression to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 bool
-js::EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task)
+js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task)
 {
     AutoLockHelperThreadState lock;
 
     auto& pending = HelperThreadState().compressionPendingList(lock);
-    auto& worklist = HelperThreadState().compressionWorklist(lock);
-    if (!pending.append(task)) {
+    if (!pending.append(Move(task))) {
         if (!cx->helperThread())
             ReportOutOfMemory(cx);
-        js_delete(task);
-        return false;
-    }
-    if (!worklist.reserve(pending.length())) {
-        if (!cx->helperThread())
-            ReportOutOfMemory(cx);
-        pending.popBack();
         return false;
     }
 
     return true;
 }
 
 template <typename T>
 static void
 ClearCompressionTaskList(T& list, JSRuntime* runtime)
 {
     for (size_t i = 0; i < list.length(); i++) {
-        SourceCompressionTask* task = list[i];
-        if (task->runtimeMatches(runtime)) {
+        if (list[i]->runtimeMatches(runtime))
             HelperThreadState().remove(list, &i);
-            js_delete(task);
-        }
     }
 }
 
 void
 js::CancelOffThreadCompressions(JSRuntime* runtime)
 {
     AutoLockHelperThreadState lock;
 
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -67,17 +67,17 @@ class GlobalHelperThreadState
     // May be accessed without locking.
     size_t cpuCount;
 
     // Number of threads to create. May be accessed without locking.
     size_t threadCount;
 
     typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
     typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
-    typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
+    typedef Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy> SourceCompressionTaskVector;
     typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector;
     typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
     typedef Vector<PromiseTask*, 0, SystemAllocPolicy> PromiseTaskVector;
 
     // List of available threads, or null if the thread state has not been initialized.
     using HelperThreadVector = Vector<HelperThread, 0, SystemAllocPolicy>;
     UniquePtr<HelperThreadVector> threads;
 
@@ -160,17 +160,20 @@ class GlobalHelperThreadState
               mozilla::TimeDuration timeout = mozilla::TimeDuration::Forever());
     void notifyAll(CondVar which, const AutoLockHelperThreadState&);
     void notifyOne(CondVar which, const AutoLockHelperThreadState&);
 
     // Helper method for removing items from the vectors below while iterating over them.
     template <typename T>
     void remove(T& vector, size_t* index)
     {
-        vector[(*index)--] = vector.back();
+        // Self-moving is undefined behavior.
+        if (*index != vector.length() - 1)
+            vector[*index] = mozilla::Move(vector.back());
+        (*index)--;
         vector.popBack();
     }
 
     IonBuilderVector& ionWorklist(const AutoLockHelperThreadState&) {
         return ionWorklist_;
     }
     IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) {
         return ionFinishedList_;
@@ -545,17 +548,17 @@ EnqueuePendingParseTasksAfterGC(JSRuntim
 struct AutoEnqueuePendingParseTasksAfterGC {
     const gc::GCRuntime& gc_;
     explicit AutoEnqueuePendingParseTasksAfterGC(const gc::GCRuntime& gc) : gc_(gc) {}
     ~AutoEnqueuePendingParseTasksAfterGC();
 };
 
 // Enqueue a compression job to be processed if there's a major GC.
 bool
-EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task);
+EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task);
 
 // Cancel all scheduled, in progress, or finished compression tasks for
 // runtime.
 void
 CancelOffThreadCompressions(JSRuntime* runtime);
 
 class MOZ_RAII AutoLockHelperThreadState : public LockGuard<Mutex>
 {
@@ -618,20 +621,16 @@ struct ParseTask
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript* script;
 
     // Holds the ScriptSourceObject generated for the script compilation.
     ScriptSourceObject* sourceObject;
 
-    // Holds the SourceCompressionTask, if any were enqueued for the
-    // ScriptSource of sourceObject.
-    SourceCompressionTask* sourceCompressionTask;
-
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<CompileError*, 0, SystemAllocPolicy> errors;
     bool overRecursed;
     bool outOfMemory;
 
     ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
               const char16_t* chars, size_t length,
@@ -699,54 +698,41 @@ class SourceCompressionTask
     friend struct HelperThread;
     friend class ScriptSource;
 
     // The runtime that the ScriptSource is associated with, in the sense that
     // it uses the runtime's immutable string cache.
     JSRuntime* runtime_;
 
     // The major GC number of the runtime when the task was enqueued.
-    static const uint64_t MajorGCNumberWaitingForFixup = UINT64_MAX;
     uint64_t majorGCNumber_;
 
     // The source to be compressed.
     ScriptSourceHolder sourceHolder_;
 
     // The resultant compressed string. If the compressed string is larger
     // than the original, or we OOM'd during compression, or nothing else
     // except the task is holding the ScriptSource alive when scheduled to
     // compress, this will remain None upon completion.
     mozilla::Maybe<SharedImmutableString> resultString_;
 
   public:
-    // The majorGCNumber is used for scheduling tasks. If the task is being
-    // enqueued from an off-thread parsing task, leave the GC number
-    // UINT64_MAX to be fixed up when the parse task finishes.
+    // The majorGCNumber is used for scheduling tasks.
     SourceCompressionTask(JSRuntime* rt, ScriptSource* source)
       : runtime_(rt),
-        majorGCNumber_(CurrentThreadCanAccessRuntime(rt)
-                       ? rt->gc.majorGCCount()
-                       : MajorGCNumberWaitingForFixup),
+        majorGCNumber_(rt->gc.majorGCCount()),
         sourceHolder_(source)
     { }
 
     bool runtimeMatches(JSRuntime* runtime) const {
         return runtime == runtime_;
     }
-
-    void fixupMajorGCNumber(JSRuntime* runtime) {
-        MOZ_ASSERT(majorGCNumber_ == MajorGCNumberWaitingForFixup);
-        majorGCNumber_ = runtime->gc.majorGCCount();
-    }
-
     bool shouldStart() const {
         // We wait 2 major GCs to start compressing, in order to avoid
         // immediate compression.
-        if (majorGCNumber_ == MajorGCNumberWaitingForFixup)
-            return false;
         return runtime_->gc.majorGCCount() > majorGCNumber_ + 1;
     }
 
     bool shouldCancel() const {
         // If the refcount is exactly 1, then nothing else is holding on to the
         // ScriptSource, so no reason to compress it and we should cancel the task.
         return sourceHolder_.get()->refs == 1;
     }
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -379,38 +379,16 @@ class TypedArrayObjectTemplate : public 
                                                SingletonObject);
 
         if (fun)
             fun->setJitInfo(&jit::JitInfo_TypedArrayConstructor);
 
         return fun;
     }
 
-    static bool
-    getOrCreateCreateArrayFromBufferFunction(JSContext* cx, MutableHandleValue fval)
-    {
-        RootedValue cache(cx, cx->global()->createArrayFromBuffer<NativeType>());
-        if (cache.isObject()) {
-            MOZ_ASSERT(cache.toObject().is<JSFunction>());
-            fval.set(cache);
-            return true;
-        }
-
-        RootedFunction fun(cx);
-        fun = NewNativeFunction(cx, ArrayBufferObject::createTypedArrayFromBuffer<NativeType>,
-                                0, nullptr);
-        if (!fun)
-            return false;
-
-        cx->global()->setCreateArrayFromBuffer<NativeType>(fun);
-
-        fval.setObject(*fun);
-        return true;
-    }
-
     static inline const Class* instanceClass()
     {
         return TypedArrayObject::classForType(ArrayTypeID());
     }
 
     static bool is(HandleValue v) {
         return v.isObject() && v.toObject().hasClass(instanceClass());
     }
@@ -474,20 +452,22 @@ class TypedArrayObjectTemplate : public 
         {
             return nullptr;
         }
 
         return &obj->as<TypedArrayObject>();
     }
 
     static TypedArrayObject*
-    makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset, uint32_t len,
-                 HandleObject proto)
+    makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset,
+                 uint32_t len, HandleObject proto)
     {
         MOZ_ASSERT_IF(!buffer, byteOffset == 0);
+        MOZ_ASSERT_IF(buffer, !buffer->isDetached());
+        MOZ_ASSERT(len < INT32_MAX / sizeof(NativeType));
 
         gc::AllocKind allocKind = buffer
                                   ? GetGCObjectKind(instanceClass())
                                   : AllocKindForLazyBuffer(len * sizeof(NativeType));
 
         // Subclassing mandates that we hand in the proto every time. Most of
         // the time, though, that [[Prototype]] will not be interesting. If
         // it isn't, we can do some more TI optimizations.
@@ -570,24 +550,16 @@ class TypedArrayObjectTemplate : public 
             if (!buffer->as<ArrayBufferObject>().addView(cx, obj))
                 return nullptr;
         }
 
         return obj;
     }
 
     static TypedArrayObject*
-    makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
-                 uint32_t byteOffset, uint32_t len)
-    {
-        RootedObject proto(cx, nullptr);
-        return makeInstance(cx, buffer, byteOffset, len, proto);
-    }
-
-    static TypedArrayObject*
     makeTemplateObject(JSContext* cx, int32_t len)
     {
         MOZ_ASSERT(len >= 0);
         size_t nbytes;
         MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
         MOZ_ASSERT(nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH);
         NewObjectKind newKind = TenuredObject;
         bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
@@ -730,16 +702,17 @@ class TypedArrayObjectTemplate : public 
 
         JSObject* obj = create(cx, args);
         if (!obj)
             return false;
         args.rval().setObject(*obj);
         return true;
     }
 
+  private:
     static JSObject*
     create(JSContext* cx, const CallArgs& args)
     {
         MOZ_ASSERT(args.isConstructing());
         RootedObject newTarget(cx, &args.newTarget().toObject());
 
         /* () or (number) */
         uint32_t len = 0;
@@ -762,170 +735,231 @@ class TypedArrayObjectTemplate : public 
          * Otherwise create a new typed array and copy elements 0..len-1
          * properties from the object, treating it as some sort of array.
          * Note that offset and length will be ignored.  Note that a
          * shared array's values are copied here.
          */
         if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
             return fromArray(cx, dataObj, newTarget);
 
-        /* (ArrayBuffer, [byteOffset, [length]]) */
+        // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
+
+        // Step 4.
+        // 22.2.4.2.1 AllocateTypedArray, step 1.
         RootedObject proto(cx);
         if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
             return nullptr;
 
         int32_t byteOffset = 0;
         if (args.hasDefined(1)) {
+            // Step 6.
             if (!ToInt32(cx, args[1], &byteOffset))
                 return nullptr;
             if (byteOffset < 0) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                           JSMSG_TYPED_ARRAY_NEGATIVE_ARG,
                                           "1");
                 return nullptr;
             }
+
+            // Step 7.
+            if (byteOffset % sizeof(NativeType) != 0) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                          JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+                return nullptr;
+            }
         }
 
         int32_t length = -1;
         if (args.hasDefined(2)) {
+            // Step 8.a.
             if (!ToInt32(cx, args[2], &length))
                 return nullptr;
             if (length < 0) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                           JSMSG_TYPED_ARRAY_NEGATIVE_ARG,
                                           "2");
                 return nullptr;
             }
         }
 
-        return fromBufferWithProto(cx, dataObj, byteOffset, length, proto);
+        // Steps 9-17.
+        if (dataObj->is<ArrayBufferObjectMaybeShared>()) {
+            HandleArrayBufferObjectMaybeShared buffer = dataObj.as<ArrayBufferObjectMaybeShared>();
+            return fromBufferSameCompartment(cx, buffer, byteOffset, length, proto);
+        }
+        return fromBufferWrapped(cx, dataObj, byteOffset, length, proto);
+    }
+
+    // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
+    // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
+    // Steps 9-12.
+    static bool
+    computeAndCheckLength(JSContext* cx, HandleArrayBufferObjectMaybeShared bufferMaybeUnwrapped,
+                          uint32_t byteOffset, int32_t lengthInt, uint32_t* length)
+    {
+        MOZ_ASSERT(byteOffset % sizeof(NativeType) == 0);
+
+        // Step 9.
+        if (bufferMaybeUnwrapped->isDetached()) {
+           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+           return false;
+        }
+
+        // Step 10.
+        uint32_t bufferByteLength = bufferMaybeUnwrapped->byteLength();
+
+        // 11.c, 12.b.
+        if (byteOffset > bufferByteLength) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                      JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+            return false; // invalid byteOffset
+        }
+
+        uint32_t len;
+        if (lengthInt < 0) {
+            // Step 11.b.
+            uint32_t newByteLength = bufferByteLength - byteOffset;
+            len = newByteLength / sizeof(NativeType);
+
+            // Step 11.a.
+            if (len * sizeof(NativeType) != newByteLength) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                          JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+                return false; // given byte array doesn't map exactly to sizeof(NativeType) * N
+            }
+
+            // ArrayBuffer is too large for TypedArrays:
+            // Standalone ArrayBuffers can hold up to INT32_MAX bytes, whereas
+            // buffers in TypedArrays must have less than or equal to
+            // |INT32_MAX - sizeof(NativeType) - INT32_MAX % sizeof(NativeType)|
+            // bytes.
+            if (len >= INT32_MAX / sizeof(NativeType)) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                          JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+                return false;
+            }
+        } else {
+            // Step 12.a (implicit).
+            len = uint32_t(lengthInt);
+
+            // Step 12.b.
+            if (len >= INT32_MAX / sizeof(NativeType)) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                          JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+                return false;
+            }
+            uint32_t newByteLength = len * sizeof(NativeType);
+
+            // Step 12.b (|byteOffset| moved to the RHS to avoid overflow).
+            if (newByteLength > bufferByteLength - byteOffset) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                          JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
+                return false; // |byteOffset + newByteLength| is too big for the arraybuffer
+            }
+        }
+
+        *length = len;
+        return true;
+    }
+
+    // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
+    // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
+    // Steps 9-17.
+    static JSObject*
+    fromBufferSameCompartment(JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
+                              uint32_t byteOffset, int32_t lengthInt, HandleObject proto)
+    {
+        // Steps 9-12.
+        uint32_t length;
+        if (!computeAndCheckLength(cx, buffer, byteOffset, lengthInt, &length))
+            return nullptr;
+
+        // Steps 13-17.
+        return makeInstance(cx, buffer, byteOffset, length, proto);
+    }
+
+    // Create a TypedArray object in another compartment.
+    //
+    // ES6 supports creating a TypedArray in global A (using global A's
+    // TypedArray constructor) backed by an ArrayBuffer created in global B.
+    //
+    // Our TypedArrayObject implementation doesn't support a TypedArray in
+    // compartment A backed by an ArrayBuffer in compartment B. So in this
+    // case, we create the TypedArray in B (!) and return a cross-compartment
+    // wrapper.
+    //
+    // Extra twist: the spec says the new TypedArray's [[Prototype]] must be
+    // A's TypedArray.prototype. So even though we're creating the TypedArray
+    // in B, its [[Prototype]] must be (a cross-compartment wrapper for) the
+    // TypedArray.prototype in A.
+    static JSObject*
+    fromBufferWrapped(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
+                      HandleObject proto)
+    {
+        JSObject* unwrapped = CheckedUnwrap(bufobj);
+        if (!unwrapped) {
+            ReportAccessDenied(cx);
+            return nullptr;
+        }
+
+        if (!unwrapped->is<ArrayBufferObjectMaybeShared>()) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+            return nullptr;
+        }
+
+        RootedArrayBufferObjectMaybeShared unwrappedBuffer(cx);
+        unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>();
+
+        uint32_t length;
+        if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthInt, &length))
+            return nullptr;
+
+        // Make sure to get the [[Prototype]] for the created typed array from
+        // this compartment.
+        RootedObject protoRoot(cx, proto);
+        if (!protoRoot) {
+            if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot))
+                return nullptr;
+        }
+
+        RootedObject typedArray(cx);
+        {
+            JSAutoCompartment ac(cx, unwrappedBuffer);
+
+            RootedObject wrappedProto(cx, protoRoot);
+            if (!cx->compartment()->wrap(cx, &wrappedProto))
+                return nullptr;
+
+            typedArray = makeInstance(cx, unwrappedBuffer, byteOffset, length, wrappedProto);
+            if (!typedArray)
+                return nullptr;
+        }
+
+        if (!cx->compartment()->wrap(cx, &typedArray))
+            return nullptr;
+
+        return typedArray;
     }
 
   public:
     static JSObject*
-    fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
-        return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, nullptr);
-    }
-
-    static JSObject*
-    fromBufferWithProto(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
-                        HandleObject proto)
+    fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt)
     {
-        if (bufobj->is<ProxyObject>()) {
-            /*
-             * Normally, NonGenericMethodGuard handles the case of transparent
-             * wrappers. However, we have a peculiar situation: we want to
-             * construct the new typed array in the compartment of the buffer,
-             * so that the typed array can point directly at their buffer's
-             * data without crossing compartment boundaries. So we use the
-             * machinery underlying NonGenericMethodGuard directly to proxy the
-             * native call. We will end up with a wrapper in the origin
-             * compartment for a view in the target compartment referencing the
-             * ArrayBufferObject in that same compartment.
-             */
-            JSObject* wrapped = CheckedUnwrap(bufobj);
-            if (!wrapped) {
-                ReportAccessDenied(cx);
-                return nullptr;
-            }
-
-            if (!IsAnyArrayBuffer(wrapped)) {
-                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-                return nullptr; // must be arrayBuffer
-            }
-
-            /*
-             * And for even more fun, the new view's prototype should be
-             * set to the origin compartment's prototype object, not the
-             * target's (specifically, the actual view in the target
-             * compartment will use as its prototype a wrapper around the
-             * origin compartment's view.prototype object).
-             *
-             * Rather than hack some crazy solution together, implement
-             * this all using a private helper function, created when
-             * ArrayBufferObject was initialized and cached in the global.
-             * This reuses all the existing cross-compartment crazy so we
-             * don't have to do anything *uniquely* crazy here.
-             */
-
-            RootedObject protoRoot(cx, proto);
-            if (!protoRoot) {
-                if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot))
-                    return nullptr;
-            }
-
-            FixedInvokeArgs<3> args(cx);
-
-            args[0].setNumber(byteOffset);
-            args[1].setInt32(lengthInt);
-            args[2].setObject(*protoRoot);
-
-            RootedValue fval(cx);
-            if (!getOrCreateCreateArrayFromBufferFunction(cx, &fval))
-                return nullptr;
-
-            RootedValue thisv(cx, ObjectValue(*bufobj));
-            RootedValue rval(cx);
-            if (!js::Call(cx, fval, thisv, args, &rval))
-                return nullptr;
-
-            return &rval.toObject();
-        }
-
-        if (!IsAnyArrayBuffer(bufobj)) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return nullptr; // must be arrayBuffer
-        }
-
-        Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
-        if (IsArrayBuffer(bufobj)) {
-            ArrayBufferObject& buf = AsArrayBuffer(bufobj);
-            if (buf.isDetached()) {
-                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
-                return nullptr;
-            }
-
-            buffer = static_cast<ArrayBufferObjectMaybeShared*>(&buf);
-        } else {
-            buffer = static_cast<ArrayBufferObjectMaybeShared*>(&AsSharedArrayBuffer(bufobj));
-        }
-
-        if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
+        if (byteOffset % sizeof(NativeType) != 0) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                       JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
             return nullptr; // invalid byteOffset
         }
 
-        uint32_t len;
-        if (lengthInt == -1) {
-            len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
-            if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) {
-                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                          JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
-                return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
-            }
-        } else {
-            len = uint32_t(lengthInt);
+        if (bufobj->is<ArrayBufferObjectMaybeShared>()) {
+            HandleArrayBufferObjectMaybeShared buffer = bufobj.as<ArrayBufferObjectMaybeShared>();
+            return fromBufferSameCompartment(cx, buffer, byteOffset, lengthInt, nullptr);
         }
-
-        // Go slowly and check for overflow.
-        uint32_t arrayByteLength = len * sizeof(NativeType);
-        if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                      JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
-            return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType)
-        }
-
-        if (arrayByteLength + byteOffset > buffer->byteLength()) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                      JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
-            return nullptr; // byteOffset + len is too big for the arraybuffer
-        }
-
-        return makeInstance(cx, buffer, byteOffset, len, proto);
+        return fromBufferWrapped(cx, bufobj, byteOffset, lengthInt, nullptr);
     }
 
     static bool
     maybeCreateArrayBuffer(JSContext* cx, uint32_t count, uint32_t unit,
                            HandleObject nonDefaultProto,
                            MutableHandle<ArrayBufferObject*> buffer)
     {
         if (count >= INT32_MAX / unit) {
@@ -1774,48 +1808,16 @@ TypedArrayObject::sharedTypedArrayProtot
     // draft 20140824 requires.)  But this is about as much as we can do
     // until we implement @@toStringTag.
     "???",
     JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
     JS_NULL_CLASS_OPS,
     &TypedArrayObjectSharedTypedArrayPrototypeClassSpec
 };
 
-template<typename T>
-bool
-ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args)
-{
-    typedef TypedArrayObjectTemplate<T> ArrayType;
-    MOZ_ASSERT(IsAnyArrayBuffer(args.thisv()));
-    MOZ_ASSERT(args.length() == 3);
-
-    Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
-    Rooted<JSObject*> proto(cx, &args[2].toObject());
-
-    Rooted<JSObject*> obj(cx);
-    double byteOffset = args[0].toNumber();
-    MOZ_ASSERT(0 <= byteOffset);
-    MOZ_ASSERT(byteOffset <= UINT32_MAX);
-    MOZ_ASSERT(byteOffset == uint32_t(byteOffset));
-    obj = ArrayType::fromBufferWithProto(cx, buffer, uint32_t(byteOffset), args[1].toInt32(),
-                                         proto);
-    if (!obj)
-        return false;
-    args.rval().setObject(*obj);
-    return true;
-}
-
-template<typename T>
-bool
-ArrayBufferObject::createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsAnyArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
-}
-
 // this default implementation is only valid for integer types
 // less than 32-bits in size.
 template<typename NativeType>
 Value
 TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject* tarray, uint32_t index)
 {
     static_assert(sizeof(NativeType) < 4,
                   "this method must only handle NativeType values that are "
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -422,33 +422,16 @@ TypedArrayElemSize(Scalar::Type viewType
 //   target[targetOffset + unsafeSrcCrossCompartment.length - 1] =
 //       unsafeSrcCrossCompartment[unsafeSrcCrossCompartment.length - 1]
 //
 // where the source element range doesn't overlap the target element range in
 // memory.
 extern void
 SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset,
                          TypedArrayObject* unsafeSrcCrossCompartment);
-static inline bool
-IsAnyArrayBuffer(HandleObject obj)
-{
-    return IsArrayBuffer(obj) || IsSharedArrayBuffer(obj);
-}
-
-static inline bool
-IsAnyArrayBuffer(JSObject* obj)
-{
-    return IsArrayBuffer(obj) || IsSharedArrayBuffer(obj);
-}
-
-static inline bool
-IsAnyArrayBuffer(HandleValue v)
-{
-    return v.isObject() && IsAnyArrayBuffer(&v.toObject());
-}
 
 } // namespace js
 
 template <>
 inline bool
 JSObject::is<js::TypedArrayObject>() const
 {
     return js::IsTypedArrayClass(getClass());
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2068,38 +2068,36 @@ nsPresContext::MediaFeatureValuesChanged
 
   if (aRestyleHint || aChangeHint) {
     RebuildAllStyleData(aChangeHint, aRestyleHint);
   }
 
   mPendingViewportChange = false;
 
   if (mDocument->IsBeingUsedAsImage()) {
-    MOZ_ASSERT(PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists()));
+    MOZ_ASSERT(mDocument->MediaQueryLists().isEmpty());
     return;
   }
 
   mDocument->NotifyMediaFeatureValuesChanged();
 
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   // Media query list listeners should be notified from a queued task
   // (in HTML5 terms), although we also want to notify them on certain
   // flushes.  (We're already running off an event.)
   //
   // Note that we do this after the new style from media queries in
   // style sheets has been computed.
 
-  if (!PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists())) {
+  if (!mDocument->MediaQueryLists().isEmpty()) {
     // We build a list of all the notifications we're going to send
     // before we send any of them.
-    for (PRCList *l = PR_LIST_HEAD(mDocument->MediaQueryLists());
-         l != mDocument->MediaQueryLists(); l = PR_NEXT_LINK(l)) {
+    for (auto mql : mDocument->MediaQueryLists()) {
       nsAutoMicroTask mt;
-      MediaQueryList *mql = static_cast<MediaQueryList*>(l);
       mql->MaybeNotify();
     }
   }
 }
 
 void
 nsPresContext::PostMediaFeatureValuesChangedEvent()
 {
--- a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html
+++ b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS transforms: Creating containing block for fixed positioned elements</title>
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-<link rel="help" href="https://drafts.csswg.org/css-transforms-1/#perspective-property">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
 <link rel="match" href="containing-block-dynamic-1-ref.html">
 <meta name="assert" content="It also establishes a containing block (somewhat similar to position: relative), just like the transform property does.">
 <meta name="flags" content="dom">
 <style>
   html, body { margin: 0; padding: 0 }
   #changeperspective {
     position: absolute;
     top: 100px;
--- a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html
+++ b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS transforms: Creating containing block for fixed positioned elements</title>
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-<link rel="help" href="https://drafts.csswg.org/css-transforms-1/#perspective-property">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
 <link rel="match" href="containing-block-dynamic-1-ref.html">
 <meta name="assert" content="It also establishes a containing block (somewhat similar to position: relative), just like the transform property does.">
 <meta name="flags" content="dom">
 <style>
   html, body { margin: 0; padding: 0 }
   #changeperspective {
     position: absolute;
     top: 100px;
--- a/layout/style/MediaQueryList.cpp
+++ b/layout/style/MediaQueryList.cpp
@@ -23,39 +23,33 @@ namespace dom {
 MediaQueryList::MediaQueryList(nsIDocument* aDocument,
                                const nsAString& aMediaQueryList)
   : mDocument(aDocument)
   , mMatchesValid(false)
 {
   mMediaList =
     MediaList::Create(aDocument->GetStyleBackendType(), aMediaQueryList);
 
-  PR_INIT_CLIST(this);
-
   KeepAliveIfHasListenersFor(ONCHANGE_STRING);
 }
 
 MediaQueryList::~MediaQueryList()
-{
-  if (mDocument) {
-    PR_REMOVE_LINK(this);
-  }
-}
+{}
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList,
                                                 DOMEventTargetHelper)
   if (tmp->mDocument) {
-    PR_REMOVE_LINK(tmp);
+    tmp->remove();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
   }
   tmp->Disconnect();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaQueryList)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
--- a/layout/style/MediaQueryList.h
+++ b/layout/style/MediaQueryList.h
@@ -8,31 +8,31 @@
 
 #ifndef mozilla_dom_MediaQueryList_h
 #define mozilla_dom_MediaQueryList_h
 
 #include "nsISupports.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
-#include "prclist.h"
+#include "mozilla/LinkedList.h"
 #include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MediaQueryListBinding.h"
 
 class nsIDocument;
 
 namespace mozilla {
 namespace dom {
 
 class MediaList;
 
 class MediaQueryList final : public DOMEventTargetHelper,
-                             public PRCList
+                             public mozilla::LinkedListElement<MediaQueryList>
 {
 public:
   // The caller who constructs is responsible for calling Evaluate
   // before calling any other methods.
   MediaQueryList(nsIDocument *aDocument,
                  const nsAString &aMediaQueryList);
 private:
   ~MediaQueryList();
new file mode 100644
--- /dev/null
+++ b/mfbt/DoublyLinkedList.h
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/** A doubly-linked list with flexible next/prev naming. */
+
+#ifndef mozilla_DoublyLinkedList_h
+#define mozilla_DoublyLinkedList_h
+
+#include <algorithm>
+#include <iterator>
+
+/**
+ * Where mozilla::LinkedList strives for ease of use above all other
+ * considerations, mozilla::DoublyLinkedList strives for flexibility. The
+ * following are things that can be done with mozilla::DoublyLinkedList that
+ * cannot be done with mozilla::LinkedList:
+ *
+ *   * Arbitrary next/prev placement and naming. With the tools provided here,
+ *     the next and previous pointers can be at the end of the structure, in a
+ *     sub-structure, stored with a tag, in a union, wherever, as long as you
+ *     can look them up and set them on demand.
+ *   * Can be used without deriving from a new base and, thus, does not require
+ *     use of constructors.
+ *
+ * Example:
+ *
+ *   class Observer : public DoublyLinkedListElement<Observer>
+ *   {
+ *   public:
+ *     void observe(char* aTopic) { ... }
+ *   };
+ *
+ *   class ObserverContainer
+ *   {
+ *   private:
+ *     DoublyLinkedList<Observer> mList;
+ *
+ *   public:
+ *     void addObserver(Observer* aObserver)
+ *     {
+ *       // Will assert if |aObserver| is part of another list.
+ *       mList.pushBack(aObserver);
+ *     }
+ *
+ *     void removeObserver(Observer* aObserver)
+ *     {
+ *       // Will assert if |aObserver| is not part of |list|.
+ *       mList.remove(aObserver);
+ *     }
+ *
+ *     void notifyObservers(char* aTopic)
+ *     {
+ *       for (Observer* o : mList) {
+ *         o->observe(aTopic);
+ *       }
+ *     }
+ *   };
+ */
+
+namespace mozilla {
+
+/**
+ * Provides access to a next and previous element pointer named |mNext| and
+ * |mPrev| respectively. This class is the default and will work if the list
+ * element derives from DoublyLinkedListElement.
+ *
+ * Although designed to work with DoublyLinkedListElement this will als work
+ * with any class that defines |mNext| and |mPrev| members with the correct
+ * type.
+ */
+template <typename T>
+struct DoublyLinkedSiblingAccess {
+  static void SetNext(T* aElm, T* aNext) { aElm->mNext = aNext; }
+  static T* GetNext(T* aElm) { return aElm->mNext; }
+  static void SetPrev(T* aElm, T* aPrev) { aElm->mPrev = aPrev; }
+  static T* GetPrev(T* aElm) { return aElm->mPrev; }
+};
+
+/**
+ *  Deriving from this will allow T to be inserted into and removed from a
+ *  DoublyLinkedList.
+ */
+template <typename T>
+struct DoublyLinkedListElement
+{
+  friend struct DoublyLinkedSiblingAccess<T>;
+  T* mNext;
+  T* mPrev;
+
+public:
+  DoublyLinkedListElement() : mNext(nullptr), mPrev(nullptr) {}
+};
+
+/**
+ * A doubly linked list. |T| is the type of element stored in this list. |T|
+ * must contain or have access to unique next and previous element pointers.
+ * The template argument |SiblingAccess| provides code to tell this list how to
+ * get and set the next and previous pointers. The actual storage of next/prev
+ * links may reside anywhere and be encoded in any way.
+ */
+template <typename T, typename SiblingAccess = DoublyLinkedSiblingAccess<T>>
+class DoublyLinkedList final
+{
+  T* mHead;
+  T* mTail;
+
+  /**
+   * Checks that either the list is empty and both mHead and mTail are nullptr
+   * or the list has entries and both mHead and mTail are non-null.
+   */
+  bool isStateValid() const {
+    return (mHead != nullptr) == (mTail != nullptr);
+  }
+
+  static bool ElementNotInList(T* aElm) {
+    return !SiblingAccess::GetNext(aElm) && !SiblingAccess::GetPrev(aElm);
+  }
+
+public:
+  DoublyLinkedList() : mHead(nullptr), mTail(nullptr) {}
+
+  class Iterator final {
+    T* mCurrent;
+
+  public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = T;
+    using difference_type = std::ptrdiff_t;
+    using pointer = T*;
+    using reference = T&;
+
+    Iterator() : mCurrent(nullptr) {}
+    explicit Iterator(T* aCurrent) : mCurrent(aCurrent) {}
+
+    T& operator *() const { return *mCurrent; }
+    T* operator ->() const { return mCurrent; }
+
+    Iterator& operator++() {
+      mCurrent = SiblingAccess::GetNext(mCurrent);
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      Iterator result = *this;
+      ++(*this);
+      return result;
+    }
+
+    Iterator& operator--() {
+      mCurrent = SiblingAccess::GetPrev(mCurrent);
+      return *this;
+    }
+
+    Iterator operator--(int) {
+      Iterator result = *this;
+      --(*this);
+      return result;
+    }
+
+    bool operator!=(const Iterator& aOther) const {
+      return mCurrent != aOther.mCurrent;
+    }
+
+    bool operator==(const Iterator& aOther) const {
+      return mCurrent == aOther.mCurrent;
+    }
+
+    explicit operator bool() const {
+      return mCurrent;
+    }
+  };
+
+  Iterator begin() { return Iterator(mHead); }
+  const Iterator begin() const { return Iterator(mHead); }
+  const Iterator cbegin() const { return Iterator(mHead); }
+
+  Iterator end() { return Iterator(); }
+  const Iterator end() const { return Iterator(); }
+  const Iterator cend() const { return Iterator(); }
+
+  /**
+   * Returns true if the list contains no elements.
+   */
+  bool isEmpty() const {
+    MOZ_ASSERT(isStateValid());
+    return mHead == nullptr;
+  }
+
+  /**
+   * Inserts aElm into the list at the head position. |aElm| must not already
+   * be in a list.
+   */
+  void pushFront(T* aElm) {
+    MOZ_ASSERT(aElm);
+    MOZ_ASSERT(ElementNotInList(aElm));
+    MOZ_ASSERT(isStateValid());
+
+    SiblingAccess::SetNext(aElm, mHead);
+    if (mHead) {
+      MOZ_ASSERT(!SiblingAccess::GetPrev(mHead));
+      SiblingAccess::SetPrev(mHead, aElm);
+    }
+
+    mHead = aElm;
+    if (!mTail) {
+      mTail = aElm;
+    }
+  }
+
+  /**
+   * Remove the head of the list and return it. Calling this on an empty list
+   * will assert.
+   */
+  T* popFront() {
+    MOZ_ASSERT(!isEmpty());
+    MOZ_ASSERT(isStateValid());
+
+    T* result = mHead;
+    mHead = result ? SiblingAccess::GetNext(result) : nullptr;
+    if (mHead) {
+      SiblingAccess::SetPrev(mHead, nullptr);
+    }
+
+    if (mTail == result) {
+      mTail = nullptr;
+    }
+
+    if (result) {
+      SiblingAccess::SetNext(result, nullptr);
+      SiblingAccess::SetPrev(result, nullptr);
+    }
+
+    return result;
+  }
+
+  /**
+   * Inserts aElm into the list at the tail position. |aElm| must not already
+   * be in a list.
+   */
+  void pushBack(T* aElm) {
+    MOZ_ASSERT(aElm);
+    MOZ_ASSERT(ElementNotInList(aElm));
+    MOZ_ASSERT(isStateValid());
+
+    SiblingAccess::SetNext(aElm, nullptr);
+    SiblingAccess::SetPrev(aElm, mTail);
+    if (mTail) {
+      MOZ_ASSERT(!SiblingAccess::GetNext(mTail));
+      SiblingAccess::SetNext(mTail, aElm);
+    }
+
+    mTail = aElm;
+    if (!mHead) {
+      mHead = aElm;
+    }
+  }
+
+  /**
+   * Remove the tail of the list and return it. Calling this on an empty list
+   * will assert.
+   */
+  T* popBack() {
+    MOZ_ASSERT(!isEmpty());
+    MOZ_ASSERT(isStateValid());
+
+    T* result = mTail;
+    mTail = result ? SiblingAccess::GetPrev(result) : nullptr;
+    if (mTail) {
+      SiblingAccess::SetNext(mTail, nullptr);
+    }
+
+    if (mHead == result) {
+      mHead = nullptr;
+    }
+
+    if (result) {
+      SiblingAccess::SetNext(result, nullptr);
+      SiblingAccess::SetPrev(result, nullptr);
+    }
+
+    return result;
+  }
+
+  /**
+   * Insert the given |aElm| *before* |aIter|.
+   */
+  void insertBefore(const Iterator& aIter, T* aElm) {
+    MOZ_ASSERT(aElm);
+    MOZ_ASSERT(ElementNotInList(aElm));
+    MOZ_ASSERT(isStateValid());
+
+    if (!aIter) {
+      return pushBack(aElm);
+    } else if (aIter == begin()) {
+      return pushFront(aElm);
+    }
+
+    T* after = &(*aIter);
+    T* before = SiblingAccess::GetPrev(after);
+    MOZ_ASSERT(before);
+
+    SiblingAccess::SetNext(before, aElm);
+    SiblingAccess::SetPrev(aElm, before);
+    SiblingAccess::SetNext(aElm, after);
+    SiblingAccess::SetPrev(after, aElm);
+  }
+
+  /**
+   * Removes the given element from the list. The element must be in this list.
+   */
+  void remove(T* aElm) {
+    MOZ_ASSERT(aElm);
+    MOZ_ASSERT(SiblingAccess::GetNext(aElm) || SiblingAccess::GetPrev(aElm) ||
+               (aElm == mHead && aElm == mTail),
+               "Attempted to remove element not in this list");
+
+    if (T* prev = SiblingAccess::GetPrev(aElm)) {
+      SiblingAccess::SetNext(prev, SiblingAccess::GetNext(aElm));
+    } else {
+      MOZ_ASSERT(mHead == aElm);
+      mHead = SiblingAccess::GetNext(aElm);
+    }
+
+    if (T* next = SiblingAccess::GetNext(aElm)) {
+      SiblingAccess::SetPrev(next, SiblingAccess::GetPrev(aElm));
+    } else {
+      MOZ_ASSERT(mTail == aElm);
+      mTail = SiblingAccess::GetPrev(aElm);
+    }
+
+    SiblingAccess::SetNext(aElm, nullptr);
+    SiblingAccess::SetPrev(aElm, nullptr);
+  }
+
+  /**
+   * Returns an iterator referencing the first found element whose value matches
+   * the given element according to operator==.
+   */
+  Iterator find(const T& aElm) {
+    return std::find(begin(), end(), aElm);
+  }
+
+  /**
+   * Returns whether the given element is in the list. Note that this uses
+   * T::operator==, not pointer comparison.
+   */
+  bool contains(const T& aElm) {
+    return find(aElm) != Iterator();
+  }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_DoublyLinkedList_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -30,16 +30,17 @@ EXPORTS.mozilla = [
     'Char16.h',
     'CheckedInt.h',
     'Compiler.h',
     'Compression.h',
     'DebugOnly.h',
     'decimal/Decimal.h',
     'double-conversion/source/double-conversion.h',
     'double-conversion/source/utils.h',
+    'DoublyLinkedList.h',
     'EndianUtils.h',
     'EnumeratedArray.h',
     'EnumeratedRange.h',
     'EnumSet.h',
     'EnumTypeTraits.h',
     'FastBernoulliTrial.h',
     'FloatingPoint.h',
     'GuardObjects.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestDoublyLinkedList.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DoublyLinkedList.h"
+
+using mozilla::DoublyLinkedList;
+using mozilla::DoublyLinkedListElement;
+
+struct SomeClass : public DoublyLinkedListElement<SomeClass> {
+  unsigned int mValue;
+  explicit SomeClass(int aValue) : mValue(aValue) {}
+  void incr() { ++mValue; }
+  bool operator==(const SomeClass& other) { return mValue == other.mValue; }
+};
+
+template <typename ListType, size_t N>
+static void
+CheckListValues(ListType& list, unsigned int (&values)[N])
+{
+  size_t count = 0;
+  for (auto& x : list) {
+    MOZ_RELEASE_ASSERT(x.mValue == values[count]);
+    ++count;
+  }
+  MOZ_RELEASE_ASSERT(count == N);
+}
+
+static void
+TestDoublyLinkedList()
+{
+  DoublyLinkedList<SomeClass> list;
+
+  SomeClass one(1), two(2), three(3);
+
+  MOZ_RELEASE_ASSERT(list.isEmpty());
+  MOZ_RELEASE_ASSERT(!list.begin());
+  MOZ_RELEASE_ASSERT(!list.end());
+
+  for (SomeClass& x : list) {
+    MOZ_RELEASE_ASSERT(x.mValue);
+    MOZ_RELEASE_ASSERT(false);
+  }
+
+  list.pushFront(&one);
+  { unsigned int check[] { 1 }; CheckListValues(list, check); }
+
+  MOZ_RELEASE_ASSERT(list.contains(one));
+  MOZ_RELEASE_ASSERT(!list.contains(two));
+  MOZ_RELEASE_ASSERT(!list.contains(three));
+
+  MOZ_RELEASE_ASSERT(!list.isEmpty());
+  MOZ_RELEASE_ASSERT(list.begin()->mValue == 1);
+  MOZ_RELEASE_ASSERT(!list.end());
+
+  list.pushFront(&two);
+  { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
+
+  MOZ_RELEASE_ASSERT(list.begin()->mValue == 2);
+  MOZ_RELEASE_ASSERT(!list.end());
+  MOZ_RELEASE_ASSERT(!list.contains(three));
+
+  list.pushBack(&three);
+  { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
+
+  MOZ_RELEASE_ASSERT(list.begin()->mValue == 2);
+  MOZ_RELEASE_ASSERT(!list.end());
+
+  list.remove(&one);
+  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+  list.insertBefore(list.find(three), &one);
+  { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
+
+  list.remove(&three);
+  { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
+
+  list.insertBefore(list.find(two), &three);
+  { unsigned int check[] { 3, 2, 1 }; CheckListValues(list, check); }
+
+  list.remove(&three);
+  { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
+
+  list.insertBefore(++list.find(two), &three);
+  { unsigned int check[] { 2, 3, 1 }; CheckListValues(list, check); }
+
+  list.remove(&one);
+  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+  list.remove(&two);
+  { unsigned int check[] { 3 }; CheckListValues(list, check); }
+
+  list.insertBefore(list.find(three), &two);
+  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+  list.remove(&three);
+  { unsigned int check[] { 2 }; CheckListValues(list, check); }
+
+  list.remove(&two);
+  MOZ_RELEASE_ASSERT(list.isEmpty());
+
+  list.pushBack(&three);
+  { unsigned int check[] { 3 }; CheckListValues(list, check); }
+
+  list.pushFront(&two);
+  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+  // This should modify the values of |two| and |three| as pointers to them are
+  // stored in the list, not copies.
+  for (SomeClass& x : list) {
+    x.incr();
+  }
+
+  MOZ_RELEASE_ASSERT(*list.begin() == two);
+  MOZ_RELEASE_ASSERT(*++list.begin() == three);
+
+  SomeClass four(4);
+  MOZ_RELEASE_ASSERT(++list.begin() == list.find(four));
+}
+
+static void
+TestCustomAccessor()
+{
+  struct InTwoLists {
+    explicit InTwoLists(unsigned int aValue) : mValue(aValue) {}
+    DoublyLinkedListElement<InTwoLists> mListOne;
+    DoublyLinkedListElement<InTwoLists> mListTwo;
+    unsigned int mValue;
+  };
+
+  struct ListOneSiblingAccess {
+    static void SetNext(InTwoLists* aElm, InTwoLists* aNext) { aElm->mListOne.mNext = aNext; }
+    static InTwoLists* GetNext(InTwoLists* aElm) { return aElm->mListOne.mNext; }
+    static void SetPrev(InTwoLists* aElm, InTwoLists* aPrev) { aElm->mListOne.mPrev = aPrev; }
+    static InTwoLists* GetPrev(InTwoLists* aElm) { return aElm->mListOne.mPrev; }
+  };
+
+  struct ListTwoSiblingAccess {
+    static void SetNext(InTwoLists* aElm, InTwoLists* aNext) { aElm->mListTwo.mNext = aNext; }
+    static InTwoLists* GetNext(InTwoLists* aElm) { return aElm->mListTwo.mNext; }
+    static void SetPrev(InTwoLists* aElm, InTwoLists* aPrev) { aElm->mListTwo.mPrev = aPrev; }
+    static InTwoLists* GetPrev(InTwoLists* aElm) { return aElm->mListTwo.mPrev; }
+  };
+
+  DoublyLinkedList<InTwoLists, ListOneSiblingAccess> listOne;
+  DoublyLinkedList<InTwoLists, ListTwoSiblingAccess> listTwo;
+
+  InTwoLists one(1);
+  InTwoLists two(2);
+
+  listOne.pushBack(&one);
+  listOne.pushBack(&two);
+  { unsigned int check[] { 1, 2 }; CheckListValues(listOne, check); }
+
+  listTwo.pushBack(&one);
+  listTwo.pushBack(&two);
+  { unsigned int check[] { 1, 2 }; CheckListValues(listTwo, check); }
+
+  (void)listTwo.popBack();
+  { unsigned int check[] { 1, 2 }; CheckListValues(listOne, check); }
+  { unsigned int check[] { 1 }; CheckListValues(listTwo, check); }
+}
+
+int
+main()
+{
+  TestDoublyLinkedList();
+  TestCustomAccessor();
+  return 0;
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -16,16 +16,17 @@ CppUnitTests([
     'TestBinarySearch',
     'TestBloomFilter',
     'TestBufferList',
     'TestCasting',
     'TestCeilingFloor',
     'TestCheckedInt',
     'TestCountPopulation',
     'TestCountZeroes',
+    'TestDoublyLinkedList',
     'TestEndian',
     'TestEnumeratedArray',
     'TestEnumSet',
     'TestEnumTypeTraits',
     'TestFastBernoulliTrial',
     'TestFloatingPoint',
     'TestIntegerPrintfMacros',
     'TestIntegerRange',
--- a/mobile/android/chrome/geckoview/geckoview.xul
+++ b/mobile/android/chrome/geckoview/geckoview.xul
@@ -5,12 +5,12 @@
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 
 <window id="main-window"
         onload="startup();"
         windowtype="navigator:browser"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
-  <browser id="content" type="content" primary="true" src="about:blank" flex="1" remote="true"/>
+  <browser id="content" type="content" primary="true" src="about:blank" flex="1"/>
 
   <script type="application/javascript" src="chrome://geckoview/content/geckoview.js"/>
 </window>
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
@@ -17,34 +17,48 @@ public final class GeckoViewSettings {
     private static class Key<T> {
         private final String text;
 
         public Key(final String text) {
             this.text = text;
         }
     }
 
+    /*
+     * Key to enabled and disable tracking protection.
+     */
     public static final Key<Boolean> USE_TRACKING_PROTECTION =
         new Key<Boolean>("useTrackingProtection");
+    /*
+     * Key to enabled and disable private mode browsing.
+     */
     public static final Key<Boolean> USE_PRIVATE_MODE =
         new Key<Boolean>("usePrivateMode");
+    /*
+     * Key to enabled and disable multiprocess browsing (e10s).
+     * Note: can only be set during GeckoView initialization, changes during an
+     * active GeckoView session will be ignored.
+     */
+    public static final Key<Boolean> USE_MULTIPROCESS =
+        new Key<Boolean>("useMultiprocess");
 
     private final EventDispatcher mEventDispatcher;
     private final GeckoBundle mBundle;
 
     public GeckoViewSettings() {
         this(null);
     }
 
     /* package */ GeckoViewSettings(EventDispatcher eventDispatcher) {
         mEventDispatcher = eventDispatcher;
         mBundle = new GeckoBundle();
 
         setBoolean(USE_TRACKING_PROTECTION, false);
         setBoolean(USE_PRIVATE_MODE, false);
+        setBoolean(USE_MULTIPROCESS, true);
     }
 
     /* package */ GeckoViewSettings(GeckoViewSettings settings, EventDispatcher eventDispatcher) {
         mBundle = new GeckoBundle(settings.mBundle);
         mEventDispatcher = eventDispatcher;
     }
 
     public void setBoolean(final Key<Boolean> key, boolean value) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareCodecCapabilityUtils.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareCodecCapabilityUtils.java
@@ -35,17 +35,25 @@ public final class HardwareCodecCapabili
     CodecCapabilities.COLOR_FormatYUV420Planar,
     CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
     CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
     COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m
   };
 
   @WrapForJNI
   public static boolean findDecoderCodecInfoForMimeType(String aMimeType) {
-    for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
+    int numCodecs = 0;
+    try {
+      numCodecs = MediaCodecList.getCodecCount();
+    } catch (final RuntimeException e) {
+      Log.e(LOGTAG, "Failed to retrieve media codec count", e);
+      return false;
+    }
+
+    for (int i = 0; i < numCodecs; ++i) {
       MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
       if (info.isEncoder()) {
         continue;
       }
       for (String mimeType : info.getSupportedTypes()) {
         if (mimeType.equals(aMimeType)) {
           return true;
         }
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -46,28 +46,28 @@ public class GeckoViewActivity extends A
 
         GeckoThread.initMainProcess(profile, /* args */ null, /* debugging */ false);
         GeckoThread.launch();
 
         loadFromIntent(getIntent());
     }
 
     @Override
-    protected void onNewIntent(Intent externalIntent) {
-        Log.d(LOGTAG, "SNORP: onNewIntent: " + externalIntent.getData().toString());
-        loadFromIntent(externalIntent);
+    protected void onNewIntent(final Intent intent) {
+        super.onNewIntent(intent);
+        setIntent(intent);
+
+        if (intent.getData() != null) {
+            loadFromIntent(intent);
+        }
     }
 
-    private void loadFromIntent(Intent intent) {
-        Uri u = intent.getData();
-        if (u != null) {
-            mGeckoView.loadUri(u.toString());
-        } else {
-            mGeckoView.loadUri(DEFAULT_URL);
-        }
+    private void loadFromIntent(final Intent intent) {
+        final Uri uri = intent.getData();
+        mGeckoView.loadUri(uri != null ? uri.toString() : DEFAULT_URL);
     }
 
     @Override
     protected void onActivityResult(final int requestCode, final int resultCode,
                                     final Intent data) {
         if (requestCode == REQUEST_FILE_PICKER) {
             final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
                     mGeckoView.getPromptDelegate();
--- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm
@@ -18,20 +18,25 @@ var dump = Cu.import("resource://gre/mod
            .AndroidLog.d.bind(null, "ViewSettings");
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 // Handles GeckoView settings including:
 // * tracking protection
+// * multiprocess
 class GeckoViewSettings extends GeckoViewModule {
   init() {
     this._isSafeBrowsingInit = false;
     this._useTrackingProtection = false;
+
+    // We only allow to set this setting during initialization, further updates
+    // will be ignored.
+    this.useMultiprocess = !!this.settings.useMultiprocess;
   }
 
   onSettingsUpdate() {
     debug("onSettingsUpdate: " + JSON.stringify(this.settings));
 
     this.useTrackingProtection = !!this.settings.useTrackingProtection;
   }
 
@@ -47,9 +52,28 @@ class GeckoViewSettings extends GeckoVie
     if (aUse != this._useTrackingProtection) {
       this.messageManager.loadFrameScript('data:,' +
         'docShell.useTrackingProtection = ' + aUse,
         true
       );
       this._useTrackingProtection = aUse;
     }
   }
+
+  get useMultiprocess() {
+    return this.browser.getAttribute("remote") == "true";
+  }
+
+  set useMultiprocess(aUse) {
+    if (aUse == this.useMultiprocess) {
+      return;
+    }
+    let parentNode = this.browser.parentNode;
+    parentNode.removeChild(this.browser);
+
+    if (aUse) {
+      this.browser.setAttribute("remote", "true");
+    } else {
+      this.browser.removeAttribute("remote");
+    }
+    parentNode.appendChild(this.browser);
+  }
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1523,17 +1523,17 @@ pref("network.http.accept.default", "tex
 // 0=don't send any, 1=send only on clicks, 2=send on image requests as well
 pref("network.http.sendRefererHeader",      2);
 // Set the default Referrer Policy to be used unless overriden by the site
 // 0=no-referrer, 1=same-origin, 2=strict-origin-when-cross-origin,
 // 3=no-referrer-when-downgrade
 pref("network.http.referer.userControlPolicy", 3);
 // false=real referer, true=spoof referer (use target URI as referer)
 pref("network.http.referer.spoofSource", false);
-// false=allow onion referer, true=hide onion referer (use target URI as referer)
+// false=allow onion referer, true=hide onion referer (use empty referer)
 pref("network.http.referer.hideOnionSource", false);
 // 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
 pref("network.http.referer.trimmingPolicy", 0);
 // 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
 pref("network.http.referer.XOriginTrimmingPolicy", 0);
 // 0=always send, 1=send iff base domains match, 2=send iff hosts match
 pref("network.http.referer.XOriginPolicy", 0);
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -925,41 +925,34 @@ nsHttpConnectionMgr::DispatchPendingQ(ns
 
         ++i;
 
     }
     return dispatchedSuccessfully;
 }
 
 uint32_t
-nsHttpConnectionMgr::AvailableNewConnectionCount(nsConnectionEntry * ent)
+nsHttpConnectionMgr::TotalActiveConnections(nsConnectionEntry *ent) const
 {
     // Add in the in-progress tcp connections, we will assume they are
     // keepalive enabled.
     // Exclude half-open's that has already created a usable connection.
     // This prevents the limit being stuck on ipv6 connections that
     // eventually time out after typical 21 seconds of no ACK+SYN reply.
-    uint32_t totalCount =
-        ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
-
-    uint16_t maxPersistConns;
-
+    return ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
+}
+
+uint32_t
+nsHttpConnectionMgr::MaxPersistConnections(nsConnectionEntry *ent) const
+{
     if (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) {
-        maxPersistConns = mMaxPersistConnsPerProxy;
-    } else {
-        maxPersistConns = mMaxPersistConnsPerHost;
+        return static_cast<uint32_t>(mMaxPersistConnsPerProxy);
     }
 
-    LOG(("nsHttpConnectionMgr::AvailableNewConnectionCount "
-         "total connection count = %d, limit %d\n",
-         totalCount, maxPersistConns));
-
-    return maxPersistConns > totalCount
-        ? maxPersistConns - totalCount
-        : 0;
+    return static_cast<uint32_t>(mMaxPersistConnsPerHost);
 }
 
 bool
 nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool considerAll)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
@@ -981,17 +974,22 @@ nsHttpConnectionMgr::ProcessPendingQForE
                                                   ent,
                                                   considerAll);
     }
 
     if (dispatchedSuccessfully && !considerAll) {
         return dispatchedSuccessfully;
     }
 
-    uint32_t availableConnections = AvailableNewConnectionCount(ent);
+    uint32_t totalCount = TotalActiveConnections(ent);
+    uint32_t maxPersistConns = MaxPersistConnections(ent);
+    uint32_t availableConnections = maxPersistConns > totalCount
+        ? maxPersistConns - totalCount
+        : 0;
+
     // No need to try dispatching if we reach the active connection limit.
     if (!availableConnections) {
         return dispatchedSuccessfully;
     }
 
     uint32_t maxFocusedWindowConnections =
         availableConnections * gHttpHandler->FocusedWindowTransactionRatio();
     MOZ_ASSERT(maxFocusedWindowConnections < availableConnections);
@@ -1094,24 +1092,25 @@ nsHttpConnectionMgr::ProcessPendingQForE
 // we're at the active connection limit if any one of the following conditions is true:
 //  (1) at max-connections
 //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
 //  (3) keep-alive disabled and at max-connections-per-server
 bool
 nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t caps)
 {
     nsHttpConnectionInfo *ci = ent->mConnInfo;
-
-    LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
-        ci->HashKey().get(), caps));
-
-    uint32_t availableConnections = AvailableNewConnectionCount(ent);
+    uint32_t totalCount = TotalActiveConnections(ent);
+    uint32_t maxPersistConns = MaxPersistConnections(ent);
+
+    LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x,"
+         "totalCount=%u, maxPersistConns=%u]\n",
+         ci->HashKey().get(), caps, totalCount, maxPersistConns));
 
     if (caps & NS_HTTP_URGENT_START) {
-        if (availableConnections > static_cast<uint32_t>(mMaxUrgentExcessiveConns)) {
+        if (totalCount >= (mMaxUrgentExcessiveConns + maxPersistConns)) {
             LOG(("The number of total connections are greater than or equal to sum of "
                  "max urgent-start queue length and the number of max persistent connections.\n"));
             return true;
         }
         return false;
     }
 
     // update maxconns if potentially limited by the max socket count
@@ -1126,17 +1125,17 @@ nsHttpConnectionMgr::AtActiveConnectionL
 
     // If there are more active connections than the global limit, then we're
     // done. Purging idle connections won't get us below it.
     if (mNumActiveConns >= mMaxConns) {
         LOG(("  num active conns == max conns\n"));
         return true;
     }
 
-    bool result = !availableConnections;
+    bool result = (totalCount >= maxPersistConns);
     LOG(("AtActiveConnectionLimit result: %s", result ? "true" : "false"));
     return result;
 }
 
 void
 nsHttpConnectionMgr::ClosePersistentConnections(nsConnectionEntry *ent)
 {
     LOG(("nsHttpConnectionMgr::ClosePersistentConnections [ci=%s]\n",
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -471,20 +471,24 @@ private:
     //-------------------------------------------------------------------------
 
     MOZ_MUST_USE bool ProcessPendingQForEntry(nsConnectionEntry *,
                                               bool considerAll);
     bool DispatchPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                                    nsConnectionEntry *ent,
                                    bool considerAll);
 
-    // Given the connection entry, return the available count for creating
-    // new connections. Return 0 if the active connection count is
-    // exceeded |mMaxPersistConnsPerProxy| or |mMaxPersistConnsPerHost|.
-    uint32_t AvailableNewConnectionCount(nsConnectionEntry * ent);
+    // Return total active connection count, which is the sum of
+    // active connections and unconnected half open connections.
+    uint32_t TotalActiveConnections(nsConnectionEntry *ent) const;
+
+    // Return |mMaxPersistConnsPerProxy| or |mMaxPersistConnsPerHost|,
+    // depending whether the proxy is used.
+    uint32_t MaxPersistConnections(nsConnectionEntry *ent) const;
+
     bool     AtActiveConnectionLimit(nsConnectionEntry *, uint32_t caps);
     MOZ_MUST_USE nsresult TryDispatchTransaction(nsConnectionEntry *ent,
                                                  bool onlyReusedConnection,
                                                  PendingTransactionInfo *pendingTransInfo);
     MOZ_MUST_USE nsresult DispatchTransaction(nsConnectionEntry *,
                                               nsHttpTransaction *,
                                               nsHttpConnection *);
     MOZ_MUST_USE nsresult DispatchAbstractTransaction(nsConnectionEntry *,
--- a/toolkit/content/widgets/scrollbox.xml
+++ b/toolkit/content/widgets/scrollbox.xml
@@ -753,20 +753,16 @@
         }
 
         this.setAttribute("notoverflowing", "true");
 
         try {
           // See bug 341047 and comments in overflow handler as to why
           // try..catch is needed here
           this._updateScrollButtonsDisabledState();
-
-          let childNodes = this._getScrollableElements();
-          if (childNodes && childNodes.length)
-            this.ensureElementIsVisible(childNodes[0], false);
         } catch (e) {
           this.removeAttribute("notoverflowing");
         }
       ]]></handler>
 
       <handler event="overflow" phase="capturing"><![CDATA[
         // filter underflow events which were dispatched on nested scrollboxes
         if (event.target != this)
--- a/toolkit/mozapps/update/UpdateTelemetry.jsm
+++ b/toolkit/mozapps/update/UpdateTelemetry.jsm
@@ -169,17 +169,16 @@ this.AUSTLMY = {
    */
   DWNLD_SUCCESS: 0,
   DWNLD_RETRY_OFFLINE: 1,
   DWNLD_RETRY_NET_TIMEOUT: 2,
   DWNLD_RETRY_CONNECTION_REFUSED: 3,
   DWNLD_RETRY_NET_RESET: 4,
   DWNLD_ERR_NO_UPDATE: 5,
   DWNLD_ERR_NO_UPDATE_PATCH: 6,
-  DWNLD_ERR_NO_PATCH_FILE: 7,
   DWNLD_ERR_PATCH_SIZE_LARGER: 8,
   DWNLD_ERR_PATCH_SIZE_NOT_EQUAL: 9,
   DWNLD_ERR_BINDING_ABORTED: 10,
   DWNLD_ERR_ABORT: 11,
   DWNLD_ERR_DOCUMENT_NOT_CACHED: 12,
   DWNLD_ERR_VERIFY_NO_REQUEST: 13,
   DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL: 14,
   DWNLD_ERR_VERIFY_NO_HASH_MATCH: 15,
--- a/toolkit/mozapps/update/common/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -56,17 +56,17 @@
 #define NO_INSTALLDIR_ERROR 34
 #define WRITE_ERROR_ACCESS_DENIED 35
 // #define WRITE_ERROR_SHARING_VIOLATION 36 // Replaced with errors 46-48
 #define WRITE_ERROR_CALLBACK_APP 37
 #define UNEXPECTED_BZIP_ERROR 39
 #define UNEXPECTED_MAR_ERROR 40
 #define UNEXPECTED_BSPATCH_ERROR 41
 #define UNEXPECTED_FILE_OPERATION_ERROR 42
-#define FILESYSTEM_MOUNT_READWRITE_ERROR 43
+// #define FILESYSTEM_MOUNT_READWRITE_ERROR 43 // Removed support for gonk
 #define DELETE_ERROR_EXPECTED_DIR 46
 #define DELETE_ERROR_EXPECTED_FILE 47
 #define RENAME_ERROR_EXPECTED_FILE 48
 
 // Error codes 24-33 and 49-51 are for the Windows maintenance service.
 #define SERVICE_COULD_NOT_COPY_UPDATER 49
 #define SERVICE_STILL_APPLYING_TERMINATED 50
 #define SERVICE_STILL_APPLYING_NO_EXIT_CODE 51
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -179,22 +179,16 @@ interface nsIUpdate : nsISupports
   /**
    * Whether or not the update is a security update or not. If this is true,
    * then we present more serious sounding user interface messages to the
    * user.
    */
   attribute boolean isSecurityUpdate;
 
   /**
-   * Whether or not the update being downloaded is an OS update. This is
-   * generally only possible in Gonk right now.
-   */
-  attribute boolean isOSUpdate;
-
-  /**
    * When the update was installed.
    */
   attribute long long installDate;
 
   /**
    * A message associated with this update, if any.
    */
   attribute AString statusText;
@@ -382,24 +376,16 @@ interface nsIApplicationUpdateService : 
   void removeDownloadListener(in nsIRequestObserver listener);
 
   /**
    *
    */
   AString downloadUpdate(in nsIUpdate update, in boolean background);
 
   /**
-   * Apply the OS update which has been downloaded and staged as applied.
-   * @param   update 
-   *          The update has been downloaded and staged as applied.
-   * @throws  if the update object is not an OS update.
-   */
-  void applyOsUpdate(in nsIUpdate update);
-
-  /**
    * Get the Active Updates directory
    * @returns An nsIFile for the active updates directory.
    */
   nsIFile getUpdatesDirectory();
 
   /**
    * Pauses the active update download process
    */
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -55,18 +55,16 @@ const PREFBRANCH_APP_UPDATE_NEVER = "app
 const URI_BRAND_PROPERTIES      = "chrome://branding/locale/brand.properties";
 const URI_UPDATE_HISTORY_DIALOG = "chrome://mozapps/content/update/history.xul";
 const URI_UPDATE_NS             = "http://www.mozilla.org/2005/app-update";
 const URI_UPDATE_PROMPT_DIALOG  = "chrome://mozapps/content/update/updates.xul";
 const URI_UPDATES_PROPERTIES    = "chrome://mozapps/locale/update/updates.properties";
 
 const KEY_UPDROOT         = "UpdRootD";
 const KEY_EXECUTABLE      = "XREExeF";
-// Gonk only
-const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD";
 
 const DIR_UPDATES         = "updates";
 
 const FILE_ACTIVE_UPDATE_XML = "active-update.xml";
 const FILE_BACKUP_UPDATE_LOG = "backup-update.log";
 const FILE_LAST_UPDATE_LOG   = "last-update.log";
 const FILE_UPDATES_XML       = "updates.xml";
 const FILE_UPDATE_LINK       = "update.link";
@@ -78,17 +76,16 @@ const FILE_UPDATE_VERSION    = "update.v
 
 const STATE_NONE            = "null";
 const STATE_DOWNLOADING     = "downloading";
 const STATE_PENDING         = "pending";
 const STATE_PENDING_SERVICE = "pending-service";
 const STATE_PENDING_ELEVATE = "pending-elevate";
 const STATE_APPLYING        = "applying";
 const STATE_APPLIED         = "applied";
-const STATE_APPLIED_OS      = "applied-os";
 const STATE_APPLIED_SERVICE = "applied-service";
 const STATE_SUCCEEDED       = "succeeded";
 const STATE_DOWNLOAD_FAILED = "download-failed";
 const STATE_FAILED          = "failed";
 
 // The values below used by this code are from common/errors.h
 const WRITE_ERROR                          = 7;
 const ELEVATION_CANCELED                   = 9;
@@ -198,27 +195,16 @@ const APPID_TO_TOPIC = {
   // Thunderbird
   "{3550f703-e582-4d05-9a08-453d09bdfdc6}": "mail-startup-done",
   // Instantbird
   "{33cb9019-c295-46dd-be21-8c4936574bee}": "xul-window-visible",
 };
 
 var gUpdateMutexHandle = null;
 
-// Gonk only
-var gSDCardMountLock = null;
-
-// Gonk only
-XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
-  if (AppConstants.platform != "gonk") {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  }
-  return Services.env.get("EXTERNAL_STORAGE");
-});
-
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
   return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
@@ -563,23 +549,16 @@ function getCanStageUpdates() {
 
   if (AppConstants.platform == "win" && shouldUseService()) {
     // No need to perform directory write checks, the maintenance service will
     // be able to write to all directories.
     LOG("getCanStageUpdates - able to stage updates using the service");
     return true;
   }
 
-  // For Gonk, the updater will remount the /system partition to move staged
-  // files into place.
-  if (AppConstants.platform == "gonk") {
-    LOG("getCanStageUpdates - able to stage updates because this is gonk");
-    return true;
-  }
-
   if (!hasUpdateMutex()) {
     LOG("getCanStageUpdates - unable to apply updates because another " +
         "instance of the application is already handling updates for this " +
         "installation.");
     return false;
   }
 
   return gCanStageUpdatesSession;
@@ -795,115 +774,16 @@ function writeStatusFile(dir, state) {
  */
 function writeVersionFile(dir, version) {
   let versionFile = dir.clone();
   versionFile.append(FILE_UPDATE_VERSION);
   writeStringToFile(versionFile, version);
 }
 
 /**
- * Gonk only function that reads the link file specified in the update.link file
- * in the specified directory and returns the nsIFile for the corresponding
- * file.
- * @param   dir
- *          The dir to look for an update.link file in
- * @return  A nsIFile for the file path specified in the
- *          update.link file or null if the update.link file
- *          doesn't exist.
- */
-function getFileFromUpdateLink(dir) {
-  if (AppConstants.platform != "gonk") {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  }
-  let linkFile = dir.clone();
-  linkFile.append(FILE_UPDATE_LINK);
-  let link = readStringFromFile(linkFile);
-  LOG("getFileFromUpdateLink linkFile.path: " + linkFile.path + ", link: " + link);
-  if (!link) {
-    return null;
-  }
-  let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
-  file.initWithPath(link);
-  return file;
-}
-
-/**
- * Gonk only function to create a link file. This allows the actual patch to
- * live in a directory different from the update directory.
- * @param   dir
- *          The patch directory where the update.link file
- *          should be written.
- * @param   patchFile
- *          The fully qualified filename of the patchfile.
- */
-function writeLinkFile(dir, patchFile) {
-  if (AppConstants.platform != "gonk") {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  }
-  let linkFile = dir.clone();
-  linkFile.append(FILE_UPDATE_LINK);
-  writeStringToFile(linkFile, patchFile.path);
-  if (patchFile.path.indexOf(gExtStorage) == 0) {
-    // The patchfile is being stored on external storage. Try to lock it
-    // so that it doesn't get shared with the PC while we're downloading
-    // to it.
-    acquireSDCardMountLock();
-  }
-}
-
-/**
- * Gonk only function to acquire a VolumeMountLock for the sdcard volume.
- *
- * This prevents the SDCard from being shared with the PC while
- * we're downloading the update.
- */
-function acquireSDCardMountLock() {
-  if (AppConstants.platform != "gonk") {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  }
-  let volsvc = Cc["@mozilla.org/telephony/volume-service;1"].
-                    getService(Ci.nsIVolumeService);
-  if (volsvc) {
-    gSDCardMountLock = volsvc.createMountLock("sdcard");
-  }
-}
-
-/**
- * Gonk only function that determines if the state corresponds to an
- * interrupted update. This could either be because the download was
- * interrupted, or because staging the update was interrupted.
- *
- * @return true if the state corresponds to an interrupted
- *         update.
- */
-function isInterruptedUpdate(status) {
-  if (AppConstants.platform != "gonk") {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  }
-  return (status == STATE_DOWNLOADING) ||
-         (status == STATE_PENDING) ||
-         (status == STATE_APPLYING);
-}
-
-/**
- * Releases any SDCard mount lock that we might have.
- *
- * This once again allows the SDCard to be shared with the PC.
- */
-function releaseSDCardMountLock() {
-  if (AppConstants.platform != "gonk") {
-    throw Cr.NS_ERROR_UNEXPECTED;
-  }
-  if (gSDCardMountLock) {
-    gSDCardMountLock.unlock();
-    gSDCardMountLock = null;
-  }
-}
-
-/**
  * Determines if the service should be used to attempt an update
  * or not.
  *
  * @return  true if the service should be used for updates.
  */
 function shouldUseService() {
   // This function will return true if the mantenance service should be used if
   // all of the following conditions are met:
@@ -1055,39 +935,27 @@ function cleanUpUpdatesDir(aRemovePatchF
           " to " + FILE_LAST_UPDATE_LOG);
     }
   }
 
   if (aRemovePatchFiles) {
     let dirEntries = updateDir.directoryEntries;
     while (dirEntries.hasMoreElements()) {
       let file = dirEntries.getNext().QueryInterface(Ci.nsIFile);
-      if (AppConstants.platform == "gonk") {
-        if (file.leafName == FILE_UPDATE_LINK) {
-          let linkedFile = getFileFromUpdateLink(updateDir);
-          if (linkedFile && linkedFile.exists()) {
-            linkedFile.remove(false);
-          }
-        }
-      }
-
       // Now, recursively remove this file.  The recursive removal is needed for
       // Mac OSX because this directory will contain a copy of updater.app,
       // which is itself a directory and the MozUpdater directory on platforms
       // other than Windows.
       try {
         file.remove(true);
       } catch (e) {
         LOG("cleanUpUpdatesDir - failed to remove file " + file.path);
       }
     }
   }
-  if (AppConstants.platform == "gonk") {
-    releaseSDCardMountLock();
-  }
 }
 
 /**
  * Clean up updates list and the updates directory.
  */
 function cleanupActiveUpdate() {
   // Move the update from the Active Update list into the Past Updates list.
   var um = Cc["@mozilla.org/updates/update-manager;1"].
@@ -1345,19 +1213,16 @@ function pingStateAndStatusCodes(aUpdate
         stateCode = 5;
         break;
       case STATE_APPLYING:
         stateCode = 6;
         break;
       case STATE_APPLIED:
         stateCode = 7;
         break;
-      case STATE_APPLIED_OS:
-        stateCode = 8;
-        break;
       case STATE_APPLIED_SERVICE:
         stateCode = 9;
         break;
       case STATE_SUCCEEDED:
         stateCode = 10;
         break;
       case STATE_DOWNLOAD_FAILED:
         stateCode = 11;
@@ -1825,22 +1690,16 @@ const UpdateServiceFactory = {
  * UpdateService
  * A Service for managing the discovery and installation of software updates.
  * @constructor
  */
 function UpdateService() {
   LOG("Creating UpdateService");
   Services.obs.addObserver(this, "xpcom-shutdown");
   Services.prefs.addObserver(PREF_APP_UPDATE_LOG, this);
-  if (AppConstants.platform == "gonk") {
-    // PowerManagerService::SyncProfile (which is called for Reboot, PowerOff
-    // and Restart) sends the profile-change-net-teardown event. We can then
-    // pause the download in a similar manner to xpcom-shutdown.
-    Services.obs.addObserver(this, "profile-change-net-teardown");
-  }
 }
 
 UpdateService.prototype = {
   /**
    * The downloader we are using to download updates. There is only ever one of
    * these.
    */
   _downloader: null,
@@ -1912,17 +1771,16 @@ UpdateService.prototype = {
       case "network:offline-status-changed":
         this._offlineStatusChanged(data);
         break;
       case "nsPref:changed":
         if (data == PREF_APP_UPDATE_LOG) {
           gLogEnabled = getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
         }
         break;
-      case "profile-change-net-teardown": // fall thru
       case "xpcom-shutdown":
         Services.obs.removeObserver(this, topic);
         Services.prefs.removeObserver(PREF_APP_UPDATE_LOG, this);
 
         if (AppConstants.platform == "win" && gUpdateMutexHandle) {
           // If we hold the update mutex, let it go!
           // The OS would clean this up sometime after shutdown,
           // but that would have no guarantee on timing.
@@ -1978,33 +1836,16 @@ UpdateService.prototype = {
     // STATE_NONE status typically means that the update.status file is present
     // but a background download error occurred.
     if (status == STATE_NONE) {
       LOG("UpdateService:_postUpdateProcessing - no status, no update");
       cleanupActiveUpdate();
       return;
     }
 
-    if (AppConstants.platform == "gonk") {
-      // This code is called very early in the boot process, before we've even
-      // had a chance to setup the UI so we can give feedback to the user.
-      //
-      // Since the download may be occuring over a link which has associated
-      // cost, we want to require user-consent before resuming the download.
-      // Also, applying an already downloaded update now is undesireable,
-      // since the phone will look dead while the update is being applied.
-      // Applying the update can take several minutes. Instead we wait until
-      // the UI is initialized so it is possible to give feedback to and get
-      // consent to update from the user.
-      if (isInterruptedUpdate(status)) {
-        LOG("UpdateService:_postUpdateProcessing - interrupted update detected - wait for user consent");
-        return;
-      }
-    }
-
     if (status == STATE_DOWNLOADING) {
       LOG("UpdateService:_postUpdateProcessing - patch found in downloading " +
           "state");
       if (update && update.state != STATE_SUCCEEDED) {
         // Resume download
         status = this.downloadUpdate(update, true);
         if (status == STATE_NONE)
           cleanupActiveUpdate();
@@ -2036,44 +1877,16 @@ UpdateService.prototype = {
       } else { // We get here even if we don't have an update object
         LOG("UpdateService:_postUpdateProcessing - patch found in applying " +
             "state for the second time");
         cleanupActiveUpdate();
       }
       return;
     }
 
-    if (AppConstants.platform == "gonk") {
-      // The update is only applied but not selected to be installed
-      if (status == STATE_APPLIED && update && update.isOSUpdate) {
-        LOG("UpdateService:_postUpdateProcessing - update staged as applied found");
-        return;
-      }
-
-      if (status == STATE_APPLIED_OS && update && update.isOSUpdate) {
-        // In gonk, we need to check for OS update status after startup, since
-        // the recovery partition won't write to update.status for us
-        let recoveryService = Cc["@mozilla.org/recovery-service;1"].
-                              getService(Ci.nsIRecoveryService);
-        let fotaStatus = recoveryService.getFotaUpdateStatus();
-        switch (fotaStatus) {
-          case Ci.nsIRecoveryService.FOTA_UPDATE_SUCCESS:
-            status = STATE_SUCCEEDED;
-            break;
-          case Ci.nsIRecoveryService.FOTA_UPDATE_FAIL:
-            status = STATE_FAILED + ": " + FOTA_GENERAL_ERROR;
-            break;
-          case Ci.nsIRecoveryService.FOTA_UPDATE_UNKNOWN:
-          default:
-            status = STATE_FAILED + ": " + FOTA_UNKNOWN_ERROR;
-            break;
-        }
-      }
-    }
-
     if (!update) {
       if (status != STATE_SUCCEEDED) {
         LOG("UpdateService:_postUpdateProcessing - previous patch failed " +
             "and no patch available");
         cleanupActiveUpdate();
         return;
       }
       update = new Update(null);
@@ -2522,21 +2335,16 @@ UpdateService.prototype = {
    *          An array of available updates
    */
   _selectAndInstallUpdate: function AUS__selectAndInstallUpdate(updates) {
     // Return early if there's an active update. The user is already aware and
     // is downloading or performed some user action to prevent notification.
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     if (um.activeUpdate) {
-      if (AppConstants.platform == "gonk") {
-        // For gonk, the user isn't necessarily aware of the update, so we need
-        // to show the prompt to make sure.
-        this._showPrompt(um.activeUpdate);
-      }
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_HAS_ACTIVEUPDATE);
       return;
     }
 
     var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
     if (!updateEnabled) {
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_PREF_DISABLED);
       LOG("UpdateService:_selectAndInstallUpdate - not prompting because " +
@@ -2729,31 +2537,16 @@ UpdateService.prototype = {
       if (update.isCompleteUpdate == this._downloader.isCompleteUpdate &&
           background == this._downloader.background) {
         LOG("UpdateService:downloadUpdate - no support for downloading more " +
             "than one update at a time");
         return readStatusFile(getUpdatesDir());
       }
       this._downloader.cancel();
     }
-    if (AppConstants.platform == "gonk") {
-      let um = Cc["@mozilla.org/updates/update-manager;1"].
-               getService(Ci.nsIUpdateManager);
-      let activeUpdate = um.activeUpdate;
-      if (activeUpdate &&
-          (activeUpdate.appVersion != update.appVersion ||
-           activeUpdate.buildID != update.buildID)) {
-        // We have an activeUpdate (which presumably was interrupted), and are
-        // about start downloading a new one. Make sure we remove all traces
-        // of the active one (otherwise we'll start appending the new update.mar
-        // the the one that's been partially downloaded).
-        LOG("UpdateService:downloadUpdate - removing stale active update.");
-        cleanupActiveUpdate();
-      }
-    }
     // Set the previous application version prior to downloading the update.
     update.previousAppVersion = Services.appinfo.version;
     this._downloader = new Downloader(background, this);
     return this._downloader.downloadUpdate(update);
   },
 
   /**
    * See nsIUpdateService.idl
@@ -2777,65 +2570,16 @@ UpdateService.prototype = {
 
   /**
    * See nsIUpdateService.idl
    */
   get isDownloading() {
     return this._downloader && this._downloader.isBusy;
   },
 
-  /**
-   * See nsIUpdateService.idl
-   */
-  applyOsUpdate: function AUS_applyOsUpdate(aUpdate) {
-    if (!aUpdate.isOSUpdate || aUpdate.state != STATE_APPLIED) {
-      aUpdate.statusText = "fota-state-error";
-      throw Cr.NS_ERROR_FAILURE;
-    }
-
-    aUpdate.QueryInterface(Ci.nsIWritablePropertyBag);
-    let osApplyToDir = aUpdate.getProperty("osApplyToDir");
-
-    if (!osApplyToDir) {
-      LOG("UpdateService:applyOsUpdate - Error: osApplyToDir is not defined" +
-          "in the nsIUpdate!");
-      pingStateAndStatusCodes(aUpdate, false,
-                              STATE_FAILED + ": " + FOTA_FILE_OPERATION_ERROR);
-      handleUpdateFailure(aUpdate, FOTA_FILE_OPERATION_ERROR);
-      return;
-    }
-
-    let updateFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
-    updateFile.initWithPath(osApplyToDir + "/update.zip");
-    if (!updateFile.exists()) {
-      LOG("UpdateService:applyOsUpdate - Error: OS update is not found at " +
-          updateFile.path);
-      pingStateAndStatusCodes(aUpdate, false,
-                              STATE_FAILED + ": " + FOTA_FILE_OPERATION_ERROR);
-      handleUpdateFailure(aUpdate, FOTA_FILE_OPERATION_ERROR);
-      return;
-    }
-
-    writeStatusFile(getUpdatesDir(), aUpdate.state = STATE_APPLIED_OS);
-    LOG("UpdateService:applyOsUpdate - Rebooting into recovery to apply " +
-        "FOTA update: " + updateFile.path);
-    try {
-      let recoveryService = Cc["@mozilla.org/recovery-service;1"]
-                            .getService(Ci.nsIRecoveryService);
-      recoveryService.installFotaUpdate(updateFile.path);
-    } catch (e) {
-      LOG("UpdateService:applyOsUpdate - Error: Couldn't reboot into recovery" +
-          " to apply FOTA update " + updateFile.path);
-      pingStateAndStatusCodes(aUpdate, false,
-                              STATE_FAILED + ": " + FOTA_RECOVERY_ERROR);
-      writeStatusFile(getUpdatesDir(), aUpdate.state = STATE_APPLIED);
-      handleUpdateFailure(aUpdate, FOTA_RECOVERY_ERROR);
-    }
-  },
-
   classID: UPDATESERVICE_CID,
   classInfo: XPCOMUtils.generateCI({classID: UPDATESERVICE_CID,
                                     contractID: UPDATESERVICE_CONTRACTID,
                                     interfaces: [Ci.nsIApplicationUpdateService,
                                                  Ci.nsITimerCallback,
                                                  Ci.nsIObserver],
                                     flags: Ci.nsIClassInfo.SINGLETON}),
 
@@ -3151,31 +2895,16 @@ UpdateManager.prototype = {
     }
 
     // Send an observer notification which the app update doorhanger uses to
     // display a restart notification
     LOG("UpdateManager:refreshUpdateStatus - Notifying observers that " +
         "the update was staged. topic: update-staged, status: " + update.state);
     Services.obs.notifyObservers(update, "update-staged", update.state);
 
-    if (AppConstants.platform == "gonk") {
-      // Do this after everything else, since it will likely cause the app to
-      // shut down.
-      if (update.state == STATE_APPLIED) {
-        // Notify the user that an update has been staged and is ready for
-        // installation (i.e. that they should restart the application). We do
-        // not notify on failed update attempts.
-        let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
-                       createInstance(Ci.nsIUpdatePrompt);
-        prompter.showUpdateDownloaded(update, true);
-      } else {
-        releaseSDCardMountLock();
-      }
-      return;
-    }
     // Only prompt when the UI isn't already open.
     let windowType = getPref("getCharPref", PREF_APP_UPDATE_ALTWINDOWTYPE, null);
     if (Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME) ||
         windowType && Services.wm.getMostRecentWindow(windowType)) {
       return;
     }
 
     if (update.state == STATE_APPLIED ||
@@ -3517,19 +3246,16 @@ Downloader.prototype = {
   cancel: function Downloader_cancel(cancelError) {
     LOG("Downloader: cancel");
     if (cancelError === undefined) {
       cancelError = Cr.NS_BINDING_ABORTED;
     }
     if (this._request && this._request instanceof Ci.nsIRequest) {
       this._request.cancel(cancelError);
     }
-    if (AppConstants.platform == "gonk") {
-      releaseSDCardMountLock();
-    }
   },
 
   /**
    * Whether or not a patch has been downloaded and staged for installation.
    */
   get patchIsStaged() {
     var readState = readStatusFile(getUpdatesDir());
     // Note that if we decide to download and apply new updates after another
@@ -3648,29 +3374,20 @@ Downloader.prototype = {
     var useComplete = false;
     if (selectedPatch) {
       LOG("Downloader:_selectPatch - found existing patch with state: " +
           state);
       if (state == STATE_DOWNLOADING) {
         LOG("Downloader:_selectPatch - resuming download");
         return selectedPatch;
       }
-
-      if (AppConstants.platform == "gonk") {
-        if (state == STATE_PENDING || state == STATE_APPLYING) {
-          LOG("Downloader:_selectPatch - resuming interrupted apply");
-          return selectedPatch;
-        }
-        if (state == STATE_APPLIED) {
-          LOG("Downloader:_selectPatch - already downloaded and staged");
-          return null;
-        }
-      } else if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
-                 state == STATE_PENDING_ELEVATE) {
-        LOG("Downloader:_selectPatch - already downloaded and staged");
+      if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
+          state == STATE_PENDING_ELEVATE || state == STATE_APPLIED ||
+          state == STATE_APPLIED_SERVICE) {
+        LOG("Downloader:_selectPatch - already downloaded");
         return null;
       }
 
       if (update && selectedPatch.type == "complete") {
         // This is a pretty fatal error.  Just bail.
         LOG("Downloader:_selectPatch - failed to apply complete patch!");
         writeStatusFile(updateDir, STATE_NONE);
         writeVersionFile(getUpdatesDir(), null);
@@ -3717,35 +3434,16 @@ Downloader.prototype = {
   /**
    * Whether or not we are currently downloading something.
    */
   get isBusy() {
     return this._request != null;
   },
 
   /**
-   * Get the nsIFile to use for downloading the active update's selected patch
-   */
-  _getUpdateArchiveFile: function Downloader__getUpdateArchiveFile() {
-    var updateArchive;
-    if (AppConstants.platform == "gonk") {
-      try {
-        updateArchive = FileUtils.getDir(KEY_UPDATE_ARCHIVE_DIR, [], true);
-      } catch (e) {
-        return null;
-      }
-    } else {
-      updateArchive = getUpdatesDir().clone();
-    }
-
-    updateArchive.append(FILE_UPDATE_MAR);
-    return updateArchive;
-  },
-
-  /**
    * Download and stage the given update.
    * @param   update
    *          A nsIUpdate object to download a patch for. Cannot be null.
    */
   downloadUpdate: function Downloader_downloadUpdate(update) {
     LOG("UpdateService:_downloadUpdate");
     if (!update) {
       AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE);
@@ -3761,106 +3459,18 @@ Downloader.prototype = {
     this._patch = this._selectPatch(update, updateDir);
     if (!this._patch) {
       LOG("Downloader:downloadUpdate - no patch to download");
       AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE_PATCH);
       return readStatusFile(updateDir);
     }
     this.isCompleteUpdate = this._patch.type == "complete";
 
-    let patchFile = null;
-
-    // Only used by gonk
-    let status = STATE_NONE;
-    if (AppConstants.platform == "gonk") {
-      status = readStatusFile(updateDir);
-      if (isInterruptedUpdate(status)) {
-        LOG("Downloader:downloadUpdate - interruptted update");
-        // The update was interrupted. Try to locate the existing patch file.
-        // For an interrupted download, this allows a resume rather than a
-        // re-download.
-        patchFile = getFileFromUpdateLink(updateDir);
-        if (!patchFile) {
-          // No link file. We'll just assume that the update.mar is in the
-          // update directory.
-          patchFile = updateDir.clone();
-          patchFile.append(FILE_UPDATE_MAR);
-        }
-        if (patchFile.exists()) {
-          LOG("Downloader:downloadUpdate - resuming with patchFile " + patchFile.path);
-          if (patchFile.fileSize == this._patch.size) {
-            LOG("Downloader:downloadUpdate - patchFile appears to be fully downloaded");
-            // Bump the status along so that we don't try to redownload again.
-            if (getElevationRequired()) {
-              status = STATE_PENDING_ELEVATE;
-            } else {
-              status = STATE_PENDING;
-            }
-          }
-        } else {
-          LOG("Downloader:downloadUpdate - patchFile " + patchFile.path +
-              " doesn't exist - performing full download");
-          // The patchfile doesn't exist, we might as well treat this like
-          // a new download.
-          patchFile = null;
-        }
-        if (patchFile && status != STATE_DOWNLOADING) {
-          // It looks like the patch was downloaded, but got interrupted while it
-          // was being verified or applied. So we'll fake the downloading portion.
-
-          if (getElevationRequired()) {
-            writeStatusFile(updateDir, STATE_PENDING_ELEVATE);
-          } else {
-            writeStatusFile(updateDir, STATE_PENDING);
-          }
-
-          // Since the code expects the onStopRequest callback to happen
-          // asynchronously (And you have to call AUS_addDownloadListener
-          // after calling AUS_downloadUpdate) we need to defer this.
-
-          this._downloadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-          this._downloadTimer.initWithCallback(function() {
-            this._downloadTimer = null;
-            // Send a fake onStopRequest. Filling in the destination allows
-            // _verifyDownload to work, and then the update will be applied.
-            this._request = {destination: patchFile};
-            this.onStopRequest(this._request, null, Cr.NS_OK);
-          }.bind(this), 0, Ci.nsITimer.TYPE_ONE_SHOT);
-
-          // Returning STATE_DOWNLOADING makes UpdatePrompt think we're
-          // downloading. The onStopRequest that we spoofed above will make it
-          // look like the download finished.
-          return STATE_DOWNLOADING;
-        }
-      }
-    }
-
-    if (!patchFile) {
-      // Find a place to put the patchfile that we're going to download.
-      patchFile = this._getUpdateArchiveFile();
-    }
-    if (!patchFile) {
-      AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
-                               AUSTLMY.DWNLD_ERR_NO_PATCH_FILE);
-      return STATE_NONE;
-    }
-
-    if (AppConstants.platform == "gonk") {
-      if (patchFile.path.indexOf(updateDir.path) != 0) {
-        // The patchFile is in a directory which is different from the
-        // updateDir, create a link file.
-        writeLinkFile(updateDir, patchFile);
-
-        if (!isInterruptedUpdate(status) && patchFile.exists()) {
-          // Remove stale patchFile
-          patchFile.remove(false);
-        }
-      }
-    }
-
+    let patchFile = getUpdatesDir().clone();
+    patchFile.append(FILE_UPDATE_MAR);
     update.QueryInterface(Ci.nsIPropertyBag);
     let interval = this.background ? update.getProperty("backgroundInterval")
                                    : DOWNLOAD_FOREGROUND_INTERVAL;
 
     LOG("Downloader:downloadUpdate - url: " + this._patch.URL + ", path: " +
         patchFile.path + ", interval: " + interval);
     var uri = Services.io.newURI(this._patch.URL);
 
@@ -4122,23 +3732,16 @@ Downloader.prototype = {
       state = STATE_DOWNLOAD_FAILED;
 
       // XXXben - if |request| (The Incremental Download) provided a means
       // for accessing the http channel we could do more here.
 
       this._update.statusText = getStatusTextFromCode(status,
                                                       Cr.NS_BINDING_FAILED);
 
-      if (AppConstants.platform == "gonk") {
-        // bug891009: On FirefoxOS, manaully retry OTA download will reuse
-        // the Update object. We need to remove selected patch so that download
-        // can be triggered again successfully.
-        this._update.selectedPatch.selected = false;
-      }
-
       // Destroy the updates directory, since we're done with it.
       cleanUpUpdatesDir();
 
       deleteActiveUpdate = true;
     }
     LOG("Downloader:onStopRequest - setting state to: " + state);
     this._patch.state = state;
     var um = Cc["@mozilla.org/updates/update-manager;1"].
@@ -4208,23 +3811,16 @@ Downloader.prototype = {
           this._update.QueryInterface(Ci.nsIWritablePropertyBag);
           if (this._update.getProperty("foregroundDownload") == "true") {
             let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
                            createInstance(Ci.nsIUpdatePrompt);
             prompter.showUpdateError(this._update);
           }
         }
 
-        if (AppConstants.platform == "gonk") {
-          // We always forward errors in B2G, since Gaia controls the update UI
-          let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
-                         createInstance(Ci.nsIUpdatePrompt);
-          prompter.showUpdateError(this._update);
-        }
-
         // Prevent leaking the update object (bug 454964).
         this._update = null;
       }
       // A complete download has been initiated or the failure was handled.
       return;
     }
 
     if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||