merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 28 Oct 2014 15:53:31 +0100
changeset 236960 c0ddb1b098ec15b8d8cc68c08c1b20f65465c9f4
parent 236868 a2d58c6420f41a1a57d2e6b688d6a7a07a27810e (current diff)
parent 236959 c4153145366ad62acfc5c01a2d2cfabae85d8bcd (diff)
child 236961 c08aa318495b4f03eb419fc3ec2c08c517c01fd7
child 236970 014db94e05b4538f68af3d038abd3f7e1b2971a7
child 237013 cf82f73b0ee382eceeda39dfbce2993b19d539ca
child 237058 7155fda159c1ae2db49155002dcf0b31ef04e034
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/media/test/test_encryptedMediaExtensions.html
testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 946065 needs a CLOBBER
+Backout of bug 1087560 needed a CLOBBER
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -1077,18 +1077,19 @@ void
 nsSHistory::GloballyEvictContentViewers()
 {
   // First, collect from each SHistory object the transactions which have a
   // cached content viewer.  Associate with each transaction its distance from
   // its SHistory's current index.
 
   nsTArray<TransactionAndDistance> transactions;
 
-  nsSHistory *shist = static_cast<nsSHistory*>(PR_LIST_HEAD(&gSHistoryList));
-  while (shist != &gSHistoryList) {
+  PRCList* listEntry = PR_LIST_HEAD(&gSHistoryList);
+  while (listEntry != &gSHistoryList) {
+    nsSHistory* shist = static_cast<nsSHistory*>(listEntry);
 
     // Maintain a list of the transactions which have viewers and belong to
     // this particular shist object.  We'll add this list to the global list,
     // |transactions|, eventually.
     nsTArray<TransactionAndDistance> shTransactions;
 
     // Content viewers are likely to exist only within shist->mIndex -/+
     // gHistoryMaxViewers, so only search within that range.
@@ -1137,17 +1138,17 @@ nsSHistory::GloballyEvictContentViewers(
 
       nsISHTransaction *temp = trans;
       temp->GetNext(getter_AddRefs(trans));
     }
 
     // We've found all the transactions belonging to shist which have viewers.
     // Add those transactions to our global list and move on.
     transactions.AppendElements(shTransactions);
-    shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
+    listEntry = PR_NEXT_LINK(shist);
   }
 
   // We now have collected all cached content viewers.  First check that we
   // have enough that we actually need to evict some.
   if ((int32_t)transactions.Length() <= sHistoryMaxTotalViewers) {
     return;
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2546,16 +2546,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       if (!outerObject) {
         NS_ERROR("out of memory");
         return NS_ERROR_FAILURE;
       }
 
       JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
 
       js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
+      js::SetProxyExtra(outerObject, 0, js::PrivateValue(nullptr));
 
       outerObject = xpc::TransplantObject(cx, obj, outerObject);
       if (!outerObject) {
         NS_ERROR("unable to transplant wrappers, probably OOM");
         return NS_ERROR_FAILURE;
       }
 
       js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -169,16 +169,34 @@ UnwrapDOMObject(JSObject* obj)
 {
   MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
              "Don't pass non-DOM objects to this function");
 
   JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
   return static_cast<T*>(val.toPrivate());
 }
 
+template <class T>
+inline T*
+UnwrapPossiblyNotInitializedDOMObject(JSObject* obj)
+{
+  // This is used by the OjectMoved JSClass hook which can be called before
+  // JS_NewObject has returned and so before we have a chance to set
+  // DOM_OBJECT_SLOT to anything useful.
+
+  MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
+             "Don't pass non-DOM objects to this function");
+
+  JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
+  if (val.isUndefined()) {
+    return nullptr;
+  }
+  return static_cast<T*>(val.toPrivate());
+}
+
 inline const DOMJSClass*
 GetDOMClass(const js::Class* clasp)
 {
   return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
 }
 
 inline const DOMJSClass*
 GetDOMClass(JSObject* obj)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1523,17 +1523,17 @@ class CGAbstractClassHook(CGAbstractStat
     Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
     'this' unwrapping as it assumes that the unwrapped type is always known.
     """
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
                                         args)
 
     def definition_body_prologue(self):
-        return ("%s* self = UnwrapDOMObject<%s>(obj);\n" %
+        return ("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
                 (self.descriptor.nativeType, self.descriptor.nativeType))
 
     def definition_body(self):
         return self.definition_body_prologue() + self.generate_code()
 
     def generate_code(self):
         assert False  # Override me!
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -664,16 +664,22 @@ CreateOffscreen(GLContext* gl,
     baseCaps.depth = options.depth;
     baseCaps.premultAlpha = options.premultipliedAlpha;
     baseCaps.preserve = options.preserveDrawingBuffer;
     baseCaps.stencil = options.stencil;
 
     if (!baseCaps.alpha)
         baseCaps.premultAlpha = true;
 
+    if (gl->IsANGLE()) {
+        // We can't use no-alpha formats on ANGLE yet because of:
+        // https://code.google.com/p/angleproject/issues/detail?id=764
+        baseCaps.alpha = true;
+    }
+
     // we should really have this behind a
     // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
     // for now it's just behind a pref for testing/evaluation.
     baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
 
 #ifdef MOZ_WIDGET_GONK
     baseCaps.surfaceAllocator = surfAllocator;
 #endif
@@ -898,54 +904,56 @@ WebGLContext::SetDimensions(int32_t sWid
 #endif
 
     mResetLayer = true;
     mOptionsFrozen = true;
 
     // increment the generation number
     ++mGeneration;
 
+    // Update our internal stuff:
+    if (gl->WorkAroundDriverBugs()) {
+        if (!mOptions.alpha && gl->Caps().alpha) {
+            mNeedsFakeNoAlpha = true;
+        }
+    }
+
+    // Update mOptions.
+    mOptions.depth = gl->Caps().depth;
+    mOptions.stencil = gl->Caps().stencil;
+    mOptions.antialias = gl->Caps().antialias;
+
     MakeContextCurrent();
 
     gl->fViewport(0, 0, mWidth, mHeight);
     mViewportWidth = mWidth;
     mViewportHeight = mHeight;
 
-    // Update mOptions.
-    mOptions.depth = gl->Caps().depth;
-    mOptions.stencil = gl->Caps().stencil;
-    mOptions.antialias = gl->Caps().antialias;
-
     // Make sure that we clear this out, otherwise
     // we'll end up displaying random memory
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     AssertCachedBindings();
     AssertCachedState();
 
     // Clear immediately, because we need to present the cleared initial
     // buffer.
     mBackbufferNeedsClear = true;
     ClearBackbufferIfNeeded();
 
     mShouldPresent = true;
 
     MOZ_ASSERT(gl->Caps().color);
-    MOZ_ASSERT(gl->Caps().alpha == mOptions.alpha);
+    MOZ_ASSERT_IF(!mNeedsFakeNoAlpha, gl->Caps().alpha == mOptions.alpha);
+    MOZ_ASSERT_IF(mNeedsFakeNoAlpha, !mOptions.alpha && gl->Caps().alpha);
     MOZ_ASSERT(gl->Caps().depth == mOptions.depth);
     MOZ_ASSERT(gl->Caps().stencil == mOptions.stencil);
     MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias);
     MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
 
-    if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) {
-        if (!mOptions.alpha) {
-            mNeedsFakeNoAlpha = true;
-        }
-    }
-
     AssertCachedBindings();
     AssertCachedState();
 
     reporter.SetSuccessful();
     return NS_OK;
 }
 
 void
@@ -1247,22 +1255,20 @@ void
 WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     dom::WebGLContextAttributes& result = retval.SetValue();
 
-    const PixelBufferFormat& format = gl->GetPixelFormat();
-
-    result.mAlpha.Construct(format.alpha > 0);
-    result.mDepth = format.depth > 0;
-    result.mStencil = format.stencil > 0;
-    result.mAntialias = format.samples > 1;
+    result.mAlpha.Construct(mOptions.alpha);
+    result.mDepth = mOptions.depth;
+    result.mStencil = mOptions.stencil;
+    result.mAntialias = mOptions.antialias;
     result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
     result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
 }
 
 /* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */
 NS_IMETHODIMP
 WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval)
 {
@@ -1840,17 +1846,16 @@ bool WebGLContext::TexImageFromVideoElem
     }
     srcImage = nullptr;
     container->UnlockCurrentImage();
     return ok;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-
 WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
     : mWebGL(webgl)
     , mNeedsChange(NeedsChange(webgl))
 {
     if (mNeedsChange) {
         mWebGL.gl->fColorMask(mWebGL.mColorWriteMask[0],
                               mWebGL.mColorWriteMask[1],
                               mWebGL.mColorWriteMask[2],
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -2099,16 +2099,68 @@ WebGLContext::PixelStorei(GLenum pname, 
             MakeContextCurrent();
             gl->fPixelStorei(pname, param);
             break;
         default:
             return ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
     }
 }
 
+// `width` in pixels.
+// `stride` in bytes.
+static bool
+SetFullAlpha(void* data, GLenum format, GLenum type, size_t width,
+             size_t height, size_t stride)
+{
+    if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
+        // Just memset the rows.
+        for (size_t j = 0; j < height; ++j) {
+            uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
+            memset(row, 0xff, width);
+            row += stride;
+        }
+
+        return true;
+    }
+
+    if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
+        for (size_t j = 0; j < height; ++j) {
+            uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
+
+            uint8_t* pAlpha = row + 3;
+            uint8_t* pAlphaEnd = pAlpha + 4*width;
+            while (pAlpha != pAlphaEnd) {
+                *pAlpha = 0xff;
+                pAlpha += 4;
+            }
+        }
+
+        return true;
+    }
+
+    if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
+        for (size_t j = 0; j < height; ++j) {
+            uint8_t* rowBytes = static_cast<uint8_t*>(data) + j*stride;
+            float* row = reinterpret_cast<float*>(rowBytes);
+
+            float* pAlpha = row + 3;
+            float* pAlphaEnd = pAlpha + 4*width;
+            while (pAlpha != pAlphaEnd) {
+                *pAlpha = 1.0f;
+                pAlpha += 4;
+            }
+        }
+
+        return true;
+    }
+
+    MOZ_ASSERT(false, "Unhandled case, how'd we get here?");
+    return false;
+}
+
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
                          GLsizei height, GLenum format,
                          GLenum type, const Nullable<ArrayBufferView> &pixels,
                          ErrorResult& rv)
 {
     if (IsContextLost())
         return;
@@ -2342,71 +2394,35 @@ WebGLContext::ReadPixels(GLint x, GLint 
                      + bytesPerPixel * subrect_x_in_dest_buffer, // destination
                    subrect_data.get() + subrect_alignedRowSize * y_inside_subrect, // source
                    subrect_plainRowSize); // size
         }
     }
 
     // if we're reading alpha, we may need to do fixup.  Note that we don't allow
     // GL_ALPHA to readpixels currently, but we had the code written for it already.
-    if (format == LOCAL_GL_ALPHA ||
-        format == LOCAL_GL_RGBA)
-    {
-        bool needAlphaFixup;
-        if (mBoundFramebuffer) {
-            needAlphaFixup = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
-        } else {
-            needAlphaFixup = gl->GetPixelFormat().alpha == 0;
-        }
-
-        if (needAlphaFixup) {
-            if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
-                // this is easy; it's an 0xff memset per row
-                uint8_t *row = static_cast<uint8_t*>(data);
-                for (GLint j = 0; j < height; ++j) {
-                    memset(row, 0xff, checked_plainRowSize.value());
-                    row += checked_alignedRowSize.value();
-                }
-            } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
-                // this is harder, we need to just set the alpha byte here
-                uint8_t *row = static_cast<uint8_t*>(data);
-                for (GLint j = 0; j < height; ++j) {
-                    uint8_t *rowp = row;
-#if MOZ_LITTLE_ENDIAN
-                    // offset to get the alpha byte; we're always going to
-                    // move by 4 bytes
-                    rowp += 3;
-#endif
-                    uint8_t *endrowp = rowp + 4 * width;
-                    while (rowp != endrowp) {
-                        *rowp = 0xff;
-                        rowp += 4;
-                    }
-
-                    row += checked_alignedRowSize.value();
-                }
-            } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
-                float* row = static_cast<float*>(data);
-
-                for (GLint j = 0; j < height; ++j) {
-                    float* pAlpha = row + 3;
-                    float* pAlphaEnd = pAlpha + 4*width;
-
-                    while (pAlpha != pAlphaEnd) {
-                        *pAlpha = 1.0f;
-                        pAlpha += 4;
-                    }
-
-                    row += checked_alignedRowSize.value();
-                }
-            } else {
-                NS_WARNING("Unhandled case, how'd we get here?");
-                return rv.Throw(NS_ERROR_FAILURE);
-            }
-        }
+
+    const bool formatHasAlpha = format == LOCAL_GL_ALPHA ||
+                                format == LOCAL_GL_RGBA;
+    if (!formatHasAlpha)
+        return;
+
+    bool needAlphaFilled;
+    if (mBoundFramebuffer) {
+        needAlphaFilled = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
+    } else {
+        needAlphaFilled = !mOptions.alpha;
+    }
+
+    if (!needAlphaFilled)
+        return;
+
+    size_t stride = checked_alignedRowSize.value(); // In bytes!
+    if (!SetFullAlpha(data, format, type, width, height, stride)) {
+        return rv.Throw(NS_ERROR_FAILURE);
     }
 }
 
 void
 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -307,22 +307,28 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_SAMPLES:
         case LOCAL_GL_MAX_VERTEX_ATTRIBS:
         case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_RED_BITS:
         case LOCAL_GL_GREEN_BITS:
         case LOCAL_GL_BLUE_BITS:
-        case LOCAL_GL_ALPHA_BITS:
         case LOCAL_GL_DEPTH_BITS: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
         }
+        case LOCAL_GL_ALPHA_BITS: {
+            GLint i = 0;
+            if (!mNeedsFakeNoAlpha) {
+                gl->fGetIntegerv(pname, &i);
+            }
+            return JS::Int32Value(i);
+        }
         case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: {
             if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
                 GLint i = 0;
                 gl->fGetIntegerv(pname, &i);
                 return JS::Int32Value(i);
             } else {
                 break;
             }
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1095,17 +1095,17 @@ WebGLContext::ValidateTexInputData(GLenu
 bool
 WebGLContext::ValidateCopyTexImage(GLenum format,
                                    WebGLTexImageFunc func,
                                    WebGLTexDimensions dims)
 {
     MOZ_ASSERT(IsCopyFunc(func));
 
     // Default framebuffer format
-    GLenum fboFormat = bool(gl->GetPixelFormat().alpha > 0) ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
+    GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
             ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", InfoFrom(func, dims));
             return false;
         }
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
--- a/dom/canvas/test/webgl-conformance/mochi-single.html
+++ b/dom/canvas/test/webgl-conformance/mochi-single.html
@@ -49,18 +49,19 @@ if (!window.ok) {
 }
 if (!window.todo) {
   window.todo = function(status, message) {
     console.log('todo(' + status + ', \'' + message + '\')');
   }
 }
 if (!window.SimpleTest) {
   window.SimpleTest = {
+    finish: function(){},
+    requestLongerTimeout: function(){},
     waitForExplicitFinish: function(){},
-    finish: function(){},
   };
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Implement our own version of `fail-if` expected failure handling.
 // `fail-if` in mochitest.ini doesn't work yet. (bug 987849)
 
 var OS_VERSION_WIN7 = 6.1;
@@ -238,16 +239,23 @@ function OnTestComplete() {
   SimpleTest.finish();
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Begin execution
 
 SimpleTest.waitForExplicitFinish();
 
+var isAndroid2_3 = (DriverInfo.getOS() == DriverInfo.OS.ANDROID &&
+                    DriverInfo.getOSVersion() < OS_VERSION_ANDROID_ICS);
+if (isAndroid2_3) {
+  var timeoutLengthMultiplier = 2.0;
+  SimpleTest.requestLongerTimeout(timeoutLengthMultiplier);
+}
+
 do {
   var arg = location.search.substr(1);
   if (arg == 'dump') {
     statusElem.innerHTML = 'Dumping';
 
     ok(true, 'OS:' + DriverInfo.getOS());
     ok(true, 'OS version:' + DriverInfo.getOSVersion());
     ok(true, 'Driver:' + DriverInfo.getDriver());
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1787,17 +1787,21 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
 {
   nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
-
+#ifdef MOZ_EME
+  if (ContainsRestrictedContent()) {
+    return nullptr;
+  }
+#endif
   OutputMediaStream* out = mOutputStreams.AppendElement();
 #ifdef DEBUG
   // Estimate hints based on the type of the media element
   // under the preference media.capturestream_hints for the
   // debug builds only. This allows WebRTC Peer Connection
   // to behave appropriately when media streams generated
   // via mozCaptureStream*() are added to the Peer Connection.
   // This functionality is planned to be used as part of Audio
@@ -3992,20 +3996,31 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC
 
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
+bool
+HTMLMediaElement::ContainsRestrictedContent()
+{
+  return GetMediaKeys() != nullptr;
+}
+
 already_AddRefed<Promise>
 HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
                                ErrorResult& aRv)
 {
+  if (MozAudioCaptured()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(OwnerDoc()->GetInnerWindow());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
@@ -4030,16 +4045,18 @@ HTMLMediaElement::SetMediaKeys(mozilla::
     if (NS_FAILED(mMediaKeys->Bind(this))) {
       promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
       mMediaKeys = nullptr;
       return promise.forget();
     }
     if (mDecoder) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     }
+    // Update the same-origin status.
+    NotifyDecoderPrincipalChanged();
   }
   promise->MaybeResolve(JS::UndefinedHandleValue);
   return promise.forget();
 }
 
 MediaWaitingFor
 HTMLMediaElement::WaitingFor() const
 {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -545,16 +545,17 @@ public:
 
 
   bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE;
 
   // Returns the principal of the "top level" document; the origin displayed
   // in the URL bar of the browser window.
   already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
 
+  bool ContainsRestrictedContent();
 #endif // MOZ_EME
 
   bool MozAutoplayEnabled() const
   {
     return mAutoplayEnabled;
   }
 
   already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1406,16 +1406,23 @@ ContentParent::ShutDownProcess(bool aClo
     // with aCloseWithError = true.  It's important that we call
     // CloseWithError() in this case; see bug 895204.
 
     if (!aCloseWithError && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
         // sequence.
         mCalledClose = true;
         Close();
+#ifdef MOZ_NUWA_PROCESS
+        // Kill Nuwa process forcibly to break its IPC channels and finalize
+        // corresponding parents.
+        if (IsNuwaProcess()) {
+            KillHard();
+        }
+#endif
     }
 
     if (aCloseWithError && !mCalledCloseWithError) {
         MessageChannel* channel = GetIPCChannel();
         if (channel) {
             mCalledCloseWithError = true;
             channel->CloseWithError();
         }
@@ -2926,27 +2933,23 @@ ContentParent::KillHard()
     mForceKillTask = nullptr;
     // This ensures the process is eventually killed, but doesn't
     // immediately KILLITWITHFIRE because we want to get a minidump if
     // possible.  After a timeout though, the process is forceably
     // killed.
     if (!KillProcess(OtherProcess(), 1, false)) {
         NS_WARNING("failed to kill subprocess!");
     }
-    mSubprocess->SetAlreadyDead();
+    if (mSubprocess) {
+        mSubprocess->SetAlreadyDead();
+    }
     XRE_GetIOMessageLoop()->PostTask(
         FROM_HERE,
         NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
                             OtherProcess(), /*force=*/true));
-    //We do clean-up here
-    MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        NewRunnableMethod(this, &ContentParent::ShutDownProcess,
-                          /* closeWithError */ true),
-        3000);
     // We've now closed the OtherProcess() handle, so must set it to null to
     // prevent our dtor closing it twice.
     SetOtherProcess(0);
 }
 
 bool
 ContentParent::IsPreallocated()
 {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -160,17 +160,17 @@ static_assert(QUICK_BUFFERING_LOW_DATA_U
 
 // The amount of instability we tollerate in calls to
 // MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
 // less than this are ignored, as they're assumed to be the result of
 // instability in the duration estimation.
 static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
 
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
-  return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
+  return TimeDuration::FromMicroseconds(aUsecs);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
 
 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
                                                    MediaDecoderReader* aReader,
@@ -2564,16 +2564,17 @@ void MediaDecoderStateMachine::RenderVid
   }
 
   VERBOSE_LOG("playing video frame %lld", aData->mTime);
 
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage,
                                aTarget);
+    MOZ_ASSERT(container->GetFrameDelay() >= 0 || mScheduler->IsRealTime());
   }
 }
 
 int64_t
 MediaDecoderStateMachine::GetAudioClock()
 {
   // We must hold the decoder monitor while using the audio stream off the
   // audio sink to ensure that it doesn't get destroyed on the audio sink
@@ -2650,16 +2651,17 @@ void MediaDecoderStateMachine::AdvanceFr
 
   // If playbackRate is 0.0, we should stop the progress, but not be in paused
   // state, per spec.
   if (mPlaybackRate == 0.0) {
     return;
   }
 
   int64_t clock_time = GetClock();
+  TimeStamp nowTime = TimeStamp::Now();
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame.
   int64_t remainingTime = AUDIO_DURATION_USECS;
   NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
   nsAutoPtr<VideoData> currentFrame;
   if (VideoQueue().GetSize() > 0) {
     VideoData* frame = VideoQueue().PeekFront();
 #ifdef PR_LOGGING
@@ -2716,18 +2718,18 @@ void MediaDecoderStateMachine::AdvanceFr
   // We've got enough data to keep playing until at least the next frame.
   // Start playing now if need be.
   if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) {
     StartPlayback();
   }
 
   if (currentFrame) {
     // Decode one frame and display it.
-    TimeStamp presTime = mPlayStartTime - UsecsToDuration(mPlayDuration) +
-                          UsecsToDuration(currentFrame->mTime - mStartTime);
+    int64_t delta = currentFrame->mTime - clock_time;
+    TimeStamp presTime = nowTime + TimeDuration::FromMicroseconds(delta / mPlaybackRate);
     NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
     // Filter out invalid frames by checking the frame time. FrameTime could be
     // zero if it's a initial frame.
     int64_t frameTime = currentFrame->mTime - mStartTime;
     if (frameTime > 0  || (frameTime == 0 && mPlayDuration == 0) ||
         mScheduler->IsRealTime()) {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       // If we have video, we want to increment the clock in steps of the frame
@@ -2763,17 +2765,17 @@ void MediaDecoderStateMachine::AdvanceFr
   }
 
   // If the number of audio/video frames queued has changed, either by
   // this function popping and playing a video frame, or by the audio
   // thread popping and playing an audio frame, we may need to update our
   // ready state. Post an update to do so.
   UpdateReadyState();
 
-  ScheduleStateMachine(remainingTime);
+  ScheduleStateMachine(remainingTime / mPlaybackRate);
 }
 
 nsresult
 MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
 {
   nsAutoPtr<VideoData> video(aSample);
   MOZ_ASSERT(video);
   DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld] dup=%d",
--- a/dom/media/fmp4/apple/AppleATDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleATDecoder.cpp
@@ -33,17 +33,17 @@ AppleATDecoder::AppleATDecoder(const mp4
   , mConverter(nullptr)
   , mStream(nullptr)
   , mCurrentAudioTimestamp(0)
   , mSamplePosition(0)
   , mHaveOutput(false)
   , mFlushed(false)
 {
   MOZ_COUNT_CTOR(AppleATDecoder);
-  LOG("Creating Apple AudioToolbox Audio decoder");
+  LOG("Creating Apple AudioToolbox decoder");
   LOG("Audio Decoder configuration: %s %d Hz %d channels %d bits per channel",
       mConfig.mime_type,
       mConfig.samples_per_second,
       mConfig.channel_count,
       mConfig.bits_per_sample);
 
   if (!strcmp(aConfig.mime_type, "audio/mpeg")) {
     mFileType = kAudioFileMP3Type;
@@ -87,17 +87,17 @@ static void
 
 nsresult
 AppleATDecoder::Init()
 {
   if (!mFileType) {
     NS_ERROR("Non recognised format");
     return NS_ERROR_FAILURE;
   }
-  LOG("Initializing Apple AudioToolbox Audio decoder");
+  LOG("Initializing Apple AudioToolbox decoder");
   OSStatus rv = AudioFileStreamOpen(this,
                                     _MetadataCallback,
                                     _SampleCallback,
                                     mFileType,
                                     &mStream);
   if (rv) {
     NS_ERROR("Couldn't open AudioFileStream");
     return NS_ERROR_FAILURE;
@@ -310,20 +310,20 @@ AppleATDecoder::SampleCallback(uint32_t 
       break;
     }
   } while (true);
 }
 
 void
 AppleATDecoder::SetupDecoder()
 {
-  AudioStreamBasicDescription inputFormat;
-
+  LOG("Setting up Apple AudioToolbox decoder.");
   mHaveOutput = false;
 
+  AudioStreamBasicDescription inputFormat;
   nsresult rv = AppleUtils::GetRichestDecodableFormat(mStream, inputFormat);
   if (NS_FAILED(rv)) {
     mCallback->Error();
     return;
   }
 
   // Fill in the output format manually.
   PodZero(&mOutputFormat);
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -1196,16 +1196,19 @@ GeckoMediaPluginService::ClearStorage()
   }
 
   nsCOMPtr<nsIFile> path; // $profileDir/gmp/
   nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  if (NS_FAILED(path->Remove(true))) {
+  bool exists = false;
+  if (NS_SUCCEEDED(path->Exists(&exists)) &&
+      exists &&
+      NS_FAILED(path->Remove(true))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
   NS_DispatchToMainThread(new StorageClearedTask(), NS_DISPATCH_NORMAL);
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -79,16 +79,28 @@ public:
   }
 
   void SetTaskQueue(MediaTaskQueue* aTaskQueue)
   {
     MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
     mTaskQueue = aTaskQueue;
   }
 
+  void BreakCycles()
+  {
+    if (mReader) {
+      mReader->BreakCycles();
+      mReader = nullptr;
+    }
+    mTaskQueue = nullptr;
+#ifdef MOZ_EME
+    mCDMProxy = nullptr;
+#endif
+  }
+
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy)
   {
     MOZ_ASSERT(NS_IsMainThread());
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mCDMProxy = aProxy;
     return NS_OK;
   }
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -386,17 +386,17 @@ TrackBuffer::ContainsTime(int64_t aTime)
 }
 
 void
 TrackBuffer::BreakCycles()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
-    mDecoders[i]->GetReader()->BreakCycles();
+    mDecoders[i]->BreakCycles();
   }
   mDecoders.Clear();
 
   // These are cleared in Shutdown()
   MOZ_ASSERT(mInitializedDecoders.IsEmpty());
   MOZ_ASSERT(!mParentDecoder);
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/eme.js
@@ -0,0 +1,178 @@
+const KEYSYSTEM_TYPE = "org.w3.clearkey";
+
+function bail(message)
+{
+  return function(err) {
+    ok(false, message);
+    if (err) {
+      info(err);
+    }
+    SimpleTest.finish();
+  }
+}
+
+function ArrayBufferToString(arr)
+{
+  var str = '';
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < view.length; i++) {
+    str += String.fromCharCode(view[i]);
+  }
+  return str;
+}
+
+function StringToArrayBuffer(str)
+{
+  var arr = new ArrayBuffer(str.length);
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < str.length; i++) {
+    view[i] = str.charCodeAt(i);
+  }
+  return arr;
+}
+
+function Base64ToHex(str)
+{
+  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+  var res = "";
+  for (var i = 0; i < bin.length; i++) {
+    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+  }
+  return res;
+}
+
+function HexToBase64(hex)
+{
+  var bin = "";
+  for (var i = 0; i < hex.length; i += 2) {
+    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+  }
+  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function UpdateSessionFunc(test) {
+  return function(ev) {
+    var msgStr = ArrayBufferToString(ev.message);
+    var msg = JSON.parse(msgStr);
+
+    info("got message from CDM: " + msgStr);
+    is(msg.type, test.sessionType, "Key session type should match");
+    ok(msg.kids, "message event should contain key ID array");
+
+    var outKeys = [];
+
+    for (var i = 0; i < msg.kids.length; i++) {
+      var id64 = msg.kids[i];
+      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
+      var key = test.keys[idHex];
+
+      if (key) {
+        info("found key " + key + " for key id " + idHex);
+        outKeys.push({
+          "kty":"oct",
+          "alg":"A128KW",
+          "kid":id64,
+          "k":HexToBase64(key)
+        });
+      } else {
+        bail("Couldn't find key for key id " + idHex);
+      }
+    }
+
+    var update = JSON.stringify({
+      "keys" : outKeys,
+      "type" : msg.type
+    });
+    info("sending update message to CDM: " + update);
+
+    ev.target.update(StringToArrayBuffer(update)).then(function() {
+      info("MediaKeySession update ok!");
+    }, bail("MediaKeySession update failed"));
+  }
+}
+
+function PlayFragmented(test, elem)
+{
+  return new Promise(function(resolve, reject) {
+    var ms = new MediaSource();
+    elem.src = URL.createObjectURL(ms);
+
+    var sb;
+    var curFragment = 0;
+
+    function addNextFragment() {
+      if (curFragment >= test.fragments.length) {
+        ms.endOfStream();
+        resolve();
+        return;
+      }
+
+      var fragmentFile = test.fragments[curFragment++];
+
+      var req = new XMLHttpRequest();
+      req.open("GET", fragmentFile);
+      req.responseType = "arraybuffer";
+
+      req.addEventListener("load", function() {
+        sb.appendBuffer(new Uint8Array(req.response));
+      });
+
+      info("fetching resource " + fragmentFile);
+      req.send(null);
+    }
+
+    ms.addEventListener("sourceopen", function () {
+      sb = ms.addSourceBuffer(test.type);
+      sb.addEventListener("updateend", addNextFragment);
+
+      addNextFragment();
+    })
+  });
+}
+
+// Returns a promise that is resovled when the media element is ready to have
+// its play() function called; when it's loaded MSE fragments, or once the load
+// has started for non-MSE video.
+function LoadTest(test, elem)
+{
+  if (test.fragments) {
+    return PlayFragmented(test, elem);
+  }
+
+  // This file isn't fragmented; set the media source normally.
+  return new Promise(function(resolve, reject) {
+    elem.src = test.name;
+    resolve();
+  });
+}
+
+function SetupEME(test, token, params)
+{
+  var v = document.createElement("video");
+
+  var onSetKeysFail = (params && params.onSetKeysFail)
+    ? params.onSetKeysFail
+    : bail(token + " Failed to set MediaKeys on <video> element");
+  
+  v.addEventListener("encrypted", function(ev) {
+    info(token + " got encrypted event");
+    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
+      info(token + " created MediaKeys object ok");
+      mediaKeys.sessions = [];
+      return v.setMediaKeys(mediaKeys);
+    }, bail("failed to create MediaKeys object")).then(function() {
+      info(token + " set MediaKeys on <video> element ok");
+
+      var session = v.mediaKeys.createSession(test.sessionType);
+      if (params && params.onsessioncreated) {
+        params.onsessioncreated(session);
+      }
+      session.addEventListener("message", UpdateSessionFunc(test));
+      session.generateRequest(ev.initDataType, ev.initData).then(function() {
+      }, bail(token + " Failed to initialise MediaKeySession"));
+
+    }, onSetKeysFail);
+  });
+
+  return v;
+}
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -135,16 +135,17 @@ support-files =
   detodos.opus
   detodos.opus^headers^
   detodos.webm
   detodos.webm^headers^
   dirac.ogg
   dirac.ogg^headers^
   dynamic_redirect.sjs
   dynamic_resource.sjs
+  eme.js
   gizmo-frag-cenc1.m4s
   gizmo-frag-cenc2.m4s
   gizmo-frag-cencinit.mp4
   file_access_controls.html
   fragment_noplay.js
   fragment_play.js
   gizmo.mp4
   gizmo.mp4^headers^
@@ -355,17 +356,21 @@ skip-if = (toolkit == 'android' && proce
 [test_contentDuration7.html]
 [test_controls.html]
 [test_currentTime.html]
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
-[test_encryptedMediaExtensions.html]
+[test_eme_canvas_blocked.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_playback.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_canvas_blocked.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token)
+{
+  manager.started(token);
+
+  var sessions = [];
+
+  var v = SetupEME(test, token);
+  v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+
+  v.addEventListener("canplay", function(ev) {
+    var video = ev.target;
+    var canvas = document.createElement("canvas");
+    canvas.width = video.videoWidth;
+    canvas.height = video.videoHeight;
+    document.body.appendChild(canvas);
+    var ctx = canvas.getContext("2d");
+    var threwError = false;
+    try {
+      ctx.drawImage(video, 0, 0);
+    } catch (ex) {
+      threwError = true;
+    }
+    ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
+    manager.finished(token);
+  });
+
+  v.addEventListener("error", bail(token + " got error event"));
+
+  LoadTest(test, v);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.ignore_codecs", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
rename from dom/media/test/test_encryptedMediaExtensions.html
rename to dom/media/test/test_eme_playback.html
--- a/dom/media/test/test_encryptedMediaExtensions.html
+++ b/dom/media/test/test_eme_playback.html
@@ -1,246 +1,96 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test Encrypted Media Extensions</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var manager = new MediaTestManager;
 
-const KEYSYSTEM_TYPE = "org.w3.clearkey";
 
-function bail(message)
-{
-  return function(err) {
-    ok(false, message);
-    if (err) {
-      info(err);
-    }
-    SimpleTest.finish();
-  }
-}
-
-function ArrayBufferToString(arr)
-{
-  var str = '';
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < view.length; i++) {
-    str += String.fromCharCode(view[i]);
-  }
-  return str;
-}
-
-function StringToArrayBuffer(str)
-{
-  var arr = new ArrayBuffer(str.length);
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < str.length; i++) {
-    view[i] = str.charCodeAt(i);
-  }
-  return arr;
-}
-
-function Base64ToHex(str)
-{
-  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
-  var res = "";
-  for (var i = 0; i < bin.length; i++) {
-    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
-  }
-  return res;
-}
-
-function HexToBase64(hex)
-{
-  var bin = "";
-  for (var i = 0; i < hex.length; i += 2) {
-    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
-  }
-  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
-}
-
-function UpdateSessionFunc(test) {
-  return function(ev) {
-    var msgStr = ArrayBufferToString(ev.message);
-    var msg = JSON.parse(msgStr);
-
-    info("got message from CDM: " + msgStr);
-    is(msg.type, test.sessionType, "Key session type should match");
-    ok(msg.kids, "message event should contain key ID array");
-
-    var outKeys = [];
-
-    for (var i = 0; i < msg.kids.length; i++) {
-      var id64 = msg.kids[i];
-      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
-      var key = test.keys[idHex];
-
-      if (key) {
-        info("found key " + key + " for key id " + idHex);
-        outKeys.push({
-          "kty":"oct",
-          "alg":"A128KW",
-          "kid":id64,
-          "k":HexToBase64(key)
-        });
-      } else {
-        bail("Couldn't find key for key id " + idHex);
-      }
-    }
-
-    var update = JSON.stringify({
-      "keys" : outKeys,
-      "type" : msg.type
-    });
-    info("sending update message to CDM: " + update);
-
-    ev.target.update(StringToArrayBuffer(update)).then(function() {
-      info("MediaKeySession update ok!");
-    }, bail("MediaKeySession update failed"));
-  }
-}
-
-function PlayFragmented(test, elem)
-{
-  var ms = new MediaSource();
-  elem.src = URL.createObjectURL(ms);
-
-  var sb;
-  var curFragment = 0;
-
-  function addNextFragment() {
-    if (curFragment >= test.fragments.length) {
-      ms.endOfStream();
-      elem.play();
-      return;
-    }
-
-    var fragmentFile = test.fragments[curFragment++];
-
-    var req = new XMLHttpRequest();
-    req.open("GET", fragmentFile);
-    req.responseType = "arraybuffer";
-
-    req.addEventListener("load", function() {
-      sb.appendBuffer(new Uint8Array(req.response));
-    });
-
-    info("fetching resource " + fragmentFile);
-    req.send(null);
-  }
-
-  ms.addEventListener("sourceopen", function () {
-    sb = ms.addSourceBuffer(test.type);
-    sb.addEventListener("updateend", addNextFragment);
-
-    addNextFragment();
-  });
-}
-
-function PlayTest(test, elem)
-{
-  if (test.fragments) {
-    PlayFragmented(test, elem);
-    return;
-  }
-
-  // This file isn't fragmented; set the media source normally.
-  elem.src = test.name;
-  elem.play();
-}
-
-function KeysChangeFunc(session, keys) {
+function KeysChangeFunc(session, keys, token) {
   session.keyIdsReceived = [];
   for (var keyid in keys) {
     info("Set " + keyid + " to false in session.keyIdsReceived");
     session.keyIdsReceived[keyid] = false;
   }
   return function(ev) {
     var session = ev.target;
     session.gotKeysChanged = true;
     session.getUsableKeyIds().then(function(keyIds) {
       for (var k = 0; k < keyIds.length; k++) {
         var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
-        ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
+        ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
         session.keyIdsReceived[kid] = true;
       }
     }, bail("Failed to get keyIds"));
   }
 }
 
 function startTest(test, token)
 {
-  manager.started(test._token);
+  manager.started(token);
+
+  var sessions = [];
 
-  var v = document.createElement("video");
+  var v = SetupEME(test, token,
+    {
+      onsessioncreated: function(session) {
+        sessions.push(session);
+        session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
+      }
+    }
+  );
+
   var gotEncrypted = false;
   var gotPlaying = false;
 
   v.addEventListener("encrypted", function(ev) {
-    gotEncrypted = true;
-
-    info(token + " got encrypted event");
     ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
        token + " MediaKeys should support this keysystem");
-
-    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
-      info(token + " created MediaKeys object ok");
-      mediaKeys.sessions = [];
-      return v.setMediaKeys(mediaKeys);
-    }, bail("failed to create MediaKeys object")).then(function() {
-      info(token + " set MediaKeys on <video> element ok");
-
-      ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
-         "MediaKeys should still support keysystem after CDM created...");
-
-      var session = v.mediaKeys.createSession(test.sessionType);
-      v.mediaKeys.sessions.push(session);
-      session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
-      session.addEventListener("message", UpdateSessionFunc(test));
-      session.generateRequest(ev.initDataType, ev.initData).then(function() {
-      }, bail(token + " Failed to initialise MediaKeySession"));
-
-    }, bail(token + " Failed to set MediaKeys on <video> element"));
+    gotEncrypted = true;
   });
 
   v.addEventListener("playing", function () { gotPlaying = true; });
 
   v.addEventListener("ended", function(ev) {
     ok(true, token + " got ended event");
-    manager.finished(test._token);
+    manager.finished(token);
 
     ok(gotEncrypted, token + " encrypted event should have fired");
     ok(gotPlaying, token + " playing event should have fired");
 
     ok(Math.abs(test.duration - v.duration) < 0.1,
        token + " Duration of video should be corrrect");
     ok(Math.abs(test.duration - v.currentTime) < 0.1,
        token + " Current time should be same as duration");
+
     // Verify all sessions had all keys went sent the to the CDM usable, and thus
     // that we received keyschange event(s).
-    var sessions = v.mediaKeys.sessions;
     is(sessions.length, 1, "should have 1 session");
     for (var i = 0; i < sessions.length; i++) {
       var session = sessions[i];
-      ok(session.gotKeysChanged, "Should have received at least one keychange event");
+      ok(session.gotKeysChanged, token + " should have received at least one keychange event");
       for (var kid in session.keyIdsReceived) {
-        ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
+        ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
       }
     }
-  });
+
+   });
 
   v.addEventListener("error", bail(token + " got error event"));
 
-  PlayTest(test, v);
+  LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
 }
 
 function testIsTypeSupported()
 {
   var t = MediaKeys.isTypeSupported;
   const clearkey = "org.w3.clearkey";
   ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
   ok(t(clearkey), "ClearKey supported.");
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_stream_capture_blocked.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token)
+{
+  // Three cases:
+  // 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
+  // 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
+  // 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
+
+  // Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
+  var case1token = token + "_case1";
+  var setKeysFailed = function() {
+    ok(true, case1token + " setMediaKeys failed as expected.");
+    manager.finished(case1token);
+  };
+  var v1 = SetupEME(test, case1token,  { onSetKeysFail: setKeysFailed });
+  var context = new AudioContext();
+  var node = context.createMediaElementSource(v1);
+  v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v1.addEventListener("error", bail(case1token + " got error event"));
+  v1.addEventListener("canplay", function(ev) {
+    ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
+  });
+  manager.started(case1token);
+  LoadTest(test, v1);
+
+
+  // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
+  var case2token = token + "_case2";
+  var v2 = SetupEME(test, case2token);
+  v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v2.addEventListener("error", bail(case2token + " got error event"));
+  v2.addEventListener("canplay", function(ev) {
+    ok(true, case2token + " should reach canplay");
+    var threw = false;
+    try {
+      var context = new AudioContext();
+      var node = context.createMediaElementSource(v2);
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
+    manager.finished(case2token);
+  });
+  manager.started(case2token);
+  LoadTest(test, v2);
+
+
+  // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
+  var case3token = token + "_case3";
+  var v3 = SetupEME(test, case3token);
+  v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v3.addEventListener("error", bail(case3token + " got error event"));
+  v3.addEventListener("canplay", function(ev) {
+    ok(true, case3token + " should reach canplay");
+    var threw = false;
+    try {
+      var stream = v3.mozCaptureStreamUntilEnded();
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
+    manager.finished(case3token);
+  });
+  manager.started(case3token);
+  LoadTest(test, v3);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.ignore_codecs", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1298,16 +1298,18 @@ DataChannelTest.prototype = Object.creat
       function dataChannelConnected(channel) {
         // in case the switch statement below had called onSuccess already we
         // don't want to call it again
         if (!dcOpened) {
           clearTimeout(dcConnectionTimeout);
           is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'");
           dcOpened = true;
           onSuccess();
+        } else {
+          info("dataChannelConnected() called, but data channel was open already");
         }
       }
 
       // TODO: drno: convert dataChannels into an object and make
       //             registerDataChannelOpenEvent a generic function
       if (peer == this.pcLocal) {
         peer.dataChannels[0].onopen = dataChannelConnected;
       } else {
@@ -2601,21 +2603,22 @@ PeerConnectionWrapper.prototype = {
 
   /**
    * Register all events during the setup of the data channel
    *
    * @param {Function} onDataChannelOpened
    *        Callback to execute when the data channel has been opened
    */
   registerDataChannelOpenEvents : function (onDataChannelOpened) {
-    info(this + ": Register callbacks for 'ondatachannel' and 'onopen'");
+    info(this + ": Register callback for 'ondatachannel'");
 
     this.ondatachannel = function (targetChannel) {
+      this.dataChannels.push(targetChannel);
+      info(this + ": 'ondatachannel' fired, registering 'onopen' callback");
       targetChannel.onopen = onDataChannelOpened;
-      this.dataChannels.push(targetChannel);
     };
   },
 
   /**
    * Returns the string representation of the class
    *
    * @returns {String} The string representation
    */
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -280,16 +280,22 @@ AudioContext::CreateAnalyser()
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
+#ifdef MOZ_EME
+  if (aMediaElement.ContainsRestrictedContent()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+#endif
   nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
     new MediaElementAudioSourceNode(this, stream);
   return mediaElementAudioSourceNode.forget();
 }
--- a/gfx/angle/src/libGLESv2/validationES2.cpp
+++ b/gfx/angle/src/libGLESv2/validationES2.cpp
@@ -586,17 +586,18 @@ bool ValidateES2CopyTexImageParameters(C
                 context->recordError(Error(GL_INVALID_OPERATION));
                 return false;
             }
             break;
           case GL_LUMINANCE_ALPHA:
           case GL_RGBA:
             if (colorbufferFormat != GL_RGBA4 &&
                 colorbufferFormat != GL_RGB5_A1 &&
-                colorbufferFormat != GL_RGBA8_OES)
+                colorbufferFormat != GL_RGBA8_OES &&
+                colorbufferFormat != GL_BGRA8_EXT)
             {
                 context->recordError(Error(GL_INVALID_OPERATION));
                 return false;
             }
             break;
           case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
--- a/gfx/gl/GLConsts.h
+++ b/gfx/gl/GLConsts.h
@@ -5182,18 +5182,16 @@
 #define LOCAL_EGL_CONTEXT_PRIORITY_MEDIUM_IMG                0x3102
 #define LOCAL_EGL_CORE_NATIVE_ENGINE                         0x305B
 #define LOCAL_EGL_COVERAGE_BUFFERS_NV                        0x30E0
 #define LOCAL_EGL_COVERAGE_SAMPLES_NV                        0x30E1
 #define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_DEFAULT_NV         0x3132
 #define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV            0x3133
 #define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NV                 0x3131
 #define LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE          0x3200
-#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE              ((EGLNativeDisplayType)-2)
-#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE                   ((EGLNativeDisplayType)-3)
 #define LOCAL_EGL_DEFAULT_DISPLAY                            ((EGLNativeDisplayType)0)
 #define LOCAL_EGL_DEPTH_ENCODING_NONE_NV                     0
 #define LOCAL_EGL_DEPTH_ENCODING_NONLINEAR_NV                0x30E3
 #define LOCAL_EGL_DEPTH_ENCODING_NV                          0x30E2
 #define LOCAL_EGL_DEPTH_SIZE                                 0x3025
 #define LOCAL_EGL_DISCARD_SAMPLES_ARM                        0x3286
 #define LOCAL_EGL_DISPLAY_SCALING                            10000
 #define LOCAL_EGL_DMA_BUF_PLANE0_FD_EXT                      0x3272
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1807,16 +1807,25 @@ GLContext::ChooseGLFormats(const Surface
             formats.color_rbFormat  = LOCAL_GL_RGBA8;
         } else {
             formats.color_texInternalFormat = IsGLES() ? LOCAL_GL_RGB : LOCAL_GL_RGB8;
             formats.color_texFormat = LOCAL_GL_RGB;
             formats.color_rbFormat  = LOCAL_GL_RGB8;
         }
     }
 
+    if (WorkAroundDriverBugs() &&
+        IsANGLE() &&
+        formats.color_rbFormat == LOCAL_GL_RGBA8)
+    {
+        formats.color_texInternalFormat = LOCAL_GL_BGRA;
+        formats.color_texFormat = LOCAL_GL_BGRA;
+        formats.color_rbFormat = LOCAL_GL_BGRA8_EXT;
+    }
+
     uint32_t msaaLevel = gfxPrefs::MSAALevel();
     GLsizei samples = msaaLevel * msaaLevel;
     samples = std::min(samples, mMaxSamples);
 
     // Bug 778765.
     if (WorkAroundDriverBugs() && samples == 1) {
         samples = 0;
     }
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -50,9 +50,13 @@
 
 #define LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
 #define LOCAL_GL_CONTEXT_LOST                           0x9242
 #define LOCAL_GL_CONTEXT_FLAGS_ARB                      0x2094
 #define LOCAL_GL_CONTEXT_CORE_PROFILE_BIT_ARB           0x00000001
 #define LOCAL_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB  0x00000002
 #define LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB          0x00000004
 
+
+#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE         ((EGLNativeDisplayType)-2)
+#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE              ((EGLNativeDisplayType)-3)
+
 #endif
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -94,16 +94,29 @@ LoadLibraryForEGLOnWindows(const nsAStri
         nsPrintfCString msg("Failed to load %s - Expect EGL initialization to fail",
                             NS_LossyConvertUTF16toASCII(filename).get());
         NS_WARNING(msg.get());
     }
     return lib;
 }
 #endif // XP_WIN
 
+static EGLDisplay
+GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
+{
+    EGLDisplay display = egl.fGetDisplay(displayType);
+    if (display == EGL_NO_DISPLAY)
+        return EGL_NO_DISPLAY;
+
+    if (!egl.fInitialize(display, nullptr, nullptr))
+        return EGL_NO_DISPLAY;
+
+    return display;
+}
+
 bool
 GLLibraryEGL::EnsureInitialized()
 {
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
@@ -180,16 +193,17 @@ GLLibraryEGL::EnsureInitialized()
 
 #endif // !Windows
 
 #define SYMBOL(name) \
 { (PRFuncPtr*) &mSymbols.f##name, { "egl" #name, nullptr } }
 
     GLLibraryLoader::SymLoadStruct earlySymbols[] = {
         SYMBOL(GetDisplay),
+        SYMBOL(Terminate),
         SYMBOL(GetCurrentSurface),
         SYMBOL(GetCurrentContext),
         SYMBOL(MakeCurrent),
         SYMBOL(DestroyContext),
         SYMBOL(CreateContext),
         SYMBOL(DestroySurface),
         SYMBOL(CreateWindowSurface),
         SYMBOL(CreatePbufferSurface),
@@ -227,45 +241,51 @@ GLLibraryEGL::EnsureInitialized()
 
     GLLibraryLoader::LoadSymbols(mEGLLibrary, &optionalSymbols[0]);
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
     MOZ_RELEASE_ASSERT(mSymbols.fQueryStringImplementationANDROID,
                        "Couldn't find eglQueryStringImplementationANDROID");
 #endif
 
-    mEGLDisplay = nullptr;
+    mEGLDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
 
-#ifdef XP_WIN
-    // XXX we have no way of knowing if this is ANGLE, or if we're just using
-    // a native EGL on windows.  We don't really do the latter right now, so
-    // let's assume it is ANGLE, and try our special types.
-
-    // D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
-    // manager, and it's pointless to try to fix it.  We also don't try D3D11
-    // ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
-    if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
-        !gfxPrefs::LayersPreferD3D9())
+    const char* vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
+    if (vendor && (strstr(vendor, "TransGaming") != 0 ||
+                   strstr(vendor, "Google Inc.") != 0))
     {
-        if (gfxPrefs::WebGLANGLEForceD3D11()) {
-            mEGLDisplay = fGetDisplay(LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
-        } else if (gfxPrefs::WebGLANGLETryD3D11()) {
-            mEGLDisplay = fGetDisplay(LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
-        }
+        mIsANGLE = true;
     }
-#endif
+
+    if (mIsANGLE) {
+        EGLDisplay newDisplay = EGL_NO_DISPLAY;
 
-    if (!mEGLDisplay)
-        mEGLDisplay = fGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (!fInitialize(mEGLDisplay, nullptr, nullptr))
-        return false;
+        // D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
+        // manager, and it's pointless to try to fix it.  We also don't try
+        // D3D11 ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
+        if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
+            !gfxPrefs::LayersPreferD3D9())
+        {
+            if (gfxPrefs::WebGLANGLEForceD3D11()) {
+                newDisplay = GetAndInitDisplay(*this,
+                                               LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
+            } else if (gfxPrefs::WebGLANGLETryD3D11()) {
+                newDisplay = GetAndInitDisplay(*this,
+                                               LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
+            }
+        }
 
-    const char *vendor = (const char*) fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
-    if (vendor && (strstr(vendor, "TransGaming") != 0 || strstr(vendor, "Google Inc.") != 0)) {
-        mIsANGLE = true;
+        if (newDisplay != EGL_NO_DISPLAY) {
+            DebugOnly<EGLBoolean> success = fTerminate(mEGLDisplay);
+            MOZ_ASSERT(success == LOCAL_EGL_TRUE);
+
+            mEGLDisplay = newDisplay;
+
+            vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
+        }
     }
 
     InitExtensions();
 
     GLLibraryLoader::PlatformLookupFunction lookupFunction =
             (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
 
     if (IsExtensionSupported(KHR_lock_surface)) {
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -99,17 +99,17 @@ namespace gl {
 #define BEFORE_GL_CALL
 #endif
 #define AFTER_GL_CALL
 #endif
 
 class GLLibraryEGL
 {
 public:
-    GLLibraryEGL() 
+    GLLibraryEGL()
         : mInitialized(false),
           mEGLLibrary(nullptr),
           mIsANGLE(false)
     {
     }
 
     void InitExtensions();
 
@@ -149,16 +149,24 @@ public:
     EGLDisplay fGetDisplay(void* display_id)
     {
         BEFORE_GL_CALL;
         EGLDisplay disp = mSymbols.fGetDisplay(display_id);
         AFTER_GL_CALL;
         return disp;
     }
 
+    EGLBoolean fTerminate(EGLDisplay display)
+    {
+        BEFORE_GL_CALL;
+        EGLBoolean ret = mSymbols.fTerminate(display);
+        AFTER_GL_CALL;
+        return ret;
+    }
+
     EGLSurface fGetCurrentSurface(EGLint id)
     {
         BEFORE_GL_CALL;
         EGLSurface surf = mSymbols.fGetCurrentSurface(id);
         AFTER_GL_CALL;
         return surf;
     }
 
@@ -464,16 +472,18 @@ public:
     bool EnsureInitialized();
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
     struct {
         typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
         pfnGetDisplay fGetDisplay;
+        typedef EGLBoolean (GLAPIENTRY * pfnTerminate)(EGLDisplay dpy);
+        pfnTerminate fTerminate;
         typedef EGLSurface (GLAPIENTRY * pfnGetCurrentSurface)(EGLint);
         pfnGetCurrentSurface fGetCurrentSurface;
         typedef EGLContext (GLAPIENTRY * pfnGetCurrentContext)(void);
         pfnGetCurrentContext fGetCurrentContext;
         typedef EGLBoolean (GLAPIENTRY * pfnMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
         pfnMakeCurrent fMakeCurrent;
         typedef EGLBoolean (GLAPIENTRY * pfnDestroyContext)(EGLDisplay dpy, EGLContext ctx);
         pfnDestroyContext fDestroyContext;
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -284,34 +284,26 @@ ChooseConfig(GLContext* gl, GLLibraryEGL
         return EGL_NO_CONFIG;
     }
 
     // The requests passed to ChooseConfig are treated as minimums. If you ask
     // for 0 bits of alpha, we might still get 8 bits.
     EGLConfig config = EGL_NO_CONFIG;
     for (int i = 0; i < foundConfigs; i++) {
         EGLConfig cur = configs[i];
-        if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
+        if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
+                                        caps.alpha) ||
+            !DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
                                         caps.depth) ||
             !DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_STENCIL_SIZE,
                                         caps.stencil))
         {
             continue;
         }
 
-        // We can't enforce alpha on ANGLE yet because of:
-        // https://code.google.com/p/angleproject/issues/detail?id=764
-        if (!gl->IsANGLE()) {
-            if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
-                                            caps.alpha))
-            {
-                continue;
-            }
-        }
-
         config = cur;
         break;
     }
 
     if (config == EGL_NO_CONFIG) {
         NS_WARNING("No acceptable EGLConfig found.");
         return EGL_NO_CONFIG;
     }
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -43,16 +43,18 @@ SharedSurface_Basic::Create(GLContext* g
     case LOCAL_GL_RGB8:
         if (formats.color_texType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
             format = SurfaceFormat::R5G6B5;
         else
             format = SurfaceFormat::B8G8R8X8;
         break;
     case LOCAL_GL_RGBA:
     case LOCAL_GL_RGBA8:
+    case LOCAL_GL_BGRA:
+    case LOCAL_GL_BGRA8_EXT:
         format = SurfaceFormat::B8G8R8A8;
         break;
     default:
         MOZ_CRASH("Unhandled Tex format.");
     }
 
     ret.reset( new SharedSurface_Basic(gl, size, hasAlpha, format, tex) );
     return Move(ret);
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -188,10 +188,180 @@ Compositor::DrawDiagnosticsInternal(Diag
                  aTransform);
   // bottom
   this->DrawQuad(gfx::Rect(aVisibleRect.x + lWidth, aVisibleRect.y + aVisibleRect.height-lWidth,
                            aVisibleRect.width - 2 * lWidth, lWidth),
                  aClipRect, effects, opacity,
                  aTransform);
 }
 
-} // namespace
-} // namespace
+static float
+WrapTexCoord(float v)
+{
+    // fmodf gives negative results for negative numbers;
+    // that is, fmodf(0.75, 1.0) == 0.75, but
+    // fmodf(-0.75, 1.0) == -0.75.  For the negative case,
+    // the result we need is 0.25, so we add 1.0f.
+    if (v < 0.0f) {
+        return 1.0f + fmodf(v, 1.0f);
+    }
+
+    return fmodf(v, 1.0f);
+}
+
+static void
+SetRects(size_t n,
+         decomposedRectArrayT* aLayerRects,
+         decomposedRectArrayT* aTextureRects,
+         float x0, float y0, float x1, float y1,
+         float tx0, float ty0, float tx1, float ty1,
+         bool flip_y)
+{
+  if (flip_y) {
+    std::swap(ty0, ty1);
+  }
+  (*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
+  (*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
+}
+
+#ifdef DEBUG
+static inline bool
+FuzzyEqual(float a, float b)
+{
+	return fabs(a - b) < 0.0001f;
+}
+static inline bool
+FuzzyLTE(float a, float b)
+{
+	return a <= b + 0.0001f;
+}
+#endif
+
+size_t
+DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+                           const gfx::Rect& aTexCoordRect,
+                           decomposedRectArrayT* aLayerRects,
+                           decomposedRectArrayT* aTextureRects)
+{
+  gfx::Rect texCoordRect = aTexCoordRect;
+
+  // If the texture should be flipped, it will have negative height. Detect that
+  // here and compensate for it. We will flip each rect as we emit it.
+  bool flipped = false;
+  if (texCoordRect.height < 0) {
+    flipped = true;
+    texCoordRect.y += texCoordRect.height;
+    texCoordRect.height = -texCoordRect.height;
+  }
+
+  // Wrap the texture coordinates so they are within [0,1] and cap width/height
+  // at 1. We rely on this below.
+  texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x),
+                                      WrapTexCoord(texCoordRect.y)),
+                           gfx::Size(std::min(texCoordRect.width, 1.0f),
+                                     std::min(texCoordRect.height, 1.0f)));
+
+  NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
+               texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
+               texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
+               texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
+               texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
+               texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
+               "We just wrapped the texture coordinates, didn't we?");
+
+  // Get the top left and bottom right points of the rectangle. Note that
+  // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
+  gfx::Point tl = texCoordRect.TopLeft();
+  gfx::Point br = texCoordRect.BottomRight();
+
+  NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
+               tl.y >= 0.0f && tl.y <= 1.0f &&
+               br.x >= tl.x && br.x <= 2.0f &&
+               br.y >= tl.y && br.y <= 2.0f &&
+               FuzzyLTE(br.x - tl.x, 1.0f) &&
+               FuzzyLTE(br.y - tl.y, 1.0f),
+               "Somehow generated invalid texture coordinates");
+
+  // Then check if we wrap in either the x or y axis.
+  bool xwrap = br.x > 1.0f;
+  bool ywrap = br.y > 1.0f;
+
+  // If xwrap is false, the texture will be sampled from tl.x .. br.x.
+  // If xwrap is true, then it will be split into tl.x .. 1.0, and
+  // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
+  // rectangle is also split appropriately, according to the calculated
+  // xmid/ymid values.
+  if (!xwrap && !ywrap) {
+    SetRects(0, aLayerRects, aTextureRects,
+             aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
+             tl.x, tl.y, br.x, br.y,
+             flipped);
+    return 1;
+  }
+
+  // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
+  // wrap them here as well.
+  br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
+                  ywrap ? WrapTexCoord(br.y) : br.y);
+
+  // If we wrap around along the x axis, we will draw first from
+  // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
+  // The same applies for the Y axis. The midpoints we calculate here are
+  // only valid if we actually wrap around.
+  GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
+  GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
+
+  NS_ASSERTION(!xwrap ||
+               (xmid > aRect.x &&
+                xmid < aRect.XMost() &&
+                FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
+               "xmid should be within [x,XMost()] and the wrapped rect should have the same width");
+  NS_ASSERTION(!ywrap ||
+               (ymid > aRect.y &&
+                ymid < aRect.YMost() &&
+                FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)),
+               "ymid should be within [y,YMost()] and the wrapped rect should have the same height");
+
+  if (!xwrap && ywrap) {
+    SetRects(0, aLayerRects, aTextureRects,
+             aRect.x, aRect.y, aRect.XMost(), ymid,
+             tl.x, tl.y, br.x, 1.0f,
+             flipped);
+    SetRects(1, aLayerRects, aTextureRects,
+             aRect.x, ymid, aRect.XMost(), aRect.YMost(),
+             tl.x, 0.0f, br.x, br.y,
+             flipped);
+    return 2;
+  }
+
+  if (xwrap && !ywrap) {
+    SetRects(0, aLayerRects, aTextureRects,
+             aRect.x, aRect.y, xmid, aRect.YMost(),
+             tl.x, tl.y, 1.0f, br.y,
+             flipped);
+    SetRects(1, aLayerRects, aTextureRects,
+             xmid, aRect.y, aRect.XMost(), aRect.YMost(),
+             0.0f, tl.y, br.x, br.y,
+             flipped);
+    return 2;
+  }
+
+  SetRects(0, aLayerRects, aTextureRects,
+           aRect.x, aRect.y, xmid, ymid,
+           tl.x, tl.y, 1.0f, 1.0f,
+           flipped);
+  SetRects(1, aLayerRects, aTextureRects,
+           xmid, aRect.y, aRect.XMost(), ymid,
+           0.0f, tl.y, br.x, 1.0f,
+           flipped);
+  SetRects(2, aLayerRects, aTextureRects,
+           aRect.x, ymid, xmid, aRect.YMost(),
+           tl.x, 0.0f, 1.0f, br.y,
+           flipped);
+  SetRects(3, aLayerRects, aTextureRects,
+           xmid, ymid, aRect.XMost(), aRect.YMost(),
+           0.0f, 0.0f, br.x, br.y,
+           flipped);
+  return 4;
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -535,12 +535,19 @@ protected:
   RefPtr<gfx::DrawTarget> mTarget;
   nsIntRect mTargetBounds;
 
 private:
   static LayersBackend sBackend;
 
 };
 
+// Returns the number of rects. (Up to 4)
+typedef gfx::Rect decomposedRectArrayT[4];
+size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+                                  const gfx::Rect& aTexCoordRect,
+                                  decomposedRectArrayT* aLayerRects,
+                                  decomposedRectArrayT* aTextureRects);
+
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_COMPOSITOR_H */
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -205,17 +205,16 @@ CompositorD3D11::Initialize()
     rastDesc.ScissorEnable = TRUE;
 
     hr = mDevice->CreateRasterizerState(&rastDesc, byRef(mAttachments->mRasterizerState));
     if (FAILED(hr)) {
       return false;
     }
 
     CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
-    samplerDesc.AddressU = samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
     hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mLinearSamplerState));
     if (FAILED(hr)) {
       return false;
     }
 
     samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
     hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mPointSamplerState));
     if (FAILED(hr)) {
@@ -569,17 +568,16 @@ CompositorD3D11::DrawQuad(const gfx::Rec
                           gfx::Float aOpacity,
                           const gfx::Matrix4x4& aTransform)
 {
   MOZ_ASSERT(mCurrentRT, "No render target");
   memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
   IntPoint origin = mCurrentRT->GetOrigin();
   mVSConstants.renderTargetOffset[0] = origin.x;
   mVSConstants.renderTargetOffset[1] = origin.y;
-  mVSConstants.layerQuad = aRect;
 
   mPSConstants.layerOpacity[0] = aOpacity;
 
   bool restoreBlendMode = false;
 
   MaskType maskType = MaskType::MaskNone;
 
   if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
@@ -622,16 +620,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
   scissor.left = aClipRect.x;
   scissor.right = aClipRect.XMost();
   scissor.top = aClipRect.y;
   scissor.bottom = aClipRect.YMost();
   mContext->RSSetScissorRects(1, &scissor);
   mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
   mContext->VSSetShader(mAttachments->mVSQuadShader[maskType], nullptr, 0);
 
+  const Rect* pTexCoordRect = nullptr;
 
   switch (aEffectChain.mPrimaryEffect->mType) {
   case EffectTypes::SOLID_COLOR: {
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, SurfaceFormat::UNKNOWN);
 
       Color color =
         static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
       mPSConstants.layerColor[0] = color.r * color.a * aOpacity;
@@ -641,17 +640,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
     }
     break;
   case EffectTypes::RGB:
   case EffectTypes::RENDER_TARGET:
     {
       TexturedEffect* texturedEffect =
         static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
 
-      mVSConstants.textureCoords = texturedEffect->mTextureCoords;
+      pTexCoordRect = &texturedEffect->mTextureCoords;
 
       TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();
 
       if (!source) {
         NS_WARNING("Missing texture source!");
         return;
       }
 
@@ -677,17 +676,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
     }
     break;
   case EffectTypes::YCBCR: {
       EffectYCbCr* ycbcrEffect =
         static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
 
       SetSamplerForFilter(Filter::LINEAR);
 
-      mVSConstants.textureCoords = ycbcrEffect->mTextureCoords;
+      pTexCoordRect = &ycbcrEffect->mTextureCoords;
 
       const int Y = 0, Cb = 1, Cr = 2;
       TextureSource* source = ycbcrEffect->mTexture;
 
       if (!source) {
         NS_WARNING("No texture to composite");
         return;
       }
@@ -744,17 +743,18 @@ CompositorD3D11::DrawQuad(const gfx::Rec
         NS_WARNING("Missing texture source(s)!");
         return;
       }
 
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, effectComponentAlpha->mOnWhite->GetFormat());
 
       SetSamplerForFilter(effectComponentAlpha->mFilter);
 
-      mVSConstants.textureCoords = effectComponentAlpha->mTextureCoords;
+      pTexCoordRect = &effectComponentAlpha->mTextureCoords;
+
       RefPtr<ID3D11ShaderResourceView> views[2];
 
       HRESULT hr;
 
       hr = mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
       if (Failed(hr)) {
         return;
       }
@@ -769,22 +769,44 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
       restoreBlendMode = true;
     }
     break;
   default:
     NS_WARNING("Unknown shader type");
     return;
   }
-  if (!UpdateConstantBuffers()) {
-    NS_WARNING("Failed to update shader constant buffers");
-    return;
+
+  if (pTexCoordRect) {
+    Rect layerRects[4];
+    Rect textureRects[4];
+    size_t rects = DecomposeIntoNoRepeatRects(aRect,
+                                              *pTexCoordRect,
+                                              &layerRects,
+                                              &textureRects);
+    for (size_t i = 0; i < rects; i++) {
+      mVSConstants.layerQuad = layerRects[i];
+      mVSConstants.textureCoords = textureRects[i];
+
+      if (!UpdateConstantBuffers()) {
+        NS_WARNING("Failed to update shader constant buffers");
+        break;
+      }
+      mContext->Draw(4, 0);
+    }
+  } else {
+    mVSConstants.layerQuad = aRect;
+
+    if (!UpdateConstantBuffers()) {
+      NS_WARNING("Failed to update shader constant buffers");
+    } else {
+      mContext->Draw(4, 0);
+    }
   }
 
-  mContext->Draw(4, 0);
   if (restoreBlendMode) {
     mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
   }
 }
 
 void
 CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
                             const Rect* aClipRectIn,
@@ -855,17 +877,17 @@ CompositorD3D11::EndFrame()
   EnsureSize();
   if (oldSize == mSize) {
     mSwapChain->Present(0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
     mDisableSequenceForNextFrame = false;
     if (mTarget) {
       PaintToTarget();
     }
   }
-  
+
   mCurrentRT = nullptr;
 }
 
 void
 CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
 {
   D3D11_VIEWPORT viewport;
   viewport.MaxDepth = 1.0f;
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -378,199 +378,34 @@ CompositorOGL::Initialize()
       msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE");
     console->LogStringMessage(msg.get());
   }
 
   reporter.SetSuccessful();
   return true;
 }
 
-static GLfloat
-WrapTexCoord(GLfloat v)
-{
-    // fmodf gives negative results for negative numbers;
-    // that is, fmodf(0.75, 1.0) == 0.75, but
-    // fmodf(-0.75, 1.0) == -0.75.  For the negative case,
-    // the result we need is 0.25, so we add 1.0f.
-    if (v < 0.0f) {
-        return 1.0f + fmodf(v, 1.0f);
-    }
-
-    return fmodf(v, 1.0f);
-}
-
-static void
-SetRects(int n,
-         Rect* aLayerRects,
-         Rect* aTextureRects,
-         GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
-         GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1,
-         bool flip_y /* = false */)
-{
-  if (flip_y) {
-    std::swap(ty0, ty1);
-  }
-  aLayerRects[n] = Rect(x0, y0, x1 - x0, y1 - y0);
-  aTextureRects[n] = Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
-}
-
-#ifdef DEBUG
-static inline bool
-FuzzyEqual(float a, float b)
-{
-  return fabs(a - b) < 0.0001f;
-}
-#endif
-
-static int
-DecomposeIntoNoRepeatRects(const Rect& aRect,
-                           const Rect& aTexCoordRect,
-                           Rect* aLayerRects,
-                           Rect* aTextureRects)
-{
-  Rect texCoordRect = aTexCoordRect;
-
-  // If the texture should be flipped, it will have negative height. Detect that
-  // here and compensate for it. We will flip each rect as we emit it.
-  bool flipped = false;
-  if (texCoordRect.height < 0) {
-    flipped = true;
-    texCoordRect.y += texCoordRect.height;
-    texCoordRect.height = -texCoordRect.height;
-  }
-
-  // Wrap the texture coordinates so they are within [0,1] and cap width/height
-  // at 1. We rely on this below.
-  texCoordRect = Rect(Point(WrapTexCoord(texCoordRect.x),
-                            WrapTexCoord(texCoordRect.y)),
-                      Size(std::min(texCoordRect.width, 1.0f),
-                           std::min(texCoordRect.height, 1.0f)));
-
-  NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
-               texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
-               texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
-               texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
-               texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
-               texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
-               "We just wrapped the texture coordinates, didn't we?");
-
-  // Get the top left and bottom right points of the rectangle. Note that
-  // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
-  Point tl = texCoordRect.TopLeft();
-  Point br = texCoordRect.BottomRight();
-
-  NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
-               tl.y >= 0.0f && tl.y <= 1.0f &&
-               br.x >= tl.x && br.x <= 2.0f &&
-               br.y >= tl.y && br.y <= 2.0f &&
-               br.x - tl.x <= 1.0f &&
-               br.y - tl.y <= 1.0f,
-               "Somehow generated invalid texture coordinates");
-
-  // Then check if we wrap in either the x or y axis.
-  bool xwrap = br.x > 1.0f;
-  bool ywrap = br.y > 1.0f;
-
-  // If xwrap is false, the texture will be sampled from tl.x .. br.x.
-  // If xwrap is true, then it will be split into tl.x .. 1.0, and
-  // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
-  // rectangle is also split appropriately, according to the calculated
-  // xmid/ymid values.
-  if (!xwrap && !ywrap) {
-    SetRects(0, aLayerRects, aTextureRects,
-             aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
-             tl.x, tl.y, br.x, br.y,
-             flipped);
-    return 1;
-  }
-
-  // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
-  // wrap them here as well.
-  br = Point(xwrap ? WrapTexCoord(br.x) : br.x,
-             ywrap ? WrapTexCoord(br.y) : br.y);
-
-  // If we wrap around along the x axis, we will draw first from
-  // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
-  // The same applies for the Y axis. The midpoints we calculate here are
-  // only valid if we actually wrap around.
-  GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
-  GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
-
-  NS_ASSERTION(!xwrap ||
-               (xmid > aRect.x &&
-                xmid < aRect.XMost() &&
-                FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
-               "xmid should be within [x,XMost()] and the wrapped rect should have the same width");
-  NS_ASSERTION(!ywrap ||
-               (ymid > aRect.y &&
-                ymid < aRect.YMost() &&
-                FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)),
-               "ymid should be within [y,YMost()] and the wrapped rect should have the same height");
-
-  if (!xwrap && ywrap) {
-    SetRects(0, aLayerRects, aTextureRects,
-             aRect.x, aRect.y, aRect.XMost(), ymid,
-             tl.x, tl.y, br.x, 1.0f,
-             flipped);
-    SetRects(1, aLayerRects, aTextureRects,
-             aRect.x, ymid, aRect.XMost(), aRect.YMost(),
-             tl.x, 0.0f, br.x, br.y,
-             flipped);
-    return 2;
-  }
-
-  if (xwrap && !ywrap) {
-    SetRects(0, aLayerRects, aTextureRects,
-             aRect.x, aRect.y, xmid, aRect.YMost(),
-             tl.x, tl.y, 1.0f, br.y,
-             flipped);
-    SetRects(1, aLayerRects, aTextureRects,
-             xmid, aRect.y, aRect.XMost(), aRect.YMost(),
-             0.0f, tl.y, br.x, br.y,
-             flipped);
-    return 2;
-  }
-
-  SetRects(0, aLayerRects, aTextureRects,
-           aRect.x, aRect.y, xmid, ymid,
-           tl.x, tl.y, 1.0f, 1.0f,
-           flipped);
-  SetRects(1, aLayerRects, aTextureRects,
-           xmid, aRect.y, aRect.XMost(), ymid,
-           0.0f, tl.y, br.x, 1.0f,
-           flipped);
-  SetRects(2, aLayerRects, aTextureRects,
-           aRect.x, ymid, xmid, aRect.YMost(),
-           tl.x, 0.0f, 1.0f, br.y,
-           flipped);
-  SetRects(3, aLayerRects, aTextureRects,
-           xmid, ymid, aRect.XMost(), aRect.YMost(),
-           0.0f, 0.0f, br.x, br.y,
-           flipped);
-  return 4;
-}
-
 // |aRect| is the rectangle we want to draw to. We will draw it with
 // up to 4 draw commands if necessary to avoid wrapping.
 // |aTexCoordRect| is the rectangle from the texture that we want to
 // draw using the given program.
 // |aTexture| is the texture we are drawing. Its actual size can be
 // larger than the rectangle given by |texCoordRect|.
 void
 CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
                                               const Rect& aRect,
                                               const Rect& aTexCoordRect,
                                               TextureSource *aTexture)
 {
   Rect layerRects[4];
   Rect textureRects[4];
-  int rects = DecomposeIntoNoRepeatRects(aRect,
-                                         aTexCoordRect,
-                                         layerRects,
-                                         textureRects);
+  size_t rects = DecomposeIntoNoRepeatRects(aRect,
+                                            aTexCoordRect,
+                                            &layerRects,
+                                            &textureRects);
   BindAndDrawQuads(aProg, rects, layerRects, textureRects);
 }
 
 void
 CompositorOGL::PrepareViewport(const gfx::IntSize& aSize)
 {
   // Set the viewport correctly.
   mGLContext->fViewport(0, 0, aSize.width, aSize.height);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -387,17 +387,19 @@ gfxWindowsPlatform::UpdateRenderMode()
 
     // Do not ever try if d2d is explicitly disabled,
     // or if we're not using DWrite fonts.
     if (d2dDisabled || mUsingGDIFonts) {
         tryD2D = false;
     }
 
     ID3D11Device *device = GetD3D11Device();
-    if (isVistaOrHigher && !safeMode && tryD2D && device &&
+    if (isVistaOrHigher && !safeMode && tryD2D &&
+        device &&
+        device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 &&
         DoesD3D11DeviceSupportResourceSharing(device)) {
 
         VerifyD2DDevice(d2dForceEnabled);
         if (mD2DDevice) {
             mRenderMode = RENDER_DIRECT2D;
             mUseDirectWrite = true;
         }
     } else {
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -346,17 +346,21 @@ struct ClassExtension
      */
     JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
 
     /*
      * Optional hook called when an object is moved by a compacting GC.
      *
      * There may exist weak pointers to an object that are not traced through
      * when the normal trace APIs are used, for example objects in the wrapper
-     * cache.  This hook allows these pointers to be updated.
+     * cache. This hook allows these pointers to be updated.
+     *
+     * Note that this hook can be called before JS_NewObject() returns if a GC
+     * is triggered during construction of the object. This can happen for
+     * global objects for example.
      */
     JSObjectMovedOp objectMovedOp;
 };
 
 #define JS_NULL_CLASS_SPEC  {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr}
 #define JS_NULL_CLASS_EXT   {nullptr,nullptr,nullptr,false,nullptr,nullptr}
 
 struct ObjectOps
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -59,16 +59,17 @@ var ignoreClasses = {
     "JSStringFinalizer" : true,
     "SprintfState" : true,
     "SprintfStateStr" : true,
     "JSLocaleCallbacks" : true,
     "JSC::ExecutableAllocator" : true,
     "PRIOMethods": true,
     "XPCOMFunctions" : true, // I'm a little unsure of this one
     "_MD_IOVector" : true,
+    "malloc_table_t": true, // replace_malloc
 };
 
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
     "js::Class.trace" : true,
     "js::Class.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -155,16 +155,17 @@ BytecodeEmitter::init()
 }
 
 static ptrdiff_t
 EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
 {
     ptrdiff_t offset = bce->code().length();
 
     // Start it off moderately large to avoid repeated resizings early on.
+    // ~98% of cases fit within 1024 bytes.
     if (bce->code().capacity() == 0 && !bce->code().reserve(1024))
         return -1;
 
     jsbytecode dummy = 0;
     if (!bce->code().appendN(dummy, delta)) {
         js_ReportOutOfMemory(cx);
         return -1;
     }
@@ -2220,18 +2221,16 @@ EmitFinishIteratorResult(ExclusiveContex
         return UINT_MAX;
 
     if (!EmitIndex32(cx, JSOP_INITPROP, value_id, bce))
         return false;
     if (Emit1(cx, bce, done ? JSOP_TRUE : JSOP_FALSE) < 0)
         return false;
     if (!EmitIndex32(cx, JSOP_INITPROP, done_id, bce))
         return false;
-    if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
-        return false;
     return true;
 }
 
 static bool
 EmitNameOp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
 {
     if (!BindNameToSlot(cx, bce, pn))
         return false;
@@ -3382,18 +3381,16 @@ EmitDestructuringOpsArrayHelper(Exclusiv
             SET_UINT24(pc, 0);
 
             if (!EmitNumberOp(cx, 0, bce))                             // ... OBJ? ITER ARRAY INDEX
                 return false;
             if (!EmitSpread(cx, bce))                                  // ... OBJ? ARRAY INDEX
                 return false;
             if (Emit1(cx, bce, JSOP_POP) < 0)                          // ... OBJ? ARRAY
                 return false;
-            if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
-                return false;
             needToPopIterator = false;
         } else {
             if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... OBJ? ITER ITER
                 return false;
             if (!EmitIteratorNext(cx, bce, pattern))                   // ... OBJ? ITER RESULT
                 return false;
             if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... OBJ? ITER RESULT RESULT
                 return false;
@@ -6416,19 +6413,16 @@ EmitObject(ExclusiveContext *cx, Bytecod
                     obj = nullptr;
             }
 
             if (!EmitIndex32(cx, op, index, bce))
                 return false;
         }
     }
 
-    if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
-        return false;
-
     if (obj) {
         /*
          * The object survived and has a predictable shape: update the original
          * bytecode.
          */
         ObjectBox *objbox = bce->parser->newObjectBox(obj);
         if (!objbox)
             return false;
@@ -6461,18 +6455,17 @@ EmitArrayComp(ExclusiveContext *cx, Byte
      */
     MOZ_ASSERT(bce->stackDepth > 0);
     uint32_t saveDepth = bce->arrayCompDepth;
     bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
     if (!EmitTree(cx, bce, pn->pn_head))
         return false;
     bce->arrayCompDepth = saveDepth;
 
-    /* Emit the usual op needed for decompilation. */
-    return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
+    return true;
 }
 
 /**
  * EmitSpread expects the current index (I) of the array, the array itself and the iterator to be
  * on the stack in that order (iterator on the bottom).
  * It will pop the iterator and I, then iterate over the iterator by calling |.next()|
  * and put the results into the I-th element of array with incrementing I, then
  * push the result I (it will be original I + iteration count).
@@ -6550,19 +6543,17 @@ EmitArray(ExclusiveContext *cx, Bytecode
             SET_UINT24(bce->code(off), atomIndex);
         }
     }
     MOZ_ASSERT(atomIndex == count);
     if (afterSpread) {
         if (Emit1(cx, bce, JSOP_POP) < 0)                                // ARRAY
             return false;
     }
-
-    /* Emit an op to finish the array and aid in decompilation. */
-    return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
+    return true;
 }
 
 static bool
 EmitUnary(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
         return false;
     /* Unary op, including unary +/-. */
@@ -7077,17 +7068,18 @@ frontend::EmitTree(ExclusiveContext *cx,
 
     return ok;
 }
 
 static int
 AllocSrcNote(ExclusiveContext *cx, SrcNotesVector &notes)
 {
     // Start it off moderately large to avoid repeated resizings early on.
-    if (notes.capacity() == 0 && !notes.reserve(1024))
+    // ~99% of cases fit within 256 bytes.
+    if (notes.capacity() == 0 && !notes.reserve(256))
         return -1;
 
     jssrcnote dummy = 0;
     if (!notes.append(dummy)) {
         js_ReportOutOfMemory(cx);
         return -1;
     }
     return notes.length() - 1;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -21,16 +21,18 @@
 
 /* Perform validation of incremental marking in debug builds but not on B2G. */
 #if defined(DEBUG) && !defined(MOZ_B2G)
 #define JS_GC_MARKING_VALIDATION
 #endif
 
 namespace js {
 
+class AutoLockGC;
+
 namespace gc {
 
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 struct FinalizePhase;
 class MarkingValidator;
 struct AutoPrepareForTracing;
 class AutoTraceSession;
@@ -59,16 +61,34 @@ class ChunkPool
         inline void popFront();
         inline void removeAndPopFront();
       private:
         ChunkPool &pool;
         Chunk **chunkp;
     };
 };
 
+// Performs extra allocation off the main thread so that when memory is
+// required on the main thread it will already be available and waiting.
+class BackgroundAllocTask : public GCParallelTask
+{
+    // Guarded by the GC lock.
+    JSRuntime *runtime;
+    ChunkPool &chunkPool_;
+
+    const bool enabled_;
+
+  public:
+    BackgroundAllocTask(JSRuntime *rt, ChunkPool &pool);
+    bool enabled() const { return enabled_; }
+
+  protected:
+    virtual void run() MOZ_OVERRIDE;
+};
+
 /*
  * Encapsulates all of the GC tunables. These are effectively constant and
  * should only be modified by setParameter.
  */
 class GCSchedulingTunables
 {
     /*
      * Soft limit on the number of bytes we are allowed to allocate in the GC
@@ -252,17 +272,17 @@ class GCRuntime
 
     bool triggerGC(JS::gcreason::Reason reason);
     bool triggerZoneGC(Zone *zone, JS::gcreason::Reason reason);
     bool maybeGC(Zone *zone);
     void maybePeriodicFullGC();
     void minorGC(JS::gcreason::Reason reason);
     void minorGC(JSContext *cx, JS::gcreason::Reason reason);
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) { minorGC(reason); }
-    void gcIfNeeded(JSContext *cx);
+    bool gcIfNeeded(JSContext *cx = nullptr);
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
     void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void gcDebugSlice(bool limit, int64_t objCount);
 
     void runDebugGC();
     inline void poke();
 
@@ -296,18 +316,24 @@ class GCRuntime
 
     size_t maxMallocBytesAllocated() { return maxMallocBytes; }
 
   public:
     // Internal public interface
     js::gc::State state() { return incrementalState; }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
     void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
-    void waitBackgroundSweepOrAllocEnd() { helperState.waitBackgroundSweepOrAllocEnd(); }
-    void startBackgroundAllocationIfIdle() { helperState.startBackgroundAllocationIfIdle(); }
+    void waitBackgroundSweepOrAllocEnd() {
+        helperState.waitBackgroundSweepEnd();
+        allocTask.cancel(GCParallelTask::CancelAndWait);
+    }
+
+#ifdef JSGC_GENERATIONAL
+    void requestMinorGC(JS::gcreason::Reason reason);
+#endif
 
 #ifdef DEBUG
 
     bool onBackgroundThread() { return helperState.onBackgroundThread(); }
 
     bool currentThreadOwnsGCLock() {
         return lockOwner == PR_GetCurrentThread();
     }
@@ -409,17 +435,17 @@ class GCRuntime
     bool isIncrementalGc() { return isIncremental; }
     bool isFullGc() { return isFull; }
 
     bool shouldCleanUpEverything() { return cleanUpEverything; }
 
     bool areGrayBitsValid() { return grayBitsValid; }
     void setGrayBitsInvalid() { grayBitsValid = false; }
 
-    bool isGcNeeded() { return isNeeded; }
+    bool isGcNeeded() { return minorGCRequested || majorGCRequested; }
 
     double computeHeapGrowthFactor(size_t lastBytes);
     size_t computeTriggerBytes(double growthFactor, size_t lastBytes);
 
     JSGCMode gcMode() const { return mode; }
     void setGCMode(JSGCMode m) {
         mode = m;
         marker.setGCMode(mode);
@@ -446,17 +472,18 @@ class GCRuntime
 
     template <AllowGC allowGC>
     static void *refillFreeListFromAnyThread(ThreadSafeContext *cx, AllocKind thingKind);
     static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
 
   private:
     // For ArenaLists::allocateFromArena()
     friend class ArenaLists;
-    Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
+    Chunk *pickChunk(const AutoLockGC &lock, Zone *zone,
+                     AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     template <AllowGC allowGC>
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
     static void *refillFreeListPJS(ForkJoinContext *cx, AllocKind thingKind);
 
     /*
@@ -464,20 +491,23 @@ class GCRuntime
      * Must be called either during the GC or with the GC lock taken.
      */
     Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
     void expireAndFreeChunkPool(bool releaseAll);
     void freeChunkList(Chunk *chunkListHead);
     void prepareToFreeChunk(ChunkInfo &info);
     void releaseChunk(Chunk *chunk);
 
-    inline bool wantBackgroundAllocation() const;
+    friend class BackgroundAllocTask;
+    friend class AutoMaybeStartBackgroundAllocation;
+    inline bool wantBackgroundAllocation(const AutoLockGC &lock) const;
+    void startBackgroundAllocTaskIfIdle();
 
     bool initZeal();
-    void requestInterrupt(JS::gcreason::Reason reason);
+    void requestMajorGC(JS::gcreason::Reason reason);
     void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     gcstats::ZoneGCStats scanZonesBeforeGC();
     void budgetIncrementalGC(int64_t *budget);
     void resetIncrementalGC(const char *reason);
     void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason);
@@ -600,22 +630,23 @@ class GCRuntime
     bool                  cleanUpEverything;
 
     /*
      * The gray bits can become invalid if UnmarkGray overflows the stack. A
      * full GC will reset this bit, since it fills in all the gray bits.
      */
     bool                  grayBitsValid;
 
-    /*
-     * These flags must be kept separate so that a thread requesting a
-     * compartment GC doesn't cancel another thread's concurrent request for a
-     * full GC.
-     */
-    volatile uintptr_t    isNeeded;
+    volatile uintptr_t    majorGCRequested;
+    JS::gcreason::Reason  majorGCTriggerReason;
+
+#ifdef JSGC_GENERATIONAL
+    bool                  minorGCRequested;
+    JS::gcreason::Reason  minorGCTriggerReason;
+#endif
 
     /* Incremented at the start of every major GC. */
     uint64_t              majorGCNumber;
 
     /* The major GC number at which to release observed type information. */
     uint64_t              jitReleaseNumber;
 
     /* Incremented on every GC slice. */
@@ -628,19 +659,16 @@ class GCRuntime
     bool                  isIncremental;
 
     /* Whether all compartments are being collected in first GC slice. */
     bool                  isFull;
 
     /* The invocation kind of the current GC, taken from the first slice. */
     JSGCInvocationKind    invocationKind;
 
-    /* The reason that an interrupt-triggered GC should be called. */
-    JS::gcreason::Reason  triggerReason;
-
     /*
      * If this is 0, all cross-compartment proxies must be registered in the
      * wrapper map. This checking must be disabled temporarily while creating
      * new wrappers. When non-zero, this records the recursion depth of wrapper
      * creation.
      */
     mozilla::DebugOnly<uintptr_t>  disableStrictProxyCheckingCount;
 
@@ -835,16 +863,17 @@ class GCRuntime
 #ifdef DEBUG
     size_t                noGCOrAllocationCheck;
 #endif
 
     /* Synchronize GC heap access between main thread and GCHelperState. */
     PRLock                *lock;
     mozilla::DebugOnly<PRThread *>   lockOwner;
 
+    BackgroundAllocTask allocTask;
     GCHelperState helperState;
 
     /*
      * During incremental sweeping, this field temporarily holds the arenas of
      * the current AllocKind being swept in order of increasing free space.
      */
     SortedArenaList incrementalSweepList;
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -761,17 +761,18 @@ Statistics::beginSlice(const ZoneGCStats
 {
     this->zoneStats = zoneStats;
 
     bool first = runtime->gc.state() == gc::NO_INCREMENTAL;
     if (first)
         beginGC();
 
     SliceData data(reason, PRMJ_Now(), GetPageFaultCount());
-    (void) slices.append(data); /* Ignore any OOMs here. */
+    if (!slices.append(data))
+        CrashAtUnhandlableOOM("Failed to allocate statistics slice.");
 
     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
         (*cb)(JS_TELEMETRY_GC_REASON, reason);
 
     // Slice callbacks should only fire for the outermost level
     if (++gcDepth == 1) {
         bool wasFullGC = zoneStats.isCollectingAllZones();
         if (sliceCallback)
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -298,17 +298,17 @@ StoreBuffer::markAll(JSTracer *trc)
     bufferRelocCell.mark(this, trc);
     bufferGeneric.mark(this, trc);
 }
 
 void
 StoreBuffer::setAboutToOverflow()
 {
     aboutToOverflow_ = true;
-    runtime_->requestInterrupt(JSRuntime::RequestInterruptMainThread);
+    runtime_->gc.requestMinorGC(JS::gcreason::FULL_STORE_BUFFER);
 }
 
 bool
 StoreBuffer::inParallelSection() const
 {
     return InParallelSection();
 }
 
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1849,22 +1849,16 @@ BaselineCompiler::emit_JSOP_INITPROP()
     frame.push(R0);
     frame.syncStack(0);
 
     // Call IC.
     ICSetProp_Fallback::Compiler compiler(cx);
     return emitOpIC(compiler.getStub(&stubSpace_));
 }
 
-bool
-BaselineCompiler::emit_JSOP_ENDINIT()
-{
-    return true;
-}
-
 typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &);
 static const VMFunction NewbornArrayPushInfo = FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush);
 
 bool
 BaselineCompiler::emit_JSOP_ARRAYPUSH()
 {
     // Keep value in R0, object in R1.
     frame.popRegsAndSync(2);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -99,17 +99,16 @@ namespace jit {
     _(JSOP_INITELEM)           \
     _(JSOP_INITELEM_GETTER)    \
     _(JSOP_INITELEM_SETTER)    \
     _(JSOP_INITELEM_INC)       \
     _(JSOP_MUTATEPROTO)        \
     _(JSOP_INITPROP)           \
     _(JSOP_INITPROP_GETTER)    \
     _(JSOP_INITPROP_SETTER)    \
-    _(JSOP_ENDINIT)            \
     _(JSOP_ARRAYPUSH)          \
     _(JSOP_GETELEM)            \
     _(JSOP_SETELEM)            \
     _(JSOP_CALLELEM)           \
     _(JSOP_DELELEM)            \
     _(JSOP_IN)                 \
     _(JSOP_GETGNAME)           \
     _(JSOP_BINDGNAME)          \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1634,19 +1634,16 @@ IonBuilder::inspectOpcode(JSOp op)
         PropertyName *name = info().getAtom(pc)->asPropertyName();
         return jsop_initprop_getter_setter(name);
       }
 
       case JSOP_INITELEM_GETTER:
       case JSOP_INITELEM_SETTER:
         return jsop_initelem_getter_setter();
 
-      case JSOP_ENDINIT:
-        return true;
-
       case JSOP_FUNCALL:
         return jsop_funcall(GET_ARGC(pc));
 
       case JSOP_FUNAPPLY:
         return jsop_funapply(GET_ARGC(pc));
 
       case JSOP_CALL:
       case JSOP_NEW:
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1064,28 +1064,39 @@ GCRuntime::moveChunkToFreePool(Chunk *ch
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
     chunkSet.remove(chunk);
     emptyChunks.put(chunk);
 }
 
 inline bool
-GCRuntime::wantBackgroundAllocation() const
-{
-    /*
-     * To minimize memory waste we do not want to run the background chunk
-     * allocation if we have empty chunks or when the runtime needs just few
-     * of them.
-     */
-    return helperState.canBackgroundAllocate() &&
+GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
+{
+    // To minimize memory waste, we do not want to run the background chunk
+    // allocation if we already have some empty chunks or when the runtime has
+    // a small heap size (and therefore likely has a small growth rate).
+    return allocTask.enabled() &&
            emptyChunks.count() < tunables.minEmptyChunkCount() &&
            chunkSet.count() >= 4;
 }
 
+void
+GCRuntime::startBackgroundAllocTaskIfIdle()
+{
+    AutoLockHelperThreadState helperLock;
+    if (allocTask.isRunning())
+        return;
+
+    // Join the previous invocation of the task. This will return immediately
+    // if the thread has never been started.
+    allocTask.joinWithLockHeld();
+    allocTask.startWithLockHeld();
+}
+
 class js::gc::AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime *runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoMaybeStartBackgroundAllocation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
@@ -1094,27 +1105,24 @@ class js::gc::AutoMaybeStartBackgroundAl
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     void tryToStartBackgroundAllocation(JSRuntime *rt) {
         runtime = rt;
     }
 
     ~AutoMaybeStartBackgroundAllocation() {
-        if (runtime && !runtime->currentThreadOwnsInterruptLock()) {
-            AutoLockHelperThreadState helperLock;
-            AutoLockGC lock(runtime);
-            runtime->gc.startBackgroundAllocationIfIdle();
-        }
+        if (runtime && !runtime->currentThreadOwnsInterruptLock())
+            runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
-/* The caller must hold the GC lock. */
 Chunk *
-GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
+GCRuntime::pickChunk(const AutoLockGC &lock, Zone *zone,
+                     AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
     Chunk **listHeadp = getAvailableChunkList(zone);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
     chunk = emptyChunks.get(rt);
     if (!chunk) {
@@ -1122,17 +1130,17 @@ GCRuntime::pickChunk(Zone *zone, AutoMay
         if (!chunk)
             return nullptr;
         MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
 
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(!chunkSet.has(chunk));
 
-    if (wantBackgroundAllocation())
+    if (wantBackgroundAllocation(lock))
         maybeStartBackgroundAllocation.tryToStartBackgroundAllocation(rt);
 
     chunkAllocationSinceLastGC = true;
 
     /*
      * FIXME bug 583732 - chunk is newly allocated and cannot be present in
      * the table so using ordinary lookupForAdd is suboptimal here.
      */
@@ -1168,23 +1176,27 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     verifyPostData(nullptr),
     chunkAllocationSinceLastGC(false),
     nextFullGCTime(0),
     lastGCTime(0),
     mode(JSGC_MODE_INCREMENTAL),
     decommitThreshold(32 * 1024 * 1024),
     cleanUpEverything(false),
     grayBitsValid(false),
-    isNeeded(0),
+    majorGCRequested(0),
+    majorGCTriggerReason(JS::gcreason::NO_REASON),
+#ifdef JSGC_GENERATIONAL
+    minorGCRequested(false),
+    minorGCTriggerReason(JS::gcreason::NO_REASON),
+#endif
     majorGCNumber(0),
     jitReleaseNumber(0),
     number(0),
     startNumber(0),
     isFull(false),
-    triggerReason(JS::gcreason::NO_REASON),
 #ifdef DEBUG
     disableStrictProxyCheckingCount(0),
 #endif
     incrementalState(gc::NO_INCREMENTAL),
     lastMarkSlice(false),
     sweepOnBackgroundThread(false),
     foundBlackGrayEdges(false),
     sweepingZones(nullptr),
@@ -1224,16 +1236,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     inUnsafeRegion(0),
 #endif
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     lock(nullptr),
     lockOwner(nullptr),
+    allocTask(rt, emptyChunks),
     helperState(rt)
 {
     setGCMode(JSGC_MODE_GLOBAL);
 }
 
 #ifdef JS_GC_ZEAL
 
 const char *gc::ZealModeHelpText =
@@ -1965,17 +1978,17 @@ ArenaLists::allocateFromArena(JS::Zone *
         return allocateFromArenaInner<HasFreeThings>(zone, aheader, thingKind);
     }
 
     // Parallel threads have their own ArenaLists, but chunks are shared;
     // if we haven't already, take the GC lock now to avoid racing.
     if (maybeLock.isNothing())
         maybeLock.emplace(rt);
 
-    Chunk *chunk = rt->gc.pickChunk(zone, maybeStartBGAlloc);
+    Chunk *chunk = rt->gc.pickChunk(maybeLock.ref(), zone, maybeStartBGAlloc);
     if (!chunk)
         return nullptr;
 
     // Although our chunk should definitely have enough space for another arena,
     // there are other valid reasons why Chunk::allocateArena() may fail.
     aheader = chunk->allocateArena(zone, thingKind);
     if (!aheader)
         return nullptr;
@@ -2932,23 +2945,35 @@ SliceBudget::checkOverBudget()
 
 void
 js::MarkCompartmentActive(InterpreterFrame *fp)
 {
     fp->script()->compartment()->zone()->active = true;
 }
 
 void
-GCRuntime::requestInterrupt(JS::gcreason::Reason reason)
-{
-    if (isNeeded)
+GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
+{
+    if (majorGCRequested)
         return;
 
-    isNeeded = true;
-    triggerReason = reason;
+    majorGCRequested = true;
+    majorGCTriggerReason = reason;
+    rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
+}
+
+void
+GCRuntime::requestMinorGC(JS::gcreason::Reason reason)
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+    if (minorGCRequested)
+        return;
+
+    minorGCRequested = true;
+    minorGCTriggerReason = reason;
     rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /* Wait till end of parallel section to trigger GC. */
     if (InParallelSection()) {
@@ -2967,17 +2992,17 @@ GCRuntime::triggerGC(JS::gcreason::Reaso
     if (rt->currentThreadOwnsInterruptLock())
         return false;
 
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
     JS::PrepareForFullGC(rt);
-    requestInterrupt(reason);
+    requestMajorGC(reason);
     return true;
 }
 
 bool
 GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
 {
     /*
      * If parallel threads are running, wait till they
@@ -3009,37 +3034,35 @@ GCRuntime::triggerZoneGC(Zone *zone, JS:
 
     if (rt->isAtomsZone(zone)) {
         /* We can't do a zone GC of the atoms compartment. */
         triggerGC(reason);
         return true;
     }
 
     PrepareZoneForGC(zone);
-    requestInterrupt(reason);
+    requestMajorGC(reason);
     return true;
 }
 
 bool
 GCRuntime::maybeGC(Zone *zone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
 #ifdef JS_GC_ZEAL
     if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) {
         JS::PrepareForFullGC(rt);
         gc(GC_NORMAL, JS::gcreason::MAYBEGC);
         return true;
     }
 #endif
 
-    if (isNeeded) {
-        gcSlice(GC_NORMAL, JS::gcreason::MAYBEGC);
+    if (gcIfNeeded())
         return true;
-    }
 
     double factor = schedulingState.inHighFrequencyGCMode() ? 0.85 : 0.9;
     if (zone->usage.gcBytes() > 1024 * 1024 &&
         zone->usage.gcBytes() >= factor * zone->threshold.gcTriggerBytes() &&
         incrementalState == NO_INCREMENTAL &&
         !isBackgroundSweeping())
     {
         PrepareZoneForGC(zone);
@@ -3264,22 +3287,18 @@ js::GetCPUCount()
 }
 
 bool
 GCHelperState::init()
 {
     if (!(done = PR_NewCondVar(rt->gc.lock)))
         return false;
 
-    if (CanUseExtraThreads()) {
-        backgroundAllocation = (GetCPUCount() >= 2);
+    if (CanUseExtraThreads())
         HelperThreadState().ensureInitialized();
-    } else {
-        backgroundAllocation = false;
-    }
 
     return true;
 }
 
 void
 GCHelperState::finish()
 {
     if (!rt->gc.lock) {
@@ -3353,46 +3372,50 @@ GCHelperState::work()
 
       case SWEEPING: {
         AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
         doSweep();
         MOZ_ASSERT(state() == SWEEPING);
         break;
       }
 
-      case ALLOCATING: {
-        AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
-        do {
-            Chunk *chunk;
-            {
-                AutoUnlockGC unlock(rt);
-                chunk = Chunk::allocate(rt);
-            }
-
-            /* OOM stops the background allocation. */
-            if (!chunk)
-                break;
-            MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
-            rt->gc.emptyChunks.put(chunk);
-        } while (state() == ALLOCATING && rt->gc.wantBackgroundAllocation());
-
-        MOZ_ASSERT(state() == ALLOCATING || state() == CANCEL_ALLOCATION);
-        break;
-      }
-
-      case CANCEL_ALLOCATION:
-        break;
     }
 
     setState(IDLE);
     thread = nullptr;
 
     PR_NotifyAllCondVar(done);
 }
 
+BackgroundAllocTask::BackgroundAllocTask(JSRuntime *rt, ChunkPool &pool)
+  : runtime(rt),
+    chunkPool_(pool),
+    enabled_(CanUseExtraThreads() && GetCPUCount() >= 2)
+{
+}
+
+/* virtual */ void
+BackgroundAllocTask::run()
+{
+    TraceLogger *logger = TraceLoggerForCurrentThread();
+    AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
+
+    AutoLockGC lock(runtime);
+    while (!cancel_ && runtime->gc.wantBackgroundAllocation(lock)) {
+        Chunk *chunk;
+        {
+            AutoUnlockGC unlock(runtime);
+            chunk = Chunk::allocate(runtime);
+            if (!chunk)
+                break;
+        }
+        chunkPool_.put(chunk);
+    }
+}
+
 void
 GCHelperState::startBackgroundSweep(bool shouldShrink)
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     AutoLockHelperThreadState helperLock;
     AutoLockGC lock(rt);
     MOZ_ASSERT(state() == IDLE);
@@ -3411,56 +3434,31 @@ GCHelperState::startBackgroundShrink()
       case IDLE:
         MOZ_ASSERT(!sweepFlag);
         shrinkFlag = true;
         startBackgroundThread(SWEEPING);
         break;
       case SWEEPING:
         shrinkFlag = true;
         break;
-      case ALLOCATING:
-      case CANCEL_ALLOCATION:
-        /*
-         * If we have started background allocation there is nothing to
-         * shrink.
-         */
-        break;
+      default:
+        MOZ_CRASH("Invalid GC helper thread state.");
     }
 }
 
 void
 GCHelperState::waitBackgroundSweepEnd()
 {
     AutoLockGC lock(rt);
     while (state() == SWEEPING)
         waitForBackgroundThread();
     if (rt->gc.incrementalState == NO_INCREMENTAL)
         rt->gc.assertBackgroundSweepingFinished();
 }
 
-void
-GCHelperState::waitBackgroundSweepOrAllocEnd()
-{
-    AutoLockGC lock(rt);
-    if (state() == ALLOCATING)
-        setState(CANCEL_ALLOCATION);
-    while (state() == SWEEPING || state() == CANCEL_ALLOCATION)
-        waitForBackgroundThread();
-    if (rt->gc.incrementalState == NO_INCREMENTAL)
-        rt->gc.assertBackgroundSweepingFinished();
-}
-
-/* Must be called with the GC lock taken. */
-inline void
-GCHelperState::startBackgroundAllocationIfIdle()
-{
-    if (state_ == IDLE)
-        startBackgroundThread(ALLOCATING);
-}
-
 /* Must be called with the GC lock taken. */
 void
 GCHelperState::doSweep()
 {
     if (sweepFlag) {
         sweepFlag = false;
         AutoUnlockGC unlock(rt);
 
@@ -5809,39 +5807,44 @@ GCRuntime::gcCycle(bool incremental, int
     /*
      * Marking can trigger many incidental post barriers, some of them for
      * objects which are not going to be live after the GC.
      */
     AutoDisableStoreBuffer adsb(this);
 
     AutoTraceSession session(rt, MajorCollecting);
 
-    isNeeded = false;
+    majorGCRequested = false;
     interFrameGC = true;
 
     number++;
     if (incrementalState == NO_INCREMENTAL)
         majorGCNumber++;
 
     // It's ok if threads other than the main thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     // Assert if this is a GC unsafe region.
     JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
-    /*
-     * As we about to purge caches and clear the mark bits we must wait for
-     * any background finalization to finish. We must also wait for the
-     * background allocation to finish so we can avoid taking the GC lock
-     * when manipulating the chunks during the GC.
-     */
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
-        waitBackgroundSweepOrAllocEnd();
+
+        // 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.
+        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);
     }
 
     State prevState = incrementalState;
 
     if (!incremental) {
         // Reset any in progress incremental GC if this was triggered via the
         // API. This isn't required for correctness, but sometimes during tests
         // the caller expects this GC to collect certain objects, and we need
@@ -6140,29 +6143,31 @@ GCRuntime::shrinkBuffers()
     else
         expireChunksAndArenas(true);
 }
 
 void
 GCRuntime::minorGC(JS::gcreason::Reason reason)
 {
 #ifdef JSGC_GENERATIONAL
+    minorGCRequested = false;
     TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
     nursery.collect(rt, reason, nullptr);
     MOZ_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty());
 #endif
 }
 
 void
 GCRuntime::minorGC(JSContext *cx, JS::gcreason::Reason reason)
 {
     // Alternate to the runtime-taking form above which allows marking type
     // objects as needing pretenuring.
 #ifdef JSGC_GENERATIONAL
+    minorGCRequested = false;
     TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
     Nursery::TypeObjectList pretenureTypes;
     nursery.collect(rt, reason, &pretenureTypes);
     for (size_t i = 0; i < pretenureTypes.length(); i++) {
         if (pretenureTypes[i]->canPreTenure())
             pretenureTypes[i]->setShouldPreTenure(cx);
     }
@@ -6191,30 +6196,36 @@ GCRuntime::enableGenerationalGC()
 #ifdef JSGC_GENERATIONAL
     if (generationalDisabled == 0) {
         nursery.enable();
         storeBuffer.enable();
     }
 #endif
 }
 
-void
-GCRuntime::gcIfNeeded(JSContext *cx)
-{
+bool
+GCRuntime::gcIfNeeded(JSContext *cx /* = nullptr */)
+{
+    // This method returns whether a major GC was performed.
+
 #ifdef JSGC_GENERATIONAL
-    /*
-     * In case of store buffer overflow perform minor GC first so that the
-     * correct reason is seen in the logs.
-     */
-    if (storeBuffer.isAboutToOverflow())
-        minorGC(cx, JS::gcreason::FULL_STORE_BUFFER);
+    if (minorGCRequested) {
+        if (cx)
+            minorGC(cx, minorGCTriggerReason);
+        else
+            minorGC(minorGCTriggerReason);
+    }
 #endif
 
-    if (isNeeded)
-        gcSlice(GC_NORMAL, rt->gc.triggerReason, 0);
+    if (majorGCRequested) {
+        gcSlice(GC_NORMAL, rt->gc.majorGCTriggerReason);
+        return true;
+    }
+
+    return false;
 }
 
 AutoFinishGC::AutoFinishGC(JSRuntime *rt)
 {
     if (JS::IsIncrementalGCInProgress(rt)) {
         JS::PrepareForIncrementalGC(rt);
         JS::FinishIncrementalGC(rt, JS::gcreason::API);
     }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1000,19 +1000,17 @@ NotifyGCPostSwap(JSObject *a, JSObject *
  * In non-threadsafe builds, all actual sweeping and allocation is performed
  * on the main thread, but GCHelperState encapsulates this from clients as
  * much as possible.
  */
 class GCHelperState
 {
     enum State {
         IDLE,
-        SWEEPING,
-        ALLOCATING,
-        CANCEL_ALLOCATION
+        SWEEPING
     };
 
     // Associated runtime.
     JSRuntime *const rt;
 
     // Condvar for notifying the main thread when work has finished. This is
     // associated with the runtime's GC lock --- the worker thread state
     // condvars can't be used here due to lock ordering issues.
@@ -1028,18 +1026,16 @@ class GCHelperState
     void waitForBackgroundThread();
 
     State state();
     void setState(State state);
 
     bool              sweepFlag;
     bool              shrinkFlag;
 
-    bool              backgroundAllocation;
-
     friend class js::gc::ArenaLists;
 
     static void freeElementsAndArray(void **array, void **end) {
         MOZ_ASSERT(array <= end);
         for (void **p = array; p != end; ++p)
             js_free(*p);
         js_free(array);
     }
@@ -1049,48 +1045,33 @@ class GCHelperState
 
   public:
     explicit GCHelperState(JSRuntime *rt)
       : rt(rt),
         done(nullptr),
         state_(IDLE),
         thread(nullptr),
         sweepFlag(false),
-        shrinkFlag(false),
-        backgroundAllocation(true)
+        shrinkFlag(false)
     { }
 
     bool init();
     void finish();
 
     void work();
 
     /* Must be called with the GC lock taken. */
     void startBackgroundSweep(bool shouldShrink);
 
     /* Must be called with the GC lock taken. */
     void startBackgroundShrink();
 
     /* Must be called without the GC lock taken. */
     void waitBackgroundSweepEnd();
 
-    /* Must be called without the GC lock taken. */
-    void waitBackgroundSweepOrAllocEnd();
-
-    /* Must be called with the GC lock taken. */
-    void startBackgroundAllocationIfIdle();
-
-    bool canBackgroundAllocate() const {
-        return backgroundAllocation;
-    }
-
-    void disableBackgroundAllocation() {
-        backgroundAllocation = false;
-    }
-
     bool onBackgroundThread();
 
     /*
      * Outside the GC lock may give true answer when in fact the sweeping has
      * been done.
      */
     bool isBackgroundSweeping() const {
         return state_ == SWEEPING;
@@ -1113,35 +1094,50 @@ class GCParallelTask
         Dispatched,
         Finished,
     } state;
 
     // Amount of time this task took to execute.
     uint64_t duration_;
 
   protected:
+    // A flag to signal a request for early completion of the off-thread task.
+    mozilla::Atomic<bool> cancel_;
+
     virtual void run() = 0;
 
   public:
     GCParallelTask() : state(NotStarted), duration_(0) {}
 
+    // Time spent in the most recent invocation of this task.
     int64_t duration() const { return duration_; }
 
     // The simple interface to a parallel task works exactly like pthreads.
     bool start();
     void join();
 
     // If multiple tasks are to be started or joined at once, it is more
     // efficient to take the helper thread lock once and use these methods.
     bool startWithLockHeld();
     void joinWithLockHeld();
 
     // Instead of dispatching to a helper, run the task on the main thread.
     void runFromMainThread(JSRuntime *rt);
 
+    // Dispatch a cancelation request.
+    enum CancelMode { CancelNoWait, CancelAndWait};
+    void cancel(CancelMode mode = CancelNoWait) {
+        cancel_ = true;
+        if (mode == CancelAndWait)
+            join();
+    }
+
+    // Check if a task is actively running.
+    bool isRunning() const;
+
     // This should be friended to HelperThread, but cannot be because it
     // would introduce several circular dependencies.
   public:
     void runFromHelperThread();
 };
 
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -799,16 +799,20 @@ js_Disassemble(JSContext *cx, HandleScri
 JS_FRIEND_API(bool)
 js_DumpPC(JSContext *cx)
 {
     js::gc::AutoSuppressGC suppressGC(cx);
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     ScriptFrameIter iter(cx);
+    if (iter.done()) {
+        fprintf(stdout, "Empty stack.\n");
+        return true;
+    }
     RootedScript script(cx, iter.script());
     bool ok = js_DisassembleAtPC(cx, script, true, iter.pc(), false, &sprinter);
     fprintf(stdout, "%s", sprinter.string());
     return ok;
 }
 
 JS_FRIEND_API(bool)
 js_DumpScript(JSContext *cx, JSScript *scriptArg)
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -21,27 +21,17 @@
 /*
  * JS operation bytecodes.
  */
 typedef enum JSOp {
 #define ENUMERATE_OPCODE(op, val, ...) op = val,
 FOR_EACH_OPCODE(ENUMERATE_OPCODE)
 #undef ENUMERATE_OPCODE
 
-    JSOP_LIMIT,
-
-    /*
-     * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETPROP,
-     * JSOP_SETELEM, and comprehension-tails, respectively.  They are never
-     * stored in bytecode, so they don't preempt valid opcodes.
-     */
-    JSOP_GETPROP2 = JSOP_LIMIT,
-    JSOP_GETELEM2 = JSOP_LIMIT + 1,
-    JSOP_FORLOCAL = JSOP_LIMIT + 2,
-    JSOP_FAKE_LIMIT = JSOP_FORLOCAL
+    JSOP_LIMIT
 } JSOp;
 
 /*
  * JS bytecode formats.
  */
 #define JOF_BYTE          0       /* single bytecode, no immediates */
 #define JOF_JUMP          1       /* signed 16-bit jump offset immediate */
 #define JOF_ATOM          2       /* unsigned 16-bit constant index */
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -347,33 +347,20 @@ TryPreserveReflector(JSContext *cx, Hand
     }
     return true;
 }
 
 static inline void
 WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key)
 {
 #ifdef JSGC_GENERATIONAL
-    /*
-     * Strip the barriers from the type before inserting into the store buffer.
-     * This will automatically ensure that barriers do not fire during GC.
-     *
-     * Some compilers complain about instantiating the WeakMap class for
-     * unbarriered type arguments, so we cast to a HashMap instead.  Because of
-     * WeakMap's multiple inheritace, We need to do this in two stages, first to
-     * the HashMap base class and then to the unbarriered version.
-     */
-    ObjectValueMap::Base *baseHashMap = static_cast<ObjectValueMap::Base *>(weakMap);
-
-    typedef HashMap<JSObject *, Value> UnbarrieredMap;
-    UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
-
-    typedef HashKeyRef<UnbarrieredMap, JSObject *> Ref;
+    // Strip the barriers from the type before inserting into the store buffer.
+    // This will automatically ensure that barriers do not fire during GC.
     if (key && IsInsideNursery(key))
-        rt->gc.storeBuffer.putGeneric(Ref((unbarrieredMap), key));
+        rt->gc.storeBuffer.putGeneric(UnbarrieredRef(weakMap, key));
 #endif
 }
 
 static MOZ_ALWAYS_INLINE bool
 SetWeakMapEntryInternal(JSContext *cx, Handle<WeakMapObject*> mapObj,
                         HandleObject key, HandleValue value)
 {
     ObjectValueMap *map = mapObj->getMap();
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -7,16 +7,17 @@
 #ifndef jsweakmap_h
 #define jsweakmap_h
 
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 
 #include "gc/Marking.h"
+#include "gc/StoreBuffer.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
 // a key is collected, the table entry disappears, dropping its reference to the value.
 //
 // More precisely:
@@ -274,16 +275,38 @@ protected:
             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k));
             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
             MOZ_ASSERT(k == r.front().key());
         }
 #endif
     }
 };
 
+/*
+ * At times, you will need to ignore barriers when accessing WeakMap entries.
+ * Localize the templatized casting craziness here.
+ */
+template <class Key, class Value>
+static inline gc::HashKeyRef<HashMap<Key, Value, DefaultHasher<Key>, RuntimeAllocPolicy>, Key>
+UnbarrieredRef(WeakMap<PreBarriered<Key>, RelocatablePtr<Value>> *map, Key key)
+{
+    /*
+     * Some compilers complain about instantiating the WeakMap class for
+     * unbarriered type arguments, so we cast to a HashMap instead. Because of
+     * WeakMap's multiple inheritance, we need to do this in two stages, first
+     * to the HashMap base class and then to the unbarriered version.
+     */
+
+    typedef typename WeakMap<PreBarriered<Key>, RelocatablePtr<Value>>::Base BaseMap;
+    auto baseMap = static_cast<BaseMap*>(map);
+    typedef HashMap<Key, Value, DefaultHasher<Key>, RuntimeAllocPolicy> UnbarrieredMap;
+    typedef gc::HashKeyRef<UnbarrieredMap, Key> UnbarrieredKeyRef;
+    return UnbarrieredKeyRef(reinterpret_cast<UnbarrieredMap*>(baseMap), key);
+}
+
 /* WeakMap methods exposed so they can be installed in the self-hosting global. */
 
 extern JSObject *
 InitBareWeakMapCtor(JSContext *cx, js::HandleObject obj);
 
 extern bool
 WeakMap_has(JSContext *cx, unsigned argc, Value *vp);
 
--- a/js/src/tests/js1_8_5/extensions/findReferences-01.js
+++ b/js/src/tests/js1_8_5/extensions/findReferences-01.js
@@ -17,32 +17,32 @@ if (typeof findReferences == "function")
     assertEq(referencesVia(o, 'objectElements[42]', o[42]), true);
     assertEq(referencesVia(o, '123456789', o[123456789]), true);
     assertEq(referencesVia(o, 'myself', o), true);
     assertEq(referencesVia(o, 'alsoMyself', o), true);
 
     function g() { return 42; }
     function s(v) { }
     var p = Object.defineProperty({}, 'a', { get:g, set:s });
-    assertEq(referencesVia(p, 'shape; base; getter', g), true);
-    assertEq(referencesVia(p, 'shape; base; setter', s), true);
+    assertEq(referencesVia(p, 'shape; getter', g), true);
+    assertEq(referencesVia(p, 'shape; setter', s), true);
 
     // If there are multiple objects with the same shape referring to a getter
     // or setter, findReferences should get all of them, even though the shape
     // gets 'marked' the first time we visit it.
     var q = Object.defineProperty({}, 'a', { get:g, set:s });
-    assertEq(referencesVia(p, 'shape; base; getter', g), true);
-    assertEq(referencesVia(q, 'shape; base; getter', g), true);
+    assertEq(referencesVia(p, 'shape; getter', g), true);
+    assertEq(referencesVia(q, 'shape; getter', g), true);
 
     // If we extend each object's shape chain, both should still be able to
     // reach the getter, even though the two shapes are each traversed twice.
     p.b = 9;
     q.b = 9;
-    assertEq(referencesVia(p, 'shape; parent; base; getter', g), true);
-    assertEq(referencesVia(q, 'shape; parent; base; getter', g), true);
+    assertEq(referencesVia(p, 'shape; parent; getter', g), true);
+    assertEq(referencesVia(q, 'shape; parent; getter', g), true);
 
     // These are really just ordinary own property references.
     assertEq(referencesVia(C, 'prototype', Object.getPrototypeOf(o)), true);
     assertEq(referencesVia(Object.getPrototypeOf(o), 'constructor', C), true);
 
     // Dense arrays should work, too.
     a = [];
     a[1] = o;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -235,16 +235,21 @@ GlobalObject::create(JSContext *cx, cons
     MOZ_ASSERT(clasp->trace == JS_GlobalObjectTraceHook);
 
     JSObject *obj = NewObjectWithGivenProto(cx, clasp, nullptr, nullptr, SingletonObject);
     if (!obj)
         return nullptr;
 
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
 
+    // Initialize the private slot to null if present, as GC can call class
+    // hooks before the caller gets to set this to a non-garbage value.
+    if (clasp->flags & JSCLASS_HAS_PRIVATE)
+        global->setPrivate(nullptr);
+
     cx->compartment()->initGlobal(*global);
 
     if (!global->setQualifiedVarObj(cx))
         return nullptr;
     if (!global->setUnqualifiedVarObj(cx))
         return nullptr;
     if (!global->setDelegate(cx))
         return nullptr;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -184,18 +184,18 @@ static const JSClass parseTaskGlobalClas
     JS_GlobalObjectTraceHook
 };
 
 ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
                      const char16_t *chars, size_t length,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
-    optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
+    exclusiveContextGlobal(initCx, exclusiveContextGlobal),
+    callback(callback), callbackData(callbackData),
     script(nullptr), errors(cx), overRecursed(false)
 {
 }
 
 bool
 ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
 {
     return this->options.copy(cx, options);
@@ -756,16 +756,17 @@ js::GCParallelTask::joinWithLockHeld()
     MOZ_ASSERT(HelperThreadState().isLocked());
 
     if (state == NotStarted)
         return;
 
     while (state != Finished)
         HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
     state = NotStarted;
+    cancel_ = false;
 }
 
 void
 js::GCParallelTask::join()
 {
     AutoLockHelperThreadState helperLock;
     joinWithLockHeld();
 }
@@ -791,16 +792,23 @@ js::GCParallelTask::runFromHelperThread(
         run();
         duration_ = PRMJ_Now() - timeStart;
     }
 
     state = Finished;
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
 }
 
+bool
+js::GCParallelTask::isRunning() const
+{
+    MOZ_ASSERT(HelperThreadState().isLocked());
+    return state == Dispatched;
+}
+
 void
 HelperThread::handleGCParallelWorkload()
 {
     MOZ_ASSERT(HelperThreadState().isLocked());
     MOZ_ASSERT(HelperThreadState().canStartGCParallelTask());
     MOZ_ASSERT(idle());
 
     MOZ_ASSERT(!gcParallelTask);
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -452,24 +452,16 @@ struct ParseTask
     OwningCompileOptions options;
     const char16_t *chars;
     size_t length;
     LifoAlloc alloc;
 
     // Rooted pointer to the global object used by 'cx'.
     PersistentRootedObject exclusiveContextGlobal;
 
-    // Saved GC-managed CompileOptions fields that will populate slots in
-    // the ScriptSourceObject. We create the ScriptSourceObject in the
-    // compilation's temporary compartment, so storing these values there
-    // at that point would create cross-compartment references. Instead we
-    // hold them here, and install them after merging the compartments.
-    PersistentRootedObject optionsElement;
-    PersistentRootedScript optionsIntroductionScript;
-
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void *callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript *script;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -565,29 +565,29 @@ static MOZ_ALWAYS_INLINE bool
 InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val)
 {
     JSOp op = JSOp(*pc);
     MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
 
     MOZ_ASSERT(obj->is<ArrayObject>());
 
     /*
-     * If val is a hole, do not call JSObject::defineElement. In this case,
-     * if the current op is the last element initialiser, set the array length
-     * to one greater than id.
+     * If val is a hole, do not call JSObject::defineElement.
      *
-     * If val is a hole and current op is JSOP_INITELEM_INC, always call
+     * Furthermore, if the current op is JSOP_INITELEM_INC, always call
      * SetLengthProperty even if it is not the last element initialiser,
      * because it may be followed by JSOP_SPREAD, which will not set the array
-     * length if nothing is spreaded.
+     * length if nothing is spread.
+     *
+     * Alternatively, if the current op is JSOP_INITELEM_ARRAY, the length will
+     * have already been set by the earlier JSOP_NEWARRAY; JSOP_INITELEM_ARRAY
+     * cannot follow JSOP_SPREAD.
      */
     if (val.isMagic(JS_ELEMENTS_HOLE)) {
-        JSOp next = JSOp(*GetNextPc(pc));
-
-        if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) || op == JSOP_INITELEM_INC) {
+        if (op == JSOP_INITELEM_INC) {
             if (!SetLengthProperty(cx, obj, index + 1))
                 return false;
         }
     } else {
         if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
             return false;
     }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1606,16 +1606,17 @@ CASE(JSOP_UNUSED46)
 CASE(JSOP_UNUSED47)
 CASE(JSOP_UNUSED48)
 CASE(JSOP_UNUSED49)
 CASE(JSOP_UNUSED50)
 CASE(JSOP_UNUSED51)
 CASE(JSOP_UNUSED52)
 CASE(JSOP_UNUSED57)
 CASE(JSOP_UNUSED83)
+CASE(JSOP_UNUSED92)
 CASE(JSOP_UNUSED103)
 CASE(JSOP_UNUSED104)
 CASE(JSOP_UNUSED105)
 CASE(JSOP_UNUSED107)
 CASE(JSOP_UNUSED124)
 CASE(JSOP_UNUSED125)
 CASE(JSOP_UNUSED126)
 CASE(JSOP_UNUSED146)
@@ -3115,24 +3116,16 @@ CASE(JSOP_NEWOBJECT)
     obj = CopyInitializerObject(cx, baseobj, newKind);
     if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWOBJECT)
 
-CASE(JSOP_ENDINIT)
-{
-    /* FIXME remove JSOP_ENDINIT bug 588522 */
-    MOZ_ASSERT(REGS.stackDepth() >= 1);
-    MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
-}
-END_CASE(JSOP_ENDINIT)
-
 CASE(JSOP_MUTATEPROTO)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
 
     if (REGS.sp[-1].isObjectOrNull()) {
         RootedObject &newProto = rootObject1;
         rootObject1 = REGS.sp[-1].toObjectOrNull();
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -765,27 +765,19 @@ 1234567890123456789012345678901234567890
      * This opcode takes an object with the final shape, which can be set at
      * the start and slots then filled in directly.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t baseobjIndex
      *   Stack: => obj
      */ \
     macro(JSOP_NEWOBJECT, 91, "newobject",  NULL,         5,  0,  1, JOF_OBJECT) \
-    /*
-     * A no-operation bytecode.
-     *
-     * Indicates the end of object/array initialization, and used for
-     * Type-Inference, decompile, etc.
-     *   Category: Literals
-     *   Type: Object
-     *   Operands:
-     *   Stack: =>
-     */ \
-    macro(JSOP_ENDINIT,   92, "endinit",    NULL,         1,  0,  0, JOF_BYTE) \
+    \
+    macro(JSOP_UNUSED92,  92, "unused92",   NULL,         1,  0,  0, JOF_BYTE) \
+    \
     /*
      * Initialize a named property in an object literal, like '{a: x}'.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1841,33 +1841,18 @@ js_IsDebugScopeSlow(ProxyObject *proxy)
 
 /*****************************************************************************/
 
 /* static */ MOZ_ALWAYS_INLINE void
 DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
                                            const PreBarrieredObject &key)
 {
 #ifdef JSGC_GENERATIONAL
-    /*
-     * Strip the barriers from the type before inserting into the store buffer.
-     * This will automatically ensure that barriers do not fire during GC.
-     *
-     * Some compilers complain about instantiating the WeakMap class for
-     * unbarriered type arguments, so we cast to a HashMap instead.  Because of
-     * WeakMap's multiple inheritace, We need to do this in two stages, first to
-     * the HashMap base class and then to the unbarriered version.
-     */
-    ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map);
-
-    typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
-    UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
-
-    typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
     if (key && IsInsideNursery(key))
-        rt->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, key.get()));
+        rt->gc.storeBuffer.putGeneric(UnbarrieredRef(map, key.get()));
 #endif
 }
 
 #ifdef JSGC_GENERATIONAL
 class DebugScopes::MissingScopesRef : public gc::BufferableRef
 {
     MissingScopeMap *map;
     ScopeIterKey key;
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -23,17 +23,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 186);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 187);
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext *cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext *cx() const {
         return context;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6087,16 +6087,22 @@ nsLayoutUtils::SurfaceFromElementResult
 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
                                   uint32_t aSurfaceFlags,
                                   DrawTarget* aTarget)
 {
   SurfaceFromElementResult result;
 
   NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
 
+#ifdef MOZ_EME
+  if (aElement->ContainsRestrictedContent()) {
+    return result;
+  }
+#endif
+
   uint16_t readyState;
   if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
       (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
        readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
     result.mIsStillLoading = true;
     return result;
   }
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2458,16 +2458,32 @@ MaxZIndexInList(nsDisplayList* aList, ns
 {
   int32_t maxZIndex = 0;
   for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
     maxZIndex = std::max(maxZIndex, item->ZIndex());
   }
   return maxZIndex;
 }
 
+// Finds the max z-index of the items in aList that meet the following conditions
+//   1) have z-index auto or z-index >= 0.
+//   2) aFrame is a proper ancestor of the item's frame.
+// Returns -1 if there is no such item.
+static int32_t
+MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList, nsIFrame* aFrame)
+{
+  int32_t maxZIndex = -1;
+  for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
+    if (nsLayoutUtils::IsProperAncestorFrame(aFrame, item->Frame())) {
+      maxZIndex = std::max(maxZIndex, item->ZIndex());
+    }
+  }
+  return maxZIndex;
+}
+
 static void
 AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
             nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
             bool aPositioned)
 {
   if (aSource->IsEmpty())
     return;
 
@@ -2992,28 +3008,35 @@ ScrollFrameHelper::BuildDisplayList(nsDi
       wrapper.WrapListsInPlace(aBuilder, mOuter, scrolledContent);
     }
 
     // In case we are not using displayport or the nsDisplayScrollLayers are
     // flattened during visibility computation, we still need to export the
     // metadata about this scroll box to the compositor process.
     nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
       aBuilder, mScrolledFrame, mOuter);
+    nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
     if (BuildScrollContainerLayers()) {
       // We process display items from bottom to top, so if we need to flatten after
       // the scroll layer items have been processed we need to be on the top.
-      nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
       if (!positionedDescendants->IsEmpty()) {
         layerItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
         positionedDescendants->AppendNewToTop(layerItem);
       } else {
         aLists.Outlines()->AppendNewToTop(layerItem);
       }
     } else {
-      scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
+      int32_t zindex =
+        MaxZIndexInListOfItemsContainedInFrame(positionedDescendants, mOuter);
+      if (zindex >= 0) {
+        layerItem->SetOverrideZIndex(zindex);
+        positionedDescendants->AppendNewToTop(layerItem);
+      } else {
+        scrolledContent.Outlines()->AppendNewToTop(layerItem);
+      }
     }
   }
   // Now display overlay scrollbars and the resizer, if we have one.
   AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent, usingDisplayport,
                       createLayersForScrollbars, true);
   scrolledContent.MoveTo(aLists);
 }
 
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -74,18 +74,17 @@ private:
 
   GMPDecryptorCallback* mCallback;
   GMPThread* mThread;
 
   Key mKey;
 };
 
 
-ClearKeyDecryptionManager::ClearKeyDecryptionManager(GMPDecryptorHost* aHost)
-  : mHost(aHost)
+ClearKeyDecryptionManager::ClearKeyDecryptionManager()
 {
   CK_LOGD("ClearKeyDecryptionManager ctor");
 }
 
 ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
 {
   CK_LOGD("ClearKeyDecryptionManager dtor");
 }
@@ -127,17 +126,17 @@ ClearKeyDecryptionManager::CreateSession
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                              nullptr /* message */, 0 /* messageLen */);
     return;
   }
 
   string sessionId = GetNewSessionId();
   assert(mSessions.find(sessionId) == mSessions.end());
 
-  ClearKeySession* session = new ClearKeySession(sessionId, mHost, mCallback);
+  ClearKeySession* session = new ClearKeySession(sessionId, mCallback);
   session->Init(aPromiseId, aInitData, aInitDataSize);
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
   for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
     if (mDecryptors.find(*it) == mDecryptors.end()) {
       // Need to request this key ID from the client.
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -14,17 +14,17 @@
 #include "gmp-api/gmp-decryption.h"
 #include "ScopedNSSTypes.h"
 
 class ClearKeyDecryptor;
 
 class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
 {
 public:
-  explicit ClearKeyDecryptionManager(GMPDecryptorHost* aHost);
+  ClearKeyDecryptionManager();
   ~ClearKeyDecryptionManager();
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
 
   virtual void CreateSession(uint32_t aPromiseId,
                              const char* aInitDataType,
                              uint32_t aInitDataTypeSize,
                              const uint8_t* aInitData,
@@ -55,15 +55,14 @@ public:
 
   virtual void Decrypt(GMPBuffer* aBuffer,
                        GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
 
   virtual void DecryptingComplete() MOZ_OVERRIDE;
 
 private:
   GMPDecryptorCallback* mCallback;
-  GMPDecryptorHost* mHost;
 
   std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
   std::map<std::string, ClearKeySession*> mSessions;
 };
 
 #endif // __ClearKeyDecryptor_h__
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -7,20 +7,18 @@
 
 #include "gmp-api/gmp-decryption.h"
 #include "mozilla/Endian.h"
 #include "pk11pub.h"
 
 using namespace mozilla;
 
 ClearKeySession::ClearKeySession(const std::string& aSessionId,
-                                 GMPDecryptorHost* aHost,
                                  GMPDecryptorCallback* aCallback)
   : mSessionId(aSessionId)
-  , mHost(aHost)
   , mCallback(aCallback)
 {
   CK_LOGD("ClearKeySession ctor %p", this);
 }
 
 ClearKeySession::~ClearKeySession()
 {
   CK_LOGD("ClearKeySession dtor %p", this);
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -16,25 +16,24 @@ class GMPEncryptedBufferMetadata;
  * Currently useless; will be fleshed out later with support for persistent
  * key sessions.
  */
 
 class ClearKeySession
 {
 public:
   ClearKeySession(const std::string& aSessionId,
-                  GMPDecryptorHost* aHost, GMPDecryptorCallback *aCallback);
+                  GMPDecryptorCallback* aCallback);
 
   ~ClearKeySession();
 
   const std::vector<KeyId>& GetKeyIds() { return mKeyIds; }
 
   void Init(uint32_t aPromiseId,
             const uint8_t* aInitData, uint32_t aInitDataSize);
 private:
   std::string mSessionId;
   std::vector<KeyId> mKeyIds;
 
   GMPDecryptorCallback* mCallback;
-  GMPDecryptorHost* mHost;
 };
 
 #endif // __ClearKeySession_h__
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
@@ -5,16 +5,18 @@
 #include <algorithm>
 #include <assert.h>
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <vector>
 
 #include "ClearKeyUtils.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Endian.h"
 #include "mozilla/NullPtr.h"
 #include "openaes/oaes_lib.h"
 
 using namespace std;
 
 #define FOURCC(a,b,c,d) ((a << 24) + (b << 16) + (c << 8) + d)
 
@@ -90,28 +92,33 @@ EncodeBase64Web(vector<uint8_t> aBinary,
   // Pad binary data in case there's rubbish past the last byte.
   aBinary.push_back(0);
 
   // Number of bytes not consumed in the previous character
   uint32_t shift = 0;
 
   auto out = aEncoded.begin();
   auto data = aBinary.begin();
-  for (int i = 0; i < aEncoded.length(); i++) {
+  for (string::size_type i = 0; i < aEncoded.length(); i++) {
     if (shift) {
       out[i] = (*data << (6 - shift)) & sMask;
       data++;
     } else {
       out[i] = 0;
     }
 
     out[i] += (*data >> (shift + 2)) & sMask;
     shift = (shift + 2) % 8;
 
-    out[i] = sAlphabet[out[i]];
+    // Cast idx to size_t before using it as an array-index,
+    // to pacify clang 'Wchar-subscripts' warning:
+    size_t idx = static_cast<size_t>(out[i]);
+    MOZ_ASSERT(idx < MOZ_ARRAY_LENGTH(sAlphabet),
+               "out of bounds index for 'sAlphabet'");
+    out[i] = sAlphabet[idx];
   }
 
   return true;
 }
 
 /* static */ void
 ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
                              vector<KeyId>& aOutKeys)
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -30,17 +30,17 @@ GMPInit(GMPPlatformAPI* aPlatformAPI)
 
 MOZ_EXPORT GMPErr
 GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
 {
   if (strcmp(aApiName, "eme-decrypt")) {
     return GMPNotImplementedErr;
   }
 
-  *aPluginAPI = new ClearKeyDecryptionManager(static_cast<GMPDecryptorHost*>(aHostAPI));
+  *aPluginAPI = new ClearKeyDecryptionManager();
 
   return GMPNoErr;
 }
 
 MOZ_EXPORT GMPErr
 GMPShutdown(void)
 {
   return GMPNoErr;
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
@@ -731,17 +731,17 @@ int WebRtcAec_GetDelayMetricsCore(AecCor
       break;
     }
   }
   // Account for lookahead.
   *median = (my_median - kLookaheadBlocks) * kMsPerBlock;
 
   // Calculate the L1 norm, with median value as central moment.
   for (i = 0; i < kHistorySizeBlocks; i++) {
-    l1_norm += (float)(fabs(i - my_median) * self->delay_histogram[i]);
+    l1_norm += (float)abs(i - my_median) * self->delay_histogram[i];
   }
   *std = (int)(l1_norm / (float)num_delay_values + 0.5f) * kMsPerBlock;
 
   // Reset histogram.
   memset(self->delay_histogram, 0, sizeof(self->delay_histogram));
 
   return 0;
 }
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c
@@ -284,17 +284,17 @@ int32_t WebRtcAgc_InitDigital(DigitalAgc
 
     return 0;
 }
 
 int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const int16_t *in_far,
                                      int16_t nrSamples)
 {
     // Check for valid pointer
-    if (&stt->vadFarend == NULL)
+    if (stt == NULL)
     {
         return -1;
     }
 
     // VAD for far end
     WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
 
     return 0;
--- a/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc
+++ b/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc
@@ -244,20 +244,20 @@ void OveruseDetector::UpdateKalman(int64
 
   const double h[2] = {fs_delta, 1.0};
   const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1],
                         E_[1][0]*h[0] + E_[1][1]*h[1]};
 
   const double residual = t_ts_delta - slope_*h[0] - offset_;
 
   const bool stable_state =
-      (BWE_MIN(num_of_deltas_, 60) * fabsf(offset_) < threshold_);
+      (BWE_MIN(num_of_deltas_, 60) * fabs(offset_) < threshold_);
   // We try to filter out very late frames. For instance periodic key
   // frames doesn't fit the Gaussian model well.
-  if (fabsf(residual) < 3 * sqrt(var_noise_)) {
+  if (fabs(residual) < 3 * sqrt(var_noise_)) {
     UpdateNoiseEstimate(residual, min_frame_period, stable_state);
   } else {
     UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state);
   }
 
   const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1];
 
   const double K[2] = {Eh[0] / denom,
@@ -353,17 +353,17 @@ void OveruseDetector::UpdateNoiseEstimat
   }
 }
 
 BandwidthUsage OveruseDetector::Detect(double ts_delta) {
   if (num_of_deltas_ < 2) {
     return kBwNormal;
   }
   const double T = BWE_MIN(num_of_deltas_, 60) * offset_;
-  if (fabsf(T) > threshold_) {
+  if (fabs(T) > threshold_) {
     if (offset_ > 0) {
       if (time_over_using_ == -1) {
         // Initialize the timer. Assume that we've been
         // over-using half of the time since the previous
         // sample.
         time_over_using_ = ts_delta / 2;
       } else {
         // Increment timer
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc
@@ -157,17 +157,17 @@ VCMJitterEstimator::UpdateEstimate(int64
     _prevFrameSize = frameSizeBytes;
 
     // Only update the Kalman filter if the sample is not considered
     // an extreme outlier. Even if it is an extreme outlier from a
     // delay point of view, if the frame size also is large the
     // deviation is probably due to an incorrect line slope.
     double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
 
-    if (abs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
+    if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
         frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize))
     {
         // Update the variance of the deviation from the
         // line given by the Kalman filter
         EstimateRandomJitter(deviation, incompleteFrame);
         // Prevent updating with frames which have been congested by a large
         // frame, and therefore arrives almost at the same time as that frame.
         // This can occur when we receive a large frame (key frame) which
@@ -252,17 +252,17 @@ VCMJitterEstimator::KalmanEstimateChanne
     Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
     Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
     // sigma weights measurements with a small deltaFS as noisy and
     // measurements with large deltaFS as good
     if (_maxFrameSize < 1.0)
     {
         return;
     }
-    double sigma = (300.0 * exp(-abs(static_cast<double>(deltaFSBytes)) /
+    double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
                    (1e0 * _maxFrameSize)) + 1) * sqrt(_varNoise);
     if (sigma < 1.0)
     {
         sigma = 1.0;
     }
     hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
     if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0))
     {
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc
@@ -154,22 +154,22 @@ VCMEncodedFrame* VCMReceiver::FrameForDe
   const int64_t now_ms = clock_->TimeInMilliseconds();
   timing_->UpdateCurrentDelay(frame_timestamp);
   next_render_time_ms = timing_->RenderTimeMs(frame_timestamp, now_ms);
   // Check render timing.
   bool timing_error = false;
   // Assume that render timing errors are due to changes in the video stream.
   if (next_render_time_ms < 0) {
     timing_error = true;
-  } else if (abs(next_render_time_ms - now_ms) > max_video_delay_ms_) {
+  } else if (std::abs(next_render_time_ms - now_ms) > max_video_delay_ms_) {
     WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
                  VCMId(vcm_id_, receiver_id_),
                  "This frame is out of our delay bounds, resetting jitter "
                  "buffer: %d > %d",
-                 static_cast<int>(abs(next_render_time_ms - now_ms)),
+                 static_cast<int>(std::abs(next_render_time_ms - now_ms)),
                  max_video_delay_ms_);
     timing_error = true;
   } else if (static_cast<int>(timing_->TargetVideoDelay()) >
              max_video_delay_ms_) {
     WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
                  VCMId(vcm_id_, receiver_id_),
                  "More than %u ms target delay. Flushing jitter buffer and"
                  "resetting timing.", max_video_delay_ms_);
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc
@@ -109,17 +109,17 @@ VCMRttFilter::Update(uint32_t rttMs)
                "RttFilter Update: sample=%u avgRtt=%f varRtt=%f maxRtt=%u",
                rttMs, _avgRtt, _varRtt, _maxRtt);
 }
 
 bool
 VCMRttFilter::JumpDetection(uint32_t rttMs)
 {
     double diffFromAvg = _avgRtt - rttMs;
-    if (abs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
+    if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
     {
         int diffSign = (diffFromAvg >= 0) ? 1 : -1;
         int jumpCountSign = (_jumpCount >= 0) ? 1 : -1;
         if (diffSign != jumpCountSign)
         {
             // Since the signs differ the samples currently
             // in the buffer is useless as they represent a
             // jump in a different direction.
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc
@@ -47,17 +47,17 @@ enum {
 struct Vp8StreamInfo {
   float framerate_fps[kMaxNumberOfTemporalLayers];
   int bitrate_kbps[kMaxNumberOfTemporalLayers];
 };
 
 MATCHER_P(MatchesVp8StreamInfo, expected, "") {
   bool res = true;
   for (int tl = 0; tl < kMaxNumberOfTemporalLayers; ++tl) {
-    if (abs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) {
+    if (fabs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) {
       *result_listener << " framerate_fps[" << tl
                        << "] = " << arg.framerate_fps[tl] << " (expected "
                        << expected.framerate_fps[tl] << ") ";
       res = false;
     }
     if (abs(expected.bitrate_kbps[tl] - arg.bitrate_kbps[tl]) > 10) {
       *result_listener << " bitrate_kbps[" << tl
                        << "] = " << arg.bitrate_kbps[tl] << " (expected "
--- a/media/webrtc/trunk/webrtc/video/call_perf_tests.cc
+++ b/media/webrtc/trunk/webrtc/video/call_perf_tests.cc
@@ -179,17 +179,17 @@ class VideoRtcpAndSyncObserver : public 
     ss << stream_offset;
     webrtc::test::PrintResult(
         "stream_offset", "", "synchronization", ss.str(), "ms", false);
     int64_t time_since_creation = now_ms - creation_time_ms_;
     // During the first couple of seconds audio and video can falsely be
     // estimated as being synchronized. We don't want to trigger on those.
     if (time_since_creation < kStartupTimeMs)
       return;
-    if (abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
+    if (std::abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
       if (first_time_in_sync_ == -1) {
         first_time_in_sync_ = now_ms;
         webrtc::test::PrintResult("sync_convergence_time",
                                   "",
                                   "synchronization",
                                   time_since_creation,
                                   "ms",
                                   false);
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -15,16 +15,17 @@ import time
 import traceback
 import unittest
 import xml.dom.minidom as dom
 
 from manifestparser import TestManifest
 from marionette import Marionette
 from mixins.b2g import B2GTestResultMixin, get_b2g_pid, get_dm
 from mozhttpd import MozHttpd
+from mozlog.structured.structuredlog import get_default_logger
 from moztest.adapters.unit import StructuredTestRunner, StructuredTestResult
 from moztest.results import TestResultCollection, TestResult, relevant_line
 
 class MarionetteTest(TestResult):
 
     @property
     def test_name(self):
         if self.test_class is not None:
@@ -217,17 +218,18 @@ class MarionetteTextTestRunner(Structure
 
     def _makeResult(self):
         return self.resultclass(self.stream,
                                 self.descriptions,
                                 self.verbosity,
                                 marionette=self.marionette,
                                 b2g_pid=self.b2g_pid,
                                 logger=self.logger,
-                                logcat_stdout=self.logcat_stdout)
+                                logcat_stdout=self.logcat_stdout,
+                                result_callbacks=self.result_callbacks)
 
     def run(self, test):
         "Run the given test case or test suite."
         for pre_run_func in self.pre_run_functions:
             pre_run_func()
 
         result = super(MarionetteTextTestRunner, self).run(test)
         result.printLogs(test)
@@ -445,17 +447,18 @@ class BaseMarionetteTestRunner(object):
     def __init__(self, address=None, emulator=None, emulator_binary=None,
                  emulator_img=None, emulator_res='480x800', homedir=None,
                  app=None, app_args=None, binary=None, profile=None,
                  logger=None, no_window=False, logdir=None, logcat_stdout=False,
                  xml_output=None, repeat=0, testvars=None, tree=None, type=None,
                  device_serial=None, symbols_path=None, timeout=None,
                  shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
                  sdcard=None, this_chunk=1, total_chunks=1, sources=None,
-                 server_root=None, gecko_log=None, **kwargs):
+                 server_root=None, gecko_log=None, result_callbacks=None,
+                 **kwargs):
         self.address = address
         self.emulator = emulator
         self.emulator_binary = emulator_binary
         self.emulator_img = emulator_img
         self.emulator_res = emulator_res
         self.homedir = homedir
         self.app = app
         self.app_args = app_args or []
@@ -485,16 +488,32 @@ class BaseMarionetteTestRunner(object):
         self.sources = sources
         self.server_root = server_root
         self.this_chunk = this_chunk
         self.total_chunks = total_chunks
         self.gecko_log = gecko_log
         self.mixin_run_tests = []
         self.manifest_skipped_tests = []
         self.tests = []
+        self.result_callbacks = result_callbacks if result_callbacks is not None else []
+
+        def gather_debug(test, status):
+            rv = {}
+            marionette = test._marionette_weakref()
+            try:
+                marionette.set_context(marionette.CONTEXT_CHROME)
+                rv['screenshot'] = marionette.screenshot()
+                marionette.set_context(marionette.CONTEXT_CONTENT)
+                rv['source'] = marionette.page_source
+            except:
+                logger = get_default_logger()
+                logger.warning('Failed to gather test failure debug.', exc_info=True)
+            return rv
+
+        self.result_callbacks.append(gather_debug)
 
         if testvars:
             if not os.path.exists(testvars):
                 raise IOError('--testvars file does not exist')
 
             try:
                 with open(testvars) as f:
                     self.testvars = json.loads(f.read())
@@ -784,17 +803,18 @@ class BaseMarionetteTestRunner(object):
                                            self.testvars,
                                            **self.test_kwargs)
                 break
 
         if suite.countTestCases():
             runner = self.textrunnerclass(logger=self.logger,
                                           marionette=self.marionette,
                                           capabilities=self.capabilities,
-                                          logcat_stdout=self.logcat_stdout)
+                                          logcat_stdout=self.logcat_stdout,
+                                          result_callbacks=self.result_callbacks)
 
             results = runner.run(suite)
             self.results.append(results)
 
             self.failed += len(results.failures) + len(results.errors)
             if hasattr(results, 'skipped'):
                 self.skipped += len(results.skipped)
                 self.todo += len(results.skipped)
--- a/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py
+++ b/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py
@@ -119,17 +119,17 @@ class HTMLFormatter(base.BaseFormatter):
                     class_='screenshot'))
             for name, content in debug.items():
                 if 'screenshot' in name:
                     href = '#'
                 else:
                     # use base64 to avoid that some browser (such as Firefox, Opera)
                     # treats '#' as the start of another link if the data URL contains.
                     # use 'charset=utf-8' to show special characters like Chinese.
-                    href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content)
+                    href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content.encode('utf-8'))
                 links_html.append(html.a(
                     name.title(),
                     class_=name,
                     href=href,
                     target='_blank'))
                 links_html.append(' ')
 
             log = html.div(class_='log')
--- a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
@@ -5,11 +5,8 @@
     expected: FAIL
 
   [Test appending an empty ArrayBufferView.]
     expected: FAIL
 
   [Test appending an empty ArrayBuffer.]
     expected: FAIL
 
-  [Test SourceBuffer.appendBuffer() triggering an 'ended' to 'open' transition.]
-    disabled:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): Unstable
--- a/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
@@ -1,3 +1,15 @@
 [mediasource-buffered.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Demuxed content with different lengths]
+    expected: FAIL
+
+  [Muxed tracks with different lengths]
+    expected: FAIL
+
+  [Demuxed content with an empty buffered range on one SourceBuffer]
+    expected: FAIL
+
+  [Muxed content empty buffered ranges.]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mediasource-config-change-webm-a-bitrate.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
deleted file mode 100644
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mediasource-config-change-webm-av-audio-bitrate.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
deleted file mode 100644
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mediasource-config-change-webm-av-video-bitrate.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
@@ -1,6 +1,5 @@
 [mediasource-config-change-webm-v-bitrate.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
   [Tests webm video-only bitrate changes.]
     expected: FAIL
 
--- a/testing/web-platform/meta/media-source/mediasource-duration.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-duration.html.ini
@@ -1,3 +1,15 @@
 [mediasource-duration.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test seek starts on duration truncation below currentTime]
+    expected: TIMEOUT
+
+  [Test appendBuffer completes previous seek to truncated duration]
+    disabled: TIMEOUT or FAIL https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+
+  [Test endOfStream completes previous seek to truncated duration]
+    expected: TIMEOUT
+
+  [Test setting same duration multiple times does not fire duplicate durationchange]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-play-then-seek-back.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-play-then-seek-back.html.ini
@@ -1,3 +1,6 @@
 [mediasource-play-then-seek-back.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test playing then seeking back.]
+    expected: TIMEOUT
+
--- a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini
@@ -1,3 +1,6 @@
 [mediasource-redundant-seek.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test redundant fully prebuffered seek]
+    expected: TIMEOUT
+
--- a/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini
@@ -1,3 +1,9 @@
 [mediasource-seek-beyond-duration.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test seeking beyond updated media duration.]
+    expected: TIMEOUT
+
+  [Test seeking beyond media duration.]
+    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+
--- a/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini
@@ -1,3 +1,9 @@
 [mediasource-seek-during-pending-seek.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test seeking to a new location before transitioning beyond HAVE_METADATA.]
+    expected: TIMEOUT
+
+  [Test seeking to a new location during a pending seek.]
+    expected: TIMEOUT
+
--- a/testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
@@ -1,3 +1,6 @@
 [mediasource-sourcebuffer-mode.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test setting SourceBuffer.mode and SourceBuffer.timestampOffset while parsing media segment.]
+    expected: FAIL
+
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -259,17 +259,20 @@ class TickSample {
       :
         pc(NULL),
         sp(NULL),
         fp(NULL),
 #ifdef ENABLE_ARM_LR_SAVING
         lr(NULL),
 #endif
         context(NULL),
-        isSamplingCurrentThread(false) {}
+        isSamplingCurrentThread(false),
+        threadProfile(nullptr),
+        rssMemory(0),
+        ussMemory(0) {}
 
   void PopulateContext(void* aContext);
 
   Address pc;  // Instruction pointer.
   Address sp;  // Stack pointer.
   Address fp;  // Frame pointer.
 #ifdef ENABLE_ARM_LR_SAVING
   Address lr;  // ARM link register
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -175,16 +175,18 @@
 
 #include "nsIContent.h"
 
 #include "mozilla/HangMonitor.h"
 #include "WinIMEHandler.h"
 
 #include "npapi.h"
 
+#include <d3d11.h>
+
 #if !defined(SM_CONVERTIBLESLATEMODE)
 #define SM_CONVERTIBLESLATEMODE 0x2003
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
@@ -6730,17 +6732,19 @@ nsWindow::GetPreferredCompositorBackends
   // want to support this case. See bug 593471
   if (!(prefs.mDisableAcceleration ||
         mTransparencyMode == eTransparencyTransparent)) {
     if (prefs.mPreferOpenGL) {
       aHints.AppendElement(LayersBackend::LAYERS_OPENGL);
     }
 
     ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
-    if (device && !DoesD3D11DeviceSupportResourceSharing(device)) {
+    if (device &&
+        device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 &&
+        !DoesD3D11DeviceSupportResourceSharing(device)) {
       // bug 1083071 - bad things - fall back to basic layers
       // This should not happen aside from driver bugs, and in particular
       // should not happen on our test machines, so let's NS_ERROR to ensure
       // that we would catch it as a test failure.
       NS_ERROR("Can't use Direct3D 11 because of a driver bug "
         "causing resource sharing to fail");
     } else {
       if (!prefs.mPreferD3D9) {