Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 25 Nov 2014 16:57:18 -0500
changeset 217489 ced1402861b8e0d0c59b42ba379281bd3a80926b
parent 217434 bca4892bbe036cbb5f46aa136f4eb12b5147e2fd (current diff)
parent 217488 997d1d16fc19878c0a370d972e2353184b59c09b (diff)
child 217496 05c138a502c2841c718b3263fcba7c80fe151049
child 217521 b98e55fb98b798bea3fdd0390baba720db2836f3
child 217653 48e460577514322a0d6875230a75464bbec37fcb
push id27882
push userryanvm@gmail.com
push dateTue, 25 Nov 2014 21:56:56 +0000
treeherdermozilla-central@ced1402861b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
ced1402861b8 / 36.0a1 / 20141126030207 / files
nightly linux64
ced1402861b8 / 36.0a1 / 20141126030207 / files
nightly mac
ced1402861b8 / 36.0a1 / 20141126030207 / files
nightly win32
ced1402861b8 / 36.0a1 / 20141126030207 / files
nightly win64
ced1402861b8 / 36.0a1 / 20141126030207 / 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 inbound to m-c. a=merge CLOSED TREE
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -356,39 +356,79 @@ nsImageLoadingContent::SetLoadingEnabled
 NS_IMETHODIMP
 nsImageLoadingContent::GetImageBlockingStatus(int16_t* aStatus)
 {
   NS_PRECONDITION(aStatus, "Null out param");
   *aStatus = ImageBlockingStatus();
   return NS_OK;
 }
 
+static void
+ReplayImageStatus(imgIRequest* aRequest, imgINotificationObserver* aObserver)
+{
+  if (!aRequest) {
+    return;
+  }
+
+  uint32_t status = 0;
+  nsresult rv = aRequest->GetImageStatus(&status);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  if (status & imgIRequest::STATUS_SIZE_AVAILABLE) {
+    aObserver->Notify(aRequest, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
+  }
+  if (status & imgIRequest::STATUS_FRAME_COMPLETE) {
+    aObserver->Notify(aRequest, imgINotificationObserver::FRAME_COMPLETE, nullptr);
+  }
+  if (status & imgIRequest::STATUS_HAS_TRANSPARENCY) {
+    aObserver->Notify(aRequest, imgINotificationObserver::HAS_TRANSPARENCY, nullptr);
+  }
+  if (status & imgIRequest::STATUS_IS_ANIMATED) {
+    aObserver->Notify(aRequest, imgINotificationObserver::IS_ANIMATED, nullptr);
+  }
+  if (status & imgIRequest::STATUS_DECODE_COMPLETE) {
+    aObserver->Notify(aRequest, imgINotificationObserver::DECODE_COMPLETE, nullptr);
+  }
+  if (status & imgIRequest::STATUS_LOAD_COMPLETE) {
+    aObserver->Notify(aRequest, imgINotificationObserver::LOAD_COMPLETE, nullptr);
+  }
+}
+
 NS_IMETHODIMP
 nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver)
 {
   NS_ENSURE_ARG_POINTER(aObserver);
 
   if (!mObserverList.mObserver) {
+    // Don't touch the linking of the list!
     mObserverList.mObserver = aObserver;
-    // Don't touch the linking of the list!
+
+    ReplayImageStatus(mCurrentRequest, aObserver);
+    ReplayImageStatus(mPendingRequest, aObserver);
+
     return NS_OK;
   }
 
   // otherwise we have to create a new entry
 
   ImageObserver* observer = &mObserverList;
   while (observer->mNext) {
     observer = observer->mNext;
   }
 
   observer->mNext = new ImageObserver(aObserver);
   if (! observer->mNext) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  ReplayImageStatus(mCurrentRequest, aObserver);
+  ReplayImageStatus(mPendingRequest, aObserver);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::RemoveObserver(imgINotificationObserver* aObserver)
 {
   NS_ENSURE_ARG_POINTER(aObserver);
 
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -103,16 +103,33 @@ AppendToString(std::stringstream& aStrea
   aStream << pfx;
   aStream << nsPrintfCString(
     "(x=%d, y=%d, w=%d, h=%d)",
     r.x, r.y, r.width, r.height).get();
   aStream << sfx;
 }
 
 void
+AppendToString(std::stringstream& aStream, const nsRegion& r,
+               const char* pfx, const char* sfx)
+{
+  aStream << pfx;
+
+  nsRegionRectIterator it(r);
+  aStream << "< ";
+  while (const nsRect* sr = it.Next()) {
+    AppendToString(aStream, *sr);
+    aStream << "; ";
+  }
+  aStream << ">";
+
+  aStream << sfx;
+}
+
+void
 AppendToString(std::stringstream& aStream, const nsIntRegion& r,
                const char* pfx, const char* sfx)
 {
   aStream << pfx;
 
   nsIntRegionRectIterator it(r);
   aStream << "< ";
   while (const nsIntRect* sr = it.Next()) {
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -8,17 +8,17 @@
 
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GraphicsFilter.h"             // for GraphicsFilter
 #include "mozilla/gfx/Point.h"          // for IntSize, etc
 #include "mozilla/gfx/Types.h"          // for Filter, SurfaceFormat
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags
 #include "nsAString.h"
 #include "nsPrintfCString.h"            // for nsPrintfCString
-#include "nsRegion.h"                   // for nsIntRegion
+#include "nsRegion.h"                   // for nsRegion, nsIntRegion
 #include "nscore.h"                     // for nsACString, etc
 
 struct gfxRGBA;
 struct nsIntPoint;
 struct nsIntRect;
 struct nsIntSize;
 
 namespace mozilla {
@@ -89,16 +89,20 @@ AppendToString(std::stringstream& aStrea
   aStream << pfx;
   aStream << nsPrintfCString(
     "(x=%d, y=%d, w=%d, h=%d)",
     r.x, r.y, r.width, r.height).get();
   aStream << sfx;
 }
 
 void
+AppendToString(std::stringstream& aStream, const nsRegion& r,
+               const char* pfx="", const char* sfx="");
+
+void
 AppendToString(std::stringstream& aStream, const nsIntRegion& r,
                const char* pfx="", const char* sfx="");
 
 void
 AppendToString(std::stringstream& aStream, const EventRegions& e,
                const char* pfx="", const char* sfx="");
 
 void
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -4,16 +4,17 @@
  * 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 "InputQueue.h"
 
 #include "AsyncPanZoomController.h"
 #include "gfxPrefs.h"
 #include "InputBlockState.h"
+#include "LayersLogging.h"
 #include "OverscrollHandoffState.h"
 
 #define INPQ_LOG(...)
 // #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -202,23 +202,21 @@ CompositableParentManager::ReceiveCompos
     case CompositableOperation::TOpUseTexture: {
       const OpUseTexture& op = aEdit.get_OpUseTexture();
       CompositableHost* compositable = AsCompositable(op);
       RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent());
 
       MOZ_ASSERT(tex.get());
       compositable->UseTextureHost(tex);
 
-      if (IsAsync()) {
+      if (IsAsync() && compositable->GetLayer()) {
         ScheduleComposition(op);
         // Async layer updates don't trigger invalidation, manually tell the layer
         // that its content have changed.
-        if (compositable->GetLayer()) {
-          compositable->GetLayer()->SetInvalidRectToVisibleRegion();
-        }
+        compositable->GetLayer()->SetInvalidRectToVisibleRegion();
       }
       break;
     }
     case CompositableOperation::TOpUseComponentAlphaTextures: {
       const OpUseComponentAlphaTextures& op = aEdit.get_OpUseComponentAlphaTextures();
       CompositableHost* compositable = AsCompositable(op);
       RefPtr<TextureHost> texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent());
       RefPtr<TextureHost> texOnWhite = TextureHost::AsTextureHost(op.textureOnWhiteParent());
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -823,16 +823,28 @@ nsGIFDecoder2::WriteInternal(const char*
       mGIFStruct.is_transparent = *q & 0x1;
       mGIFStruct.tpixel = q[3];
       mGIFStruct.disposal_method = ((*q) >> 2) & 0x7;
       // Some specs say 3rd bit (value 4), other specs say value 3
       // Let's choose 3 (the more popular)
       if (mGIFStruct.disposal_method == 4) {
         mGIFStruct.disposal_method = 3;
       }
+
+      {
+        int32_t method =
+          FrameBlender::FrameDisposalMethod(mGIFStruct.disposal_method);
+        if (method == FrameBlender::kDisposeClearAll ||
+            method == FrameBlender::kDisposeClear) {
+          // We may have to display the background under this image during
+          // animation playback, so we regard it as transparent.
+          PostHasTransparency();
+        }
+      }
+
       mGIFStruct.delay_time = GETINT16(q + 1) * 10;
       GETN(1, gif_consume_block);
       break;
 
     case gif_comment_extension:
       if (*q) {
         GETN(*q, gif_consume_comment);
       } else {
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -54,16 +54,19 @@ nsIconDecoder::WriteInternal(const char*
 
       case iconStateHaveHeight:
 
         // Grab the Height
         mHeight = (uint8_t)*aBuffer;
 
         // Post our size to the superclass
         PostSize(mWidth, mHeight);
+
+        PostHasTransparency();
+
         if (HasError()) {
           // Setting the size led to an error.
           mState = iconStateFinished;
           return;
         }
 
         // If We're doing a size decode, we're done
         if (IsSizeDecode()) {
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -150,19 +150,27 @@ void nsPNGDecoder::CreateFrame(png_uint_
   if (format == gfx::SurfaceFormat::B8G8R8A8) {
     PostHasTransparency();
   }
 
   // Our first full frame is automatically created by the image decoding
   // infrastructure. Just use it as long as it matches up.
   nsIntRect neededRect(x_offset, y_offset, width, height);
   nsRefPtr<imgFrame> currentFrame = GetCurrentFrame();
-  if (mNumFrames != 0 || !currentFrame->GetRect().IsEqualEdges(neededRect)) {
+  if (!currentFrame->GetRect().IsEqualEdges(neededRect)) {
+    if (mNumFrames == 0) {
+      // We need padding on the first frame, which means that we don't draw into
+      // part of the image at all. Report that as transparency.
+      PostHasTransparency();
+    }
+
     NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
-  } else if (mNumFrames == 0) {
+  } else if (mNumFrames != 0) {
+    NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
+  } else {
     // Our preallocated frame matches up, with the possible exception of alpha.
     if (format == gfx::SurfaceFormat::B8G8R8X8) {
       currentFrame->SetHasNoAlpha();
     }
   }
 
   mFrameRect = neededRect;
   mFrameHasNoAlpha = true;
@@ -171,16 +179,22 @@ void nsPNGDecoder::CreateFrame(png_uint_
          ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created "
           "image frame with %dx%d pixels in container %p",
           width, height,
           &mImage));
 
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
     mAnimInfo = AnimFrameInfo(mPNG, mInfo);
+
+    if (mAnimInfo.mDispose == FrameBlender::kDisposeClear) {
+      // We may have to display the background under this image during
+      // animation playback, so we regard it as transparent.
+      PostHasTransparency();
+    }
   }
 #endif
 }
 
 // set timeout and frame disposal method for the current frame
 void
 nsPNGDecoder::EndImageFrame()
 {
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -64,17 +64,17 @@ native nsIntSizeByVal(nsIntSize);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(f8bb7671-5f36-490b-b828-3f4c6ad38665)]
+[scriptable, builtinclass, uuid(14ea6fa5-183e-4409-ac88-110bd2e05292)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -210,22 +210,19 @@ interface imgIContainer : nsISupports
    *
    * @param aWhichFrame Frame specifier of the FRAME_* variety.
    * @param aFlags Flags of the FLAG_* variety
    */
   [noscript, notxpcom] TempRefSourceSurface getFrame(in uint32_t aWhichFrame,
                                                      in uint32_t aFlags);
 
   /**
-   * Whether the given frame is opaque; that is, needs the background painted
-   * behind it.
-   *
-   * @param aWhichFrame Frame specifier of the FRAME_* variety.
+   * Whether this image is opaque (i.e., needs a background painted behind it).
    */
-  [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame);
+  [notxpcom] boolean isOpaque();
 
   /**
    * Attempts to create an ImageContainer (and Image) containing the current
    * frame. Only valid for RASTER type images.
    */
   [noscript] ImageContainer getImageContainer(in LayerManager aManager);
 
   /**
--- a/image/public/imgIRequest.idl
+++ b/image/public/imgIRequest.idl
@@ -14,17 +14,17 @@ interface nsIPrincipal;
 
 /**
  * imgIRequest interface
  *
  * @author Stuart Parmenter <stuart@mozilla.com>
  * @version 0.1
  * @see imagelib2
  */
-[scriptable, builtinclass, uuid(dc61f0ea-4139-4c2a-ae69-cec82d33e089)]
+[scriptable, builtinclass, uuid(83a7708b-5c35-409f-bab3-7fc08be6a264)]
 interface imgIRequest : nsIRequest
 {
   /**
    * the image container...
    * @return the image object associated with the request.
    * @attention NEED DOCS
    */
   readonly attribute imgIContainer image;
@@ -49,25 +49,31 @@ interface imgIRequest : nsIRequest
    *
    * STATUS_DECODE_STARTED: The decoding process has begun, but not yet
    * finished.
    *
    * STATUS_FRAME_COMPLETE: The first frame has been
    * completely decoded.
    *
    * STATUS_DECODE_COMPLETE: The whole image has been decoded.
+   *
+   * STATUS_IS_ANIMATED: The image is animated.
+   *
+   * STATUS_HAS_TRANSPARENCY: The image is partially or completely transparent.
    */
   //@{
   const long STATUS_NONE             = 0x0;
   const long STATUS_SIZE_AVAILABLE   = 0x1;
   const long STATUS_LOAD_COMPLETE    = 0x2;
   const long STATUS_ERROR            = 0x4;
   const long STATUS_DECODE_STARTED   = 0x8;
   const long STATUS_FRAME_COMPLETE   = 0x10;
   const long STATUS_DECODE_COMPLETE  = 0x20;
+  const long STATUS_IS_ANIMATED      = 0x40;
+  const long STATUS_HAS_TRANSPARENCY = 0x80;
   //@}
 
   /**
    * Status flags of the STATUS_* variety.
    */
   readonly attribute unsigned long imageStatus;
 
   /*
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -210,16 +210,25 @@ protected:
 
   // Called by decoders when they determine the size of the image. Informs
   // the image of its size and sends notifications.
   void PostSize(int32_t aWidth,
                 int32_t aHeight,
                 Orientation aOrientation = Orientation());
 
   // Called by decoders if they determine that the image has transparency.
+  //
+  // This should be fired as early as possible to allow observers to do things
+  // that affect content, so it's necessarily pessimistic - if there's a
+  // possibility that the image has transparency, for example because its header
+  // specifies that it has an alpha channel, we fire PostHasTransparency
+  // immediately. PostFrameStop's aFrameAlpha argument, on the other hand, is
+  // only used internally to ImageLib. Because PostFrameStop isn't delivered
+  // until the entire frame has been decoded, decoders may take into account the
+  // actual contents of the frame and give a more accurate result.
   void PostHasTransparency();
 
   // Called by decoders when they begin a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
   void PostFrameStart();
 
   // Called by decoders when they end a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
--- a/image/src/DynamicImage.cpp
+++ b/image/src/DynamicImage.cpp
@@ -223,17 +223,17 @@ DynamicImage::GetFrame(uint32_t aWhichFr
                      aWhichFrame, GraphicsFilter::FILTER_NEAREST,
                      Nothing(), aFlags);
 
   NS_ENSURE_SUCCESS(rv, nullptr);
   return dt->Snapshot();
 }
 
 NS_IMETHODIMP_(bool)
-DynamicImage::FrameIsOpaque(uint32_t aWhichFrame)
+DynamicImage::IsOpaque()
 {
   // XXX(seth): For performance reasons it'd be better to return true here, but
   // I'm not sure how we can guarantee it for an arbitrary gfxDrawable.
   return false;
 }
 
 NS_IMETHODIMP
 DynamicImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
--- a/image/src/FrozenImage.cpp
+++ b/image/src/FrozenImage.cpp
@@ -45,22 +45,16 @@ FrozenImage::GetAnimated(bool* aAnimated
 
 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 FrozenImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
 {
   return InnerImage()->GetFrame(FRAME_FIRST, aFlags);
 }
 
-NS_IMETHODIMP_(bool)
-FrozenImage::FrameIsOpaque(uint32_t aWhichFrame)
-{
-  return InnerImage()->FrameIsOpaque(FRAME_FIRST);
-}
-
 NS_IMETHODIMP
 FrozenImage::GetImageContainer(layers::LayerManager* aManager,
                                layers::ImageContainer** _retval)
 {
   // XXX(seth): GetImageContainer does not currently support anything but the
   // current frame. We work around this by always returning null, but if it ever
   // turns out that FrozenImage is widely used on codepaths that can actually
   // benefit from GetImageContainer, it would be a good idea to fix that method
--- a/image/src/FrozenImage.h
+++ b/image/src/FrozenImage.h
@@ -33,17 +33,16 @@ public:
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
   virtual void DecrementAnimationConsumers() MOZ_OVERRIDE;
 
   NS_IMETHOD GetAnimated(bool* aAnimated) MOZ_OVERRIDE;
   NS_IMETHOD_(TemporaryRef<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
-  NS_IMETHOD_(bool) FrameIsOpaque(uint32_t aWhichFrame) MOZ_OVERRIDE;
   NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
                                layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t aWhichFrame,
                   GraphicsFilter aFilter,
                   const Maybe<SVGImageContext>& aSVGContext,
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -204,19 +204,19 @@ ImageWrapper::GetAnimated(bool* aAnimate
 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 ImageWrapper::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   return mInnerImage->GetFrame(aWhichFrame, aFlags);
 }
 
 NS_IMETHODIMP_(bool)
-ImageWrapper::FrameIsOpaque(uint32_t aWhichFrame)
+ImageWrapper::IsOpaque()
 {
-  return mInnerImage->FrameIsOpaque(aWhichFrame);
+  return mInnerImage->IsOpaque();
 }
 
 NS_IMETHODIMP
 ImageWrapper::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
 {
   return mInnerImage->GetImageContainer(aManager, _retval);
 }
 
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -107,17 +107,17 @@ OrientedImage::GetFrame(uint32_t aWhichF
   gfxIntSize size;
   rv = InnerImage()->GetWidth(&size.width);
   NS_ENSURE_SUCCESS(rv, nullptr);
   rv = InnerImage()->GetHeight(&size.height);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   // Determine an appropriate format for the surface.
   gfx::SurfaceFormat surfaceFormat;
-  if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
+  if (InnerImage()->IsOpaque()) {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
   }
 
   // Create a surface to draw into.
   RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
--- a/image/src/ProgressTracker.cpp
+++ b/image/src/ProgressTracker.cpp
@@ -74,17 +74,16 @@ CheckProgressConsistency(Progress aProgr
                             FLAG_IS_MULTIPART |
                             FLAG_HAS_ERROR));
   }
   if (aProgress & FLAG_IS_ANIMATED) {
     MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
     MOZ_ASSERT(aProgress & FLAG_SIZE_AVAILABLE);
   }
   if (aProgress & FLAG_HAS_TRANSPARENCY) {
-    MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
     MOZ_ASSERT(aProgress & FLAG_SIZE_AVAILABLE);
   }
   if (aProgress & FLAG_IS_MULTIPART) {
     // No preconditions.
   }
   if (aProgress & FLAG_LAST_PART_COMPLETE) {
     MOZ_ASSERT(aProgress & FLAG_LOAD_COMPLETE);
   }
@@ -148,16 +147,22 @@ ProgressTracker::GetImageStatus() const
     status |= imgIRequest::STATUS_DECODE_COMPLETE;
   }
   if (mProgress & FLAG_FRAME_COMPLETE) {
     status |= imgIRequest::STATUS_FRAME_COMPLETE;
   }
   if (mProgress & FLAG_LOAD_COMPLETE) {
     status |= imgIRequest::STATUS_LOAD_COMPLETE;
   }
+  if (mProgress & FLAG_IS_ANIMATED) {
+    status |= imgIRequest::STATUS_IS_ANIMATED;
+  }
+  if (mProgress & FLAG_HAS_TRANSPARENCY) {
+    status |= imgIRequest::STATUS_HAS_TRANSPARENCY;
+  }
   if (mProgress & FLAG_HAS_ERROR) {
     status |= imgIRequest::STATUS_ERROR;
   }
 
   return status;
 }
 
 // A helper class to allow us to call SyncNotify asynchronously.
--- a/image/src/ProgressTracker.h
+++ b/image/src/ProgressTracker.h
@@ -29,18 +29,18 @@ class Image;
 enum {
   FLAG_SIZE_AVAILABLE     = 1u << 0,  // STATUS_SIZE_AVAILABLE
   FLAG_DECODE_STARTED     = 1u << 1,  // STATUS_DECODE_STARTED
   FLAG_DECODE_COMPLETE    = 1u << 2,  // STATUS_DECODE_COMPLETE
   FLAG_FRAME_COMPLETE     = 1u << 3,  // STATUS_FRAME_COMPLETE
   FLAG_LOAD_COMPLETE      = 1u << 4,  // STATUS_LOAD_COMPLETE
   FLAG_ONLOAD_BLOCKED     = 1u << 5,
   FLAG_ONLOAD_UNBLOCKED   = 1u << 6,
-  FLAG_IS_ANIMATED        = 1u << 7,
-  FLAG_HAS_TRANSPARENCY   = 1u << 8,
+  FLAG_IS_ANIMATED        = 1u << 7,  // STATUS_IS_ANIMATED
+  FLAG_HAS_TRANSPARENCY   = 1u << 8,  // STATUS_HAS_TRANSPARENCY
   FLAG_IS_MULTIPART       = 1u << 9,
   FLAG_LAST_PART_COMPLETE = 1u << 10,
   FLAG_HAS_ERROR          = 1u << 11  // STATUS_ERROR
 };
 
 typedef uint32_t Progress;
 
 const uint32_t NoProgress = 0;
@@ -96,16 +96,19 @@ public:
 
   // Returns whether we are in the process of loading; that is, whether we have
   // not received OnStopRequest from Necko.
   bool IsLoading() const;
 
   // Get the current image status (as in imgIRequest).
   uint32_t GetImageStatus() const;
 
+  // Get the current Progress.
+  Progress GetProgress() const { return mProgress; }
+ 
   // Schedule an asynchronous "replaying" of all the notifications that would
   // have to happen to put us in the current state.
   // We will also take note of any notifications that happen between the time
   // Notify() is called and when we call SyncNotify on |proxy|, and replay them
   // as well.
   // Should be called on the main thread only, since imgRequestProxy and GetURI
   // are not threadsafe.
   void Notify(imgRequestProxy* proxy);
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -649,43 +649,32 @@ RasterImage::GetFirstFrameRect()
   if (mAnim) {
     return mAnim->GetFirstFrameRefreshArea();
   }
 
   // Fall back to our size. This is implicitly zero-size if !mHasSize.
   return nsIntRect(nsIntPoint(0,0), mSize);
 }
 
-//******************************************************************************
-/* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
 NS_IMETHODIMP_(bool)
-RasterImage::FrameIsOpaque(uint32_t aWhichFrame)
+RasterImage::IsOpaque()
 {
-  if (aWhichFrame > FRAME_MAX_VALUE) {
-    NS_WARNING("aWhichFrame outside valid range!");
+  if (mError) {
     return false;
   }
 
-  if (mError)
+  Progress progress = mProgressTracker->GetProgress();
+
+  // If we haven't yet finished decoding, the safe answer is "not opaque".
+  if (!(progress & FLAG_DECODE_COMPLETE)) {
     return false;
-
-  // See if we can get an image frame.
-  nsRefPtr<imgFrame> frame =
-    LookupFrameNoDecode(GetRequestedFrameIndex(aWhichFrame));
-
-  // If we don't get a frame, the safe answer is "not opaque".
-  if (!frame)
-    return false;
-
-  // Other, the frame is transparent if either:
-  //  1. It needs a background.
-  //  2. Its size doesn't cover our entire area.
-  nsIntRect framerect = frame->GetRect();
-  return !frame->GetNeedsBackground() &&
-         framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
+  }
+
+  // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
+  return !(progress & FLAG_HAS_TRANSPARENCY);
 }
 
 nsIntRect
 RasterImage::FrameRect(uint32_t aWhichFrame)
 {
   if (aWhichFrame > FRAME_MAX_VALUE) {
     NS_WARNING("aWhichFrame outside valid range!");
     return nsIntRect();
@@ -762,17 +751,17 @@ RasterImage::CopyFrame(uint32_t aWhichFr
                        bool aShouldSyncNotify /* = true */)
 {
   if (aWhichFrame > FRAME_MAX_VALUE)
     return nullptr;
 
   if (mError)
     return nullptr;
 
-  if (!ApplyDecodeFlags(aFlags, aWhichFrame))
+  if (!ApplyDecodeFlags(aFlags))
     return nullptr;
 
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE
   DrawableFrameRef frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
                                           aFlags, aShouldSyncNotify);
   if (!frameRef) {
@@ -839,17 +828,17 @@ RasterImage::GetFrameInternal(uint32_t a
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
 
   if (aWhichFrame > FRAME_MAX_VALUE)
     return nullptr;
 
   if (mError)
     return nullptr;
 
-  if (!ApplyDecodeFlags(aFlags, aWhichFrame))
+  if (!ApplyDecodeFlags(aFlags))
     return nullptr;
 
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE
   DrawableFrameRef frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
                                           aFlags, aShouldSyncNotify);
   if (!frameRef) {
@@ -1115,30 +1104,30 @@ RasterImage::InternalAddFrame(uint32_t f
 
   rv = InternalAddFrameHelper(framenum, frame, imageData, imageLength,
                               paletteData, paletteLength, aRetFrame);
 
   return rv;
 }
 
 bool
-RasterImage::ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame)
+RasterImage::ApplyDecodeFlags(uint32_t aNewFlags)
 {
   if (mFrameDecodeFlags == (aNewFlags & DECODE_FLAGS_MASK))
     return true; // Not asking very much of us here.
 
   if (mDecoded) {
     // If the requested frame is opaque and the current and new decode flags
     // only differ in the premultiply alpha bit then we can use the existing
     // frame, we don't need to discard and re-decode.
     uint32_t currentNonAlphaFlags =
       (mFrameDecodeFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
     uint32_t newNonAlphaFlags =
       (aNewFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
-    if (currentNonAlphaFlags == newNonAlphaFlags && FrameIsOpaque(aWhichFrame)) {
+    if (currentNonAlphaFlags == newNonAlphaFlags && IsOpaque()) {
       return true;
     }
 
     // if we can't discard, then we're screwed; we have no way
     // to re-decode.  Similarly if we aren't allowed to do a sync
     // decode.
     if (!(aNewFlags & FLAG_SYNC_DECODE))
       return false;
@@ -2532,18 +2521,17 @@ RasterImage::Draw(gfxContext* aContext,
   NS_ENSURE_ARG_POINTER(aContext);
 
   // We can only draw without discarding and redecoding in these cases:
   //  * We have the default decode flags.
   //  * We have exactly FLAG_DECODE_NO_PREMULTIPLY_ALPHA and the current frame
   //    is opaque.
   bool haveDefaultFlags = (mFrameDecodeFlags == DECODE_FLAGS_DEFAULT);
   bool haveSafeAlphaFlags =
-    (mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) &&
-    FrameIsOpaque(FRAME_CURRENT);
+    (mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) && IsOpaque();
 
   if (!(haveDefaultFlags || haveSafeAlphaFlags)) {
     if (!CanForciblyDiscardAndRedecode())
       return NS_ERROR_NOT_AVAILABLE;
     ForceDiscard();
 
     mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
   }
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -333,17 +333,17 @@ private:
   nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
                             gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth,
                             uint8_t **imageData, uint32_t *imageLength,
                             uint32_t **paletteData, uint32_t *paletteLength,
                             imgFrame** aRetFrame);
 
   nsresult DoImageDataComplete();
 
-  bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame);
+  bool ApplyDecodeFlags(uint32_t aNewFlags);
 
   already_AddRefed<layers::Image> GetCurrentImage();
   void UpdateImageContainer();
 
   enum RequestDecodeType {
       ASYNCHRONOUS,
       SYNCHRONOUS_NOTIFY,
       SYNCHRONOUS_NOTIFY_AND_SOME_DECODE
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -682,25 +682,19 @@ VectorImage::GetFirstFrameDelay()
   if (!mSVGDocumentWrapper->IsAnimated())
     return -1;
 
   // We don't really have a frame delay, so just pretend that we constantly
   // need updates.
   return 0;
 }
 
-
-//******************************************************************************
-/* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
 NS_IMETHODIMP_(bool)
-VectorImage::FrameIsOpaque(uint32_t aWhichFrame)
+VectorImage::IsOpaque()
 {
-  if (aWhichFrame > FRAME_MAX_VALUE)
-    NS_WARNING("aWhichFrame outside valid range!");
-
   return false; // In general, SVG content is not opaque.
 }
 
 //******************************************************************************
 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
  *                                   in uint32_t aFlags; */
 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 VectorImage::GetFrame(uint32_t aWhichFrame,
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -579,22 +579,16 @@ imgFrame::GetStride() const
   return VolatileSurfaceStride(mSize, mFormat);
 }
 
 SurfaceFormat imgFrame::GetFormat() const
 {
   return mFormat;
 }
 
-bool imgFrame::GetNeedsBackground() const
-{
-  // We need a background painted if we have alpha or we're incomplete.
-  return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
-}
-
 uint32_t imgFrame::GetImageBytesPerRow() const
 {
   if (mVBuf)
     return mSize.width * BytesPerPixel(mFormat);
 
   if (mPaletteDepth)
     return mSize.width;
 
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -83,17 +83,16 @@ public:
 
   nsresult ImageUpdated(const nsIntRect &aUpdateRect);
 
   nsIntRect GetRect() const;
   IntSize GetSize() const { return mSize; }
   bool NeedsPadding() const { return mOffset != nsIntPoint(0, 0); }
   int32_t GetStride() const;
   SurfaceFormat GetFormat() const;
-  bool GetNeedsBackground() const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;
   bool GetIsPaletted() const;
   bool GetHasAlpha() const;
   void GetImageData(uint8_t **aData, uint32_t *length) const;
   uint8_t* GetImageData() const;
   void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
   uint32_t* GetPaletteData() const;
--- a/image/test/mochitest/animationPolling.js
+++ b/image/test/mochitest/animationPolling.js
@@ -75,17 +75,17 @@ function failTest()
  *        chain tests together, so they are all finished exactly once.
  * @returns {AnimationTest}
  */
 function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
                        debugElementId, cleanId, srcAttr, xulTest, closeFunc)
 {
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
   this.wereFailures = false;
   this.pollFreq = pollFreq;
   this.timeout = timeout;
   this.imageElementId = imageElementId;
   this.referenceElementId = referenceElementId;
 
   if (!document.getElementById(referenceElementId)) {
--- a/image/test/mochitest/imgutils.js
+++ b/image/test/mochitest/imgutils.js
@@ -1,14 +1,24 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 // Helper file for shared image functionality
 // 
 // Note that this is use by tests elsewhere in the source tree. When in doubt,
 // check mxr before removing or changing functionality.
 
+// Helper function to clear both the content and chrome image caches
+function clearAllImageCaches()
+{
+  var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  var imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
 // Helper function to clear the image cache of content images
 function clearImageCache()
 {
   var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
                              .getService(SpecialPowers.Ci.imgITools);
   var imageCache = tools.getImgCacheForDocument(window.document);
   imageCache.clearCache(false); // true=chrome, false=content
 }
--- a/image/test/mochitest/test_animSVGImage.html
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -27,17 +27,17 @@ SimpleTest.waitForExplicitFinish();
 const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const gImg = document.getElementsByTagName("img")[0];
 
 var gMyDecoderObserver; // value will be set in main()
 var gReferenceSnapshot; // value will be set in takeReferenceSnapshot()
-var gOnStopFrameCounter = 0;
+var gPollCounter = 0;
 var gIsTestFinished = false;
 
 
 function takeReferenceSnapshot() {
   // Take a snapshot of the initial (essentially blank) page
   let blankSnapshot = snapshotWindow(window, false);
 
   // Show reference div, & take a snapshot
@@ -49,57 +49,57 @@ function takeReferenceSnapshot() {
 
   // Re-hide reference div, and take another snapshot to be sure it's gone
   referenceDiv.style.display = "none";
   let blankSnapshot2 = snapshotWindow(window, false);
   ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
      "reference div should disappear when it becomes display:none");
 }
 
-function myOnStopFrame() {
-  gOnStopFrameCounter++;
-  ok(true, "myOnStopFrame called");
+function myPoll() {
+  gPollCounter++;
+  ok(true, "myPoll called");
   let currentSnapshot = snapshotWindow(window, false);
   if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
     // SUCCESS!
     ok(true, "Animated image looks correct, " +
-             "at call #" + gOnStopFrameCounter + " to onStopFrame");
+             "at call #" + gPollCounter + " to myPoll");
     cleanUpAndFinish();
   }
   else
-    setTimeout(myOnStopFrame, 1);
+    setTimeout(myPoll, 1);
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "Animated image still doesn't look correct, " +
-            "after call #" + gOnStopFrameCounter + " to onStopFrame");
+            "after call #" + gPollCounter + " to myPoll");
   cleanUpAndFinish();
 }
 
 function cleanUpAndFinish() {
-  // On the off chance that failTest and myOnStopFrame are triggered
+  // On the off chance that failTest and myPoll are triggered
   // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
   if (gIsTestFinished) {
     return;
   }
   SimpleTest.finish();
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
-  setTimeout(myOnStopFrame, 1);
+  setTimeout(myPoll, 1);
 
-  // kick off image-loading! myOnStopFrame handles the rest.
+  // kick off image-loading! myPoll handles the rest.
   gImg.setAttribute("src", "lime-anim-100x100.svg");
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
 
 window.onload = main;
--- a/image/test/mochitest/test_animSVGImage2.html
+++ b/image/test/mochitest/test_animSVGImage2.html
@@ -27,19 +27,18 @@ SimpleTest.waitForExplicitFinish();
 const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const gImg = document.getElementsByTagName("img")[0];
 
 var gMyDecoderObserver; // value will be set in main()
 var gReferenceSnapshot; // value will be set in takeReferenceSnapshot()
-var gOnStopFrameCounter = 0;
+var gOnFrameUpdateCounter = 0;
 var gIsTestFinished = false;
-var gTimer = null;
 
 
 function takeReferenceSnapshot() {
   // Take a snapshot of the initial (essentially blank) page
   let blankSnapshot = snapshotWindow(window, false);
 
   // Show reference div, & take a snapshot
   let referenceDiv = document.getElementById("referenceDiv");
@@ -50,67 +49,70 @@ function takeReferenceSnapshot() {
 
   // Re-hide reference div, and take another snapshot to be sure it's gone
   referenceDiv.style.display = "none";
   let blankSnapshot2 = snapshotWindow(window, false);
   ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
      "reference div should disappear when it becomes display:none");
 }
 
-function myOnStopFrame(aRequest) {
-  gOnStopFrameCounter++;
-  ok(true, "myOnStopFrame called");
+function myOnFrameUpdate(aRequest) {
+  if (gIsTestFinished) {
+    return;
+  }
+  gOnFrameUpdateCounter++;
+  ok(true, "myOnFrameUpdate called");
   let currentSnapshot = snapshotWindow(window, false);
   if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
     // SUCCESS!
     ok(true, "Animated image looks correct, " +
-             "at call #" + gOnStopFrameCounter + " to onStopFrame");
+             "at call #" + gOnFrameUpdateCounter + " to myOnFrameUpdate");
     cleanUpAndFinish();
   }
-  if (!gTimer)
-    gTimer = setTimeout(function() { gTimer = null; myOnStopFrame(0, 0); }, 1000);
 }
 
 function failTest() {
+  if (gIsTestFinished) {
+    return;
+  }
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "Animated image still doesn't look correct, " +
-            "after call #" + gOnStopFrameCounter + " to onStopFrame");
+            "after call #" + gOnFrameUpdateCounter + " to myOnFrameUpdate");
   cleanUpAndFinish();
 }
 
 function cleanUpAndFinish() {
-  clearTimeout(gTimer);
-  // On the off chance that failTest and myOnStopFrame are triggered
+  // On the off chance that failTest and myOnFrameUpdate are triggered
   // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
   if (gIsTestFinished) {
     return;
   }
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.removeObserver(gMyDecoderObserver);
   SimpleTest.finish();
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
   // Create, customize & attach decoder observer
   observer = new ImageDecoderObserverStub();
-  observer.frameComplete = myOnStopFrame;
+  observer.frameUpdate = myOnFrameUpdate;
   gMyDecoderObserver =
     Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
       .createScriptedObserver(observer);
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.addObserver(gMyDecoderObserver);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
-  // kick off image-loading! myOnStopFrame handles the rest.
+  // kick off image-loading! myOnFrameUpdate handles the rest.
   gImg.setAttribute("src", "lime-anim-100x100-2.svg");
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
 
 window.onload = main;
--- a/image/test/mochitest/test_has_transparency.html
+++ b/image/test/mochitest/test_has_transparency.html
@@ -22,16 +22,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const gContent = document.getElementById("content");
 
+var gCanvas;
+var gCanvasCtx;
 var gImg;
 var gMyDecoderObserver;
 var gIsTestFinished = false;
 var gFiles;
 var gCurrentFileIsTransparent = false;
 var gHasTransparencyWasCalled = false;
 
 function testFiles() {
@@ -94,16 +96,21 @@ function onError() {
   loadNext();
 }
 
 function onLoad() {
   if (gIsTestFinished) {
     return;
   }
   ok(true, "Should successfully load " + gImg.src);
+
+  // Force decoding of the image.
+  SimpleTest.executeSoon(function() {
+    gCanvasCtx.drawImage(gImg, 0, 0);
+  });
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "currently displaying " + gImg.src);
   cleanUpAndFinish();
 }
 
@@ -114,33 +121,35 @@ function cleanUpAndFinish() {
   gIsTestFinished = true;
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.removeObserver(gMyDecoderObserver);
   SimpleTest.finish();
 }
 
 function main() {
   gFiles = testFiles();
+  gCanvas = document.createElement('canvas');
+  gCanvasCtx = gCanvas.getContext('2d');
   gImg = new Image();
   gImg.onload = onLoad;
   gImg.onerror = onError;
 
   // Create, customize & attach decoder observer.
   observer = new ImageDecoderObserverStub();
   observer.hasTransparency = onHasTransparency;
   observer.decodeComplete = onDecodeComplete;
   gMyDecoderObserver =
     Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
       .createScriptedObserver(observer);
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.addObserver(gMyDecoderObserver);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
   // Load the first image.
   loadNext();
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
--- a/image/test/mochitest/test_removal_ondecode.html
+++ b/image/test/mochitest/test_removal_ondecode.html
@@ -104,17 +104,17 @@ function main() {
   gMyDecoderObserver =
     Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
       .createScriptedObserver(observer);
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.addObserver(gMyDecoderObserver);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
   // kick off image-loading! myOnStopFrame handles the rest.
   gImg.setAttribute("src", gFiles.next());
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
--- a/image/test/mochitest/test_removal_onload.html
+++ b/image/test/mochitest/test_removal_onload.html
@@ -104,17 +104,17 @@ function main() {
   gMyDecoderObserver =
     Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
       .createScriptedObserver(observer);
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.addObserver(gMyDecoderObserver);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
   // kick off image-loading! myOnStopFrame handles the rest.
   gImg.setAttribute("src", gFiles.next());
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
--- a/image/test/mochitest/test_synchronized_animation.html
+++ b/image/test/mochitest/test_synchronized_animation.html
@@ -101,17 +101,17 @@ function main() {
   // Create and customize decoder observer
   var obs = new ImageDecoderObserverStub();
   obs.frameUpdate = frameUpdate;
 
   gOuter = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools).createScriptedObserver(obs);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
-  clearImageCache();
+  clearAllImageCaches();
 
   // These are two copies of the same image; hence, they have the same frame rate.
   gImg1.src = "animated1.gif";
   gImg2.src = "animated2.gif";
 
   // Wait for each image to load.
   gImg1.addEventListener('load', waitForLoadAndTest(gImg1));
   gImg2.addEventListener('load', waitForLoadAndTest(gImg2));
--- a/ipc/chromium/src/chrome/common/ipc_message.cc
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -31,17 +31,17 @@ Message::Message()
     : Pickle(sizeof(Header)) {
   header()->routing = header()->type = header()->flags = 0;
 #if defined(OS_POSIX)
   header()->num_fds = 0;
 #endif
 #ifdef MOZ_TASK_TRACER
   header()->source_event_id = 0;
   header()->parent_task_id = 0;
-  header()->source_event_type = SourceEventType::UNKNOWN;
+  header()->source_event_type = SourceEventType::Unknown;
 #endif
   InitLoggingVariables();
 }
 
 Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority,
                  MessageCompression compression, const char* const name)
     : Pickle(sizeof(Header)) {
   header()->routing = routing_id;
@@ -56,17 +56,17 @@ Message::Message(int32_t routing_id, msg
   header()->interrupt_local_stack_depth = static_cast<uint32_t>(-1);
   header()->seqno = 0;
 #if defined(OS_MACOSX)
   header()->cookie = 0;
 #endif
 #ifdef MOZ_TASK_TRACER
   header()->source_event_id = 0;
   header()->parent_task_id = 0;
-  header()->source_event_type = SourceEventType::UNKNOWN;
+  header()->source_event_type = SourceEventType::Unknown;
 #endif
   InitLoggingVariables(name);
 }
 
 Message::Message(const char* data, int data_len) : Pickle(data, data_len) {
   InitLoggingVariables();
 }
 
--- a/js/src/asmjs/AsmJSFrameIterator.cpp
+++ b/js/src/asmjs/AsmJSFrameIterator.cpp
@@ -74,17 +74,17 @@ AsmJSFrameIterator::settle()
       case AsmJSModule::CodeRange::Function:
         callsite_ = module_->lookupCallSite(returnAddress);
         MOZ_ASSERT(callsite_);
         break;
       case AsmJSModule::CodeRange::Entry:
         fp_ = nullptr;
         MOZ_ASSERT(done());
         break;
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         MOZ_CRASH("Should not encounter an exit during iteration");
     }
 }
 
@@ -453,17 +453,17 @@ AsmJSProfilingFrameIterator::initFromFP(
         callerFP_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
         fp = CallerFPFromFP(fp);
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
         break;
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
 
     // Since, despite the above reasoning for skipping a frame, we do want FFI
@@ -508,17 +508,17 @@ AsmJSProfilingFrameIterator::AsmJSProfil
     }
 
     // Note: fp may be null while entering and leaving the activation.
     uint8_t *fp = activation.fp();
 
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(state.pc);
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Function:
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Thunk: {
         // While codeRange describes the *current* frame, the fp/pc state stored in
         // the iterator is the *caller's* frame. The reason for this is that the
         // activation.fp isn't always the AsmJSFrame for state.pc; during the
         // prologue/epilogue, activation.fp will point to the caller's frame.
         // Naively unwinding starting at activation.fp could thus lead to the
@@ -610,17 +610,17 @@ AsmJSProfilingFrameIterator::operator++(
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
         MOZ_ASSERT(callerFP_ == nullptr);
         MOZ_ASSERT(callerPC_ != nullptr);
         callerPC_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
         callerFP_ = CallerFPFromFP(callerFP_);
@@ -667,38 +667,38 @@ AsmJSProfilingFrameIterator::label() con
 {
     MOZ_ASSERT(!done());
 
     // Use the same string for both time inside and under so that the two
     // entries will be coalesced by the profiler.
     //
     // NB: these labels are regexp-matched by
     //     browser/devtools/profiler/cleopatra/js/parserWorker.js.
-    const char *ionFFIDescription = "fast FFI trampoline (in asm.js)";
+    const char *jitFFIDescription = "fast FFI trampoline (in asm.js)";
     const char *slowFFIDescription = "slow FFI trampoline (in asm.js)";
     const char *interruptDescription = "interrupt due to out-of-bounds or long execution (in asm.js)";
 
     switch (AsmJSExit::ExtractReasonKind(exitReason_)) {
       case AsmJSExit::Reason_None:
         break;
-      case AsmJSExit::Reason_IonFFI:
-        return ionFFIDescription;
+      case AsmJSExit::Reason_JitFFI:
+        return jitFFIDescription;
       case AsmJSExit::Reason_SlowFFI:
         return slowFFIDescription;
       case AsmJSExit::Reason_Interrupt:
         return interruptDescription;
       case AsmJSExit::Reason_Builtin:
         return BuiltinToName(AsmJSExit::ExtractBuiltinKind(exitReason_));
     }
 
     auto codeRange = reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_);
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Function:  return codeRange->functionProfilingLabel(*module_);
       case AsmJSModule::CodeRange::Entry:     return "entry trampoline (in asm.js)";
-      case AsmJSModule::CodeRange::IonFFI:    return ionFFIDescription;
+      case AsmJSModule::CodeRange::JitFFI:    return jitFFIDescription;
       case AsmJSModule::CodeRange::SlowFFI:   return slowFFIDescription;
       case AsmJSModule::CodeRange::Interrupt: return interruptDescription;
       case AsmJSModule::CodeRange::Inline:    return "inline stub (in asm.js)";
       case AsmJSModule::CodeRange::Thunk:     return BuiltinToName(codeRange->thunkTarget());
     }
 
     MOZ_CRASH("Bad exit kind");
 }
--- a/js/src/asmjs/AsmJSFrameIterator.h
+++ b/js/src/asmjs/AsmJSFrameIterator.h
@@ -64,17 +64,17 @@ namespace AsmJSExit
 {
     // List of reasons for execution leaving asm.js-generated code, stored in
     // AsmJSActivation. The initial and default state is AsmJSNoExit. If
     // AsmJSNoExit is observed when the pc isn't in asm.js code, execution must
     // have been interrupted asynchronously (viz., by a exception/signal
     // handler).
     enum ReasonKind {
         Reason_None,
-        Reason_IonFFI,
+        Reason_JitFFI,
         Reason_SlowFFI,
         Reason_Interrupt,
         Reason_Builtin
     };
 
     // For Reason_Builtin, the list of builtins, so they can be displayed in the
     // profile call stack.
     enum BuiltinKind {
@@ -101,17 +101,17 @@ namespace AsmJSExit
         Builtin_Limit
     };
 
     // A Reason contains both a ReasonKind and (if Reason_Builtin) a
     // BuiltinKind.
     typedef uint32_t Reason;
 
     static const uint32_t None = Reason_None;
-    static const uint32_t IonFFI = Reason_IonFFI;
+    static const uint32_t JitFFI = Reason_JitFFI;
     static const uint32_t SlowFFI = Reason_SlowFFI;
     static const uint32_t Interrupt = Reason_Interrupt;
     static inline Reason Builtin(BuiltinKind builtin) {
         return uint16_t(Reason_Builtin) | (uint16_t(builtin) << 16);
     }
     static inline ReasonKind ExtractReasonKind(Reason reason) {
         return ReasonKind(uint16_t(reason));
     }
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -100,21 +100,21 @@ AsmJSModule::~AsmJSModule()
 {
     MOZ_ASSERT(!interrupted_);
 
     scriptSource_->decref();
 
     if (code_) {
         for (unsigned i = 0; i < numExits(); i++) {
             AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
-            if (!exitDatum.ionScript)
+            if (!exitDatum.baselineScript)
                 continue;
 
             jit::DependentAsmJSModuleExit exit(this, i);
-            exitDatum.ionScript->removeDependentAsmJSModule(exit);
+            exitDatum.baselineScript->removeDependentAsmJSModule(exit);
         }
 
         DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
     }
 
     for (size_t i = 0; i < numFunctionCounts(); i++)
         js_delete(functionCounts(i));
 
@@ -496,69 +496,77 @@ CoerceInPlace_ToNumber(MutableHandleValu
     if (!ToNumber(cx, val, &dbl))
         return false;
     val.set(DoubleValue(dbl));
 
     return true;
 }
 
 static bool
-TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
+TryEnablingJit(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
                int32_t argc, Value *argv)
 {
     if (!fun->hasScript())
         return true;
 
-    // Test if the function is Ion compiled
+    // Test if the function is JIT compiled.
     JSScript *script = fun->nonLazyScript();
-    if (!script->hasIonScript())
+    if (!script->hasBaselineScript()) {
+        MOZ_ASSERT(!script->hasIonScript());
         return true;
+    }
 
     // Currently we can't rectify arguments. Therefore disabling if argc is too low.
     if (fun->nargs() > size_t(argc))
         return true;
 
-    // Normally the types should correspond, since we just ran with those types,
-    // but there are reports this is asserting. Therefore doing it as a check, instead of DEBUG only.
+    // Ensure the argument types are included in the argument TypeSets stored in
+    // the TypeScript. This is necessary for Ion, because the FFI exit will
+    // use the skip-arg-checks entry point.
+    //
+    // Note that the TypeScript is never discarded while the script has a
+    // BaselineScript, so if those checks hold now they must hold at least until
+    // the BaselineScript is discarded and when that happens the FFI exit is
+    // patched back.
     if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::UndefinedType()))
         return true;
     for (uint32_t i = 0; i < fun->nargs(); i++) {
         types::StackTypeSet *typeset = types::TypeScript::ArgTypes(script, i);
         types::Type type = types::Type::DoubleType();
         if (!argv[i].isDouble())
             type = types::Type::PrimitiveType(argv[i].extractNonDoubleType());
         if (!typeset->hasType(type))
             return true;
     }
 
     // The exit may have become optimized while executing the FFI.
     if (module.exitIsOptimized(exitIndex))
         return true;
 
-    IonScript *ionScript = script->ionScript();
-    if (!ionScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
+    BaselineScript *baselineScript = script->baselineScript();
+    if (!baselineScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
         return false;
 
-    module.optimizeExit(exitIndex, ionScript);
+    module.optimizeExit(exitIndex, baselineScript);
     return true;
 }
 
 static bool
 InvokeFromAsmJS(AsmJSActivation *activation, int32_t exitIndex, int32_t argc, Value *argv,
                 MutableHandleValue rval)
 {
     JSContext *cx = activation->cx();
     AsmJSModule &module = activation->module();
 
     RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun);
     RootedValue fval(cx, ObjectValue(*fun));
     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
         return false;
 
-    return TryEnablingIon(cx, module, fun, exitIndex, argc, argv);
+    return TryEnablingJit(cx, module, fun, exitIndex, argc, argv);
 }
 
 // Use an int32_t return type instead of bool since bool does not have a
 // specified width and the caller is assuming a word-sized return.
 static int32_t
 InvokeFromAsmJS_Ignore(int32_t exitIndex, int32_t argc, Value *argv)
 {
     AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
@@ -743,17 +751,17 @@ AsmJSModule::staticallyLink(ExclusiveCon
     }
 
     // Initialize global data segment
 
     for (size_t i = 0; i < exits_.length(); i++) {
         AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
         exitDatum.exit = interpExitTrampoline(exits_[i]);
         exitDatum.fun = nullptr;
-        exitDatum.ionScript = nullptr;
+        exitDatum.baselineScript = nullptr;
     }
 
     MOZ_ASSERT(isStaticallyLinked());
 }
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
 static inline size_t
 ViewTypeByteSize(AsmJSHeapAccess::ViewType vt)
@@ -899,17 +907,17 @@ AsmJSModule::detachHeap(JSContext *cx)
     if (interrupted_) {
         JS_ReportError(cx, "attempt to detach from inside interrupt handler");
         return false;
     }
 
     // Even if this->active(), to reach here, the activation must have called
     // out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
     // and throw an exception if so.
-    MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
+    MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_JitFFI ||
                             activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
 
     restoreHeapToInitialState(maybeHeap_);
 
     MOZ_ASSERT(hasDetachedHeap());
     return true;
 }
 
@@ -1333,17 +1341,17 @@ AsmJSModule::CodeRange::CodeRange(Kind k
   : begin_(begin),
     profilingReturn_(profilingReturn),
     end_(end)
 {
     u.kind_ = kind;
 
     MOZ_ASSERT(begin_ < profilingReturn_);
     MOZ_ASSERT(profilingReturn_ < end_);
-    MOZ_ASSERT(u.kind_ == IonFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
+    MOZ_ASSERT(u.kind_ == JitFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
 }
 
 AsmJSModule::CodeRange::CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
                                   uint32_t profilingReturn, uint32_t end)
   : begin_(begin),
     profilingReturn_(profilingReturn),
     end_(end)
 {
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -366,43 +366,43 @@ class AsmJSModule
         bool clone(ExclusiveContext *cx, Global *out) const;
     };
 
     class Exit
     {
         unsigned ffiIndex_;
         unsigned globalDataOffset_;
         unsigned interpCodeOffset_;
-        unsigned ionCodeOffset_;
+        unsigned jitCodeOffset_;
 
         friend class AsmJSModule;
 
       public:
         Exit() {}
         Exit(unsigned ffiIndex, unsigned globalDataOffset)
           : ffiIndex_(ffiIndex), globalDataOffset_(globalDataOffset),
-            interpCodeOffset_(0), ionCodeOffset_(0)
+            interpCodeOffset_(0), jitCodeOffset_(0)
         {}
         unsigned ffiIndex() const {
             return ffiIndex_;
         }
         unsigned globalDataOffset() const {
             return globalDataOffset_;
         }
         void initInterpOffset(unsigned off) {
             MOZ_ASSERT(!interpCodeOffset_);
             interpCodeOffset_ = off;
         }
-        void initIonOffset(unsigned off) {
-            MOZ_ASSERT(!ionCodeOffset_);
-            ionCodeOffset_ = off;
+        void initJitOffset(unsigned off) {
+            MOZ_ASSERT(!jitCodeOffset_);
+            jitCodeOffset_ = off;
         }
         void updateOffsets(jit::MacroAssembler &masm) {
             interpCodeOffset_ = masm.actualOffset(interpCodeOffset_);
-            ionCodeOffset_ = masm.actualOffset(ionCodeOffset_);
+            jitCodeOffset_ = masm.actualOffset(jitCodeOffset_);
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, Exit *out) const;
     };
 
@@ -414,17 +414,17 @@ class AsmJSModule
     typedef int32_t (*CodePtr)(EntryArg *args, uint8_t *global);
 
     // An Exit holds bookkeeping information about an exit; the ExitDatum
     // struct overlays the actual runtime data stored in the global data
     // section.
     struct ExitDatum
     {
         uint8_t *exit;
-        jit::IonScript *ionScript;
+        jit::BaselineScript *baselineScript;
         HeapPtrFunction fun;
     };
 
     typedef Vector<AsmJSCoercion, 0, SystemAllocPolicy> ArgCoercionVector;
 
     enum ReturnType { Return_Int32, Return_Double, Return_Int32x4, Return_Float32x4, Return_Void };
 
     class ExportedFunction
@@ -553,29 +553,29 @@ class AsmJSModule
                 uint16_t target_;
             } thunk;
             uint8_t kind_;
         } u;
 
         void setDeltas(uint32_t entry, uint32_t profilingJump, uint32_t profilingEpilogue);
 
       public:
-        enum Kind { Function, Entry, IonFFI, SlowFFI, Interrupt, Thunk, Inline };
+        enum Kind { Function, Entry, JitFFI, SlowFFI, Interrupt, Thunk, Inline };
 
         CodeRange() {}
         CodeRange(uint32_t nameIndex, uint32_t lineNumber, const AsmJSFunctionLabels &l);
         CodeRange(Kind kind, uint32_t begin, uint32_t end);
         CodeRange(Kind kind, uint32_t begin, uint32_t profilingReturn, uint32_t end);
         CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin, uint32_t pret, uint32_t end);
         void updateOffsets(jit::MacroAssembler &masm);
 
         Kind kind() const { return Kind(u.kind_); }
         bool isFunction() const { return kind() == Function; }
         bool isEntry() const { return kind() == Entry; }
-        bool isFFI() const { return kind() == IonFFI || kind() == SlowFFI; }
+        bool isFFI() const { return kind() == JitFFI || kind() == SlowFFI; }
         bool isInterrupt() const { return kind() == Interrupt; }
         bool isThunk() const { return kind() == Thunk; }
 
         uint32_t begin() const {
             return begin_;
         }
         uint32_t entry() const {
             MOZ_ASSERT(isFunction());
@@ -1320,20 +1320,20 @@ class AsmJSModule
         return pc >= code_ && pc < (code_ + codeBytes());
     }
   private:
     uint8_t *interpExitTrampoline(const Exit &exit) const {
         MOZ_ASSERT(isFinished());
         MOZ_ASSERT(exit.interpCodeOffset_);
         return code_ + exit.interpCodeOffset_;
     }
-    uint8_t *ionExitTrampoline(const Exit &exit) const {
+    uint8_t *jitExitTrampoline(const Exit &exit) const {
         MOZ_ASSERT(isFinished());
-        MOZ_ASSERT(exit.ionCodeOffset_);
-        return code_ + exit.ionCodeOffset_;
+        MOZ_ASSERT(exit.jitCodeOffset_);
+        return code_ + exit.jitCodeOffset_;
     }
   public:
 
     // Lookup a callsite by the return pc (from the callee to the caller).
     // Return null if no callsite was found.
     const jit::CallSite *lookupCallSite(void *returnAddress) const;
 
     // Lookup the name the code range containing the given pc. Return null if no
@@ -1458,27 +1458,27 @@ class AsmJSModule
         MOZ_ASSERT(isFinished());
         return *(ExitDatum *)(globalData() + exitIndexToGlobalDataOffset(exitIndex));
     }
     bool exitIsOptimized(unsigned exitIndex) const {
         MOZ_ASSERT(isFinished());
         ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
         return exitDatum.exit != interpExitTrampoline(exit(exitIndex));
     }
-    void optimizeExit(unsigned exitIndex, jit::IonScript *ionScript) const {
+    void optimizeExit(unsigned exitIndex, jit::BaselineScript *baselineScript) const {
         MOZ_ASSERT(!exitIsOptimized(exitIndex));
         ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
-        exitDatum.exit = ionExitTrampoline(exit(exitIndex));
-        exitDatum.ionScript = ionScript;
+        exitDatum.exit = jitExitTrampoline(exit(exitIndex));
+        exitDatum.baselineScript = baselineScript;
     }
-    void detachIonCompilation(size_t exitIndex) const {
+    void detachJitCompilation(size_t exitIndex) const {
         MOZ_ASSERT(isFinished());
         ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
         exitDatum.exit = interpExitTrampoline(exit(exitIndex));
-        exitDatum.ionScript = nullptr;
+        exitDatum.baselineScript = nullptr;
     }
 
     /*************************************************************************/
     // These functions are called after finish() but before staticallyLink():
 
     bool addRelativeLink(RelativeLink link) {
         MOZ_ASSERT(isFinished() && !isStaticallyLinked());
         return staticLinkData_.relativeLinks.append(link);
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1865,23 +1865,23 @@ class MOZ_STACK_CLASS ModuleCompiler
     bool finishGeneratingInterpExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
         MOZ_ASSERT(finishedFunctionBodies_);
         uint32_t beg = begin->offset();
         module_->exit(exitIndex).initInterpOffset(beg);
         uint32_t pret = profilingReturn->offset();
         uint32_t end = masm_.currentOffset();
         return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end);
     }
-    bool finishGeneratingIonExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
+    bool finishGeneratingJitExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
         MOZ_ASSERT(finishedFunctionBodies_);
         uint32_t beg = begin->offset();
-        module_->exit(exitIndex).initIonOffset(beg);
+        module_->exit(exitIndex).initJitOffset(beg);
         uint32_t pret = profilingReturn->offset();
         uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::IonFFI, beg, pret, end);
+        return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, beg, pret, end);
     }
     bool finishGeneratingInterrupt(Label *begin, Label *profilingReturn) {
         MOZ_ASSERT(finishedFunctionBodies_);
         uint32_t beg = begin->offset();
         uint32_t pret = profilingReturn->offset();
         uint32_t end = masm_.currentOffset();
         return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, beg, pret, end);
     }
@@ -8448,17 +8448,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     coerceArgTypes.infallibleAppend(MIRType_Pointer); // argv
     unsigned offsetToCoerceArgv = AlignBytes(StackArgBytes(coerceArgTypes), sizeof(double));
     unsigned totalCoerceBytes = offsetToCoerceArgv + sizeof(Value) + MaybeSavedGlobalReg;
     unsigned coerceFrameSize = StackDecrementForCall(masm, AsmJSStackAlignment, totalCoerceBytes);
 
     unsigned framePushed = Max(ionFrameSize, coerceFrameSize);
 
     Label begin;
-    GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::IonFFI, &begin);
+    GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::JitFFI, &begin);
 
     // 1. Descriptor
     size_t argOffset = offsetToIonArgs;
     uint32_t descriptor = MakeFrameDescriptor(framePushed, JitFrame_Entry);
     masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 2. Callee
@@ -8496,17 +8496,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     argOffset += sizeof(Value);
 
     // 5. Fill the arguments
     unsigned offsetToCallerStackArgs = framePushed + sizeof(AsmJSFrame);
     FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
     argOffset += exit.sig().args().length() * sizeof(Value);
     MOZ_ASSERT(argOffset == offsetToIonArgs + ionArgBytes);
 
-    // 6. Ion will clobber all registers, even non-volatiles. GlobalReg and
+    // 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and
     //    HeapReg are removed from the general register set for asm.js code, so
     //    these will not have been saved by the caller like all other registers,
     //    so they must be explicitly preserved. Only save GlobalReg since
     //    HeapReg must be reloaded (from global data) after the call since the
     //    heap may change during the FFI call.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     JS_STATIC_ASSERT(MaybeSavedGlobalReg > 0);
     unsigned savedGlobalOffset = framePushed - MaybeSavedGlobalReg;
@@ -8548,17 +8548,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop()));
         masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
         masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
     }
 
     // 2. Call
     AssertStackAlignment(masm, AsmJSStackAlignment);
-    masm.callIonFromAsmJS(callee);
+    masm.callJitFromAsmJS(callee);
     AssertStackAlignment(masm, AsmJSStackAlignment);
 
     {
         // Disable Activation.
         //
         // This sequence needs three registers, and must preserve the JSReturnReg_Data and
         // JSReturnReg_Type, so there are five live registers.
         MOZ_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData);
@@ -8620,17 +8620,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 #endif
 
     // The heap pointer has to be reloaded anyway since Ion could have clobbered
     // it. Additionally, the FFI may have detached the heap buffer.
     masm.loadAsmJSHeapRegisterFromGlobalData();
     GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
 
     Label profilingReturn;
-    GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn);
+    GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::JitFFI, &profilingReturn);
 
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(framePushed);
 
         // Store return value into argv[0]
         masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToCoerceArgv));
 
@@ -8664,17 +8664,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         }
 
         masm.jump(&done);
         masm.setFramePushed(0);
     }
 
     MOZ_ASSERT(masm.framePushed() == 0);
 
-    return m.finishGeneratingIonExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
+    return m.finishGeneratingJitExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
 }
 
 // See "asm.js FFI calls" comment above.
 static bool
 GenerateFFIExits(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, unsigned exitIndex,
                  Label *throwLabel)
 {
     // Generate the slow path through the interpreter
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -378,16 +378,24 @@ class LifoAlloc
     // Get the total size of the arena chunks (including unused space).
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t n = 0;
         for (BumpChunk *chunk = first; chunk; chunk = chunk->next())
             n += chunk->sizeOfIncludingThis(mallocSizeOf);
         return n;
     }
 
+    // Get the total size of the arena chunks (including unused space).
+    size_t computedSizeOfExcludingThis() const {
+        size_t n = 0;
+        for (BumpChunk *chunk = first; chunk; chunk = chunk->next())
+            n += chunk->computedSizeOfIncludingThis();
+        return n;
+    }
+
     // Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself.
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
     }
 
     // Get the peak size of the arena chunks (including unused space and
     // bookkeeping space).
     size_t peakSizeOfExcludingThis() const { return peakSize_; }
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -142,13 +142,12 @@ struct MovingTracer : JSTracer {
 
     static void Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind);
     static bool IsMovingTracer(JSTracer *trc) {
         return trc->callback == Visit;
     }
 };
 #endif
 
-
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_GCInternals_h */
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -594,23 +594,24 @@ class GCRuntime
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     bool sweepPhase(SliceBudget &sliceBudget);
     void endSweepPhase(bool lastGC);
     void sweepZones(FreeOp *fop, bool lastGC);
     void decommitAllWithoutUnlocking(const AutoLockGC &lock);
     void decommitArenas(AutoLockGC &lock);
     void expireChunksAndArenas(bool shouldShrink, AutoLockGC &lock);
-    void sweepBackgroundThings();
+    void queueZonesForBackgroundSweep(js::gc::ZoneList& zones);
+    void sweepBackgroundThings(js::gc::ZoneList &zones, ThreadType threadType);
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
+    bool compactPhase(bool lastGC);
 #ifdef JSGC_COMPACTING
     void sweepTypesAfterCompacting(Zone *zone);
     void sweepZoneAfterCompacting(Zone *zone);
-    void compactPhase(bool lastGC);
     ArenaHeader *relocateArenas();
     void updateAllCellPointersParallel(ArenasToUpdate &source);
     void updateAllCellPointersSerial(MovingTracer *trc, ArenasToUpdate &source);
     void updatePointersToRelocatedCells();
     void releaseRelocatedArenas(ArenaHeader *relocatedList);
     void releaseRelocatedArenasWithoutUnlocking(ArenaHeader *relocatedList, const AutoLockGC& lock);
 #ifdef DEBUG
     void protectRelocatedArenas(ArenaHeader *relocatedList);
@@ -753,19 +754,18 @@ class GCRuntime
     bool sweepOnBackgroundThread;
 
     /* Whether observed type information is being released in the current GC. */
     bool releaseObservedTypes;
 
     /* Whether any black->gray edges were found during marking. */
     bool foundBlackGrayEdges;
 
-    /* List head of zones to be swept in the background. */
-    JS::Zone *sweepingZones;
-
+    /* Singly linekd list of zones to be swept in the background. */
+    js::gc::ZoneList backgroundSweepZones;
     /*
      * Free LIFO blocks are transferred to this allocator before being freed on
      * the background GC thread.
      */
     js::LifoAlloc freeLifoAlloc;
 
     /* Index of current zone group (for stats). */
     unsigned zoneGroupIndex;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -14,16 +14,18 @@
 #include "vm/Debugger.h"
 #include "vm/Runtime.h"
 
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
+Zone * const Zone::NotOnList = reinterpret_cast<Zone *>(1);
+
 JS::Zone::Zone(JSRuntime *rt)
   : JS::shadow::Zone(rt, &rt->gc.marker),
     allocator(this),
     types(this),
     compartments(),
     gcGrayRoots(),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
@@ -32,17 +34,18 @@ JS::Zone::Zone(JSRuntime *rt)
     data(nullptr),
     isSystem(false),
     usedByExclusiveThread(false),
     active(false),
     jitZone_(nullptr),
     gcState_(NoGC),
     gcScheduled_(false),
     gcPreserveCode_(false),
-    jitUsingBarriers_(false)
+    jitUsingBarriers_(false),
+    listNext_(NotOnList)
 {
     /* Ensure that there are no vtables to mess us up here. */
     MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
                static_cast<JS::shadow::Zone *>(this));
 
     threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState);
     setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
 }
@@ -260,8 +263,100 @@ js::ZoneOfValue(const JS::Value &value)
     return js::gc::TenuredCell::fromPointer(value.toGCThing())->zone();
 }
 
 bool
 js::ZonesIter::atAtomsZone(JSRuntime *rt)
 {
     return rt->isAtomsZone(*it);
 }
+
+bool Zone::isOnList()
+{
+    return listNext_ != NotOnList;
+}
+
+ZoneList::ZoneList()
+  : head(nullptr), tail(nullptr)
+{}
+
+ZoneList::ZoneList(Zone *zone)
+  : head(zone), tail(zone)
+{
+    MOZ_ASSERT(!zone->isOnList());
+    zone->listNext_ = nullptr;
+}
+
+void
+ZoneList::check() const
+{
+#ifdef DEBUG
+    MOZ_ASSERT((head == nullptr) == (tail == nullptr));
+    if (head) {
+        Zone *zone = head;
+        while (zone != tail) {
+            zone = zone->listNext_;
+            MOZ_ASSERT(zone);
+        }
+        MOZ_ASSERT(!zone->listNext_);
+    }
+#endif
+}
+
+bool ZoneList::isEmpty() const
+{
+    return head == nullptr;
+}
+
+Zone *
+ZoneList::front() const
+{
+    MOZ_ASSERT(!isEmpty());
+    return head;
+}
+
+void
+ZoneList::append(Zone *zone)
+{
+    ZoneList singleZone(zone);
+    append(singleZone);
+}
+
+void
+ZoneList::append(ZoneList &other)
+{
+    check();
+    other.check();
+    MOZ_ASSERT(tail != other.tail);
+
+    if (tail)
+        tail->listNext_ = other.head;
+    else
+        head = other.head;
+    tail = other.tail;
+}
+
+Zone *
+ZoneList::removeFront()
+{
+    MOZ_ASSERT(!isEmpty());
+    check();
+
+    Zone *front = head;
+    head = head->listNext_;
+    if (!head)
+        tail = nullptr;
+
+    front->listNext_ = Zone::NotOnList;
+    return front;
+}
+
+void
+ZoneList::transferFrom(ZoneList& other)
+{
+    MOZ_ASSERT(isEmpty());
+    other.check();
+
+    head = other.head;
+    tail = other.tail;
+    other.head = nullptr;
+    other.tail = nullptr;
+}
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -245,16 +245,20 @@ struct Zone : public JS::shadow::Zone,
 #endif
 
   private:
     void sweepBreakpoints(js::FreeOp *fop);
     void sweepCompartments(js::FreeOp *fop, bool keepAtleastOne, bool lastGC);
 
     js::jit::JitZone *createJitZone(JSContext *cx);
 
+    bool isQueuedForBackgroundSweep() {
+        return isOnList();
+    }
+
   public:
     js::Allocator allocator;
 
     js::types::TypeZone types;
 
     // The set of compartments in this zone.
     typedef js::Vector<JSCompartment *, 1, js::SystemAllocPolicy> CompartmentVector;
     CompartmentVector compartments;
@@ -309,16 +313,22 @@ struct Zone : public JS::shadow::Zone,
   private:
     js::jit::JitZone *jitZone_;
 
     GCState gcState_;
     bool gcScheduled_;
     bool gcPreserveCode_;
     bool jitUsingBarriers_;
 
+    // Allow zones to be linked into a list
+    friend class js::gc::ZoneList;
+    static Zone * const NotOnList;
+    Zone *listNext_;
+    bool isOnList();
+
     friend bool js::CurrentThreadCanAccessZone(Zone *zone);
     friend class js::gc::GCRuntime;
 };
 
 } // namespace JS
 
 namespace js {
 
--- a/js/src/jit/AtomicOp.h
+++ b/js/src/jit/AtomicOp.h
@@ -34,40 +34,40 @@ enum MemoryBarrierBits {
 
     MembarSynchronizing = 16,
 
     // For validity testing
     MembarNobits = 0,
     MembarAllbits = 31,
 };
 
-inline MemoryBarrierBits
+static inline MOZ_CONSTEXPR MemoryBarrierBits
 operator|(MemoryBarrierBits a, MemoryBarrierBits b)
 {
     return MemoryBarrierBits(int(a) | int(b));
 }
 
-inline MemoryBarrierBits
+static inline MOZ_CONSTEXPR MemoryBarrierBits
 operator&(MemoryBarrierBits a, MemoryBarrierBits b)
 {
     return MemoryBarrierBits(int(a) & int(b));
 }
 
-inline MemoryBarrierBits
+static inline MOZ_CONSTEXPR MemoryBarrierBits
 operator~(MemoryBarrierBits a)
 {
     return MemoryBarrierBits(~int(a));
 }
 
 // Standard barrier bits for a full barrier.
-static const MemoryBarrierBits MembarFull = MembarLoadLoad|MembarLoadStore|MembarStoreLoad|MembarStoreStore;
+static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarFull = MembarLoadLoad|MembarLoadStore|MembarStoreLoad|MembarStoreStore;
 
 // Standard sets of barrier bits for atomic loads and stores.
 // See http://gee.cs.oswego.edu/dl/jmm/cookbook.html for more.
-static const MemoryBarrierBits MembarBeforeLoad = MembarNobits;
-static const MemoryBarrierBits MembarAfterLoad = MembarLoadLoad|MembarLoadStore;
-static const MemoryBarrierBits MembarBeforeStore = MembarStoreStore;
-static const MemoryBarrierBits MembarAfterStore = MembarStoreLoad;
+static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarBeforeLoad = MembarNobits;
+static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarAfterLoad = MembarLoadLoad|MembarLoadStore;
+static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarBeforeStore = MembarStoreStore;
+static MOZ_CONSTEXPR_VAR MemoryBarrierBits MembarAfterStore = MembarStoreLoad;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_AtomicOp_h */
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineJIT.h"
 
 #include "mozilla/MemoryReporting.h"
 
+#include "asmjs/AsmJSModule.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/BaselineIC.h"
 #include "jit/CompileInfo.h"
 #include "jit/JitCommon.h"
 #include "jit/JitSpewer.h"
 #include "vm/Interpreter.h"
 #include "vm/TraceLogging.h"
 
@@ -40,16 +41,17 @@ PCMappingSlotInfo::ToSlotLocation(const 
     return SlotIgnore;
 }
 
 BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
                                uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset)
   : method_(nullptr),
     templateScope_(nullptr),
     fallbackStubSpace_(),
+    dependentAsmJSModules_(nullptr),
     prologueOffset_(prologueOffset),
     epilogueOffset_(epilogueOffset),
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
     postDebugPrologueOffset_(postDebugPrologueOffset),
     flags_(0)
@@ -435,19 +437,64 @@ BaselineScript::Destroy(FreeOp *fop, Bas
      * will contain entries refering to the referenced things. Since we can
      * destroy scripts outside the context of a GC, this situation can result
      * in invalid store buffer entries. Assert that if we do destroy scripts
      * outside of a GC that we at least emptied the nursery first.
      */
     MOZ_ASSERT(fop->runtime()->gc.nursery.isEmpty());
 #endif
 
+    script->unlinkDependentAsmJSModules(fop);
+
     fop->delete_(script);
 }
 
+void
+BaselineScript::unlinkDependentAsmJSModules(FreeOp *fop)
+{
+    // Remove any links from AsmJSModules that contain optimized FFI calls into
+    // this BaselineScript.
+    if (dependentAsmJSModules_) {
+        for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
+            DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
+            exit.module->detachJitCompilation(exit.exitIndex);
+        }
+
+        fop->delete_(dependentAsmJSModules_);
+        dependentAsmJSModules_ = nullptr;
+    }
+}
+
+bool
+BaselineScript::addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit)
+{
+    if (!dependentAsmJSModules_) {
+        dependentAsmJSModules_ = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
+        if (!dependentAsmJSModules_)
+            return false;
+    }
+    return dependentAsmJSModules_->append(exit);
+}
+
+void
+BaselineScript::removeDependentAsmJSModule(DependentAsmJSModuleExit exit)
+{
+    if (!dependentAsmJSModules_)
+        return;
+
+    for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
+        if ((*dependentAsmJSModules_)[i].module == exit.module &&
+            (*dependentAsmJSModules_)[i].exitIndex == exit.exitIndex)
+        {
+            dependentAsmJSModules_->erase(dependentAsmJSModules_->begin() + i);
+            break;
+        }
+    }
+}
+
 ICEntry &
 BaselineScript::icEntry(size_t index)
 {
     MOZ_ASSERT(index < numICEntries());
     return icEntryList()[index];
 }
 
 PCMappingIndexEntry &
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -88,16 +88,29 @@ struct PCMappingIndexEntry
 
     // Native code offset.
     uint32_t nativeOffset;
 
     // Offset in the CompactBuffer where data for pcOffset starts.
     uint32_t bufferOffset;
 };
 
+// Describes a single AsmJSModule which jumps (via an FFI exit with the given
+// index) directly to a BaselineScript or IonScript.
+struct DependentAsmJSModuleExit
+{
+    const AsmJSModule *module;
+    size_t exitIndex;
+
+    DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
+      : module(module),
+        exitIndex(exitIndex)
+    { }
+};
+
 struct BaselineScript
 {
   public:
     static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu;
 
     // Limit the locals on a given script so that stack check on baseline frames
     // doesn't overflow a uint32_t value.
     // (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t.
@@ -109,16 +122,20 @@ struct BaselineScript
 
     // For heavyweight scripts, template objects to use for the call object and
     // decl env object (linked via the call object's enclosing scope).
     HeapPtrObject templateScope_;
 
     // Allocated space for fallback stubs.
     FallbackICStubSpace fallbackStubSpace_;
 
+    // If non-null, the list of AsmJSModules that contain an optimized call
+    // directly to this script.
+    Vector<DependentAsmJSModuleExit> *dependentAsmJSModules_;
+
     // Native code offset right before the scope chain is initialized.
     uint32_t prologueOffset_;
 
     // Native code offset right before the frame is popped and the method
     // returned from.
     uint32_t epilogueOffset_;
 
     // The offsets for the toggledJump instructions for SPS update ICs.
@@ -342,16 +359,20 @@ struct BaselineScript
     uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = nullptr);
 
     jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
     jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
 
     jsbytecode *pcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
     jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset);
 
+    bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
+    void unlinkDependentAsmJSModules(FreeOp *fop);
+    void removeDependentAsmJSModule(DependentAsmJSModuleExit exit);
+
   private:
     jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset, bool isReturn);
 
   public:
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is nullptr, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1212,18 +1212,18 @@ PrepareAndExecuteRegExp(JSContext *cx, M
     masm.storePtr(temp3, lazySourceAddress);
     masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
     masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
 
     return true;
 }
 
 static void
-CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
-                size_t fromWidth, size_t toWidth);
+CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
+                Register byteOpScratch, size_t fromWidth, size_t toWidth);
 
 static void
 CreateDependentString(MacroAssembler &masm, const JSAtomState &names,
                       bool latin1, Register string,
                       Register base, Register temp1, Register temp2,
                       BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
                       Label *failure)
 {
@@ -5916,18 +5916,18 @@ CodeGenerator::visitConcatPar(LConcatPar
     MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
     MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
     MOZ_ASSERT(output == CallTempReg5);
 
     return emitConcat(lir, lhs, rhs, output);
 }
 
 static void
-CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
-                size_t fromWidth, size_t toWidth)
+CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
+                Register byteOpScratch, size_t fromWidth, size_t toWidth)
 {
     // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
     // (checked below in debug builds), and when done |to| must point to the
     // next available char.
 
 #ifdef DEBUG
     Label ok;
     masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
@@ -5937,23 +5937,23 @@ CopyStringChars(MacroAssembler &masm, Re
 
     MOZ_ASSERT(fromWidth == 1 || fromWidth == 2);
     MOZ_ASSERT(toWidth == 1 || toWidth == 2);
     MOZ_ASSERT_IF(toWidth == 1, fromWidth == 1);
 
     Label start;
     masm.bind(&start);
     if (fromWidth == 2)
-        masm.load16ZeroExtend(Address(from, 0), scratch);
+        masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
     else
-        masm.load8ZeroExtend(Address(from, 0), scratch);
+        masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
     if (toWidth == 2)
-        masm.store16(scratch, Address(to, 0));
+        masm.store16(byteOpScratch, Address(to, 0));
     else
-        masm.store8(scratch, Address(to, 0));
+        masm.store8(byteOpScratch, Address(to, 0));
     masm.addPtr(Imm32(fromWidth), from);
     masm.addPtr(Imm32(toWidth), to);
     masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
 }
 
 static void
 CopyStringCharsMaybeInflate(MacroAssembler &masm, Register input, Register destChars,
                             Register temp1, Register temp2)
@@ -6060,18 +6060,22 @@ static const VMFunction SubstringKernelI
 
 bool CodeGenerator::visitSubstr(LSubstr *lir)
 {
     Register string = ToRegister(lir->string());
     Register begin = ToRegister(lir->begin());
     Register length = ToRegister(lir->length());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
-    Register temp2 = ToRegister(lir->temp2());
     Register temp3 = ToRegister(lir->temp3());
+
+    // On x86 there are not enough registers. In that case reuse the string
+    // register as temporary.
+    Register temp2 = lir->temp2()->isBogusTemp() ? string : ToRegister(lir->temp2());
+
     Address stringFlags(string, JSString::offsetOfFlags());
 
     Label isLatin1, notInline, nonZero, isInlinedLatin1;
 
     // For every edge case use the C++ variant.
     // Note: we also use this upon allocation failure in newGCString and
     // newGCFatInlineString. To squeeze out even more performance those failures
     // can be handled by allocate in ool code and returning to jit code to fill
@@ -6105,35 +6109,43 @@ bool CodeGenerator::visitSubstr(LSubstr 
     Address outputStorage(output, JSInlineString::offsetOfInlineStorage());
 
     masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
                       &isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
                      Address(output, JSString::offsetOfFlags()));
         masm.computeEffectiveAddress(stringStorage, temp);
+        if (temp2 == string)
+            masm.push(string);
         BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
         masm.computeEffectiveAddress(chars, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
         CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
         masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store16(Imm32(0), Address(temp, 0));
+        if (temp2 == string)
+            masm.pop(string);
         masm.jump(done);
     }
     masm.bind(&isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
                      Address(output, JSString::offsetOfFlags()));
+        if (temp2 == string)
+            masm.push(string);
         masm.computeEffectiveAddress(stringStorage, temp2);
         static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
         masm.addPtr(begin, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
         CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
         masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store8(Imm32(0), Address(temp, 0));
+        if (temp2 == string)
+            masm.pop(string);
         masm.jump(done);
     }
 
     // Handle other cases with a DependentString.
     masm.bind(&notInline);
     masm.newGCString(output, temp, slowPath);
     masm.store32(length, Address(output, JSString::offsetOfLength()));
     masm.storePtr(string, Address(output, JSDependentString::offsetOfBase()));
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -7,17 +7,16 @@
 #include "jit/Ion.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsprf.h"
 
-#include "asmjs/AsmJSModule.h"
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/BacktrackingAllocator.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CodeGenerator.h"
 #include "jit/EdgeCaseAnalysis.h"
@@ -839,17 +838,16 @@ IonScript::IonScript()
     callTargetList_(0),
     callTargetEntries_(0),
     backedgeList_(0),
     backedgeEntries_(0),
     invalidationCount_(0),
     parallelAge_(0),
     recompileInfo_(),
     osrPcMismatchCounter_(0),
-    dependentAsmJSModules(nullptr),
     pendingBuilder_(nullptr)
 {
 }
 
 IonScript *
 IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
                uint32_t frameSlots, uint32_t frameSize,
                size_t snapshotsListSize, size_t snapshotsRVATableSize,
@@ -1217,42 +1215,19 @@ IonScript::purgeCaches()
 
 void
 IonScript::destroyCaches()
 {
     for (size_t i = 0; i < numCaches(); i++)
         getCacheFromIndex(i).destroy();
 }
 
-bool
-IonScript::addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit)
-{
-    if (!dependentAsmJSModules) {
-        dependentAsmJSModules = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
-        if (!dependentAsmJSModules)
-            return false;
-    }
-    return dependentAsmJSModules->append(exit);
-}
-
 void
 IonScript::unlinkFromRuntime(FreeOp *fop)
 {
-    // Remove any links from AsmJSModules that contain optimized FFI calls into
-    // this IonScript.
-    if (dependentAsmJSModules) {
-        for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
-            DependentAsmJSModuleExit exit = dependentAsmJSModules->begin()[i];
-            exit.module->detachIonCompilation(exit.exitIndex);
-        }
-
-        fop->delete_(dependentAsmJSModules);
-        dependentAsmJSModules = nullptr;
-    }
-
     // The writes to the executable buffer below may clobber backedge jumps, so
     // make sure that those backedges are unlinked from the runtime and not
     // reclobbered with garbage if an interrupt is requested.
     JitRuntime *jrt = fop->runtime()->jitRuntime();
     JitRuntime::AutoMutateBackedges amb(jrt);
     for (size_t i = 0; i < backedgeEntries_; i++)
         jrt->removePatchableBackedge(&backedgeList()[i]);
 
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -155,29 +155,16 @@ class SnapshotWriter;
 class RecoverWriter;
 class SafepointWriter;
 class SafepointIndex;
 class OsiIndex;
 class IonCache;
 struct PatchableBackedgeInfo;
 struct CacheLocation;
 
-// Describes a single AsmJSModule which jumps (via an FFI exit with the given
-// index) directly into an IonScript.
-struct DependentAsmJSModuleExit
-{
-    const AsmJSModule *module;
-    size_t exitIndex;
-
-    DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
-      : module(module),
-        exitIndex(exitIndex)
-    { }
-};
-
 // An IonScript attaches Ion-generated information to a JSScript.
 struct IonScript
 {
   private:
     // Code pointer containing the actual method.
     PreBarrieredJitCode method_;
 
     // Deoptimization table used by this method.
@@ -293,20 +280,16 @@ struct IonScript
 
     // The optimization level this script was compiled in.
     OptimizationLevel optimizationLevel_;
 
     // Number of times we tried to enter this script via OSR but failed due to
     // a LOOPENTRY pc other than osrPc_.
     uint32_t osrPcMismatchCounter_;
 
-    // If non-null, the list of AsmJSModules
-    // that contain an optimized call directly into this IonScript.
-    Vector<DependentAsmJSModuleExit> *dependentAsmJSModules;
-
     IonBuilder *pendingBuilder_;
 
   private:
     inline uint8_t *bottomBuffer() {
         return reinterpret_cast<uint8_t *>(this);
     }
     inline const uint8_t *bottomBuffer() const {
         return reinterpret_cast<const uint8_t *>(this);
@@ -347,29 +330,16 @@ struct IonScript
         return  &bottomBuffer()[runtimeData_];
     }
     JSScript **callTargetList() {
         return (JSScript **) &bottomBuffer()[callTargetList_];
     }
     PatchableBackedge *backedgeList() {
         return (PatchableBackedge *) &bottomBuffer()[backedgeList_];
     }
-    bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
-    void removeDependentAsmJSModule(DependentAsmJSModuleExit exit) {
-        if (!dependentAsmJSModules)
-            return;
-        for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
-            if (dependentAsmJSModules->begin()[i].module == exit.module &&
-                dependentAsmJSModules->begin()[i].exitIndex == exit.exitIndex)
-            {
-                dependentAsmJSModules->erase(dependentAsmJSModules->begin() + i);
-                break;
-            }
-        }
-    }
 
   private:
     void trace(JSTracer *trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2177,31 +2177,16 @@ LIRGenerator::visitStringReplace(MString
 
     LStringReplace *lir = new(alloc()) LStringReplace(useRegisterOrConstantAtStart(ins->string()),
                                                       useRegisterAtStart(ins->pattern()),
                                                       useRegisterOrConstantAtStart(ins->replacement()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitSubstr(MSubstr *ins)
-{
-    // The last temporary need to be a register that can handle 8bit moves, but
-    // there is no way to signal that to register allocator, except to give a
-    // fixed temporary that is able to do this.
-    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
-                                         useRegister(ins->begin()),
-                                         useRegister(ins->length()),
-                                         temp(),
-                                         temp(),
-                                         tempFixed(CallTempReg1));
-    return define(lir, ins) && assignSafepoint(lir, ins);
-}
-
-bool
 LIRGenerator::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // If the function has a singleton type, this instruction will only be
         // executed once so we don't bother inlining it.
         //
         // If UseNewTypeForClone is true, we will assign a singleton type to
         // the clone and we have to clone the script, we can't do that inline.
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -141,17 +141,16 @@ class LIRGenerator : public LIRGenerator
     bool visitMul(MMul *ins);
     bool visitDiv(MDiv *ins);
     bool visitMod(MMod *ins);
     bool visitConcat(MConcat *ins);
     bool visitConcatPar(MConcatPar *ins);
     bool visitCharCodeAt(MCharCodeAt *ins);
     bool visitFromCharCode(MFromCharCode *ins);
     bool visitStringSplit(MStringSplit *ins);
-    bool visitSubstr(MSubstr *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitNop(MNop *nop);
     bool visitLimitedTruncate(MLimitedTruncate *nop);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitOsrReturnValue(MOsrReturnValue *value);
     bool visitOsrArgumentsObject(MOsrArgumentsObject *object);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -50,16 +50,22 @@ LIRGeneratorARM::useByteOpRegister(MDefi
 }
 
 LAllocation
 LIRGeneratorARM::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useRegisterOrNonDoubleConstant(mir);
 }
 
+LDefinition
+LIRGeneratorARM::tempByteOpRegister()
+{
+    return temp();
+}
+
 bool
 LIRGeneratorARM::lowerConstantDouble(double d, MInstruction *mir)
 {
     return define(new(alloc()) LDouble(d), mir);
 }
 
 bool
 LIRGeneratorARM::lowerConstantFloat32(float d, MInstruction *mir)
@@ -669,8 +675,20 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHe
 
     LAsmJSAtomicBinopHeap *lir =
         new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr),
                                            useRegister(ins->value()),
                                            LDefinition::BogusTemp());
 
     return define(lir, ins);
 }
+
+bool
+LIRGeneratorARM::visitSubstr(MSubstr *ins)
+{
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         temp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -25,16 +25,17 @@ class LIRGeneratorARM : public LIRGenera
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on ARM all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return false; }
 
     // x64 has a scratch register, so no need for another temp for dispatch
@@ -106,16 +107,17 @@ class LIRGeneratorARM : public LIRGenera
     bool visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
     bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins);
     bool visitSimdSplatX4(MSimdSplatX4 *ins);
     bool visitSimdValueX4(MSimdValueX4 *ins);
     bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
     bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
+    bool visitSubstr(MSubstr *ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1856,21 +1856,21 @@ MacroAssemblerARMCompat::callIon(Registe
         ma_callIonHalfPush(callee);
     } else {
         adjustFrame(sizeof(void*));
         ma_callIon(callee);
     }
 }
 
 void
-MacroAssemblerARMCompat::callIonFromAsmJS(Register callee)
+MacroAssemblerARMCompat::callJitFromAsmJS(Register callee)
 {
     ma_callIonNoPush(callee);
 
-    // The Ion ABI has the callee pop the return address off the stack.
+    // The JIT ABI has the callee pop the return address off the stack.
     // The asm.js caller assumes that the call leaves sp unchanged, so bump
     // the stack.
     subPtr(Imm32(sizeof(void*)), sp);
 }
 
 void
 MacroAssembler::alignFrameForICArguments(AfterICSaveLive &aic)
 {
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1288,17 +1288,17 @@ class MacroAssemblerARMCompat : public M
 
     void callWithExitFrame(Label *target);
     void callWithExitFrame(JitCode *target);
     void callWithExitFrame(JitCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // independent code to make a call.
     void callIon(Register callee);
-    void callIonFromAsmJS(Register callee);
+    void callJitFromAsmJS(Register callee);
 
     void reserveStack(uint32_t amount);
     void freeStack(uint32_t amount);
     void freeStack(Register amount);
 
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address &dest);
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -52,16 +52,22 @@ LIRGeneratorMIPS::useByteOpRegister(MDef
 }
 
 LAllocation
 LIRGeneratorMIPS::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useRegisterOrNonDoubleConstant(mir);
 }
 
+LDefinition
+LIRGeneratorMIPS::tempByteOpRegister()
+{
+    return temp();
+}
+
 bool
 LIRGeneratorMIPS::lowerConstantDouble(double d, MInstruction *mir)
 {
     return define(new(alloc()) LDouble(d), mir);
 }
 
 bool
 LIRGeneratorMIPS::lowerConstantFloat32(float d, MInstruction *mir)
@@ -521,16 +527,28 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(
 {
     MDefinition *opd = ins->input();
     MOZ_ASSERT(opd->type() == MIRType_Float32);
 
     return define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
 }
 
 bool
+LIRGeneratorMIPS::visitSubstr(MSubstr *ins)
+{
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         temp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGeneratorMIPS::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
 {
     MOZ_CRASH("NYI");
 }
 
 bool
 LIRGeneratorMIPS::visitForkJoinGetSlice(MForkJoinGetSlice *ins)
 {
--- a/js/src/jit/mips/Lowering-mips.h
+++ b/js/src/jit/mips/Lowering-mips.h
@@ -25,16 +25,17 @@ class LIRGeneratorMIPS : public LIRGener
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on MIPS all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return false; }
 
     // MIPS has a scratch register, so no need for another temp for dispatch
@@ -104,16 +105,17 @@ class LIRGeneratorMIPS : public LIRGener
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
     bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins);
     bool visitSimdSplatX4(MSimdSplatX4 *ins);
     bool visitSimdValueX4(MSimdValueX4 *ins);
     bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
     bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
+    bool visitSubstr(MSubstr *ins);
 };
 
 typedef LIRGeneratorMIPS LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_Lowering_mips_h */
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -1544,21 +1544,21 @@ MacroAssemblerMIPSCompat::callIon(Regist
     if ((framePushed() & 7) == 4) {
         ma_callIonHalfPush(callee);
     } else {
         adjustFrame(sizeof(uint32_t));
         ma_callIon(callee);
     }
 }
 void
-MacroAssemblerMIPSCompat::callIonFromAsmJS(Register callee)
+MacroAssemblerMIPSCompat::callJitFromAsmJS(Register callee)
 {
     ma_callIonNoPush(callee);
 
-    // The Ion ABI has the callee pop the return address off the stack.
+    // The JIT ABI has the callee pop the return address off the stack.
     // The asm.js caller assumes that the call leaves sp unchanged, so bump
     // the stack.
     subPtr(Imm32(sizeof(void*)), StackPointer);
 }
 
 void
 MacroAssemblerMIPSCompat::reserveStack(uint32_t amount)
 {
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -1135,17 +1135,17 @@ public:
 
     void callWithExitFrame(Label *target);
     void callWithExitFrame(JitCode *target);
     void callWithExitFrame(JitCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // indep code to make a call
     void callIon(Register callee);
-    void callIonFromAsmJS(Register callee);
+    void callJitFromAsmJS(Register callee);
 
     void reserveStack(uint32_t amount);
     void freeStack(uint32_t amount);
     void freeStack(Register amount);
 
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address &dest);
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -24,16 +24,17 @@ class LIRGeneratorNone : public LIRGener
     bool useBox(LInstruction *, size_t, MDefinition *,
                 LUse::Policy a = LUse::REGISTER, bool b = false) {
         MOZ_CRASH();
     }
     bool useBoxFixed(LInstruction *, size_t, MDefinition *, Register, Register) { MOZ_CRASH(); }
 
     LAllocation useByteOpRegister(MDefinition *) { MOZ_CRASH(); }
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *) { MOZ_CRASH(); }
+    LDefinition tempByteOpRegister() { MOZ_CRASH(); }
     LDefinition tempToUnbox() { MOZ_CRASH(); }
     bool needTempForPostBarrier() { MOZ_CRASH(); }
     LDefinition tempForDispatchCache(MIRType v = MIRType_None) { MOZ_CRASH(); }
     void lowerUntypedPhiInput(MPhi *, uint32_t, LBlock *, size_t) { MOZ_CRASH(); }
     bool defineUntypedPhi(MPhi *, size_t) { MOZ_CRASH(); }
     bool lowerForShift(LInstructionHelper<1, 2, 0> *, MDefinition *, MDefinition *, MDefinition *) {
         MOZ_CRASH();
     }
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -186,17 +186,17 @@ class MacroAssemblerNone : public Assemb
     void setupUnalignedABICall(uint32_t, Register) { MOZ_CRASH(); }
     template <typename T> void passABIArg(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
 
     void callWithExitFrame(Label *) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode *) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode *, Register) { MOZ_CRASH(); }
 
     void callIon(Register callee) { MOZ_CRASH(); }
-    void callIonFromAsmJS(Register callee) { MOZ_CRASH(); }
+    void callJitFromAsmJS(Register callee) { MOZ_CRASH(); }
 
     void nop() { MOZ_CRASH(); }
     void breakpoint() { MOZ_CRASH(); }
     void abiret() { MOZ_CRASH(); }
     void ret() { MOZ_CRASH(); }
 
     CodeOffsetLabel toggledJump(Label *) { MOZ_CRASH(); }
     CodeOffsetLabel toggledCall(JitCode *, bool) { MOZ_CRASH(); }
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -1200,17 +1200,17 @@ class MacroAssemblerX86Shared : public A
     }
     void call(const CallSiteDesc &desc, Register reg) {
         call(reg);
         append(desc, currentOffset(), framePushed_);
     }
     void callIon(Register callee) {
         call(callee);
     }
-    void callIonFromAsmJS(Register callee) {
+    void callJitFromAsmJS(Register callee) {
         call(callee);
     }
     void call(AsmJSImmPtr target) {
         mov(target, eax);
         call(eax);
     }
     void callAndPushReturnAddress(Label *label) {
         call(label);
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -45,16 +45,22 @@ LIRGeneratorX64::useByteOpRegister(MDefi
 
 LAllocation
 LIRGeneratorX64::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useRegisterOrNonDoubleConstant(mir);
 }
 
 LDefinition
+LIRGeneratorX64::tempByteOpRegister()
+{
+    return temp();
+}
+
+LDefinition
 LIRGeneratorX64::tempToUnbox()
 {
     return temp();
 }
 
 bool
 LIRGeneratorX64::visitBox(MBox *box)
 {
@@ -186,12 +192,24 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAs
 
 bool
 LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
 }
 
 bool
+LIRGeneratorX64::visitSubstr(MSubstr *ins)
+{
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         temp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
 {
     MOZ_CRASH("NYI");
 }
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -27,16 +27,17 @@ class LIRGeneratorX64 : public LIRGenera
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on x64 all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     LDefinition tempToUnbox();
 
     bool needTempForPostBarrier() { return false; }
 
     // x64 has a scratch register, so no need for another temp for dispatch
     // ICs.
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
@@ -48,16 +49,17 @@ class LIRGeneratorX64 : public LIRGenera
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
+    bool visitSubstr(MSubstr *ins);
 
     static bool allowInlineForkJoinGetSlice() {
         return true;
     }
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -71,16 +71,22 @@ LIRGeneratorX86::useByteOpRegister(MDefi
 }
 
 LAllocation
 LIRGeneratorX86::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useFixed(mir, eax);
 }
 
+LDefinition
+LIRGeneratorX86::tempByteOpRegister()
+{
+    return tempFixed(eax);
+}
+
 bool
 LIRGeneratorX86::visitBox(MBox *box)
 {
     MDefinition *inner = box->getOperand(0);
 
     // If the box wrapped a double, it needs a new register.
     if (IsFloatingPointType(inner->type()))
         return defineBox(new(alloc()) LBoxFloatingPoint(useRegisterAtStart(inner), tempCopy(inner, 0),
@@ -304,8 +310,23 @@ LIRGeneratorX86::visitStoreTypedArrayEle
     return add(lir, ins);
 }
 
 bool
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
+
+bool
+LIRGeneratorX86::visitSubstr(MSubstr *ins)
+{
+    // Due to lack of registers on x86, we reuse the string register as
+    // temporary. As a result we only need two temporary registers and take a
+    // bugos temporary as fifth argument.
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         LDefinition::BogusTemp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -28,16 +28,17 @@ class LIRGeneratorX86 : public LIRGenera
 
     // It's a trap! On x86, the 1-byte store can only use one of
     // {al,bl,cl,dl,ah,bh,ch,dh}. That means if the register allocator
     // gives us one of {edi,esi,ebp,esp}, we're out of luck. (The formatter
     // will assert on us.) Ideally, we'd just ask the register allocator to
     // give us one of {al,bl,cl,dl}. For now, just useFixed(al).
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return true; }
 
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None);
@@ -50,16 +51,17 @@ class LIRGeneratorX86 : public LIRGenera
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
+    bool visitSubstr(MSubstr *ins);
     bool lowerPhi(MPhi *phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -555,17 +555,17 @@ FinalizeTypedArenas(FreeOp *fop,
                     ArenaHeader **src,
                     SortedArenaList &dest,
                     AllocKind thingKind,
                     SliceBudget &budget,
                     ArenaLists::KeepArenasEnum keepArenas)
 {
     // When operating in the foreground, take the lock at the top.
     Maybe<AutoLockGC> maybeLock;
-    if (!fop->runtime()->gc.isBackgroundSweeping())
+    if (!fop->onBackgroundThread())
         maybeLock.emplace(fop->runtime());
 
     /*
      * During parallel sections, we sometimes finalize the parallel arenas,
      * but in that case, we want to hold on to the memory in our arena
      * lists, not offer it up for reuse.
      */
     MOZ_ASSERT_IF(InParallelSection(), keepArenas);
@@ -575,19 +575,19 @@ FinalizeTypedArenas(FreeOp *fop,
 
     while (ArenaHeader *aheader = *src) {
         *src = aheader->next;
         size_t nmarked = aheader->getArena()->finalize<T>(fop, thingKind, thingSize);
         size_t nfree = thingsPerArena - nmarked;
 
         if (nmarked) {
             dest.insertAt(aheader, nfree);
-        } else if (keepArenas) {
+        } else if (keepArenas == ArenaLists::KEEP_ARENAS) {
             aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena);
-        } else if (fop->runtime()->gc.isBackgroundSweeping()) {
+        } else if (fop->onBackgroundThread()) {
             // When background sweeping, take the lock around each release so
             // that we do not block the foreground for extended periods.
             AutoLockGC lock(fop->runtime());
             fop->runtime()->gc.releaseArena(aheader, lock);
         } else {
             fop->runtime()->gc.releaseArena(aheader, maybeLock.ref());
         }
 
@@ -1154,17 +1154,16 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     isFull(false),
 #ifdef DEBUG
     disableStrictProxyCheckingCount(0),
 #endif
     incrementalState(gc::NO_INCREMENTAL),
     lastMarkSlice(false),
     sweepOnBackgroundThread(false),
     foundBlackGrayEdges(false),
-    sweepingZones(nullptr),
     freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     zoneGroupIndex(0),
     zoneGroups(nullptr),
     currentZoneGroup(nullptr),
     sweepZone(nullptr),
     sweepKindIndex(0),
     abortSweepAfterCurrentGroup(false),
     arenasAllocatedDuringSweep(nullptr),
@@ -2864,47 +2863,49 @@ ArenaLists::queueForBackgroundSweep(Free
     for (unsigned i = 0; i < phase.length; ++i)
         queueForBackgroundSweep(fop, phase.kinds[i]);
 }
 
 inline void
 ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
 {
     MOZ_ASSERT(IsBackgroundFinalized(thingKind));
-    MOZ_ASSERT(!fop->runtime()->gc.isBackgroundSweeping());
 
     ArenaList *al = &arenaLists[thingKind];
     if (al->isEmpty()) {
         MOZ_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
         return;
     }
 
     MOZ_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
 
     arenaListsToSweep[thingKind] = al->head();
     al->clear();
     backgroundFinalizeState[thingKind] = BFS_RUN;
 }
 
 /*static*/ void
-ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
+ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, ArenaHeader **empty)
 {
     MOZ_ASSERT(listHead);
     MOZ_ASSERT(!InParallelSection());
+    MOZ_ASSERT(empty);
 
     AllocKind thingKind = listHead->getAllocKind();
     Zone *zone = listHead->zone;
 
     size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
     SortedArenaList finalizedSorted(thingsPerArena);
 
     SliceBudget budget;
-    FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, RELEASE_ARENAS);
+    FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, KEEP_ARENAS);
     MOZ_ASSERT(!listHead);
 
+    finalizedSorted.extractEmpty(empty);
+
     // When arenas are queued for background finalization, all arenas are moved
     // to arenaListsToSweep[], leaving the arenaLists[] empty. However, new
     // arenas may be allocated before background finalization finishes; now that
     // finalization is complete, we want to merge these lists back together.
     ArenaLists *lists = &zone->allocator.arenas;
     ArenaList *al = &lists->arenaLists[thingKind];
 
     // Flatten |finalizedSorted| into a regular ArenaList.
@@ -3439,48 +3440,51 @@ GCRuntime::expireChunksAndArenas(bool sh
         FreeChunkPool(rt, toFree);
     }
 
     if (shouldShrink)
         decommitArenas(lock);
 }
 
 void
-GCRuntime::sweepBackgroundThings()
-{
-    /*
-     * We must finalize in the correct order, see comments in
-     * finalizeObjects.
-     */
-    FreeOp fop(rt);
-    for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
-        for (Zone *zone = sweepingZones; zone; zone = zone->gcNextGraphNode) {
+GCRuntime::sweepBackgroundThings(ZoneList &zones, ThreadType threadType)
+{
+    // We must finalize thing kinds in the order specified by BackgroundFinalizePhases.
+    FreeOp fop(rt, threadType);
+    while (!zones.isEmpty()) {
+        Zone *zone = zones.front();
+        ArenaHeader *emptyArenas = nullptr;
+        for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
             for (unsigned index = 0 ; index < BackgroundFinalizePhases[phase].length ; ++index) {
                 AllocKind kind = BackgroundFinalizePhases[phase].kinds[index];
                 ArenaHeader *arenas = zone->allocator.arenas.arenaListsToSweep[kind];
                 if (arenas)
-                    ArenaLists::backgroundFinalize(&fop, arenas);
+                    ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
-    }
-
-    sweepingZones = nullptr;
+
+        AutoLockGC lock(rt);
+        ReleaseArenaList(rt, emptyArenas, lock);
+        zones.removeFront();
+    }
 }
 
 void
 GCRuntime::assertBackgroundSweepingFinished()
 {
 #ifdef DEBUG
-    MOZ_ASSERT(!sweepingZones);
+    MOZ_ASSERT(backgroundSweepZones.isEmpty());
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+        MOZ_ASSERT(!zone->isOnList());
         for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) {
             MOZ_ASSERT(!zone->allocator.arenas.arenaListsToSweep[i]);
             MOZ_ASSERT(zone->allocator.arenas.doneBackgroundFinalize(AllocKind(i)));
         }
     }
+    MOZ_ASSERT(freeLifoAlloc.computedSizeOfExcludingThis() == 0);
 #endif
 }
 
 unsigned
 js::GetCPUCount()
 {
     static unsigned ncpus = 0;
     if (ncpus == 0) {
@@ -3617,32 +3621,53 @@ BackgroundAllocTask::run()
             if (!chunk)
                 break;
         }
         chunkPool_.push(chunk);
     }
 }
 
 void
-GCHelperState::startBackgroundSweep()
-{
-    MOZ_ASSERT(CanUseExtraThreads());
-
+GCRuntime::queueZonesForBackgroundSweep(ZoneList &zones)
+{
     AutoLockHelperThreadState helperLock;
     AutoLockGC lock(rt);
-    MOZ_ASSERT(state() == IDLE);
-    MOZ_ASSERT(!sweepFlag);
+    backgroundSweepZones.append(zones);
+    helperState.maybeStartBackgroundSweep(lock);
+}
+
+void
+GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc *lifo)
+{
+    MOZ_ASSERT(isHeapBusy());
+    AutoLockGC lock(rt);
+    freeLifoAlloc.transferUnusedFrom(lifo);
+}
+
+void
+GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc *lifo)
+{
+    MOZ_ASSERT(isHeapBusy());
+    AutoLockGC lock(rt);
+    freeLifoAlloc.transferFrom(lifo);
+}
+
+void
+GCHelperState::maybeStartBackgroundSweep(const AutoLockGC &lock)
+{
+    MOZ_ASSERT(CanUseExtraThreads());
+
     sweepFlag = true;
     shrinkFlag = false;
-    startBackgroundThread(SWEEPING);
-}
-
-/* Must be called with the GC lock taken. */
-void
-GCHelperState::startBackgroundShrink()
+    if (state() == IDLE)
+        startBackgroundThread(SWEEPING);
+}
+
+void
+GCHelperState::startBackgroundShrink(const AutoLockGC &lock)
 {
     MOZ_ASSERT(CanUseExtraThreads());
     switch (state()) {
       case IDLE:
         MOZ_ASSERT(!sweepFlag);
         shrinkFlag = true;
         startBackgroundThread(SWEEPING);
         break;
@@ -3662,23 +3687,26 @@ GCHelperState::waitBackgroundSweepEnd()
         waitForBackgroundThread();
     if (rt->gc.incrementalState == NO_INCREMENTAL)
         rt->gc.assertBackgroundSweepingFinished();
 }
 
 void
 GCHelperState::doSweep(AutoLockGC &lock)
 {
-    if (sweepFlag) {
+    while (sweepFlag) {
         sweepFlag = false;
+        ZoneList zones;
+        zones.transferFrom(rt->gc.backgroundSweepZones);
+        LifoAlloc freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
+        freeLifoAlloc.transferFrom(&rt->gc.freeLifoAlloc);
         AutoUnlockGC unlock(lock);
 
-        rt->gc.sweepBackgroundThings();
-
-        rt->gc.freeLifoAlloc.freeAll();
+        rt->gc.sweepBackgroundThings(zones, BackgroundThread);
+        freeLifoAlloc.freeAll();
     }
 
     bool shrinking = shrinkFlag;
     rt->gc.expireChunksAndArenas(shrinking, lock);
 
     /*
      * The main thread may have called ShrinkGCBuffers while
      * ExpireChunksAndArenas(rt, false) was running, so we recheck the flag
@@ -3757,68 +3785,58 @@ Zone::sweepCompartments(FreeOp *fop, boo
     }
     compartments.resize(write - compartments.begin());
     MOZ_ASSERT_IF(keepAtleastOne, !compartments.empty());
 }
 
 void
 GCRuntime::sweepZones(FreeOp *fop, bool lastGC)
 {
+    AutoLockGC lock(rt); // Avoid race with background sweeping.
+
     JSZoneCallback callback = rt->destroyZoneCallback;
 
     /* Skip the atomsCompartment zone. */
     Zone **read = zones.begin() + 1;
     Zone **end = zones.end();
     Zone **write = read;
     MOZ_ASSERT(zones.length() >= 1);
     MOZ_ASSERT(rt->isAtomsZone(zones[0]));
 
     while (read < end) {
         Zone *zone = *read++;
 
         if (zone->wasGCStarted()) {
-            if ((zone->allocator.arenas.arenaListsAreEmpty() && !zone->hasMarkedCompartments()) ||
-                lastGC)
+            if ((!zone->isQueuedForBackgroundSweep() &&
+                 zone->allocator.arenas.arenaListsAreEmpty() &&
+                 !zone->hasMarkedCompartments()) || lastGC)
             {
                 zone->allocator.arenas.checkEmptyFreeLists();
+                AutoUnlockGC unlock(lock);
+
                 if (callback)
                     callback(zone);
                 zone->sweepCompartments(fop, false, lastGC);
                 MOZ_ASSERT(zone->compartments.empty());
                 fop->delete_(zone);
                 continue;
             }
             zone->sweepCompartments(fop, true, lastGC);
         }
         *write++ = zone;
     }
     zones.resize(write - zones.begin());
 }
 
 void
-GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc *lifo)
-{
-    MOZ_ASSERT(isHeapBusy());
-    freeLifoAlloc.transferUnusedFrom(lifo);
-}
-
-void
-GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc *lifo)
-{
-    MOZ_ASSERT(isHeapBusy());
-    freeLifoAlloc.transferFrom(lifo);
-}
-
-void
 GCRuntime::purgeRuntime()
 {
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->purge();
 
-
     freeUnusedLifoBlocksAfterSweeping(&rt->tempLifoAlloc);
 
     rt->interpreterStack().purge(rt);
     rt->gsnCache.purge();
     rt->scopeCoordinateNameCache.purge();
     rt->newObjectCache.purge();
     rt->nativeIterCache.purge();
     rt->uncompressedSourceCache.purge();
@@ -4259,16 +4277,18 @@ js::gc::MarkingValidator::nonIncremental
      */
 
     if (!map.init())
         return;
 
     JSRuntime *runtime = gc->rt;
     GCMarker *gcmarker = &gc->marker;
 
+    gc->waitBackgroundSweepEnd();
+
     /* Save existing mark bits. */
     for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
         ChunkBitmap *bitmap = &chunk->bitmap;
 	ChunkBitmap *entry = js_new<ChunkBitmap>();
         if (!entry)
             return;
 
         memcpy((void *)entry->bitmap, (void *)bitmap->bitmap, sizeof(bitmap->bitmap));
@@ -4367,16 +4387,18 @@ js::gc::MarkingValidator::validate()
     /*
      * Validates the incremental marking for a single compartment by comparing
      * the mark bits to those previously recorded for a non-incremental mark.
      */
 
     if (!initialized)
         return;
 
+    gc->waitBackgroundSweepEnd();
+
     for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
         BitmapMap::Ptr ptr = map.lookup(chunk);
         if (!ptr)
             continue;  /* Allocated after we did the non-incremental mark. */
 
         ChunkBitmap *bitmap = ptr->value();
         ChunkBitmap *incBitmap = &chunk->bitmap;
 
@@ -5164,22 +5186,30 @@ GCRuntime::beginSweepingZoneGroup()
         gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
         callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END);
     }
 }
 
 void
 GCRuntime::endSweepingZoneGroup()
 {
-    /* Update the GC state for zones we have swept and unlink the list. */
+    /* Update the GC state for zones we have swept. */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCSweeping());
         zone->setGCState(Zone::Finished);
     }
 
+    /* Start background thread to sweep zones if required. */
+    if (sweepOnBackgroundThread) {
+        ZoneList zones;
+        for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
+            zones.append(zone);
+        queueZonesForBackgroundSweep(zones);
+    }
+
     /* Reset the list of arenas marked as being allocated during sweep phase. */
     while (ArenaHeader *arena = arenasAllocatedDuringSweep) {
         arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
     }
 }
 
 void
@@ -5201,17 +5231,17 @@ GCRuntime::beginSweepPhase(bool lastGC)
     relocatedArenasToRelease = nullptr;
 #endif
 
     computeNonIncrementalMarkingForValidation();
 
     gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
 
     sweepOnBackgroundThread =
-        !lastGC && !TraceEnabled() && CanUseExtraThreads() && !shouldCompact();
+        !lastGC && !TraceEnabled() && CanUseExtraThreads();
 
     releaseObservedTypes = shouldReleaseObservedTypes();
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         MOZ_ASSERT(!c->gcIncomingGrayPointers);
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
@@ -5478,28 +5508,24 @@ GCRuntime::endSweepPhase(bool lastGC)
         gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
         callFinalizeCallbacks(&fop, JSFINALIZE_COLLECTION_END);
 
         /* If we finished a full GC, then the gray bits are correct. */
         if (isFull)
             grayBitsValid = true;
     }
 
-    /* Set up list of zones for sweeping of background things. */
-    MOZ_ASSERT(!sweepingZones);
-    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-        zone->gcNextGraphNode = sweepingZones;
-        sweepingZones = zone;
-    }
-
     /* If not sweeping on background thread then we must do it here. */
     if (!sweepOnBackgroundThread) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_DESTROY);
 
-        sweepBackgroundThings();
+        ZoneList zones;
+        for (GCZonesIter zone(rt); !zone.done(); zone.next())
+            zones.append(zone);
+        sweepBackgroundThings(zones, MainThread);
 
         /*
          * Destroy arenas after we finished the sweeping so finalizers can
          * safely use IsAboutToBeFinalized(). This is done on the
          * GCHelperState if possible. We acquire the lock only because
          * Expire needs to unlock it for other callers.
          */
         {
@@ -5529,29 +5555,37 @@ GCRuntime::endSweepPhase(bool lastGC)
         MOZ_ASSERT(!c->gcIncomingGrayPointers);
 
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
                 AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
         }
     }
 #endif
-
-    if (sweepOnBackgroundThread)
-        helperState.startBackgroundSweep();
-}
-
-#ifdef JSGC_COMPACTING
-void
+}
+
+bool
 GCRuntime::compactPhase(bool lastGC)
 {
+#ifndef JSGC_COMPACTING
+    MOZ_CRASH();
+#else
+    gcstats::AutoPhase ap(stats, gcstats::PHASE_COMPACT);
+
+    if (isIncremental) {
+        // Poll for end of background sweeping
+        AutoLockGC lock(rt);
+        if (isBackgroundSweeping())
+            return false;
+    } else {
+        waitBackgroundSweepEnd();
+    }
+
     MOZ_ASSERT(rt->gc.nursery.isEmpty());
-    MOZ_ASSERT(!sweepOnBackgroundThread);
-
-    gcstats::AutoPhase ap(stats, gcstats::PHASE_COMPACT);
+    assertBackgroundSweepingFinished();
 
     ArenaHeader *relocatedList = relocateArenas();
     updatePointersToRelocatedCells();
 
 #ifdef DEBUG
     for (ArenaHeader *arena = relocatedList; arena; arena = arena->next) {
         for (ArenaCellIterUnderFinalize i(arena); !i.done(); i.next()) {
             TenuredCell *src = i.getCell();
@@ -5578,18 +5612,20 @@ GCRuntime::compactPhase(bool lastGC)
 
 #ifdef DEBUG
     CheckHashTablesAfterMovingGC(rt);
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         if (CanRelocateZone(rt, zone) && !zone->isPreservingCode())
             zone->allocator.arenas.checkEmptyFreeLists();
     }
 #endif
-}
+
 #endif // JSGC_COMPACTING
+    return true;
+}
 
 void
 GCRuntime::finishCollection()
 {
     MOZ_ASSERT(marker.isDrained());
     marker.stop();
 
     uint64_t currentTime = PRMJ_Now();
@@ -5705,16 +5741,18 @@ GCRuntime::resetIncrementalGC(const char
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             MOZ_ASSERT(zone->isGCMarking());
             zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
             zone->setGCState(Zone::NoGC);
         }
         rt->setNeedsIncrementalBarrier(false);
         AssertNeedsBarrierFlagsConsistent(rt);
 
+        freeLifoAlloc.freeAll();
+
         incrementalState = NO_INCREMENTAL;
 
         MOZ_ASSERT(!marker.shouldCheckCompartments());
 
         break;
       }
 
       case SWEEP: {
@@ -5730,16 +5768,21 @@ GCRuntime::resetIncrementalGC(const char
 
         {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
             rt->gc.waitBackgroundSweepOrAllocEnd();
         }
         break;
       }
 
+#ifdef JSGC_COMPACTING
+      case COMPACT:
+        break;
+#endif
+
       default:
         MOZ_CRASH("Invalid incremental GC state");
     }
 
     stats.reset(reason);
 
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
@@ -5922,34 +5965,41 @@ GCRuntime::incrementalCollectSlice(Slice
          * mode, so RunDebugGC can reset the slice buget.
          */
         if (isIncremental && zeal == ZealIncrementalMultipleSlices)
             break;
 
         /* fall through */
       }
 
-      case SWEEP: {
-        bool finished = sweepPhase(budget);
-        if (!finished)
-            break;
+      case SWEEP:
+        {
+            bool finished = sweepPhase(budget);
+            if (!finished)
+                break;
+        }
 
         endSweepPhase(lastGC);
 
-#ifdef JSGC_COMPACTING
+        incrementalState = COMPACT;
+
+        /* Yield before compacting since it is not incremental. */
+        if (shouldCompact() && isIncremental)
+            break;
+
+      case COMPACT:
         if (shouldCompact()) {
-            incrementalState = COMPACT;
-            compactPhase(lastGC);
+            bool finished = compactPhase(lastGC);
+            if (!finished)
+                break;
         }
-#endif
 
         finishCollection();
         incrementalState = NO_INCREMENTAL;
         break;
-      }
 
       default:
         MOZ_ASSERT(false);
     }
 }
 
 IncrementalSafety
 gc::IsIncrementalGCSafe(JSRuntime *rt)
@@ -6073,19 +6123,18 @@ GCRuntime::gcCycle(bool incremental, Sli
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     // Assert if this is a GC unsafe region.
     JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
 
-        // As we are about to purge caches and clear the mark bits, wait for
-        // background finalization to finish. It cannot run between slices
-        // so we only need to wait on the first slice.
+        // As we are about to clear the mark bits, wait for background
+        // finalization to finish. We only need to wait on the first slice.
         if (incrementalState == NO_INCREMENTAL)
             waitBackgroundSweepEnd();
 
         // We must also wait for background allocation to finish so we can
         // avoid taking the GC lock when manipulating the chunks during the GC.
         // The background alloc task can run between slices, so we must wait
         // for it at the start of every slice.
         allocTask.cancel(GCParallelTask::CancelAndWait);
@@ -6384,17 +6433,17 @@ JS::ShrinkGCBuffers(JSRuntime *rt)
 void
 GCRuntime::shrinkBuffers()
 {
     AutoLockHelperThreadState helperLock;
     AutoLockGC lock(rt);
     MOZ_ASSERT(!rt->isHeapBusy());
 
     if (CanUseExtraThreads())
-        helperState.startBackgroundShrink();
+        helperState.startBackgroundShrink(lock);
     else
         expireChunksAndArenas(true, lock);
 }
 
 void
 GCRuntime::onOutOfMallocMemory()
 {
     // Stop allocating new chunks.
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -34,32 +34,36 @@ unsigned GetCPUCount();
 
 enum HeapState {
     Idle,             // doing nothing with the GC heap
     Tracing,          // tracing the GC heap without collecting, e.g. IterateCompartments()
     MajorCollecting,  // doing a GC of the major heap
     MinorCollecting   // doing a GC of the minor heap (nursery)
 };
 
+enum ThreadType
+{
+    MainThread,
+    BackgroundThread
+};
+
 namespace jit {
     class JitCode;
 }
 
 namespace gc {
 
 struct FinalizePhase;
 
 enum State {
     NO_INCREMENTAL,
     MARK_ROOTS,
     MARK,
     SWEEP,
-#ifdef JSGC_COMPACTING
     COMPACT
-#endif
 };
 
 /* Return a printable string for the given kind, for diagnostic purposes. */
 const char *
 TraceKindAsAscii(JSGCTraceKind kind);
 
 /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
@@ -840,17 +844,17 @@ class ArenaLists
 
     void queueForegroundObjectsForSweep(FreeOp *fop);
     void queueForegroundThingsForSweep(FreeOp *fop);
 
     void mergeForegroundSweptObjectArenas();
 
     bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
                             SortedArenaList &sweepList);
-    static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead);
+    static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, ArenaHeader **empty);
 
     void wipeDuringParallelExecution(JSRuntime *rt);
 
     // When finalizing arenas, whether to keep empty arenas on the list or
     // release them immediately.
     enum KeepArenasEnum {
         RELEASE_ARENAS,
         KEEP_ARENAS
@@ -1035,21 +1039,18 @@ class GCHelperState
         shrinkFlag(false)
     { }
 
     bool init();
     void finish();
 
     void work();
 
-    /* Must be called with the GC lock taken. */
-    void startBackgroundSweep();
-
-    /* Must be called with the GC lock taken. */
-    void startBackgroundShrink();
+    void maybeStartBackgroundSweep(const AutoLockGC &lock);
+    void startBackgroundShrink(const AutoLockGC &lock);
 
     /* Must be called without the GC lock taken. */
     void waitBackgroundSweepEnd();
 
     bool onBackgroundThread();
 
     /*
      * Outside the GC lock may give true answer when in fact the sweeping has
@@ -1452,16 +1453,44 @@ class AutoEnterOOMUnsafeRegion {};
 #endif /* DEBUG */
 
 // This tests whether something is inside the GGC's nursery only;
 // use sparingly, mostly testing for any nursery, using IsInsideNursery,
 // is appropriate.
 bool
 IsInsideGGCNursery(const gc::Cell *cell);
 
+// A singly linked list of zones.
+class ZoneList
+{
+    static Zone * const End;
+
+    Zone *head;
+    Zone *tail;
+
+  public:
+    ZoneList();
+    explicit ZoneList(Zone *singleZone);
+
+    bool isEmpty() const;
+    Zone *front() const;
+
+    void append(Zone *zone);
+    void append(ZoneList& list);
+    Zone *removeFront();
+
+    void transferFrom(ZoneList &other);
+
+  private:
+    void check() const;
+
+    ZoneList(const ZoneList &other) MOZ_DELETE;
+    ZoneList &operator=(const ZoneList &other) MOZ_DELETE;
+};
+
 } /* namespace gc */
 
 #ifdef DEBUG
 /* Use this to avoid assertions when manipulating the wrapper map. */
 class AutoDisableProxyCheck
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
     gc::GCRuntime &gc;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -359,31 +359,36 @@ class NewObjectCache
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
  * need to pass a JSContext to those hooks.
  */
 class FreeOp : public JSFreeOp
 {
     Vector<void *, 0, SystemAllocPolicy> freeLaterList;
+    ThreadType threadType;
 
   public:
     static FreeOp *get(JSFreeOp *fop) {
         return static_cast<FreeOp *>(fop);
     }
 
-    explicit FreeOp(JSRuntime *rt)
-      : JSFreeOp(rt)
+    explicit FreeOp(JSRuntime *rt, ThreadType thread = MainThread)
+      : JSFreeOp(rt), threadType(thread)
     {}
 
     ~FreeOp() {
         for (size_t i = 0; i < freeLaterList.length(); i++)
             free_(freeLaterList[i]);
     }
 
+    bool onBackgroundThread() {
+        return threadType == BackgroundThread;
+    }
+
     inline void free_(void *p);
     inline void freeLater(void *p);
 
     template <class T>
     inline void delete_(T *p) {
         if (p) {
             p->~T();
             free_(p);
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1944,21 +1944,23 @@ nsDisplaySolidColor::Paint(nsDisplayList
   DrawTarget* drawTarget = aCtx->GetDrawTarget();
   Rect rect =
     NSRectToSnappedRect(mVisibleRect, appUnitsPerDevPixel, *drawTarget);
   drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplaySolidColor::WriteDebugInfo(nsACString& aTo)
-{
-  aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)",
-                 NS_GET_R(mColor), NS_GET_G(mColor),
-                 NS_GET_B(mColor), NS_GET_A(mColor));
+nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream)
+{
+  aStream << " (rgba "
+          << (int)NS_GET_R(mColor) << ","
+          << (int)NS_GET_G(mColor) << ","
+          << (int)NS_GET_B(mColor) << ","
+          << (int)NS_GET_A(mColor) << ")";
 }
 #endif
 
 static void
 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
 {
   if (!aBuilder->IsInSubdocument() && !aBuilder->IsInTransform()) {
     nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
@@ -2658,19 +2660,19 @@ nsDisplayThemedBackground::~nsDisplayThe
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
   MOZ_COUNT_DTOR(nsDisplayThemedBackground);
 #endif
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplayThemedBackground::WriteDebugInfo(nsACString& aTo)
-{
-  aTo += nsPrintfCString(" (themed, appearance:%d)", mAppearance);
+nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream)
+{
+  aStream << " (themed, appearance:" << (int)mAppearance << ")";
 }
 #endif
 
 void
 nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
                                   const nsRect& aRect,
                                   HitTestState* aState,
                                   nsTArray<nsIFrame*> *aOutFrames)
@@ -2863,21 +2865,20 @@ nsDisplayBackgroundColor::HitTest(nsDisp
     return;
   }
 
   aOutFrames->AppendElement(mFrame);
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplayBackgroundColor::WriteDebugInfo(nsACString& aTo)
-{
-  aTo += nsPrintfCString(" (rgba %f,%f,%f,%f)",
-          mColor.r, mColor.g,
-          mColor.b, mColor.a);
+nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
+{
+  aStream << " (rgba " << mColor.r << "," << mColor.g << ","
+          << mColor.b << "," << mColor.a << ")";
 }
 #endif
 
 already_AddRefed<Layer>
 nsDisplayClearBackground::BuildLayer(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      const ContainerLayerParameters& aParameters)
 {
@@ -2982,16 +2983,32 @@ nsDisplayLayerEventRegions::AddFrame(nsD
 }
 
 void
 nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
 {
   mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
 }
 
+#ifdef MOZ_DUMP_PAINTING
+void
+nsDisplayLayerEventRegions::WriteDebugInfo(std::stringstream& aStream)
+{
+  if (!mHitRegion.IsEmpty()) {
+    AppendToString(aStream, mHitRegion, " (hitRegion ", ")");
+  }
+  if (!mMaybeHitRegion.IsEmpty()) {
+    AppendToString(aStream, mMaybeHitRegion, " (maybeHitRegion ", ")");
+  }
+  if (!mDispatchToContentHitRegion.IsEmpty()) {
+    AppendToString(aStream, mDispatchToContentHitRegion, " (dispatchToContentRegion ", ")");
+  }
+}
+#endif
+
 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aCaretFrame)
   : nsDisplayItem(aBuilder, aCaretFrame)
   , mCaret(aBuilder->GetCaret())
   , mBounds(aBuilder->GetCaretRect() + ToReferenceFrame())
 {
   MOZ_COUNT_CTOR(nsDisplayCaret);
 }
@@ -3673,19 +3690,19 @@ bool nsDisplayOpacity::TryMerge(nsDispla
   if (aItem->GetClip() != GetClip())
     return false;
   MergeFromTrackingMergedFrames(static_cast<nsDisplayOpacity*>(aItem));
   return true;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplayOpacity::WriteDebugInfo(nsACString& aTo)
-{
-  aTo += nsPrintfCString(" (opacity %f)", mOpacity);
+nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream)
+{
+  aStream << " (opacity " << mOpacity << ")";
 }
 #endif
 
 nsDisplayMixBlendMode::nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder,
                                              nsIFrame* aFrame, nsDisplayList* aList,
                                              uint32_t aFlags)
 : nsDisplayWrapList(aBuilder, aFrame, aList) {
   MOZ_COUNT_CTOR(nsDisplayMixBlendMode);
@@ -4438,20 +4455,20 @@ nsDisplayScrollLayer::GetScrollLayerCoun
   return result;
 #else
   return reinterpret_cast<intptr_t>(props.Get(nsIFrame::ScrollLayerCount()));
 #endif
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplayScrollLayer::WriteDebugInfo(nsACString& aTo)
-{
-  aTo += nsPrintfCString(" (scrollframe %p scrolledframe %p)",
-                         mScrollFrame, mScrolledFrame);
+nsDisplayScrollLayer::WriteDebugInfo(std::stringstream& aStream)
+{
+  aStream << " (scrollframe " << mScrollFrame
+          << " scrolledFrame " << mScrolledFrame << ")";
 }
 #endif
 
 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
   nsDisplayListBuilder* aBuilder,
   nsIFrame* aScrolledFrame,
   nsIFrame* aScrollFrame)
   : nsDisplayScrollLayer(aBuilder, aScrollFrame, aScrolledFrame, aScrollFrame)
@@ -5626,21 +5643,19 @@ bool nsDisplayTransform::UntransformVisi
 
   *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
 
   return true;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplayTransform::WriteDebugInfo(nsACString& aTo)
-{
-  std::stringstream ss;
-  AppendToString(ss, GetTransform());
-  aTo += ss.str().c_str();
+nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream)
+{
+  AppendToString(aStream, GetTransform());
 }
 #endif
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayWrapList(aBuilder, aFrame, aList),
       mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
 {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1340,17 +1340,17 @@ public:
   }
   
 #ifdef MOZ_DUMP_PAINTING
   /**
    * For debugging and stuff
    */
   virtual const char* Name() = 0;
 
-  virtual void WriteDebugInfo(nsACString& aTo) {}
+  virtual void WriteDebugInfo(std::stringstream& aStream) {}
 #endif
 
   nsDisplayItem* GetAbove() { return mAbove; }
 
   /**
    * Like ComputeVisibility, but does the work that nsDisplayList
    * does per-item:
    * -- Intersects GetBounds with aVisibleRegion and puts the result
@@ -2160,17 +2160,17 @@ public:
       bool dummy;
       aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy));
       return;
     }
     ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion);
   }
 
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 
   NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
 
 private:
   nsRect  mBounds;
   nscolor mColor;
 };
@@ -2321,17 +2321,17 @@ public:
     return new nsDisplayThemedBackgroundGeometry(this, aBuilder);
   }
 
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) MOZ_OVERRIDE;
 
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 protected:
   nsRect GetBoundsInternal();
 
   void PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
                      const nsRect& aBounds, nsRect* aClipRect);
 
   nsRect mBounds;
@@ -2384,17 +2384,17 @@ public:
       aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy));
       return;
     }
     ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion);
   }
 
   NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR)
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 
 protected:
   const nsStyleBackground* mBackgroundStyle;
   gfxRGBA mColor;
 };
 
 class nsDisplayClearBackground : public nsDisplayItem
@@ -2624,16 +2624,20 @@ public:
   // dispatch-to-content region, to ensure that APZ lets content create a
   // displayport.
   void AddInactiveScrollPort(const nsRect& aRect);
 
   const nsRegion& HitRegion() { return mHitRegion; }
   const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
   const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
 
+#ifdef MOZ_DUMP_PAINTING
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
+#endif
+
 private:
   // Relative to aFrame's reference frame.
   // These are the points that are definitely in the hit region.
   nsRegion mHitRegion;
   // These are points that may or may not be in the hit region. Only main-thread
   // event handling can tell for sure (e.g. because complex shapes are present).
   nsRegion mMaybeHitRegion;
   // These are points that need to be dispatched to the content thread for
@@ -2829,17 +2833,17 @@ public:
   }
   virtual bool ApplyOpacity(nsDisplayListBuilder* aBuilder,
                             float aOpacity,
                             const DisplayItemClip* aClip) MOZ_OVERRIDE;
   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder);
   NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
 
 private:
   float mOpacity;
 };
 
@@ -3127,17 +3131,17 @@ public:
   // number does not include nsDisplayScrollInfoLayers. If this number is not 1
   // after merging, all the nsDisplayScrollLayers should flatten away.
   intptr_t GetScrollLayerCount();
 
   virtual nsIFrame* GetScrollFrame() { return mScrollFrame; }
   virtual nsIFrame* GetScrolledFrame() { return mScrolledFrame; }
 
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 
   bool IsDisplayPortOpaque() { return mDisplayPortContentsOpaque; }
 
   static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame,
                                           nsIFrame* aScrollFrame,
                                           const nsIFrame* aReferenceFrame,
                                           Layer* aLayer,
@@ -3538,17 +3542,17 @@ public:
   bool MaybePrerender() const { return mMaybePrerender; }
   /**
    * Check if this element will be prerendered. This must be done after the
    * display list has been fully built.
    */
   bool ShouldPrerender(nsDisplayListBuilder* aBuilder);
 
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 
 private:
   void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
   void Init(nsDisplayListBuilder* aBuilder);
 
   static gfx3DMatrix GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
                                                          const nsPoint& aOrigin,
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -186,19 +186,17 @@ PrintDisplayItemTo(nsDisplayListBuilder*
         aStream << ",";
       }
       aStream << NS_LossyConvertUTF16toASCII(aItem->Frame()->StyleDisplay()->mWillChange[i]).get();
     }
     aStream << ")";
   }
 
   // Display item specific debug info
-  nsCString itemStr;
-  aItem->WriteDebugInfo(itemStr);
-  aStream << itemStr.get();
+  aItem->WriteDebugInfo(aStream);
 
   if (aDumpHtml && aItem->Painted()) {
     aStream << "</a>";
   }
   uint32_t key = aItem->GetPerFrameKey();
   Layer* layer = mozilla::FrameLayerBuilder::GetDebugOldLayerFor(f, key);
   if (layer) {
     if (aDumpHtml) {
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -281,21 +281,23 @@ nsDisplayCanvasBackgroundColor::Paint(ns
     Rect devPxRect =
       NSRectToSnappedRect(bgClipRect, appUnitsPerDevPixel, *drawTarget);
     drawTarget->FillRect(devPxRect, ColorPattern(ToDeviceColor(mColor)));
   }
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
-nsDisplayCanvasBackgroundColor::WriteDebugInfo(nsACString& aTo)
+nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
 {
-  aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)",
-          NS_GET_R(mColor), NS_GET_G(mColor),
-          NS_GET_B(mColor), NS_GET_A(mColor));
+  aStream << " (rgba "
+          << (int)NS_GET_R(mColor) << ","
+          << (int)NS_GET_G(mColor) << ","
+          << (int)NS_GET_B(mColor) << ","
+          << (int)NS_GET_A(mColor) << ")";
 }
 #endif
 
 static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource)
 {
   RefPtr<SourceSurface> source = aSource->Snapshot();
   aDest->DrawSurface(source,
                      Rect(aRect.x, aRect.y, aRect.width, aRect.height),
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -209,17 +209,17 @@ public:
 
   void SetExtraBackgroundColor(nscolor aColor)
   {
     mColor = aColor;
   }
 
   NS_DISPLAY_DECL_NAME("CanvasBackgroundColor", TYPE_CANVAS_BACKGROUND_COLOR)
 #ifdef MOZ_DUMP_PAINTING
-  virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
+  virtual void WriteDebugInfo(std::stringstream& aStream) MOZ_OVERRIDE;
 #endif
 
 private:
   nscolor mColor;
 };
 
 class nsDisplayCanvasBackgroundImage : public nsDisplayBackgroundImage {
 public:
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1448,19 +1448,17 @@ nsDisplayImage::GetLayerState(nsDisplayL
 }
 
 
 /* virtual */ nsRegion
 nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                 bool* aSnap)
 {
   *aSnap = true;
-  bool animated;
-  if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated &&
-      mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
+  if (mImage && mImage->IsOpaque()) {
     // OK, the entire region painted by the image is opaque. But what is that
     // region? It's the image's "dest rect" (the rect where a full copy of
     // the image is mapped), clipped to the container's content box (which is
     // what GetBounds() returns). So, we grab those rects and intersect them.
     const nsRect frameContentBox = GetBounds(aSnap);
 
     // Note: To get the "dest rect", we have to provide the "constraint rect"
     // (which is the content-box, with the effects of fragmentation undone).
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2049,18 +2049,18 @@ nsStyleImage::IsOpaque() const
     return false;
 
   NS_ABORT_IF_FALSE(mType == eStyleImageType_Image, "unexpected image type");
 
   nsCOMPtr<imgIContainer> imageContainer;
   mImage->GetImage(getter_AddRefs(imageContainer));
   NS_ABORT_IF_FALSE(imageContainer, "IsComplete() said image container is ready");
 
-  // Check if the crop region of the current image frame is opaque.
-  if (imageContainer->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
+  // Check if the crop region of the image is opaque.
+  if (imageContainer->IsOpaque()) {
     if (!mCropRect)
       return true;
 
     // Must make sure if mCropRect contains at least a pixel.
     // XXX Is this optimization worth it? Maybe I should just return false.
     nsIntRect actualCropRect;
     bool rv = ComputeActualCropRect(actualCropRect);
     NS_ASSERTION(rv, "ComputeActualCropRect() can not fail here");
--- a/netwerk/streamconv/converters/ParseFTPList.cpp
+++ b/netwerk/streamconv/converters/ParseFTPList.cpp
@@ -718,17 +718,17 @@ int ParseFTPList(const char *line, struc
       /*
        * "10-23-00  01:27PM       <DIR>          veronist"
        * "06-15-00  07:37AM       <DIR>          zoe"
        * "07-14-00  01:35PM              2094926 canprankdesk.tif"
        * "07-21-00  01:19PM                95077 Jon Kauffman Enjoys the Good Life.jpg"
        * "07-21-00  01:19PM                52275 Name Plate.jpg"
        * "07-14-00  01:38PM              2250540 Valentineoffprank-HiRes.jpg"
       */
-      if ((numtoks >= 4) && toklen[0] == 8 && toklen[1] == 7 && 
+      if ((numtoks >= 4) && (toklen[0] == 8 || toklen[0] == 10) && toklen[1] == 7 && 
           (*tokens[2] == '<' || isdigit(*tokens[2])) )
       {
         p = tokens[0];
         if ( isdigit(p[0]) && isdigit(p[1]) && p[2]=='-' && 
              isdigit(p[3]) && isdigit(p[4]) && p[5]=='-' &&
              isdigit(p[6]) && isdigit(p[7]) )
         {
           p = tokens[1];
--- a/security/certverifier/ExtendedValidation.cpp
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -1002,16 +1002,99 @@ static struct nsMyTrustedEVInfo myTruste
     SEC_OID_UNKNOWN,
     { 0x8F, 0xE4, 0xFB, 0x0A, 0xF9, 0x3A, 0x4D, 0x0D, 0x67, 0xDB, 0x0B,
       0xEB, 0xB2, 0x3E, 0x37, 0xC7, 0x1B, 0xF3, 0x25, 0xDC, 0xBC, 0xDD,
       0x24, 0x0E, 0xA0, 0x4D, 0xAF, 0x58, 0xB4, 0x7E, 0x18, 0x40 },
     "MEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYD"
     "VQQDExVRdW9WYWRpcyBSb290IENBIDIgRzM=",
     "RFc0JFuBiZs18s64KztbpybwdSg=",
     nullptr
+  },
+  {
+    // CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
+    "1.3.6.1.4.1.6449.1.2.1.5.1",
+    "COMODORSACertificationAuthority",
+    SEC_OID_UNKNOWN,
+    { 0x52, 0xF0, 0xE1, 0xC4, 0xE5, 0x8E, 0xC6, 0x29, 0x29, 0x1B, 0x60,
+      0x31, 0x7F, 0x07, 0x46, 0x71, 0xB8, 0x5D, 0x7E, 0xA8, 0x0D, 0x5B,
+      0x07, 0x27, 0x34, 0x63, 0x53, 0x4B, 0x32, 0xB4, 0x02, 0x34 },
+    "MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw"
+    "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG"
+    "A1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+    "TKr5yttjb+Af907YWwOGnQ==",
+    nullptr
+  },
+  {
+    // CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
+    "1.3.6.1.4.1.6449.1.2.1.5.1",
+    "USERTrustRSACertificationAuthority",
+    SEC_OID_UNKNOWN,
+    { 0xE7, 0x93, 0xC9, 0xB0, 0x2F, 0xD8, 0xAA, 0x13, 0xE2, 0x1C, 0x31,
+      0x22, 0x8A, 0xCC, 0xB0, 0x81, 0x19, 0x64, 0x3B, 0x74, 0x9C, 0x89,
+      0x89, 0x64, 0xB1, 0x74, 0x6D, 0x46, 0xC3, 0xD4, 0xCB, 0xD2 },
+    "MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML"
+    "SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
+    "A1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+    "Af1tMPyjylGoG7xkDjUDLQ==",
+    nullptr
+  },
+  {
+    // CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
+    "1.3.6.1.4.1.6449.1.2.1.5.1",
+    "USERTrust ECC Certification Authority",
+    SEC_OID_UNKNOWN,
+    { 0x4F, 0xF4, 0x60, 0xD5, 0x4B, 0x9C, 0x86, 0xDA, 0xBF, 0xBC, 0xFC,
+      0x57, 0x12, 0xE0, 0x40, 0x0D, 0x2B, 0xED, 0x3F, 0xBC, 0x4D, 0x4F,
+      0xBD, 0xAA, 0x86, 0xE0, 0x6A, 0xDC, 0xD2, 0xA9, 0xAD, 0x7A },
+    "MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML"
+    "SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
+    "A1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+    "XIuZxVqUxdJxVt7NiYDMJg==",
+    nullptr
+  },
+  {
+    // CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R4
+    "1.3.6.1.4.1.4146.1.1",
+    "GlobalSign ECC Root CA - R4",
+    SEC_OID_UNKNOWN,
+    { 0xBE, 0xC9, 0x49, 0x11, 0xC2, 0x95, 0x56, 0x76, 0xDB, 0x6C, 0x0A,
+      0x55, 0x09, 0x86, 0xD7, 0x6E, 0x3B, 0xA0, 0x05, 0x66, 0x7C, 0x44,
+      0x2C, 0x97, 0x62, 0xB4, 0xFB, 0xB7, 0x73, 0xDE, 0x22, 0x8C },
+    "MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UE"
+    "ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
+    "KjikHJYKBN5CsiilC+g0mAI=",
+    nullptr
+  },
+  {
+    // CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R5
+    "1.3.6.1.4.1.4146.1.1",
+    "GlobalSign ECC Root CA - R5",
+    SEC_OID_UNKNOWN,
+    { 0x17, 0x9F, 0xBC, 0x14, 0x8A, 0x3D, 0xD0, 0x0F, 0xD2, 0x4E, 0xA1,
+      0x34, 0x58, 0xCC, 0x43, 0xBF, 0xA7, 0xF5, 0x9C, 0x81, 0x82, 0xD7,
+      0x83, 0xA5, 0x13, 0xF6, 0xEB, 0xEC, 0x10, 0x0C, 0x89, 0x24 },
+    "MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNTETMBEGA1UE"
+    "ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
+    "YFlJ4CYuu1X5CneKcflK2Gw=",
+    nullptr
+  },
+  {
+    // CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net
+    "2.16.840.1.114028.10.1.2",
+    "EntrustCA2048",
+    SEC_OID_UNKNOWN,
+    { 0x6D, 0xC4, 0x71, 0x72, 0xE0, 0x1C, 0xBC, 0xB0, 0xBF, 0x62, 0x58,
+      0x0D, 0x89, 0x5F, 0xE2, 0xB8, 0xAC, 0x9A, 0xD4, 0xF8, 0x73, 0x80,
+      0x1E, 0x0C, 0x10, 0xB9, 0xC8, 0x37, 0xD2, 0x1E, 0xB1, 0x77 },
+    "MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3Qu"
+    "bmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMG"
+    "A1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50"
+    "cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp",
+    "OGPe+A==",
+    nullptr
   }
 };
 
 static SECOidTag
 register_oid(const SECItem* oid_item, const char* oid_name)
 {
   if (!oid_item)
     return SEC_OID_UNKNOWN;
--- a/security/manager/ssl/tests/unit/test_ocsp_fetch_method.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_fetch_method.js
@@ -8,17 +8,17 @@
 // In which we try to validate several ocsp responses, checking in particular
 // that we use the specified method for fetching ocsp. We also check what
 // POST fallback when an invalid GET response is received.
 
 do_get_profile(); // must be called before getting nsIX509CertDB
 const certdb = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
 
-const SERVER_PORT = 8080;
+const SERVER_PORT = 8888;
 
 function start_ocsp_responder(expectedCertNames, expectedPaths,
                               expectedMethods) {
   return startOCSPResponder(SERVER_PORT, "www.example.com", [],
                             "test_ocsp_fetch_method", expectedCertNames,
                             expectedPaths, expectedMethods);
 }
 
index a8a0f65507efd184ddb5e22f31dd6250825f34b1..48b71256a6befd2675b22410362d6bc5d6160464
GIT binary patch
literal 898
zc$_n6Vy-i2VoF=U%*4pV#LOMB!GM>IQ>)FR?K>|cBR4C9L7XAC0Vf-CC<~h~Q)sZE
zuz?_m!y(M&oS2iDmS2>YnP@0xAOaF(7v}ZNFU>1S%*;~=%S<gd6fqD2iE{~a2L$^n
zgrpXiWagzC@)&S|M45$|GxJIe<ivRmO$-f<3=K?;42+GV#CeU7xdt&*x1Y;^17bg8
zqCpd*60(OGSs9p{82K51;#^EkjEoE`qE04UziDZv`ypaC6PIK0a=%-u%T^h)2TK^8
zYw5A<?%mm8a_r~lB>qW@L)@FDX9h<t+wpei>9au#qa^Mf=$^(aZV}7)rCs5C9nXhT
z%dLvuKY6+5ZQ)a~g2LX3`!5Kc_-Ik{WkcHKg{9kKZ=QP*dcoyk$DGufV3o2*^Rif<
z>ppF1T;yoceg1^f(|JtaZRUpl%6C3?zGPo;RL033UBSiY_Qa}Aezxkk!;A1|yN(22
zd44y1^2*AlD6eZ5Rvwl9*8F%;pRh=8f|hsN_uY>KZQe^yotw6M_B6Re97}q3F|h9w
zKA<vF(laocvvW(Ga%))3MepX)7f0ooSQWZ+njWb;Rx>d(GB7T7H*htuVB^qcV`ODz
zXJlm2G0-y5fbk6&+hj6IN(!v>^~=l4^-?Pma|?1(^^)^*tt@~*9~^tK!YoV%3}|VK
z85F{6m+#&q7{wNM#JggzjOv_qxhw`+$}(XuecJD9>;LpgJ`wADso8t7jrv^I36+A}
z$1WLu{A%&r!|LuM?{BK%pZhCIr))j*T6jifRMM9{7d80<KL5*lnHRsQ+2rQQD*o*M
ziv_*8=ZWcw9N(mM)F81aSx>?3zTBMDim<s^8%@ktGF`A^@lLPIub)?ZDPZ^6<ue$b
z$ehUS+PL|6ZoKznp;YhHe#TkFoC2S=C|&p+DXc7_7Z$d1iPJq@k>Yjx_HMpsWFa0e
z-@~>e&zbei<O$0bd{;d4J$HZOb?Xe)@5xz46^A>YbDdcczG2@-f!MF}O9kqeF&<hu
QC&M&krgvY|f^VF^0V?H50RR91
index b488321fb0868281225a2cfcd2cf79afee9d4d14..b7a751f540da98e11903956f1413ed14611a5701
GIT binary patch
literal 899
zc$_n6Vy-u6VoG1Y%*4pV#KN%8_@V(X8>d#AN85K^Mn-N{27@?5ZUas>=1>+kVW!Yv
zLtz6!5QjsU%Q-P8GcCU;FEi0l%s>Pr$S%z5n_rq&l9-vN5SE!*ZYW|P1QO>G<_-w<
zRR~EfF3HSGH{>zk0*NvUGiT<N7|4n98k!gy8W|dx8W|XyMv3zpA#)94sBS-(0SCl>
z#w3F#MkQnqGqN%;H!<=v0L8hOniv@wPIA66cJi53DSOXGuksJWopn9W&*a@Sc~fv}
zqnL1+!htnK(;g&DQ#IJVjAtXyHTk^o2GNXrUwbF0J~%Z&K<Lb;($X*AmTs`y%)4AZ
zOhWN;%A=<xMSuUrXnvox(!2TwV~W9>1&O;1-x<`s`XKn5Eo;H{D(*!GSK60H%>Vr^
zGP3LWY^R>76`xj~J-m0%35D}D;-a3?4-02~Qh1PfXzixn>rKrE)lxdQ*d#1Hlh<-C
zw?yZJ$0oU4t(2n)pU#%&g=D`K>szQ}*BN@j&#cDwK!R<N_@$7y9p#SSa?h_5V>tiz
z^5Q^-=G_PFt%`Wp8@Nx9npZtpDdE^H?FaMtguk5$m*`|-W@KPo>~7#{V8O<r&Bn;e
z%Ff8hqGO<CpaJ6>Ft*8Ll#~=$>Fbx5m+PfgB<2?6r0ON-=UQ0+fj&6)WQAFn3>eVT
z7BeV><Cn6z|A@YQFX(<@j$s=s`&EN89W2Wa-w8OtTHJPI$*haIKU^Y&o~}7xGiT{N
zuX5Md!Yd2+w#|r3+5Ivzxwb~{*t%e&xUP8~MYEPa+L|0IDO?{_@pNxA*HN>Y#t9L3
z=1Z}@?P=K3f9$r_qee}Ic|9GE9M*)aRMIjxnV>em@UwcXEW7ijM&9pRCLUuA{FLh}
zX<eN2pH-p5ZL-y(^GB~s{8isNnWgXEj}<;GAG&vBYp(jK+I{o>oT7B`8;dn1)@5IJ
z&=(as`tgMDsT17Np)waP3(Tya&Ycp(_)MPH-iV3QCBETOO6%T3ykcQ9H|9s$mRIhI
SSQetuyYWx1!};&$&H@0nuS-4v
index a100132d1e0591f7d795b8cd2170f5b70cb56fe1..20e8db54b0893a46b85356ab5c59bdd83cdd4e9f
GIT binary patch
literal 845
zc$_n6V)is>VlrI7%*4pV#LBRc>vMnsFB_*;n@8JsUPeZ4RtAGuLv903Hs(+kHesgF
zU_)U8K@f*Sn9DgaCo?U-C@(Y7P|QFCB*-qz>ziMiSCW{Srx2EzT5c#}AOsTU66OvF
z_EiW;EiTE-ONW`qEX<UgXdoxfYiMF<Xk=($YGhz+7$weYgv_P2^-YXQ$R1{7WngY%
z<Yxeib1^kBGBP~iZC0F_^=y&ir+rTQKP&C&RWn)r=-Im1pL>pZiEI<Ge=R6nx^a`l
z6UIHyXP!RC@tjX#t&hY<Z`QY*0m+HZY!7Q^@T?6`cqA6-93&sNI#!p{^WB}i|E7HJ
z)4b-Esb{PapFfj@CH#%tt1IP_4=#m$d*Rf~<g_lnV*zj6yUFHp9~Z9vI%~_ORQ&*f
z2*v-*d0pO4e?{x>eXel2vO%ppVs+08#aq{ZmvUE!n8z=3uM9KY(WZN+yC-JeRj#>*
zrgyp}B_{7%|9<(QgpO6pe?uNwxfw3-Ty$)UdE1S1sXbCFCtdi+_4&?Yzgo^&|3lrj
zxb9}$%6iR4?VnBDv_%e=Ed6V@GchwVFfJA_;4|O>2AZre3#$P$BjbOxM8XUTs<n;3
ztfe|;^Ca>fn9TI^-E^%hn|}s|7~C}0*&us4Z3};|(Ua)4{K4DK&9aue7$q2DddbGV
zxW+l-mC*gGw-3)s;hgEz5w&)zSIt)ApA1_JH%^X<{!re%GS~C4(H*rz%KiS6=CJB)
zNGt9&oO77BSYN70=eE&QS9|ZUOHbrZI_IT@*IX@I!g2cfwMnWxMZI@(Yl|-|j+pdV
zuSVCqYU|83_J@7u@I+qklFz7((n*_t+5T(a<A9l4JTH{}n{N9-^O?yFeMa%K8}G)X
zyS#U}mw$|z<!M@B%c@x+B6p+rP70c#c{h8jmP9Tm??j2RC$}cWUbuglBW0e|){nE+
L9^LkTU&1#4B&a;l
index bf248fdf9dbabcd0fefa1f75f778a5e0df8205af..e54db78087a5091c79ff2756a5bbabbbb2577001
GIT binary patch
literal 17408
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
--- a/security/manager/ssl/tests/unit/test_ocsp_fetch_method/generate.py
+++ b/security/manager/ssl/tests/unit/test_ocsp_fetch_method/generate.py
@@ -17,13 +17,13 @@ def generate_child_cert(db_dir, dest_dir
                         ocsp_url):
     return CertUtils.generate_child_cert(db_dir, dest_dir, noise_file, name,
                                          ca_nick, 3, True, is_ee, ocsp_url)
 
 def generate_certs():
     [noise_file, pwd_file] = CertUtils.init_nss_db(srcdir)
     generate_ca_cert(srcdir, srcdir, noise_file, 'ca')
     generate_child_cert(srcdir, srcdir, noise_file, 'int', 'ca', False, '')
-    ocsp_url = "http://www.example.com:8080/"
+    ocsp_url = "http://www.example.com:8888/"
     generate_child_cert(srcdir, srcdir, noise_file, "a", 'int', True, ocsp_url)
     generate_child_cert(srcdir, srcdir, noise_file, "b", 'int', True, ocsp_url)
 
 generate_certs()
index 717c55d32105705eb7a21c35937442d1f1207aa3..661e814efaa9f67bc37bcef536b7f358ee34a666
GIT binary patch
literal 845
zc$_n6V)is>VlrI7%*4pV#KLgu$_oQtHcqWJkGAi;jEvl@3<j}=+y<O%%%Lo7!c3vT
zhQbDdAP$ExmvdrHW?FtxUS^`9n1KjLkX@M9H@`HmBr!8jAuKbs+)%_o2qexW%pDNy
zs}Pb}T#}iW4l|Egm?=5YKu(<3(8SQt$k4#l$iUb*N}SgSnQIV7b?bQyxFFUuXXcd{
zG%+e6`<RiHfw_s1p8+V&#ni;e$Z)4KL#Csm<Dbx-t*@V1F~`|eW=E`FEq2oQ;hhT+
zy8SvAdgVWbGrv~dlovI%=wy~%^p29h0;&%du3Dd0r|)><^QZW=VR@QcH^*e$R`25e
zURL1q@5H5VoZk7Kf!4h9cO}<7-(U0DPw-@L&hP5|%#J&%w^*#2)T~vZ(I6|n@92V)
zn_FCdTv>X=|BcB(<tcm1+%pUP`&4Q!WbJcdR?=g(t!BTvKh-?jJnLp*!P!lv7pC*u
zzN>TJyZ#vK6*2L+hjVoF#5fLco2aE6k_ZW2mwe~MpXsOGEtaqUBq(jj+xk;CzIflW
zETh$%STDZF%Gv5Ov)s;q!qa*$*5fSk3ufzn(=cXYW@KPoEMUNAzyl05Sz#7d17=3X
z|7fX%85C4WY*#wxo|)1t6=AD(;@E3dtw_1}xpGY0bMKtK-h8BStBhh&j`gYhJ<r+_
zlwHp%eJv=v;`mS2BvVgK#O!#j;XLLCyLo$G<%%bUE>Lgr`uhLT4F4Zo_tsxB;hX#T
z)hQ3=vp2Q>M-{M}I&6Fzu;n(-gys|7%36!{(|<6;HJSvM_8C~*o%7W{tnu2cJFYh_
zJ$~S@*(|vE!I1{r28MgOtc{1SRzCSYX<7QLmp@|?&a;cnw`;7QJ?&@m61(@WIiwZ~
zE9mk%EwKLZqg3QW<t(+In=kOKw7q8e@mB7HuXg`!8l4#8?L023EkF9d)h)aISp2lP
V>mNTk%qJS<Ipbk#sGYv>UjTQPJ`?}|
index 7471e140d8a821ccac14d0fa016e91b7358095e0..ada3335ecf24660ab3a4e0979a133ed8df901389
GIT binary patch
literal 29696
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
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -86,33 +86,28 @@ skip-if = os == "android"
 skip-if = os == "android" || (buildapp == "b2g" && processor == "arm")
 requesttimeoutfactor = 4
 [test_pinning.js]
 run-sequentially = hardcoded ports
 # Bug 1009158: this test times out on Android
 skip-if = os == "android"
 [test_ocsp_url.js]
 run-sequentially = hardcoded ports
-# Bug 1009158: this test times out on Android
-skip-if = os == "android"
 [test_ocsp_fetch_method.js]
 run-sequentially = hardcoded ports
-# Bug 1009158: this test times out on Android
-skip-if = os == "android"
 [test_ocsp_no_hsts_upgrade.js]
 run-sequentially = hardcoded ports
 # Bug 1009158: this test times out on Android
 skip-if = os == "android"
 [test_add_preexisting_cert.js]
 [test_keysize.js]
 [test_keysize_ev.js]
 run-sequentially = hardcoded ports
-# Bug 1009158: this test times out on Android
 # Bug 1008316: B2G doesn't have EV enabled
-skip-if = os == "android" || buildapp == "b2g"
+skip-if = buildapp == "b2g"
 [test_cert_chains.js]
 run-sequentially = hardcoded ports
 # Bug 1009158: this test times out on Android
 skip-if = os == "android"
 [test_client_cert.js]
 run-sequentially = hardcoded ports
 # Bug 1009158: this test times out on Android
 skip-if = os == "android"
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/actions.rst
@@ -0,0 +1,46 @@
+Actions
+=======
+
+.. py:currentmodule:: marionette
+
+Action Sequences
+----------------
+
+:class:`Actions` are designed as a way to simulate user input as closely as possible
+on a touch device like a smart phone. A common operation is to tap the screen
+and drag your finger to another part of the screen and lift it off.
+
+This can be simulated using an Action::
+
+    from marionette import Actions
+
+    start_element = marionette.find_element('id', 'start')
+    end_element = marionette.find_element('id', 'end')
+
+    action = Actions(marionette)
+    action.press(start_element).wait(1).move(end_element).release()
+    action.perform()
+
+This will simulate pressing an element, waiting for one second, moving the
+finger over to another element and then lifting the finger off the screen. The
+wait is optional in this case, but can be useful for simulating delays typical
+to a users behaviour.
+
+Multi-Action Sequences
+----------------------
+
+Sometimes it may be necessary to simulate multiple actions at the same time.
+For example a user may be dragging one finger while tapping another. This is
+where :class:`MultiActions` come in. MultiActions are simply a way of combining
+two or more actions together and performing them all at the same time::
+
+    action1 = Actions(marionette)
+    action1.press(start_element).move(end_element).release()
+
+    action2 = Actions(marionette)
+    action2.press(another_element).wait(1).release()
+
+    multi = MultiActions(marionette)
+    multi.add(action1)
+    multi.add(action2)
+    multi.perform()
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/debug.rst
@@ -0,0 +1,54 @@
+Debugging
+=========
+
+.. py:currentmodule:: marionette
+
+Sometimes when working with Marionette you'll run into unexpected behaviour and
+need to do some debugging. This page outlines some of the Marionette methods
+that can be useful to you.
+
+Please note that the best tools for debugging are the `ones that ship with
+Gecko`_. This page doesn't describe how to use those with Marionette. Also see
+a related topic about `using the debugger with Marionette`_ on MDN.
+
+.. _ones that ship with Gecko: https://developer.mozilla.org/en-US/docs/Tools
+.. _using the debugger with Marionette: https://developer.mozilla.org/en-US/docs/Marionette/Debugging
+
+
+Storing Logs on the Server
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By calling `~Marionette.log` it is possible to store a message on the server.
+Logs can later be retrieved using `~Marionette.get_logs`. For example::
+
+    try:
+        marionette.log("Sending a click event") # logged at INFO level
+        elem.click()
+    except:
+        marionette.log("Something went wrong!", "ERROR")
+
+    print(marionette.get_logs())
+
+Disclaimer: Example for illustrative purposes only, don't actually hide
+tracebacks like that!
+
+
+Seeing What's on the Page
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes it's difficult to tell what is actually on the page that is being
+manipulated. Either because it happens too fast, the window isn't big enough or
+you are manipulating a remote server! There are two methods that can help you
+out. The first is `~Marionette.screenshot`::
+
+    marionette.screenshot() # takes screenshot of entire frame
+    elem = marionette.find_element(By.ID, 'some-div')
+    marionette.screenshot(elem) # takes a screenshot of only the given element
+
+Sometimes you just want to see the DOM layout. You can do this with the
+`~Marionette.page_source` property. Note that the page source depends on the
+context you are in::
+
+    print(marionette.page_source)
+    marionette.set_context('chrome')
+    print(marionette.page_source)
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/findelement.rst
@@ -0,0 +1,126 @@
+Finding Elements
+================
+.. py:currentmodule:: marionette
+
+One of the most common and yet often most difficult tasks in Marionette is
+finding a DOM element on a webpage or in the chrome UI. Marionette provides
+several different search strategies to use when finding elements. All search
+strategies work with both :func:`~Marionette.find_element` and
+:func:`~Marionette.find_elements`, though some strategies are not implemented
+in chrome scope.
+
+In the event that more than one element is matched by the query,
+:func:`~Marionette.find_element` will only return the first element found. In
+the event that no elements are matched by the query,
+:func:`~Marionette.find_element` will raise `NoSuchElementException` while
+:func:`~Marionette.find_elements` will return an empty list.
+
+Search Strategies
+-----------------
+
+Search strategies are defined in the :class:`By` class::
+
+    from marionette import By
+    print(By.ID)
+
+The strategies are:
+
+* `id` - The easiest way to find an element is to refer to its id directly::
+
+        container = client.find_element(By.ID, 'container')
+
+* `class name` - To find elements belonging to a certain class, use `class name`::
+
+        buttons = client.find_elements(By.CLASS_NAME, 'button')
+
+* `css selector` - It's also possible to find elements using a `css selector`_::
+
+        container_buttons = client.find_elements(By.CSS_SELECTOR, '#container .buttons')
+
+* `name` - Find elements by their name attribute (not implemented in chrome
+  scope)::
+
+        form = client.find_element(By.NAME, 'signup')
+
+* `tag name` - To find all the elements with a given tag, use `tag name`::
+
+        paragraphs = client.find_elements(By.TAG_NAME, 'p')
+
+* `link text` - A convenience strategy for finding link elements by their
+  innerHTML (not implemented in chrome scope)::
+
+        link = client.find_element(By.LINK_TEXT, 'Click me!')
+
+* `partial link text` - Same as `link text` except substrings of the innerHTML
+  are matched (not implemented in chrome scope)::
+
+        link = client.find_element(By.PARTIAL_LINK_TEXT, 'Clic')
+
+* `xpath` - Find elements using an xpath_ query::
+
+        elem = client.find_element(By.XPATH, './/*[@id="foobar"')
+
+.. _css selector: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors
+.. _xpath: https://developer.mozilla.org/en-US/docs/Web/XPath
+
+
+
+Chaining Searches
+-----------------
+
+In addition to the methods on the Marionette object, HTMLElement objects also
+provide :func:`~HTMLElement.find_element` and :func:`~HTMLElement.find_elements`
+methods. The difference is that only child nodes of the element will be searched.
+Consider the following html snippet::
+
+    <div id="content">
+        <span id="main"></span>
+    </div>
+    <div id="footer"></div>
+
+Doing the following will work::
+
+    client.find_element(By.ID, 'container').find_element(By.ID, 'main')
+
+But this will raise a `NoSuchElementException`::
+
+    client.find_element(By.ID, 'container').find_element(By.ID, 'footer')
+
+
+Finding Anonymous Nodes
+-----------------------
+
+When working in chrome scope, for example manipulating the Firefox user
+interface, you may run into something called an anonymous node.
+
+Firefox uses a markup language called XUL_ for its interface. XUL is similar
+to HTML in that it has a DOM and tags that render controls on the display. One
+ability of XUL is to create re-useable widgets that are made up out of several
+smaller XUL elements. These widgets can be bound to the DOM using something
+called the `XML binding language (XBL)`_.
+
+The end result is that the DOM sees the widget as a single entity. It doesn't
+know anything about how that widget is made up. All of the smaller XUL elements
+that make up the widget are called `anonymous content`_. It is not possible to
+query such elements using traditional DOM methods like `getElementById`.
+
+Marionette provides two special strategies used for finding anonymous content.
+Unlike normal elements, anonymous nodes can only be seen by their parent. So
+it's necessary to first find the parent element and then search for the
+anonymous children from there.
+
+* `anon` - Finds all anonymous children of the element, there is no search term
+  so `None` must be passed in::
+
+    anon_children = client.find_element('id', 'parent').find_elements('anon', None)
+
+* `anon attribute` - Find an anonymous child based on an attribute. An
+  unofficial convention is for anonymous nodes to have an
+  `anonid` attribute::
+
+    anon_child = client.find_element('id', 'parent').find_element('anon attribute', {'anonid': 'container'})
+
+
+.. _XUL: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL
+.. _XML binding language (XBL): https://developer.mozilla.org/en-US/docs/XBL
+.. _anonymous content: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/landing.rst
@@ -0,0 +1,13 @@
+Advanced Topics
+===============
+
+Here are a collection of articles explaining some of the more complicated
+aspects of Marionette.
+
+.. toctree::
+ :maxdepth: 1
+
+ findelement
+ stale
+ actions
+ debug
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/stale.rst
@@ -0,0 +1,71 @@
+Dealing with Stale Elements
+===========================
+.. py:currentmodule:: marionette
+
+Marionette does not keep a live representation of the DOM saved. All it can do
+is send commands to the Marionette server which queries the DOM on the client's
+behalf. References to elements are also not passed from server to client. A
+unique id is generated for each element that gets referenced and a mapping of
+id to element object is stored on the server. When commands such as
+:func:`~HTMLElement.click()` are run, the client sends the element's id along
+with the command. The server looks up the proper DOM element in its reference
+table and executes the command on it.
+
+In practice this means that the DOM can change state and Marionette will never
+know until it sends another query. For example, look at the following HTML::
+
+    <head>
+    <script type=text/javascript>
+        function addDiv() {
+           var div = document.createElement("div");
+           document.getElementById("container").appendChild(div);
+        }
+    </script>
+    </head>
+
+    <body>
+        <div id="container">
+        </div>
+        <input id="button" type=button onclick="addDiv();">
+    </body>
+
+Care needs to be taken as the DOM is being modified after the page has loaded.
+The following code has a race condition::
+
+    button = client.find_element('id', 'button')
+    button.click()
+    assert len(client.find_elements('css selector', '#container div')) > 0
+
+
+Explicit Waiting and Expected Conditions
+----------------------------------------
+
+To avoid the above scenario, manual synchronisation is needed. Waits are used
+to pause program execution until a given condition is true. This is a useful
+technique to employ when documents load new content or change after
+``Document.readyState``'s value changes to "complete".
+
+The :class:`Wait` helper class provided by Marionette avoids some of the
+caveats of ``time.sleep(n)``. It will return immediately once the provided
+condition evaluates to true.
+
+To avoid the race condition in the above example, one could do::
+
+    button = client.find_element('id', 'button')
+    button.click()
+
+    def find_divs():
+        return client.find_elements('css selector', '#container div')
+
+    divs = Wait(client).until(find_divs)
+    assert len(divs) > 0
+
+This avoids the race condition. Because finding elements is a common condition
+to wait for, it is built in to Marionette. Instead of the above, you could
+write::
+
+    button = client.find_element('id', 'button')
+    button.click()
+    assert len(Wait(client).until(expected.elements_present('css selector', '#container div'))) > 0
+
+For a full list of built-in conditions, see :mod:`~marionette.expected`.
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/basics.rst
@@ -0,0 +1,185 @@
+.. py:currentmodule:: marionette
+
+Marionette Python Client
+========================
+
+The Marionette python client library allows you to remotely control a
+Gecko-based browser or device which is running a Marionette_
+server. This includes desktop Firefox and FirefoxOS (support for
+Firefox for Android is planned, but not yet fully implemented).
+
+The Marionette server is built directly into Gecko and can be started by
+passing in a command line option to Gecko, or by using a Marionette-enabled
+build. The server listens for connections from various clients. Clients can
+then control Gecko by sending commands to the server.
+
+This is the official python client for Marionette. There also exists a
+`NodeJS client`_ maintained by the Firefox OS automation team.
+
+.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette
+.. _NodeJS client: https://github.com/mozilla-b2g/marionette-js-client
+
+Getting the Client
+------------------
+
+The python client is officially supported. To install it, first make sure you
+have `pip installed`_ then run:
+
+.. parsed-literal::
+   pip install marionette_client
+
+It's highly recommended to use virtualenv_ when installing Marionette to avoid
+package conflicts and other general nastiness.
+
+You should now be ready to start using Marionette. The best way to learn is to
+play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up
+a python shell and follow along with the
+:doc:`interactive tutorial <interactive>`!
+
+.. _pip installed: https://pip.pypa.io/en/latest/installing.html
+.. _virtualenv: http://virtualenv.readthedocs.org/en/latest/
+.. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds
+
+Using the Client for Testing
+----------------------------
+
+Please visit the `Marionette Tests`_ section on MDN for information regarding
+testing with Marionette.
+
+.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests
+
+Session Management
+------------------
+A session is a single instance of a Marionette client connected to a Marionette
+server. Before you can start executing commands, you need to start a session
+with :func:`start_session() <Marionette.start_session>`:
+
+.. parsed-literal::
+   client = Marionette('localhost', port=2828)
+   client.start_session()
+
+This returns a session id and an object listing the capabilities of the
+Marionette server. For example, a server running on a Firefox OS device will
+have the ability to rotate the window, while a server running from Firefox
+won't. It's also possible to access the capabilities using the
+:attr:`~Marionette.session_capabilities` attribute. After finishing with a
+session, you can delete it with :func:`~Marionette.delete_session()`. Note that
+this will also happen automatically when the Marionette object is garbage
+collected.
+
+Context Management
+------------------
+Commands can only be executed in a single window, frame and scope at a time. In
+order to run commands elsewhere, it's necessary to explicitly switch to the
+appropriate context.
+
+Use :func:`~Marionette.switch_to_window` to execute commands in the context of a
+new window:
+
+.. parsed-literal::
+   original_window = client.current_window_handle
+   for handle in client.window_handles:
+       if handle != original_window:
+           client.switch_to_window(handle)
+           print("Switched to window with '{}' loaded.".format(client.get_url()))
+   client.switch_to_window(original_window)
+
+Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the
+context of a new frame (e.g an <iframe> element):
+
+.. parsed-literal::
+   iframe = client.find_element(By.TAG_NAME, 'iframe')
+   client.switch_to_frame(iframe)
+   assert iframe == client.get_active_frame()
+
+Finally Marionette can switch between `chrome` and `content` scope. Chrome is a
+privileged scope where you can access things like the Firefox UI itself or the
+system app in Firefox OS. Content scope is where things like webpages or normal
+Firefox OS apps live. You can switch between `chrome` and `content` using the
+:func:`~Marionette.set_context` and :func:`~Marionette.using_context` functions:
+
+.. parsed-literal::
+   client.set_context(client.CONTEXT_CONTENT)
+   # content scope
+   with client.using_context(client.CONTEXT_CHROME):
+       #chrome scope
+       ... do stuff ...
+   # content scope restored
+
+
+Navigation
+----------
+
+Use :func:`~Marionette.navigate` to open a new website. It's also possible to
+move through the back/forward cache using :func:`~Marionette.go_forward` and
+:func:`~Marionette.go_back` respectively. To retrieve the currently
+open website, use :func:`~Marionette.get_url`:
+
+.. parsed-literal::
+   url = 'http://mozilla.org'
+   client.navigate(url)
+   client.go_back()
+   client.go_forward()
+   assert client.get_url() == url
+
+
+DOM Elements
+------------
+
+In order to inspect or manipulate actual DOM elements, they must first be found
+using the :func:`~Marionette.find_element` or :func:`~Marionette.find_elements`
+methods:
+
+.. parsed-literal::
+   from marionette import HTMLElement
+   element = client.find_element(By.ID, 'my-id')
+   assert type(element) == HTMLElement
+   elements = client.find_elements(By.TAG_NAME, 'a')
+   assert type(elements) == list
+
+For a full list of valid search strategies, see :doc:`advanced/findelement`.
+
+Now that an element has been found, it's possible to manipulate it:
+
+.. parsed-literal::
+   element.click()
+   element.send_keys('hello!')
+   print(element.get_attribute('style'))
+
+For the full list of possible commands, see the :class:`HTMLElement`
+reference.
+
+Be warned that a reference to an element object can become stale if it was
+modified or removed from the document. See :doc:`advanced/stale` for tips
+on working around this limitation.
+
+Script Execution
+----------------
+
+Sometimes Marionette's provided APIs just aren't enough and it is necessary to
+run arbitrary javascript. This is accomplished with the
+:func:`~Marionette.execute_script` and :func:`~Marionette.execute_async_script`
+functions. They accomplish what their names suggest, the former executes some
+synchronous JavaScript, while the latter provides a callback mechanism for
+running asynchronous JavaScript:
+
+.. parsed-literal::
+   result = client.execute_script("return arguments[0] + arguments[1];",
+                                  script_args=[2, 3])
+   assert result == 5
+
+The async method works the same way, except it won't return until a special
+`marionetteScriptFinished()` function is called:
+
+.. parsed-literal::
+   result = client.execute_async_script("""
+       setTimeout(function() {
+         marionetteScriptFinished("all done");
+       }, arguments[0]);
+   """, script_args=[1000])
+   assert result == "all done"
+
+Beware that running asynchronous scripts can potentially hang the program
+indefinitely if they are not written properly. It is generally a good idea to
+set a script timeout using :func:`~Marionette.set_script_timeout` and handling
+`ScriptTimeoutException`.
--- a/testing/marionette/client/docs/conf.py
+++ b/testing/marionette/client/docs/conf.py
@@ -90,18 +90,30 @@ pygments_style = 'sphinx'
 # A list of ignored prefixes for module index sorting.
 #modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
+
 html_theme = 'default'
 
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+if not on_rtd:
+    try:
+        import sphinx_rtd_theme
+        html_theme = 'sphinx_rtd_theme'
+        html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+    except ImportError:
+        pass
+
+
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
 #html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
 #html_theme_path = []
 
--- a/testing/marionette/client/docs/index.rst
+++ b/testing/marionette/client/docs/index.rst
@@ -1,220 +1,16 @@
-.. Marionette Python Client documentation master file, created by
-   sphinx-quickstart on Tue Aug  6 13:54:46 2013.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
-
-Marionette Python Client
-========================
-
-The Marionette python client library allows you to remotely control a
-Gecko-based browser or device which is running a Marionette_
-server. This includes desktop Firefox and FirefoxOS (support for
-Firefox for Android is planned, but not yet fully implemented).
-
-.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette
-
-Getting Started
----------------
-
-Getting the Client
-^^^^^^^^^^^^^^^^^^
-
-We officially support a python client. The latest supported version of
-the client is available on pypi_, so you can download it via pip.
-This client should be used within a virtual environment to ensure that
-your environment is pristine:
-
-.. parsed-literal::
-
-   virtualenv venv
-   source venv/bin/activate
-   pip install marionette_client
-
-.. _pypi: https://pypi.python.org/pypi/marionette_client/
-
-Using the Client Interactively
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Once you installed the client and have Marionette running, you can fire
-up your favourite interactive python environment and start playing with
-Marionette. Let's use a typical python shell:
-
-.. parsed-literal::
-
-   python
-
-First, import Marionette:
-
-.. parsed-literal::
-   from marionette import Marionette
-
-Now create the client for this session. Assuming you're using the default
-port on a Marionette instance running locally:
-
-.. parsed-literal::
-
-   client = Marionette(host='localhost', port=2828)
-   client.start_session()
-
-This will return some id representing your session id. Now that you've
-established a connection, let's start doing interesting things:
-
-.. parsed-literal::
-
-   client.execute_script("alert('o hai there!');")
-
-You should now see this alert pop up! How exciting! Okay, let's do
-something practical. Close the dialog and try this:
-
-.. parsed-literal::
-
-   client.navigate("http://www.mozilla.org")
-
-Now you're at mozilla.org! You can even verify it using the following:
-
-.. parsed-literal::
-   client.get_url()
-
-You can even find an element and click on it. Let's say you want to get
-the first link:
-
-.. parsed-literal::
-   first_link = client.find_element("tag name", "a")
-
-first_link now holds a reference to the first link on the page. You can click it:
-
-.. parsed-literal::
-   first_link.click()
-
-Using the Client for Testing
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Please visit, our `Marionette Tests`_ section for information regarding testing.
-
-.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests
-
-.. automodule:: marionette
-
-Marionette Objects
-------------------
-.. autoclass:: Marionette
-
-Session Management
-^^^^^^^^^^^^^^^^^^
-.. automethod:: Marionette.start_session
-.. automethod:: Marionette.delete_session
-.. autoattribute:: Marionette.session_capabilities
-.. automethod:: Marionette.get_cookie
-.. automethod:: Marionette.get_cookies
-.. automethod:: Marionette.add_cookie
-.. automethod:: Marionette.delete_all_cookies
-
-Context Management
-^^^^^^^^^^^^^^^^^^
-.. autoattribute:: Marionette.current_window_handle
-.. autoattribute:: Marionette.window_handles
-.. automethod:: Marionette.set_context
-.. automethod:: Marionette.switch_to_frame
-.. automethod:: Marionette.switch_to_window
-.. automethod:: Marionette.get_active_frame
-.. automethod:: Marionette.close
-
-Navigation Methods
-^^^^^^^^^^^^^^^^^^
-.. autoattribute:: Marionette.title
-.. automethod:: Marionette.navigate
-.. automethod:: Marionette.get_url
-.. automethod:: Marionette.go_back
-.. automethod:: Marionette.go_forward
-.. automethod:: Marionette.refresh
-.. automethod:: Marionette.absolute_url
-.. automethod:: Marionette.get_window_type
-
-DOM Element Methods
-^^^^^^^^^^^^^^^^^^^
-.. automethod:: Marionette.set_search_timeout
-.. automethod:: Marionette.find_element
-.. automethod:: Marionette.find_elements
-
-Script Execution
-^^^^^^^^^^^^^^^^
-.. automethod:: Marionette.execute_script
-.. automethod:: Marionette.execute_async_script
-.. automethod:: Marionette.set_script_timeout
-
-Debugging
-^^^^^^^^^
-.. autoattribute:: Marionette.page_source
-.. automethod:: Marionette.log
-.. automethod:: Marionette.get_logs
-.. automethod:: Marionette.screenshot
-
-Querying and Modifying Document Content
----------------------------------------
-.. autoclass:: HTMLElement
-   :members:
-
-.. autoclass:: DateTimeValue
-   :members:
-
-Action Objects
---------------
-
-Action Sequences
-^^^^^^^^^^^^^^^^
-.. autoclass:: Actions
-   :members:
-
-Multi-action Sequences
-^^^^^^^^^^^^^^^^^^^^^^
-.. autoclass:: MultiActions
-   :members:
-
-Explicit Waiting and Expected Conditions
-----------------------------------------
-
-Waits are used to pause program execution
-until a given condition is true.
-This is a useful technique to employ
-when documents load new content or change
-after ``Document.readyState``'s value changes to "complete".
-
-Because Marionette returns control to the user
-when the document is completely loaded,
-any subsequent interaction with elements
-are subject to manual synchronisation.
-The reason for this is that Marionette
-does not keep a direct representation of the DOM,
-but instead exposes a way for the user to
-query the browser's DOM state.
-
-The `Wait` helper class provided by Marionette
-avoids some of the caveats of ``time.sleep(n)``,
-which sets the condition to an exact time period to wait.
-It will return immediately
-once the provided condition evaluates to true.
-
-In addition to writing your own custom conditions
-you can combine `Wait`
-with a number of ready-made expected conditions
-that are listed below.
-
-Waits
-^^^^^
-.. autoclass:: marionette.wait.Wait
-   :members:
-   :special-members:
-.. autoattribute marionette.wait.DEFAULT_TIMEOUT
-.. autoattribute marionette.wait.DEFAULT_INTERVAL
-
-Expected Conditions
-^^^^^^^^^^^^^^^^^^^
-.. automodule:: marionette.expected
-   :members:
+.. include:: basics.rst
 
 Indices and tables
-==================
+------------------
 
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
+
+.. toctree::
+ :hidden:
+
+ Getting Started <basics>
+ Interactive Tutorial <interactive>
+ advanced/landing
+ reference
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/interactive.rst
@@ -0,0 +1,55 @@
+Using the Client Interactively
+==============================
+
+Once you installed the client and have Marionette running, you can fire
+up your favourite interactive python environment and start playing with
+Marionette. Let's use a typical python shell:
+
+.. parsed-literal::
+
+   python
+
+First, import Marionette:
+
+.. parsed-literal::
+   from marionette import Marionette
+
+Now create the client for this session. Assuming you're using the default
+port on a Marionette instance running locally:
+
+.. parsed-literal::
+
+   client = Marionette(host='localhost', port=2828)
+   client.start_session()
+
+This will return some id representing your session id. Now that you've
+established a connection, let's start doing interesting things:
+
+.. parsed-literal::
+
+   client.execute_script("alert('o hai there!');")
+
+You should now see this alert pop up! How exciting! Okay, let's do
+something practical. Close the dialog and try this:
+
+.. parsed-literal::
+
+   client.navigate("http://www.mozilla.org")
+
+Now you're at mozilla.org! You can even verify it using the following:
+
+.. parsed-literal::
+   client.get_url()
+
+You can even find an element and click on it. Let's say you want to get
+the first link:
+
+.. parsed-literal::
+   from marionette import By
+   first_link = client.find_element(By.TAG_NAME, "a")
+
+first_link now holds a reference to the first link on the page. You can click it:
+
+.. parsed-literal::
+   first_link.click()
+
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/docs/reference.rst
@@ -0,0 +1,43 @@
+=============
+API Reference
+=============
+.. py:currentmodule:: marionette
+
+Marionette
+----------
+.. autoclass:: Marionette
+   :members:
+
+HTMLElement
+-----------
+.. autoclass:: HTMLElement
+   :members:
+
+DateTimeValue
+-------------
+.. autoclass:: DateTimeValue
+   :members:
+
+Actions
+-------
+.. autoclass:: Actions
+   :members:
+
+MultiActions
+------------
+.. autoclass:: MultiActions
+   :members:
+
+Wait
+----
+
+.. autoclass:: Wait
+   :members:
+   :special-members:
+.. autoattribute marionette.wait.DEFAULT_TIMEOUT
+.. autoattribute marionette.wait.DEFAULT_INTERVAL
+
+Built-in Conditions
+^^^^^^^^^^^^^^^^^^^
+.. automodule:: marionette.expected
+   :members:
--- a/testing/marionette/client/marionette/wait.py
+++ b/testing/marionette/client/marionette/wait.py
@@ -120,17 +120,17 @@ class Wait(object):
         while not until(self.clock, self.end):
             try:
                 rv = condition(self.marionette)
             except (KeyboardInterrupt, SystemExit) as e:
                 raise e
             except self.exceptions as e:
                 last_exc = sys.exc_info()
 
-            if isinstance(rv, bool) and not rv:
+            if not rv:
                 self.clock.sleep(self.interval)
                 continue
 
             if rv is not None:
                 return rv
 
             self.clock.sleep(self.interval)
 
--- a/toolkit/xre/nsWindowsWMain.cpp
+++ b/toolkit/xre/nsWindowsWMain.cpp
@@ -11,16 +11,20 @@
 #endif
 
 #include "nsUTF8Utils.h"
 
 #ifndef XRE_DONT_PROTECT_DLL_LOAD
 #include "nsSetDllDirectory.h"
 #endif
 
+#if defined(MOZ_METRO) || defined(__GNUC__)
+#define XRE_DONT_SUPPORT_XPSP2
+#endif
+
 #ifndef XRE_DONT_SUPPORT_XPSP2
 #include "WindowsCrtPatch.h"
 #endif
 
 #ifdef __MINGW32__
 
 /* MingW currently does not implement a wide version of the
    startup routines.  Workaround is to implement something like
@@ -75,17 +79,17 @@ FreeAllocStrings(int argc, char **argv)
     delete [] argv[argc];
   }
 
   delete [] argv;
 }
 
 int wmain(int argc, WCHAR **argv)
 {
-#if !defined(XRE_DONT_SUPPORT_XPSP2) && !defined(MOZ_METRO)
+#if !defined(XRE_DONT_SUPPORT_XPSP2)
   WindowsCrtPatch::Init();
 #endif
 
 #ifndef XRE_DONT_PROTECT_DLL_LOAD
   mozilla::SanitizeEnvironmentVariables();
   SetDllDirectoryW(L"");
 #endif