diff --git a/browser/components/about/AboutRedirector.cpp b/browser/components/about/AboutRedirector.cpp index 3d01f8f4e9..7e74469bff 100644 --- a/browser/components/about/AboutRedirector.cpp +++ b/browser/components/about/AboutRedirector.cpp @@ -35,6 +35,13 @@ struct RedirEntry { URI_SAFE_FOR_UNTRUSTED_CONTENT. */ static RedirEntry kRedirMap[] = { +#ifdef MOZ_SAFE_BROWSING + { "blocked", "chrome://browser/content/blockedSite.xhtml", + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::URI_CAN_LOAD_IN_CHILD | + nsIAboutModule::ALLOW_SCRIPT | + nsIAboutModule::HIDE_FROM_ABOUTABOUT }, +#endif { "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::ALLOW_SCRIPT | @@ -75,6 +82,7 @@ static RedirEntry kRedirMap[] = { { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul", nsIAboutModule::ALLOW_SCRIPT }, #endif + // Linkable because of indexeddb use (bug 1228118) { "home", "chrome://browser/content/abouthome/aboutHome.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::MAKE_LINKABLE | @@ -85,6 +93,29 @@ static RedirEntry kRedirMap[] = { nsIAboutModule::ALLOW_SCRIPT }, { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul", nsIAboutModule::ALLOW_SCRIPT }, + { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml", + nsIAboutModule::ALLOW_SCRIPT }, + // Linkable because of indexeddb use (bug 1228118) + { "loopconversation", "chrome://loop/content/panels/conversation.html", + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::ALLOW_SCRIPT | + nsIAboutModule::HIDE_FROM_ABOUTABOUT | + nsIAboutModule::MAKE_LINKABLE | + nsIAboutModule::ENABLE_INDEXED_DB }, + // Linkable because of indexeddb use (bug 1228118) + { "looppanel", "chrome://loop/content/panels/panel.html", + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::ALLOW_SCRIPT | + nsIAboutModule::HIDE_FROM_ABOUTABOUT | + nsIAboutModule::MAKE_LINKABLE | + nsIAboutModule::ENABLE_INDEXED_DB, + // Shares an IndexedDB origin with about:loopconversation. + "loopconversation" }, + { "reader", "chrome://global/content/reader/aboutReader.html", + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::ALLOW_SCRIPT | + nsIAboutModule::URI_MUST_LOAD_IN_CHILD | + nsIAboutModule::HIDE_FROM_ABOUTABOUT }, }; static const int kRedirTotal = ArrayLength(kRedirMap); diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 4b0152f1be..afb9b2cc5f 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -302,7 +302,12 @@ nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin) // The app could contain a cross-origin iframe - make sure that the content // is actually same-origin with the app. MOZ_ASSERT(inIsolatedMozBrowser == false, "Checked this above"); - OriginAttributes attrs(appId, false); + nsAutoCString suffix; + OriginAttributes attrs; + NS_ENSURE_TRUE(attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(appOrigin), suffix), + nsIPrincipal::APP_STATUS_NOT_INSTALLED); + attrs.mAppId = appId; + attrs.mInBrowser = false; nsCOMPtr appPrin = BasePrincipal::CreateCodebasePrincipal(appURI, attrs); NS_ENSURE_TRUE(appPrin, nsIPrincipal::APP_STATUS_NOT_INSTALLED); return aPrin->Equals(appPrin) ? status @@ -337,9 +342,17 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel, aChannel->GetLoadInfo(getter_AddRefs(loadInfo)); if (loadInfo) { if (loadInfo->GetLoadingSandboxed()) { - RefPtr prin = - nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal()); - NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); + RefPtr prin; + if (loadInfo->LoadingPrincipal()) { + prin = + nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal()); + NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); + } else { + OriginAttributes oAttrs; + loadInfo->GetOriginAttributes(&oAttrs); + prin = nsNullPrincipal::Create(oAttrs); + NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); + } prin.forget(aPrincipal); return NS_OK; } diff --git a/chrome/nsChromeRegistry.cpp b/chrome/nsChromeRegistry.cpp index b4c8b3063d..ed37b6be08 100644 --- a/chrome/nsChromeRegistry.cpp +++ b/chrome/nsChromeRegistry.cpp @@ -457,11 +457,13 @@ nsresult nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow) } // Iterate over our old sheets and kick off a sync load of the new - // sheet if and only if it's a chrome URL. + // sheet if and only if it's a non-inline sheet with a chrome URL. for (StyleSheetHandle sheet : oldSheets) { - nsIURI* uri = sheet ? sheet->GetOriginalURI() : nullptr; + MOZ_ASSERT(sheet, "GetStyleSheetAt shouldn't return nullptr for " + "in-range sheet indexes"); + nsIURI* uri = sheet->GetSheetURI(); - if (uri && IsChromeURI(uri)) { + if (!sheet->IsInline() && IsChromeURI(uri)) { // Reload the sheet. StyleSheetHandle::RefPtr newSheet; // XXX what about chrome sheets that have a title or are disabled? This diff --git a/docshell/base/nsAboutRedirector.cpp b/docshell/base/nsAboutRedirector.cpp index d0c71e6b28..d570a20d0d 100644 --- a/docshell/base/nsAboutRedirector.cpp +++ b/docshell/base/nsAboutRedirector.cpp @@ -48,6 +48,7 @@ static RedirEntry kRedirMap[] = { { "logo", "chrome://branding/content/about.png", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + // Linkable for testing reasons. nsIAboutModule::MAKE_LINKABLE }, { @@ -117,7 +118,8 @@ static RedirEntry kRedirMap[] = { nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::HIDE_FROM_ABOUTABOUT | // Needs to be linkable so content can touch its own srcdoc frames - nsIAboutModule::MAKE_LINKABLE + nsIAboutModule::MAKE_LINKABLE | + nsIAboutModule::URI_CAN_LOAD_IN_CHILD } }; static const int kRedirTotal = mozilla::ArrayLength(kRedirMap); diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp index c061613f27..88341a4bd4 100644 --- a/docshell/base/nsDefaultURIFixup.cpp +++ b/docshell/base/nsDefaultURIFixup.cpp @@ -1119,7 +1119,7 @@ nsDefaultURIFixup::KeywordURIFixup(const nsACString& aURIString, } bool -nsDefaultURIFixup::IsDomainWhitelisted(const nsAutoCString aAsciiHost, +nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aAsciiHost, const uint32_t aDotLoc) { if (sDNSFirstForSingleWords) { @@ -1146,7 +1146,7 @@ nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aDomain, const uint32_t aDotLoc, bool* aResult) { - *aResult = IsDomainWhitelisted(nsAutoCString(aDomain), aDotLoc); + *aResult = IsDomainWhitelisted(aDomain, aDotLoc); return NS_OK; } diff --git a/docshell/base/nsDefaultURIFixup.h b/docshell/base/nsDefaultURIFixup.h index 4a37438e93..9616281f8e 100644 --- a/docshell/base/nsDefaultURIFixup.h +++ b/docshell/base/nsDefaultURIFixup.h @@ -40,7 +40,7 @@ private: bool PossiblyHostPortUrl(const nsACString& aUrl); bool MakeAlternateURI(nsIURI* aURI); bool IsLikelyFTP(const nsCString& aHostSpec); - bool IsDomainWhitelisted(const nsAutoCString aAsciiHost, + bool IsDomainWhitelisted(const nsACString& aAsciiHost, const uint32_t aDotLoc); }; diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 7ac59e7cc7..728e272766 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -12064,9 +12064,18 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, if (loadInfo) { // For now keep storing just the principal in the SHEntry. if (loadInfo->GetLoadingSandboxed()) { - owner = nsNullPrincipal::CreateWithInheritedAttributes( - loadInfo->LoadingPrincipal()); - NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); + if (loadInfo->LoadingPrincipal()) { + owner = nsNullPrincipal::CreateWithInheritedAttributes( + loadInfo->LoadingPrincipal()); + NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); + } else { + // get the OriginAttributes + OriginAttributes oAttrs; + loadInfo->GetOriginAttributes(&oAttrs); + + owner = nsNullPrincipal::Create(oAttrs); + NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); + } } else if (loadInfo->GetForceInheritPrincipal()) { owner = loadInfo->TriggeringPrincipal(); } diff --git a/dom/base/DOMException.h b/dom/base/DOMException.h index 1c11d52fee..df7f7cb725 100644 --- a/dom/base/DOMException.h +++ b/dom/base/DOMException.h @@ -75,6 +75,18 @@ public: void GetName(nsString& retval); + virtual void GetErrorMessage(nsAString& aRetVal) + { + // Since GetName and GetMessageMoz are non-virtual and they deal with + // different member variables in Exception vs. DOMException, have a + // virtual method to ensure the right error message creation. + nsAutoString name; + nsAutoString message; + GetName(name); + GetMessageMoz(message); + CreateErrorMessage(name, message, aRetVal); + } + // The XPCOM GetFilename does the right thing. It might throw, but we want to // return an empty filename in that case anyway, instead of throwing. @@ -102,6 +114,23 @@ public: protected: virtual ~Exception(); + void CreateErrorMessage(const nsAString& aName, const nsAString& aMessage, + nsAString& aRetVal) + { + // Create similar error message as what ErrorReport::init does in jsexn.cpp. + if (!aName.IsEmpty() && !aMessage.IsEmpty()) { + aRetVal.Assign(aName); + aRetVal.AppendLiteral(": "); + aRetVal.Append(aMessage); + } else if (!aName.IsEmpty()) { + aRetVal.Assign(aName); + } else if (!aMessage.IsEmpty()) { + aRetVal.Assign(aMessage); + } else { + aRetVal.Truncate(); + } + } + nsCString mMessage; nsresult mResult; nsCString mName; @@ -151,6 +180,16 @@ public: void GetMessageMoz(nsString& retval); void GetName(nsString& retval); + virtual void GetErrorMessage(nsAString& aRetVal) override + { + // See the comment in Exception::GetErrorMessage. + nsAutoString name; + nsAutoString message; + GetName(name); + GetMessageMoz(message); + CreateErrorMessage(name, message, aRetVal); + } + static already_AddRefed Create(nsresult aRv); diff --git a/dom/base/ScriptSettings.cpp b/dom/base/ScriptSettings.cpp index 359c89816d..5a32a281df 100644 --- a/dom/base/ScriptSettings.cpp +++ b/dom/base/ScriptSettings.cpp @@ -572,7 +572,8 @@ AutoJSAPI::ReportException() JSAutoCompartment ac(cx(), errorGlobal); JS::Rooted exn(cx()); js::ErrorReport jsReport(cx()); - if (StealException(&exn) && jsReport.init(cx(), exn)) { + if (StealException(&exn) && + jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) { if (mIsMainThread) { RefPtr xpcReport = new xpc::ErrorReport(); diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp index 9b733ebc33..32cfffec2b 100644 --- a/dom/base/nsStyleLinkElement.cpp +++ b/dom/base/nsStyleLinkElement.cpp @@ -219,7 +219,7 @@ nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver, nsCOMPtr doc = thisContent->IsInShadowTree() ? thisContent->OwnerDoc() : thisContent->GetUncomposedDoc(); if (doc && doc->CSSLoader()->GetEnabled() && - mStyleSheet && mStyleSheet->GetOriginalURI()) { + mStyleSheet && !mStyleSheet->IsInline()) { doc->CSSLoader()->ObsoleteSheet(mStyleSheet->GetOriginalURI()); } } diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index 7786a38790..0eb0d7de2f 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -294,6 +294,15 @@ public: static nsWeakPtr sPointerLockedElement; static nsWeakPtr sPointerLockedDoc; + /** + * If the absolute values of mMultiplierX and/or mMultiplierY are equal or + * larger than this value, the computed scroll amount isn't rounded down to + * the page width or height. + */ + enum { + MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000 + }; + protected: /** * Prefs class capsules preference management. @@ -567,15 +576,6 @@ protected: void Reset(); - /** - * If the abosolute values of mMultiplierX and/or mMultiplierY are equals or - * larger than this value, the computed scroll amount isn't rounded down to - * the page width or height. - */ - enum { - MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000 - }; - bool mInit[COUNT_OF_MULTIPLIERS]; double mMultiplierX[COUNT_OF_MULTIPLIERS]; double mMultiplierY[COUNT_OF_MULTIPLIERS]; diff --git a/dom/events/test/test_wheel_default_action.html b/dom/events/test/test_wheel_default_action.html index 497f8c5306..c7831bdae6 100644 --- a/dom/events/test/test_wheel_default_action.html +++ b/dom/events/test/test_wheel_default_action.html @@ -9,7 +9,7 @@

 
diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp
index d70db1eedf..7dd46cc1d8 100644
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1941,7 +1941,7 @@ ScriptExecutorRunnable::LogExceptionToConsole(JSContext* aCx,
   MOZ_ASSERT(!mScriptLoader.mRv.Failed());
 
   js::ErrorReport report(aCx);
-  if (!report.init(aCx, exn)) {
+  if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
     JS_ClearPendingException(aCx);
     return;
   }
diff --git a/dom/xbl/nsXBLPrototypeResources.cpp b/dom/xbl/nsXBLPrototypeResources.cpp
index 10a9f7494b..5e9986240c 100644
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -80,7 +80,9 @@ nsXBLPrototypeResources::FlushSkinSheets()
 
   // We have scoped stylesheets.  Reload any chrome stylesheets we
   // encounter.  (If they aren't skin sheets, it doesn't matter, since
-  // they'll still be in the chrome cache.
+  // they'll still be in the chrome cache.  Skip inline sheets, which
+  // skin sheets can't be, and which in any case don't have a usable
+  // URL to reload.)
 
   nsTArray oldSheets;
 
@@ -94,7 +96,7 @@ nsXBLPrototypeResources::FlushSkinSheets()
     nsIURI* uri = oldSheet->GetSheetURI();
 
     StyleSheetHandle::RefPtr newSheet;
-    if (IsChromeURI(uri)) {
+    if (!oldSheet->IsInline() && IsChromeURI(uri)) {
       if (NS_FAILED(cssLoader->LoadSheetSync(uri, &newSheet)))
         continue;
     }
diff --git a/extensions/auth/nsHttpNegotiateAuth.cpp b/extensions/auth/nsHttpNegotiateAuth.cpp
index 7ac7266b8d..e255dcfc62 100644
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -38,6 +38,8 @@
 #include "prnetdb.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Snprintf.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
 
 //-----------------------------------------------------------------------------
 
@@ -52,6 +54,15 @@ static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
 
 //-----------------------------------------------------------------------------
 
+// Return false when the channel comes from a Private browsing window.
+static bool
+TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel)
+{
+    nsCOMPtr bareChannel = do_QueryInterface(authChannel);
+    MOZ_ASSERT(bareChannel);
+    return !NS_UsePrivateBrowsing(bareChannel);
+}
+
 NS_IMETHODIMP
 nsHttpNegotiateAuth::GetAuthFlags(uint32_t *flags)
 {
@@ -113,8 +124,9 @@ nsHttpNegotiateAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel,
         proxyInfo->GetHost(service);
     }
     else {
-        bool allowed = TestNonFqdn(uri) ||
-                       TestPref(uri, kNegotiateAuthTrustedURIs);
+        bool allowed = TestNotInPBMode(authChannel) &&
+                       (TestNonFqdn(uri) ||
+                       TestPref(uri, kNegotiateAuthTrustedURIs));
         if (!allowed) {
             LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
             return NS_ERROR_ABORT;
diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp
index eb62595a26..eaa0c2e08b 100644
--- a/gfx/2d/PathD2D.cpp
+++ b/gfx/2d/PathD2D.cpp
@@ -334,7 +334,7 @@ PathBuilderD2D::Finish()
 
   HRESULT hr = mSink->Close();
   if (FAILED(hr)) {
-    gfxDebug() << "Failed to close PathSink. Code: " << hexa(hr);
+    gfxCriticalNote << "Failed to close PathSink. Code: " << hexa(hr);
     return nullptr;
   }
 
diff --git a/gfx/2d/RadialGradientEffectD2D1.cpp b/gfx/2d/RadialGradientEffectD2D1.cpp
index e449210a19..8f929d8e9b 100644
--- a/gfx/2d/RadialGradientEffectD2D1.cpp
+++ b/gfx/2d/RadialGradientEffectD2D1.cpp
@@ -153,13 +153,13 @@ RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
     float transform[8];
   };
 
-  PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0,
+  PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0.0f,
                               { mCenter1.x, mCenter1.y },
                               A, mRadius1, mRadius1 * mRadius1,
-                              mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1 : 0,
-                              mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1 : 0,
-                              { 0 }, { mat._11, mat._21, mat._31, 0,
-                                             mat._12, mat._22, mat._32, 0 } };
+                              mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1.0f : 0.0f,
+                              mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1.0f : 0.0f,
+                              { 0.0f }, { mat._11, mat._21, mat._31, 0.0f,
+                                             mat._12, mat._22, mat._32, 0.0f } };
 
   hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
 
diff --git a/gfx/gl/GLBlitHelper.cpp b/gfx/gl/GLBlitHelper.cpp
index 90e1ef8330..2a6fb6094c 100644
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -68,6 +68,9 @@ GLBlitHelper::GLBlitHelper(GLContext* gl)
 
 GLBlitHelper::~GLBlitHelper()
 {
+    if (!mGL->MakeCurrent())
+        return;
+
     DeleteTexBlitProgram();
 
     GLuint tex[] = {
@@ -91,6 +94,7 @@ bool
 GLBlitHelper::InitTexQuadProgram(BlitType target)
 {
     const char kTexBlit_VertShaderSource[] = "\
+        #version 100                                  \n\
         #ifdef GL_ES                                  \n\
         precision mediump float;                      \n\
         #endif                                        \n\
@@ -109,10 +113,13 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
     ";
 
     const char kTex2DBlit_FragShaderSource[] = "\
+        #version 100                                        \n\
+        #ifdef GL_ES                                        \n\
         #ifdef GL_FRAGMENT_PRECISION_HIGH                   \n\
             precision highp float;                          \n\
         #else                                               \n\
-            prevision mediump float;                        \n\
+            precision mediump float;                        \n\
+        #endif                                              \n\
         #endif                                              \n\
         uniform sampler2D uTexUnit;                         \n\
                                                             \n\
@@ -125,6 +132,7 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
     ";
 
     const char kTex2DRectBlit_FragShaderSource[] = "\
+        #version 100                                                  \n\
         #ifdef GL_FRAGMENT_PRECISION_HIGH                             \n\
             precision highp float;                                    \n\
         #else                                                         \n\
@@ -144,6 +152,7 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
     ";
 #ifdef ANDROID /* MOZ_WIDGET_ANDROID || MOZ_WIDGET_GONK */
     const char kTexExternalBlit_FragShaderSource[] = "\
+        #version 100                                                    \n\
         #extension GL_OES_EGL_image_external : require                  \n\
         #ifdef GL_FRAGMENT_PRECISION_HIGH                               \n\
             precision highp float;                                      \n\
@@ -172,6 +181,7 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
     [B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
     */
     const char kTexYUVPlanarBlit_FragShaderSource[] = "\
+        #version 100                                                        \n\
         #ifdef GL_ES                                                        \n\
         precision mediump float;                                            \n\
         #endif                                                              \n\
@@ -183,9 +193,9 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
         uniform vec2 uCbCrTexScale;                                         \n\
         void main()                                                         \n\
         {                                                                   \n\
-            float y = texture2D(uYTexture, vTexCoord * uYTexScale).a;       \n\
-            float cb = texture2D(uCbTexture, vTexCoord * uCbCrTexScale).a;  \n\
-            float cr = texture2D(uCrTexture, vTexCoord * uCbCrTexScale).a;  \n\
+            float y = texture2D(uYTexture, vTexCoord * uYTexScale).r;       \n\
+            float cb = texture2D(uCbTexture, vTexCoord * uCbCrTexScale).r;  \n\
+            float cr = texture2D(uCrTexture, vTexCoord * uCbCrTexScale).r;  \n\
             y = (y - 0.06275) * 1.16438;                                    \n\
             cb = cb - 0.50196;                                              \n\
             cr = cr - 0.50196;                                              \n\
@@ -198,6 +208,7 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
 
 #ifdef XP_MACOSX
     const char kTexNV12PlanarBlit_FragShaderSource[] = "\
+        #version 100                                                             \n\
         #extension GL_ARB_texture_rectangle : require                            \n\
         #ifdef GL_ES                                                             \n\
         precision mediump float                                                  \n\
@@ -381,18 +392,16 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
         // Cache and set attribute and uniform
         mGL->fUseProgram(program);
         switch (target) {
+#ifdef ANDROID
+            case ConvertSurfaceTexture:
+            case ConvertGralloc:
+#endif
             case BlitTex2D:
             case BlitTexRect:
-            case ConvertEGLImage:
-            case ConvertSurfaceTexture:
-            case ConvertGralloc: {
-#ifdef ANDROID
+            case ConvertEGLImage: {
                 GLint texUnitLoc = mGL->fGetUniformLocation(program, "uTexUnit");
                 MOZ_ASSERT(texUnitLoc != -1, "uniform uTexUnit not found");
                 mGL->fUniform1i(texUnitLoc, 0);
-#else
-                MOZ_ASSERT_UNREACHABLE("gralloc not support on non-android");
-#endif
                 break;
             }
             case ConvertPlanarYCbCr: {
@@ -432,6 +441,8 @@ GLBlitHelper::InitTexQuadProgram(BlitType target)
 #endif
                 break;
             }
+            default:
+                return false;
         }
         MOZ_ASSERT(mGL->fGetAttribLocation(program, "aPosition") == 0);
         mYFlipLoc = mGL->fGetUniformLocation(program, "uYflip");
@@ -596,9 +607,23 @@ GLBlitHelper::BindAndUploadYUVTexture(Channel which,
     MOZ_ASSERT(which < Channel_Max, "Invalid channel!");
     GLuint* srcTexArr[3] = {&mSrcTexY, &mSrcTexCb, &mSrcTexCr};
     GLuint& tex = *srcTexArr[which];
+
+    // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles.
+    // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
+    GLenum format;
+    GLenum internalFormat;
+    if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
+        mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
+        format = LOCAL_GL_RED;
+        internalFormat = LOCAL_GL_R8;
+    } else {
+        format = LOCAL_GL_LUMINANCE;
+        internalFormat = LOCAL_GL_LUMINANCE;
+    }
+
     if (!tex) {
         MOZ_ASSERT(needsAllocation);
-        tex = CreateTexture(mGL, LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE,
+        tex = CreateTexture(mGL, internalFormat, format, LOCAL_GL_UNSIGNED_BYTE,
                             gfx::IntSize(width, height), false);
     }
     mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + which);
@@ -611,17 +636,17 @@ GLBlitHelper::BindAndUploadYUVTexture(Channel which,
                             0,
                             width,
                             height,
-                            LOCAL_GL_ALPHA,
+                            format,
                             LOCAL_GL_UNSIGNED_BYTE,
                             data);
     } else {
         mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
                          0,
-                         LOCAL_GL_ALPHA,
+                         internalFormat,
                          width,
                          height,
                          0,
-                         LOCAL_GL_ALPHA,
+                         format,
                          LOCAL_GL_UNSIGNED_BYTE,
                          data);
     }
diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp
index bdd954a48d..94fb03c877 100644
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -2322,12 +2322,13 @@ GLContext::MarkDestroyed()
     if (IsDestroyed())
         return;
 
+    // Null these before they're naturally nulled after dtor, as we want GLContext to
+    // still be alive in *their* dtors.
+    mScreen = nullptr;
+    mBlitHelper = nullptr;
+    mReadTexImageHelper = nullptr;
+
     if (MakeCurrent()) {
-        DestroyScreenBuffer();
-
-        mBlitHelper = nullptr;
-        mReadTexImageHelper = nullptr;
-
         mTexGarbageBin->GLContextTeardown();
     } else {
         NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
@@ -2571,8 +2572,6 @@ GLContext::CreateScreenBufferImpl(const IntSize& size, const SurfaceCaps& caps)
         return false;
     }
 
-    DestroyScreenBuffer();
-
     // This will rebind to 0 (Screen) if needed when
     // it falls out of scope.
     ScopedBindFramebuffer autoFB(this);
@@ -2591,12 +2590,6 @@ GLContext::ResizeScreenBuffer(const IntSize& size)
     return mScreen->Resize(size);
 }
 
-void
-GLContext::DestroyScreenBuffer()
-{
-    mScreen = nullptr;
-}
-
 void
 GLContext::ForceDirtyScreen()
 {
diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h
index f2ffc7b27e..44ac106036 100644
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -284,8 +284,8 @@ public:
         }
 
         if (profile == ContextProfile::OpenGL) {
-            return profile == ContextProfile::OpenGLCore ||
-                   profile == ContextProfile::OpenGLCompatibility;
+            return mProfile == ContextProfile::OpenGLCore ||
+                   mProfile == ContextProfile::OpenGLCompatibility;
         }
 
         return profile == mProfile;
@@ -3455,8 +3455,6 @@ protected:
     friend class GLScreenBuffer;
     UniquePtr mScreen;
 
-    void DestroyScreenBuffer();
-
     SharedSurface* mLockedSurface;
 
 public:
diff --git a/gfx/gl/GLReadTexImageHelper.cpp b/gfx/gl/GLReadTexImageHelper.cpp
index 8d4b9f3d00..4a1836d2bb 100644
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -32,6 +32,9 @@ GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl)
 
 GLReadTexImageHelper::~GLReadTexImageHelper()
 {
+    if (!mGL->MakeCurrent())
+        return;
+
     mGL->fDeleteProgram(mPrograms[0]);
     mGL->fDeleteProgram(mPrograms[1]);
     mGL->fDeleteProgram(mPrograms[2]);
diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp
index a5c8290aea..7097435a58 100644
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -117,8 +117,10 @@ SharedSurface_ANGLEShareHandle::~SharedSurface_ANGLEShareHandle()
 {
     mEGL->fDestroySurface(Display(), mPBuffer);
 
+    if (!mGL->MakeCurrent())
+        return;
+
     if (mFence) {
-        mGL->MakeCurrent();
         mGL->fDeleteFences(1, &mFence);
     }
 }
diff --git a/gfx/gl/SharedSurfaceD3D11Interop.cpp b/gfx/gl/SharedSurfaceD3D11Interop.cpp
index 4d2d410996..ce77a1015e 100644
--- a/gfx/gl/SharedSurfaceD3D11Interop.cpp
+++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp
@@ -6,6 +6,7 @@
 #include "SharedSurfaceD3D11Interop.h"
 
 #include 
+#include "gfxPrefs.h"
 #include "GLContext.h"
 #include "WGLLibrary.h"
 
@@ -264,15 +265,9 @@ SharedSurface_D3D11Interop::Create(const RefPtr& dxgl,
         return nullptr;
     }
 
-    GLuint fence = 0;
-    if (gl->IsExtensionSupported(GLContext::NV_fence)) {
-        gl->MakeCurrent();
-        gl->fGenFences(1, &fence);
-    }
-
     typedef SharedSurface_D3D11Interop ptrT;
     UniquePtr ret ( new ptrT(gl, size, hasAlpha, renderbufferGL, dxgl, objectWGL,
-                                   textureD3D, sharedHandle, keyedMutex, fence) );
+                                   textureD3D, sharedHandle, keyedMutex) );
     return Move(ret);
 }
 
@@ -284,8 +279,7 @@ SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl,
                                                        HANDLE objectWGL,
                                                        const RefPtr& textureD3D,
                                                        HANDLE sharedHandle,
-                                                       const RefPtr& keyedMutex,
-                                                       GLuint fence)
+                                                       const RefPtr& keyedMutex)
     : SharedSurface(SharedSurfaceType::DXGLInterop2,
                     AttachmentType::GLRenderbuffer,
                     gl,
@@ -296,9 +290,9 @@ SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl,
     , mDXGL(dxgl)
     , mObjectWGL(objectWGL)
     , mTextureD3D(textureD3D)
+    , mNeedsFinish(gfxPrefs::WebGLDXGLNeedsFinish())
     , mSharedHandle(sharedHandle)
     , mKeyedMutex(keyedMutex)
-    , mFence(fence)
     , mLockedForGL(false)
 { }
 
@@ -306,16 +300,14 @@ SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
 {
     MOZ_ASSERT(!mLockedForGL);
 
-    mGL->fDeleteRenderbuffers(1, &mProdRB);
-
     if (!mDXGL->UnregisterObject(mObjectWGL)) {
         NS_WARNING("Failed to release a DXGL object, possibly leaking it.");
     }
 
-    if (mFence) {
-        mGL->MakeCurrent();
-        mGL->fDeleteFences(1, &mFence);
-    }
+    if (!mGL->MakeCurrent())
+        return;
+
+    mGL->fDeleteRenderbuffers(1, &mProdRB);
 
     // mDXGL is closed when it runs out of refs.
 }
@@ -364,8 +356,9 @@ SharedSurface_D3D11Interop::ProducerReleaseImpl()
         mKeyedMutex->ReleaseSync(0);
     }
 
-    // TODO fence properly. This kills performance.
-    mGL->fFinish();
+    if (mNeedsFinish) {
+        mGL->fFinish();
+    }
 }
 
 bool
@@ -373,8 +366,8 @@ SharedSurface_D3D11Interop::ToSurfaceDescriptor(layers::SurfaceDescriptor* const
 {
     gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
                                           : gfx::SurfaceFormat::B8G8R8X8;
-    *out_descriptor = layers::SurfaceDescriptorD3D10((WindowsHandle)GetSharedHandle(),
-                                                     format, mSize);
+    *out_descriptor = layers::SurfaceDescriptorD3D10(WindowsHandle(mSharedHandle), format,
+                                                     mSize);
     return true;
 }
 
diff --git a/gfx/gl/SharedSurfaceD3D11Interop.h b/gfx/gl/SharedSurfaceD3D11Interop.h
index afff243c46..bfe583090c 100644
--- a/gfx/gl/SharedSurfaceD3D11Interop.h
+++ b/gfx/gl/SharedSurfaceD3D11Interop.h
@@ -22,17 +22,19 @@ class WGLLibrary;
 class SharedSurface_D3D11Interop
     : public SharedSurface
 {
+public:
     const GLuint mProdRB;
     const RefPtr mDXGL;
     const HANDLE mObjectWGL;
     const HANDLE mSharedHandle;
     const RefPtr mTextureD3D;
+    const bool mNeedsFinish;
+
+protected:
     RefPtr mKeyedMutex;
     RefPtr mConsumerKeyedMutex;
     RefPtr mConsumerTexture;
-    const GLuint mFence;
 
-protected:
     bool mLockedForGL;
 
 public:
@@ -56,8 +58,7 @@ protected:
                                HANDLE objectWGL,
                                const RefPtr& textureD3D,
                                HANDLE sharedHandle,
-                               const RefPtr& keyedMutex,
-                               GLuint fence);
+                               const RefPtr& keyedMutex);
 
 public:
     virtual ~SharedSurface_D3D11Interop();
@@ -73,11 +74,6 @@ public:
     }
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
-
-    // Implementation-specific functions below:
-    HANDLE GetSharedHandle() const {
-        return mSharedHandle;
-    }
 };
 
 
diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp
index aebf18bf09..61174c81e9 100644
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -88,9 +88,12 @@ SharedSurface_EGLImage::~SharedSurface_EGLImage()
 {
     mEGL->fDestroyImage(Display(), mImage);
 
-    mGL->MakeCurrent();
-    mGL->fDeleteTextures(1, &mProdTex);
-    mProdTex = 0;
+    if (mSync) {
+        // We can't call this unless we have the ext, but we will always have
+        // the ext if we have something to destroy.
+        mEGL->fDestroySync(Display(), mSync);
+        mSync = 0;
+    }
 
     if (mConsTex) {
         MOZ_ASSERT(mGarbageBin);
@@ -98,12 +101,11 @@ SharedSurface_EGLImage::~SharedSurface_EGLImage()
         mConsTex = 0;
     }
 
-    if (mSync) {
-        // We can't call this unless we have the ext, but we will always have
-        // the ext if we have something to destroy.
-        mEGL->fDestroySync(Display(), mSync);
-        mSync = 0;
-    }
+    if (!mGL->MakeCurrent())
+        return;
+
+    mGL->fDeleteTextures(1, &mProdTex);
+    mProdTex = 0;
 }
 
 layers::TextureFlags
diff --git a/gfx/gl/SharedSurfaceGLX.cpp b/gfx/gl/SharedSurfaceGLX.cpp
index 82ad71bcb2..01f42058a5 100644
--- a/gfx/gl/SharedSurfaceGLX.cpp
+++ b/gfx/gl/SharedSurfaceGLX.cpp
@@ -77,11 +77,11 @@ SharedSurface_GLXDrawable::UnlockProdImpl()
 bool
 SharedSurface_GLXDrawable::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
-  if (!mXlibSurface)
-      return false;
+    if (!mXlibSurface)
+        return false;
 
-   *out_descriptor = layers::SurfaceDescriptorX11(mXlibSurface, mInSameProcess);
-   return true;
+    *out_descriptor = layers::SurfaceDescriptorX11(mXlibSurface, mInSameProcess);
+    return true;
 }
 
 bool
diff --git a/gfx/gl/SharedSurfaceGralloc.cpp b/gfx/gl/SharedSurfaceGralloc.cpp
index d4ecc820a3..c2f08ce864 100644
--- a/gfx/gl/SharedSurfaceGralloc.cpp
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -135,7 +135,6 @@ SharedSurface_Gralloc::SharedSurface_Gralloc(GLContext* prodGL,
 {
 }
 
-
 bool
 SharedSurface_Gralloc::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
 {
@@ -145,10 +144,11 @@ SharedSurface_Gralloc::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
 
 SharedSurface_Gralloc::~SharedSurface_Gralloc()
 {
-
     DEBUG_PRINT("[SharedSurface_Gralloc %p] destroyed\n", this);
 
-    mGL->MakeCurrent();
+    if (!mGL->MakeCurrent())
+        return;
+
     mGL->fDeleteTextures(1, &mProdTex);
 
     if (mSync) {
diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp
index c515ae6312..be4e858346 100644
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -165,11 +165,10 @@ SharedSurface_IOSurface::SharedSurface_IOSurface(const RefPtr& ioS
 
 SharedSurface_IOSurface::~SharedSurface_IOSurface()
 {
-    if (mProdTex) {
-        DebugOnly success = mGL->MakeCurrent();
-        MOZ_ASSERT(success);
-        mGL->fDeleteTextures(1, &mProdTex);
-    }
+    if (!mGL->MakeCurrent())
+        return;
+
+    mGL->fDeleteTextures(1, &mProdTex);
 }
 
 bool
diff --git a/gfx/gl/TextureGarbageBin.cpp b/gfx/gl/TextureGarbageBin.cpp
index c2f03fb8f9..f7c8f23878 100644
--- a/gfx/gl/TextureGarbageBin.cpp
+++ b/gfx/gl/TextureGarbageBin.cpp
@@ -36,6 +36,7 @@ TextureGarbageBin::EmptyGarbage()
     if (!mGL)
         return;
 
+    MOZ_RELEASE_ASSERT(mGL->IsCurrent());
     while (!mGarbageTextures.empty()) {
         GLuint tex = mGarbageTextures.top();
         mGarbageTextures.pop();
diff --git a/gfx/layers/AxisPhysicsMSDModel.cpp b/gfx/layers/AxisPhysicsMSDModel.cpp
index f9232d5f1a..58649cc244 100644
--- a/gfx/layers/AxisPhysicsMSDModel.cpp
+++ b/gfx/layers/AxisPhysicsMSDModel.cpp
@@ -58,7 +58,7 @@ AxisPhysicsMSDModel::Acceleration(const State &aState)
 
 
 double
-AxisPhysicsMSDModel::GetDestination()
+AxisPhysicsMSDModel::GetDestination() const
 {
   return mDestination;
 }
diff --git a/gfx/layers/AxisPhysicsMSDModel.h b/gfx/layers/AxisPhysicsMSDModel.h
index 838007a510..370f812248 100644
--- a/gfx/layers/AxisPhysicsMSDModel.h
+++ b/gfx/layers/AxisPhysicsMSDModel.h
@@ -27,7 +27,7 @@ public:
   /**
    * Gets the raw destination of this axis at this moment.
    */
-  double GetDestination();
+  double GetDestination() const;
 
   /**
    * Sets the raw destination of this axis at this moment.
diff --git a/gfx/layers/LayerMetricsWrapper.h b/gfx/layers/LayerMetricsWrapper.h
index cdd565d5da..b2c933e983 100644
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -423,6 +423,12 @@ public:
     }
   }
 
+  bool IsScrollbarContainer() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->IsScrollbarContainer();
+  }
+
   // Expose an opaque pointer to the layer. Mostly used for printf
   // purposes. This is not intended to be a general-purpose accessor
   // for the underlying layer.
diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp
index 1c78df2995..67fd31bd5e 100644
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -367,7 +367,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
         GetEventRegionsOverride(aParent, aLayer));
     node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                            aLayer.GetScrollbarDirection(),
-                           aLayer.GetScrollbarSize());
+                           aLayer.GetScrollbarSize(),
+                           aLayer.IsScrollbarContainer());
     return node;
   }
 
@@ -546,7 +547,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
 
   node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                          aLayer.GetScrollbarDirection(),
-                         aLayer.GetScrollbarSize());
+                         aLayer.GetScrollbarSize(),
+                         aLayer.IsScrollbarContainer());
   return node;
 }
 
@@ -1080,6 +1082,14 @@ APZCTreeManager::ProcessMouseEvent(WidgetMouseEventBase& aEvent,
   return status;
 }
 
+void
+APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
+{
+  if (mApzcForInputBlock) {
+    mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
+  }
+}
+
 nsEventStatus
 APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
@@ -1938,7 +1948,7 @@ APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) c
     // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
     ancestorUntransform = parent->GetAncestorTransform().Inverse();
     // asyncUntransform is updated to PA.Inverse() when parent == P
-    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix();
+    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
     // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
     Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
 
@@ -1970,7 +1980,7 @@ APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) co
   // leftmost matrix in a multiplication is applied first.
 
   // asyncUntransform is LA.Inverse()
-  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix();
+  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
 
   // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
   result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h
index 7aaa361769..52b7e56b3f 100644
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -18,9 +18,8 @@
 #include "mozilla/layers/APZUtils.h"    // for HitTestResult
 #include "mozilla/layers/TouchCounter.h"// for TouchCounter
 #include "mozilla/Mutex.h"              // for Mutex
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for mozilla::TimeStamp
-#include "mozilla/Vector.h"             // for mozilla::Vector
-#include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsTArrayForwardDeclare.h"     // for nsTArray, nsTArray_Impl, etc
@@ -442,6 +441,14 @@ public:
                                                          bool* aOutHitScrollbar = nullptr);
   ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const;
   ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const;
+
+  /**
+   * Process touch velocity.
+   * Sometimes the touch move event will have a velocity even though no scrolling
+   * is occurring such as when the toolbar is being hidden/shown in Fennec.
+   * This function can be called to have the y axis' velocity queue updated.
+   */
+  void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY);
 private:
   typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
 
diff --git a/gfx/layers/apz/src/AsyncPanZoomAnimation.h b/gfx/layers/apz/src/AsyncPanZoomAnimation.h
index a2eebb68ac..a227029a63 100644
--- a/gfx/layers/apz/src/AsyncPanZoomAnimation.h
+++ b/gfx/layers/apz/src/AsyncPanZoomAnimation.h
@@ -10,14 +10,15 @@
 #include "base/message_loop.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/Vector.h"
 #include "FrameMetrics.h"
 #include "nsISupportsImpl.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace layers {
 
 class WheelScrollAnimation;
+class SmoothScrollAnimation;
 
 class AsyncPanZoomAnimation {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
@@ -45,13 +46,16 @@ public:
    * Get the deferred tasks in |mDeferredTasks| and place them in |aTasks|. See
    * |mDeferredTasks| for more information.  Clears |mDeferredTasks|.
    */
-  Vector TakeDeferredTasks() {
+  nsTArray TakeDeferredTasks() {
     return Move(mDeferredTasks);
   }
 
   virtual WheelScrollAnimation* AsWheelScrollAnimation() {
     return nullptr;
   }
+  virtual SmoothScrollAnimation* AsSmoothScrollAnimation() {
+    return nullptr;
+  }
 
   virtual bool WantsRepaints() {
     return true;
@@ -67,7 +71,7 @@ protected:
    * Derived classes can add tasks here in Sample(), and the APZC can call
    * ExecuteDeferredTasks() to execute them.
    */
-  Vector mDeferredTasks;
+  nsTArray mDeferredTasks;
 };
 
 } // namespace layers
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
index c3e8f93e35..ffad278ce5 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -35,6 +35,7 @@
 #include "mozilla/MouseEvents.h"        // for WidgetWheelEvent
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/StaticPtr.h"          // for StaticAutoPtr
 #include "mozilla/Telemetry.h"          // for Telemetry
 #include "mozilla/TimeStamp.h"          // for TimeDuration, TimeStamp
@@ -58,7 +59,6 @@
 #include "mozilla/unused.h"             // for unused
 #include "mozilla/FloatingPoint.h"      // for FuzzyEquals*
 #include "nsAlgorithm.h"                // for clamped
-#include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
@@ -516,11 +516,10 @@ public:
       //   while holding mMonitor, because otherwise, if the overscrolled APZC
       //   is this one, then the SetState(NOTHING) in UpdateAnimation will
       //   stomp on the SetState(SNAP_BACK) it does.
-      if (!mDeferredTasks.append(NewRunnableMethod(mOverscrollHandoffChain.get(),
-                                                   &OverscrollHandoffChain::SnapBackOverscrolledApzc,
-                                                   &mApzc))) {
-        MOZ_CRASH();
-      }
+      mDeferredTasks.AppendElement(
+            NewRunnableMethod(mOverscrollHandoffChain.get(),
+                              &OverscrollHandoffChain::SnapBackOverscrolledApzc,
+                              &mApzc));
       return false;
     }
 
@@ -569,13 +568,12 @@ public:
       // the lock ordering. Instead we schedule HandleFlingOverscroll() to be
       // called after mMonitor is released.
       APZC_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str());
-      if (!mDeferredTasks.append(NewRunnableMethod(&mApzc,
-                                                   &AsyncPanZoomController::HandleFlingOverscroll,
-                                                   velocity,
-                                                   mOverscrollHandoffChain,
-                                                   mScrolledApzc))) {
-        MOZ_CRASH();
-      }
+      mDeferredTasks.AppendElement(
+            NewRunnableMethod(&mApzc,
+                              &AsyncPanZoomController::HandleFlingOverscroll,
+                              velocity,
+                              mOverscrollHandoffChain,
+                              mScrolledApzc));
 
       // If there is a remaining velocity on this APZC, continue this fling
       // as well. (This fling and the handed-off fling will run concurrently.)
@@ -703,10 +701,9 @@ public:
       // The scroll snapping is done in a deferred task, otherwise the state
       // change to NOTHING caused by the overscroll animation ending would
       // clobber a possible state change to SMOOTH_SCROLL in ScrollSnap().
-      if (!mDeferredTasks.append(NewRunnableMethod(&mApzc,
-                                                   &AsyncPanZoomController::ScrollSnap))) {
-        MOZ_CRASH();
-      }
+      mDeferredTasks.AppendElement(
+            NewRunnableMethod(&mApzc,
+            &AsyncPanZoomController::ScrollSnap));
       return false;
     }
     return true;
@@ -742,11 +739,19 @@ public:
    * frame. Returns true if the smooth scroll should be advanced by one frame,
    * or false if the smooth scroll has ended.
    */
-  bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) {
+  bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override {
     nsPoint oneParentLayerPixel =
       CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
     if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
         mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
+      // Set the scroll offset to the exact destination. If we allow the scroll
+      // offset to end up being a bit off from the destination, we can get
+      // artefacts like "scroll to the next snap point in this direction"
+      // scrolling to the snap point we're already supposed to be at.
+      aFrameMetrics.SetScrollOffset(
+          aFrameMetrics.CalculateScrollRange().ClampPoint(
+              CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetDestination(),
+                                             mYAxisModel.GetDestination()))));
       return false;
     }
 
@@ -814,12 +819,10 @@ public:
       // HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate
       // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be
       // called after mMonitor is released.
-      if (!mDeferredTasks.append(NewRunnableMethod(&mApzc,
-                                                   &AsyncPanZoomController::HandleSmoothScrollOverscroll,
-                                                   velocity))) {
-        MOZ_CRASH();
-      }
-
+      mDeferredTasks.AppendElement(
+            NewRunnableMethod(&mApzc,
+                              &AsyncPanZoomController::HandleSmoothScrollOverscroll,
+                              velocity));
       return false;
     }
 
@@ -831,6 +834,15 @@ public:
     mYAxisModel.SetDestination(static_cast(aNewDestination.y));
   }
 
+  CSSPoint GetDestination() const {
+    return CSSPoint::FromAppUnits(
+        nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
+  }
+
+  SmoothScrollAnimation* AsSmoothScrollAnimation() override {
+    return this;
+  }
+
 private:
   AsyncPanZoomController& mApzc;
   AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
@@ -1236,6 +1248,11 @@ nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent
   return rv;
 }
 
+void AsyncPanZoomController::HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY)
+{
+  mY.HandleTouchVelocity(aTimesampMs, aSpeedY);
+}
+
 nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   mPanDirRestricted = false;
@@ -1628,6 +1645,14 @@ AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, CSSPoint* a
   return false;
 }
 
+static bool
+AllowsScrollingMoreThanOnePage(double aMultiplier)
+{
+  const int32_t kMinAllowPageScroll =
+    EventStateManager::MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
+  return Abs(aMultiplier) >= kMinAllowPageScroll;
+}
+
 ParentLayerPoint
 AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) const
 {
@@ -1698,12 +1723,16 @@ AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) cons
     }
   }
 
-  if (Abs(delta.x) > pageScrollSize.width) {
+  // We shouldn't scroll more than one page at once except when the
+  // user preference is large.
+  if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierX) &&
+      Abs(delta.x) > pageScrollSize.width) {
     delta.x = (delta.x >= 0)
               ? pageScrollSize.width
               : -pageScrollSize.width;
   }
-  if (Abs(delta.y) > pageScrollSize.height) {
+  if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierY) &&
+      Abs(delta.y) > pageScrollSize.height) {
     delta.y = (delta.y >= 0)
               ? pageScrollSize.height
               : -pageScrollSize.height;
@@ -1814,8 +1843,16 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
       (uint32_t) ScrollInputMethodForWheelDeltaType(aEvent.mDeltaType));
 
+
   switch (aEvent.mScrollMode) {
     case ScrollWheelInput::SCROLLMODE_INSTANT: {
+
+      // Wheel events from "clicky" mouse wheels trigger scroll snapping to the
+      // next snap point. Check for this, and adjust the delta to take into
+      // account the snap point.
+      CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+      MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition);
+
       ScreenPoint distance = ToScreenCoordinates(
         ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
 
@@ -1845,6 +1882,25 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
       // update it.
       ReentrantMonitorAutoEnter lock(mMonitor);
 
+      // Perform scroll snapping if appropriate.
+      CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+      // If we're already in a wheel scroll or smooth scroll animation,
+      // the delta is applied to its destination, not to the current
+      // scroll position. Take this into account when finding a snap point.
+      if (mState == WHEEL_SCROLL) {
+        startPosition = mAnimation->AsWheelScrollAnimation()->GetDestination();
+      } else if (mState == SMOOTH_SCROLL) {
+        startPosition = mAnimation->AsSmoothScrollAnimation()->GetDestination();
+      }
+      if (MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition)) {
+        // If we're scroll snapping, use a smooth scroll animation to get
+        // the desired physics. Note that SmoothScrollTo() will re-use an
+        // existing smooth scroll animation if there is one.
+        SmoothScrollTo(startPosition);
+        break;
+      }
+
+      // Otherwise, use a wheel scroll animation, also reusing one if possible.
       if (mState != WHEEL_SCROLL) {
         CancelAnimation();
         SetState(WHEEL_SCROLL);
@@ -2815,7 +2871,6 @@ const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
 
   CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
   CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
-  CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
 
   // Calculate the displayport size based on how fast we're moving along each axis.
@@ -2825,18 +2880,22 @@ const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
     RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
   }
 
+  // We calculate a "displayport" here which is relative to the scroll offset.
+  // Note that the scroll offset we have here in the APZ code may not be the
+  // same as the base rect that gets used on the layout side when the displayport
+  // margins are actually applied, so it is important to only consider the
+  // displayport as margins relative to a scroll offset rather than relative to
+  // something more unchanging like the scrollable rect origin.
+
+  // Center the displayport based on its expansion over the composition size.
+  CSSRect displayPort((compositionSize.width - displayPortSize.width) / 2.0f,
+                      (compositionSize.height - displayPortSize.height) / 2.0f,
+                      displayPortSize.width, displayPortSize.height);
+
   // Offset the displayport, depending on how fast we're moving and the
   // estimated time it takes to paint, to try to minimise checkerboarding.
   float paintFactor = kDefaultEstimatedPaintDurationMs;
-  CSSRect displayPort = CSSRect(scrollOffset + (velocity * paintFactor * gfxPrefs::APZVelocityBias()),
-                                displayPortSize);
-
-  // Re-center the displayport based on its expansion over the composition size.
-  displayPort.MoveBy((compositionSize.width - displayPort.width)/2.0f,
-                     (compositionSize.height - displayPort.height)/2.0f);
-
-  // Make sure the displayport remains within the scrollable rect.
-  displayPort = displayPort.MoveInsideAndClamp(scrollableRect) - scrollOffset;
+  displayPort.MoveBy(velocity * paintFactor * gfxPrefs::APZVelocityBias());
 
   APZC_LOG_FM(aFrameMetrics,
     "Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
@@ -3001,7 +3060,7 @@ AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
 }
 
 bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
-                                             Vector* aOutDeferredTasks)
+                                             nsTArray* aOutDeferredTasks)
 {
   APZThreadUtils::AssertOnCompositorThread();
 
@@ -3035,8 +3094,15 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
   return false;
 }
 
-AsyncTransformComponentMatrix AsyncPanZoomController::GetOverscrollTransform() const {
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetOverscrollTransform(AsyncMode aMode) const
+{
   ReentrantMonitorAutoEnter lock(mMonitor);
+
+  if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) {
+    return AsyncTransformComponentMatrix();
+  }
+
   if (!IsOverscrolled()) {
     return AsyncTransformComponentMatrix();
   }
@@ -3095,7 +3161,7 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
   // responsibility to schedule a composite.
   mAsyncTransformAppliedToContent = false;
   bool requestAnimationFrame = false;
-  Vector deferredTasks;
+  nsTArray deferredTasks;
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
@@ -3117,7 +3183,7 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
   // UpdateAnimation()). This needs to be done after the monitor is released
   // since the tasks are allowed to call APZCTreeManager methods which can grab
   // the tree lock.
-  for (uint32_t i = 0; i < deferredTasks.length(); ++i) {
+  for (uint32_t i = 0; i < deferredTasks.Length(); ++i) {
     deferredTasks[i]->Run();
     delete deferredTasks[i];
   }
@@ -3129,18 +3195,28 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
   return requestAnimationFrame;
 }
 
-void AsyncPanZoomController::SampleContentTransformForFrame(AsyncTransform* aOutTransform,
-                                                            ParentLayerPoint& aScrollOffset)
+ParentLayerPoint
+AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncMode aMode) const
 {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
-  aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
-  *aOutTransform = GetCurrentAsyncTransform();
+  if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) {
+    return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom();
+  }
+
+  return (mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset)
+      * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
 }
 
-AsyncTransform AsyncPanZoomController::GetCurrentAsyncTransform() const {
+AsyncTransform
+AsyncPanZoomController::GetCurrentAsyncTransform(AsyncMode aMode) const
+{
   ReentrantMonitorAutoEnter lock(mMonitor);
 
+  if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) {
+    return AsyncTransform();
+  }
+
   CSSPoint lastPaintScrollOffset;
   if (mLastContentPaintMetrics.IsScrollable()) {
     lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
@@ -3175,9 +3251,11 @@ AsyncTransform AsyncPanZoomController::GetCurrentAsyncTransform() const {
     -translation);
 }
 
-AsyncTransformComponentMatrix AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll() const {
-  return AsyncTransformComponentMatrix(GetCurrentAsyncTransform())
-       * GetOverscrollTransform();
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const
+{
+  return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode))
+       * GetOverscrollTransform(aMode);
 }
 
 Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
@@ -3879,13 +3957,14 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() {
   }
 }
 
-void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
+Maybe AsyncPanZoomController::FindSnapPointNear(
+    const CSSPoint& aDestination, nsIScrollableFrame::ScrollUnit aUnit) {
   mMonitor.AssertCurrentThreadIn();
   APZC_LOG("%p scroll snapping near %s\n", this, Stringify(aDestination).c_str());
   CSSRect scrollRange = mFrameMetrics.CalculateScrollRange();
   if (Maybe snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
           mScrollMetadata.GetSnapInfo(),
-          nsIScrollableFrame::DEVICE_PIXELS,
+          aUnit,
           CSSSize::ToAppUnits(mFrameMetrics.CalculateCompositedSizeInCssPixels()),
           CSSRect::ToAppUnits(scrollRange),
           CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()),
@@ -3895,8 +3974,17 @@ void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
     // of the scroll frame's scroll range. Clamp it here (this matches the
     // behaviour of the main-thread code path, which clamps it in
     // nsGfxScrollFrame::ScrollTo()).
-    cssSnapPoint = scrollRange.ClampPoint(cssSnapPoint);
-    SmoothScrollTo(cssSnapPoint);
+    return Some(scrollRange.ClampPoint(cssSnapPoint));
+  }
+  return Nothing();
+}
+
+void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
+  if (Maybe snapPoint =
+        FindSnapPointNear(aDestination, nsIScrollableFrame::DEVICE_PIXELS)) {
+    if (*snapPoint != mFrameMetrics.GetScrollOffset()) {
+      SmoothScrollTo(*snapPoint);
+    }
   }
 }
 
@@ -3938,5 +4026,31 @@ void AsyncPanZoomController::ScrollSnapToDestination() {
   }
 }
 
+bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
+    const ScrollWheelInput& aEvent,
+    ParentLayerPoint& aDelta,
+    CSSPoint& aStartPosition)
+{
+  // Don't scroll snap for pixel scrolls. This matches the main thread
+  // behaviour in EventStateManager::DoScrollText().
+  if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PIXEL) {
+    return false;
+  }
+
+  ReentrantMonitorAutoEnter lock(mMonitor);
+  CSSToParentLayerScale2D zoom = mFrameMetrics.GetZoom();
+  CSSPoint destination = mFrameMetrics.CalculateScrollRange().ClampPoint(
+      aStartPosition + (aDelta / zoom));
+  nsIScrollableFrame::ScrollUnit unit =
+      ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType);
+
+  if (Maybe snapPoint = FindSnapPointNear(destination, unit)) {
+    aDelta = (*snapPoint - aStartPosition) * zoom;
+    aStartPosition = *snapPoint;
+    return true;
+  }
+  return false;
+}
+
 } // namespace layers
 } // namespace mozilla
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h
index eec8e971aa..b822c0a5d2 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -25,7 +25,9 @@
 #include "Layers.h"                     // for Layer::ScrollDirection
 #include "LayersTypes.h"
 #include "mozilla/gfx/Matrix.h"
+#include "nsIScrollableFrame.h"
 #include "nsRegion.h"
+#include "nsTArray.h"
 #include "PotentialCheckerboardDurationTracker.h"
 
 #include "base/message_loop.h"
@@ -160,22 +162,7 @@ public:
   bool AdvanceAnimations(const TimeStamp& aSampleTime);
 
   bool UpdateAnimation(const TimeStamp& aSampleTime,
-                       Vector* aOutDeferredTasks);
-
-  /**
-   * Query the transforms that should be applied to the layer corresponding
-   * to this APZC due to asynchronous panning and zooming.
-   * This function returns the async transform via the |aOutTransform|
-   * out parameter.
-   */
-  void SampleContentTransformForFrame(AsyncTransform* aOutTransform,
-                                      ParentLayerPoint& aScrollOffset);
-
-  /**
-   * Return a visual effect that reflects this apzc's
-   * overscrolled state, if any.
-   */
-  AsyncTransformComponentMatrix GetOverscrollTransform() const;
+                       nsTArray* aOutDeferredTasks);
 
   /**
    * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata
@@ -215,20 +202,6 @@ public:
    */
   bool IsDestroyed() const;
 
-  /**
-   * Returns the incremental transformation corresponding to the async pan/zoom
-   * in progress. That is, when this transform is multiplied with the layer's
-   * existing transform, it will make the layer appear with the desired pan/zoom
-   * amount.
-   */
-  AsyncTransform GetCurrentAsyncTransform() const;
-
-  /**
-   * Returns the same transform as GetCurrentAsyncTransform(), but includes
-   * any transform due to axis over-scroll.
-   */
-  AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll() const;
-
   /**
    * Returns the transform to take something from the coordinate space of the
    * last thing we know gecko painted, to the coordinate space of the last thing
@@ -285,6 +258,14 @@ public:
    */
   nsEventStatus HandleGestureEvent(const InputData& aEvent);
 
+  /**
+   * Handler for touch velocity.
+   * Sometimes the touch move event will have a velocity even though no scrolling
+   * is occurring such as when the toolbar is being hidden/shown in Fennec.
+   * This function can be called to have the y axis' velocity queue updated.
+   */
+  void HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY);
+
   /**
    * Populates the provided object (if non-null) with the scrollable guid of this apzc.
    */
@@ -636,15 +617,6 @@ protected:
   // Common processing at the end of a touch block.
   void OnTouchEndOrCancel();
 
-  // Snap to a snap position nearby the current scroll position, if appropriate.
-  void ScrollSnap();
-  // Snap to a snap position nearby the destination predicted based on the
-  // current velocity, if appropriate.
-  void ScrollSnapToDestination();
-
-  // Helper function for ScrollSnap() and ScrollSnapToDestination().
-  void ScrollSnapNear(const CSSPoint& aDestination);
-
   uint64_t mLayersId;
   RefPtr mCompositorBridgeParent;
 
@@ -730,6 +702,52 @@ private:
   friend class Axis;
 
 
+  /* ===================================================================
+   * The functions and members in this section are used to expose
+   * the current async transform state to callers.
+   */
+public:
+  /**
+   * Allows callers to specify which type of async transform they want:
+   * NORMAL provides the actual async transforms of the APZC, whereas
+   * RESPECT_FORCE_DISABLE will provide empty async transforms if and only if
+   * the metrics has the mForceDisableApz flag set. In general the latter should
+   * only be used by call sites that are applying the transform to update
+   * a layer's position.
+   */
+  enum AsyncMode {
+    NORMAL,
+    RESPECT_FORCE_DISABLE,
+  };
+
+  /**
+   * Query the transforms that should be applied to the layer corresponding
+   * to this APZC due to asynchronous panning and zooming.
+   * This function returns the async transform via the |aOutTransform|
+   * out parameter.
+   */
+  ParentLayerPoint GetCurrentAsyncScrollOffset(AsyncMode aMode) const;
+
+  /**
+   * Return a visual effect that reflects this apzc's
+   * overscrolled state, if any.
+   */
+  AsyncTransformComponentMatrix GetOverscrollTransform(AsyncMode aMode) const;
+
+  /**
+   * Returns the incremental transformation corresponding to the async pan/zoom
+   * in progress. That is, when this transform is multiplied with the layer's
+   * existing transform, it will make the layer appear with the desired pan/zoom
+   * amount.
+   */
+  AsyncTransform GetCurrentAsyncTransform(AsyncMode aMode) const;
+
+  /**
+   * Returns the same transform as GetCurrentAsyncTransform(), but includes
+   * any transform due to axis over-scroll.
+   */
+  AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const;
+
 
   /* ===================================================================
    * The functions and members in this section are used to manage
@@ -1099,9 +1117,9 @@ public:
   }
 
 private:
-  // Extra offset to add in SampleContentTransformForFrame for testing
+  // Extra offset to add to the async scroll position for testing
   CSSPoint mTestAsyncScrollOffset;
-  // Extra zoom to include in SampleContentTransformForFrame for testing
+  // Extra zoom to include in the aync zoom for testing
   LayerToParentLayerScale mTestAsyncZoom;
   // Flag to track whether or not the APZ transform is not used. This
   // flag is recomputed for every composition frame.
@@ -1123,6 +1141,39 @@ private:
   // be checkerboarding. Combined with other info, this allows us to meaningfully
   // say how frequently users actually encounter checkerboarding.
   PotentialCheckerboardDurationTracker mPotentialCheckerboardTracker;
+
+
+  /* ===================================================================
+   * The functions in this section are used for CSS scroll snapping.
+   */
+
+  // If |aEvent| should trigger scroll snapping, adjust |aDelta| to reflect
+  // the snapping (that is, make it a delta that will take us to the desired
+  // snap point). The delta is interpreted as being relative to
+  // |aStartPosition|, and if a target snap point is found, |aStartPosition|
+  // is also updated, to the value of the snap point.
+  // Returns true iff. a target snap point was found.
+  bool MaybeAdjustDeltaForScrollSnapping(const ScrollWheelInput& aEvent,
+                                         ParentLayerPoint& aDelta,
+                                         CSSPoint& aStartPosition);
+
+  // Snap to a snap position nearby the current scroll position, if appropriate.
+  void ScrollSnap();
+
+  // Snap to a snap position nearby the destination predicted based on the
+  // current velocity, if appropriate.
+  void ScrollSnapToDestination();
+
+  // Snap to a snap position nearby the provided destination, if appropriate.
+  void ScrollSnapNear(const CSSPoint& aDestination);
+
+  // Find a snap point near |aDestination| that we should snap to.
+  // Returns the snap point if one was found, or an empty Maybe otherwise.
+  // |aUnit| affects the snapping behaviour (see ScrollSnapUtils::
+  // GetSnapPointForDestination). It should generally be determined by the
+  // type of event that's triggering the scroll.
+  Maybe FindSnapPointNear(const CSSPoint& aDestination,
+                                    nsIScrollableFrame::ScrollUnit aUnit);
 };
 
 } // namespace layers
diff --git a/gfx/layers/apz/src/Axis.cpp b/gfx/layers/apz/src/Axis.cpp
index efa7e46a39..f89e6c7319 100644
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -30,6 +30,13 @@
 namespace mozilla {
 namespace layers {
 
+// When we compute the velocity we do so by taking two input events and
+// dividing the distance delta over the time delta. In some cases the time
+// delta can be really small, which can make the velocity computation very
+// volatile. To avoid this we impose a minimum time delta below which we do
+// not recompute the velocity.
+const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5;
+
 bool FuzzyEqualsCoordinate(float aValue1, float aValue2)
 {
   return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON)
@@ -40,7 +47,8 @@ extern StaticAutoPtr gVelocityCurveFunction;
 
 Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
   : mPos(0),
-    mPosTimeMs(0),
+    mVelocitySampleTimeMs(0),
+    mVelocitySamplePos(0),
     mVelocity(0.0f),
     mAxisLocked(false),
     mAsyncPanZoomController(aAsyncPanZoomController),
@@ -67,16 +75,35 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord
   // mVelocityQueue is controller-thread only
   APZThreadUtils::AssertOnControllerThread();
 
-  if (aTimestampMs == mPosTimeMs) {
-    // This could be a duplicate event, or it could be a legitimate event
-    // on some platforms that generate events really fast. As a compromise
-    // update mPos so we don't run into problems like bug 1042734, even though
-    // that means the velocity will be stale. Better than doing a divide-by-zero.
+  if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) {
+    // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS.
+    // We still update mPos so that the positioning is correct (and we don't run
+    // into problems like bug 1042734) but the velocity will remain where it was.
+    // In particular we don't update either mVelocitySampleTimeMs or
+    // mVelocitySamplePos so that eventually when we do get an event with the
+    // required time delta we use the corresponding distance delta as well.
+    AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n",
+        mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs));
     mPos = aPos;
     return;
   }
 
-  float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
+  float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs);
+
+  newVelocity = ApplyFlingCurveToVelocity(newVelocity);
+
+  AXIS_LOG("%p|%s updating velocity to %f with touch\n",
+    mAsyncPanZoomController, Name(), newVelocity);
+  mVelocity = newVelocity;
+  mPos = aPos;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
+
+  AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
+float Axis::ApplyFlingCurveToVelocity(float aVelocity) const {
+  float newVelocity = aVelocity;
   if (gfxPrefs::APZMaxVelocity() > 0.0f) {
     bool velocityIsNegative = (newVelocity < 0);
     newVelocity = fabs(newVelocity);
@@ -105,23 +132,31 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord
     }
   }
 
-  AXIS_LOG("%p|%s updating velocity to %f with touch\n",
-    mAsyncPanZoomController, Name(), newVelocity);
-  mVelocity = newVelocity;
-  mPos = aPos;
-  mPosTimeMs = aTimestampMs;
+  return newVelocity;
+}
 
-  // Limit queue size pased on pref
-  mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, mVelocity));
+void Axis::AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity) {
+  mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, aVelocity));
   if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
+void Axis::HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed) {
+  // mVelocityQueue is controller-thread only
+  APZThreadUtils::AssertOnControllerThread();
+
+  mVelocity = ApplyFlingCurveToVelocity(aSpeed);
+  mVelocitySampleTimeMs = aTimestampMs;
+
+  AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
 void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
   mStartPos = aPos;
   mPos = aPos;
-  mPosTimeMs = aTimestampMs;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
   mAxisLocked = false;
 }
 
@@ -424,7 +459,7 @@ bool Axis::CanScroll(ParentLayerCoord aDelta) const
     return false;
   }
 
-  return DisplacementWillOverscrollAmount(aDelta) != aDelta;
+  return fabs(DisplacementWillOverscrollAmount(aDelta) - aDelta) > COORDINATE_EPSILON;
 }
 
 CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const
@@ -438,7 +473,7 @@ CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const
   } else if (origin + GetCompositionLength() > GetPageEnd()) {
     result = GetPageEnd() - GetCompositionLength();
   } else {
-    result = origin;
+    return aOrigin;
   }
 
   return result / zoom;
diff --git a/gfx/layers/apz/src/Axis.h b/gfx/layers/apz/src/Axis.h
index 1da9dcf3e4..034377ac6d 100644
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -49,6 +49,13 @@ public:
    */
   void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs);
 
+protected:
+  float ApplyFlingCurveToVelocity(float aVelocity) const;
+  void AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity);
+
+public:
+  void HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed);
+
   /**
    * Notify this Axis that a touch has begun, i.e. the user has put their finger
    * on the screen but has not yet tried to pan.
@@ -251,7 +258,14 @@ public:
 
 protected:
   ParentLayerCoord mPos;
-  uint32_t mPosTimeMs;
+
+  // mVelocitySampleTimeMs and mVelocitySamplePos are the time and position
+  // used in the last velocity sampling. They get updated when a new sample is
+  // taken (which may not happen on every input event, if the time delta is too
+  // small).
+  uint32_t mVelocitySampleTimeMs;
+  ParentLayerCoord mVelocitySamplePos;
+
   ParentLayerCoord mStartPos;
   float mVelocity;      // Units: ParentLayerCoords per millisecond
   bool mAxisLocked;     // Whether movement on this axis is locked.
diff --git a/gfx/layers/apz/src/GestureEventListener.h b/gfx/layers/apz/src/GestureEventListener.h
index 5bd1a6e1e5..0a4fa0891c 100644
--- a/gfx/layers/apz/src/GestureEventListener.h
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -10,7 +10,7 @@
 #include "InputData.h"                  // for MultiTouchInput, etc
 #include "Units.h"
 #include "mozilla/EventForwards.h"      // for nsEventStatus
-#include "nsAutoPtr.h"                  // for nsRefPtr
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"                   // for nsTArray
 
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp
index ce392a98c3..7d397ac712 100644
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -28,6 +28,7 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
   , mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
   , mScrollDir(Layer::NONE)
   , mScrollSize(0)
+  , mIsScrollbarContainer(false)
   , mOverride(EventRegionsOverride::NoOverride)
 {
 if (mIsPrimaryApzcHolder) {
@@ -92,11 +93,15 @@ HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
 }
 
 void
-HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize)
+HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+                                     Layer::ScrollDirection aDir,
+                                     int32_t aScrollSize,
+                                     bool aIsScrollContainer)
 {
   mScrollViewId = aScrollViewId;
   mScrollDir = aDir;
   mScrollSize = aScrollSize;;
+  mIsScrollbarContainer = aIsScrollContainer;
 }
 
 bool
@@ -118,7 +123,7 @@ HitTestingTreeNode::GetScrollSize() const
 bool
 HitTestingTreeNode::IsScrollbarNode() const
 {
-  return (mScrollDir != Layer::NONE);
+  return mIsScrollbarContainer || (mScrollDir != Layer::NONE);
 }
 
 void
@@ -241,7 +246,7 @@ HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
   LayerToParentLayerMatrix4x4 transform = mTransform *
       CompleteAsyncTransform(
         mApzc
-      ? mApzc->GetCurrentAsyncTransformWithOverscroll()
+      ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL)
       : AsyncTransformComponentMatrix());
   return UntransformBy(transform.Inverse(), aPoint);
 }
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.h b/gfx/layers/apz/src/HitTestingTreeNode.h
index f19562914a..4fb7642ddb 100644
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -92,7 +92,10 @@ public:
 
   /* Scrollbar info */
 
-  void SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize);
+  void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+                        Layer::ScrollDirection aDir,
+                        int32_t aScrollSize,
+                        bool aIsScrollContainer);
   bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
   int32_t GetScrollSize() const;
   bool IsScrollbarNode() const;
@@ -124,6 +127,7 @@ private:
   FrameMetrics::ViewID mScrollViewId;
   Layer::ScrollDirection mScrollDir;
   int32_t mScrollSize;
+  bool mIsScrollbarContainer;
 
   /* Let {L,M} be the {layer, scrollable metrics} pair that this node
    * corresponds to in the layer tree. mEventRegions contains the event regions
diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp
index 0b23ebb337..d57893ef82 100644
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -10,6 +10,7 @@
 #include "gfxPrefs.h"                       // for gfxPrefs
 #include "mozilla/MouseEvents.h"
 #include "mozilla/SizePrintfMacros.h"       // for PRIuSIZE
+#include "mozilla/Telemetry.h"              // for Telemetry
 #include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
 #include "OverscrollHandoffState.h"
 
@@ -153,6 +154,13 @@ CancelableBlockState::SetContentResponse(bool aPreventDefault)
   return true;
 }
 
+void
+CancelableBlockState::StartContentResponseTimer()
+{
+  MOZ_ASSERT(mContentResponseTimer.IsNull());
+  mContentResponseTimer = TimeStamp::Now();
+}
+
 bool
 CancelableBlockState::TimeoutContentResponse()
 {
@@ -181,6 +189,12 @@ CancelableBlockState::IsDefaultPrevented() const
   return mPreventDefault;
 }
 
+bool
+CancelableBlockState::HasReceivedAllContentNotifications() const
+{
+  return IsTargetConfirmed() && mContentResponded;
+}
+
 bool
 CancelableBlockState::IsReadyForHandling() const
 {
@@ -204,6 +218,26 @@ CancelableBlockState::DispatchEvent(const InputData& aEvent) const
   GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
 }
 
+void
+CancelableBlockState::RecordContentResponseTime()
+{
+  if (!mContentResponseTimer) {
+    // We might get responses from content even though we didn't wait for them.
+    // In that case, ignore the time on them, because they're not relevant for
+    // tuning our timeout value. Also this function might get called multiple
+    // times on the same input block, so we should only record the time from the
+    // first successful call.
+    return;
+  }
+  if (!HasReceivedAllContentNotifications()) {
+    // Not done yet, we'll get called again
+    return;
+  }
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::CONTENT_RESPONSE_DURATION,
+    (uint32_t)(TimeStamp::Now() - mContentResponseTimer).ToMilliseconds());
+  mContentResponseTimer = TimeStamp();
+}
+
 DragBlockState::DragBlockState(const RefPtr& aTargetApzc,
                                bool aTargetConfirmed,
                                const MouseInput& aInitialEvent)
@@ -385,15 +419,6 @@ WheelBlockState::AddEvent(const ScrollWheelInput& aEvent)
   mEvents.AppendElement(aEvent);
 }
 
-bool
-WheelBlockState::IsReadyForHandling() const
-{
-  if (!CancelableBlockState::IsReadyForHandling()) {
-    return false;
-  }
-  return true;
-}
-
 bool
 WheelBlockState::HasEvents() const
 {
@@ -654,6 +679,13 @@ PanGestureBlockState::SetContentResponse(bool aPreventDefault)
   return stateChanged;
 }
 
+bool
+PanGestureBlockState::HasReceivedAllContentNotifications() const
+{
+  return CancelableBlockState::HasReceivedAllContentNotifications()
+      && !mWaitingForContentResponse;
+}
+
 bool
 PanGestureBlockState::IsReadyForHandling() const
 {
@@ -721,6 +753,13 @@ TouchBlockState::CopyPropertiesFrom(const TouchBlockState& aOther)
   mTransformToApzc = aOther.mTransformToApzc;
 }
 
+bool
+TouchBlockState::HasReceivedAllContentNotifications() const
+{
+  return CancelableBlockState::HasReceivedAllContentNotifications()
+      && (!gfxPrefs::TouchActionEnabled() || mAllowedTouchBehaviorSet);
+}
+
 bool
 TouchBlockState::IsReadyForHandling() const
 {
diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h
index 4648dfecb0..03ba5e1595 100644
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -8,10 +8,11 @@
 #define mozilla_layers_InputBlockState_h
 
 #include "InputData.h"                      // for MultiTouchInput
+#include "mozilla/RefPtr.h"                 // for RefPtr
 #include "mozilla/gfx/Matrix.h"             // for Matrix4x4
 #include "mozilla/layers/APZUtils.h"        // for TouchBehaviorFlags
 #include "mozilla/layers/AsyncDragMetrics.h"
-#include "nsAutoPtr.h"                      // for nsRefPtr
+#include "mozilla/TimeStamp.h"              // for TimeStamp
 #include "nsTArray.h"                       // for nsTArray
 #include "TouchCounter.h"
 
@@ -121,6 +122,20 @@ public:
    */
   virtual bool SetContentResponse(bool aPreventDefault);
 
+  /**
+   * This should be called when this block is starting to wait for the
+   * necessary content response notifications. It is used to gather data
+   * on how long the content response notifications take.
+   */
+  void StartContentResponseTimer();
+
+  /**
+   * This should be called when a content response notification has been
+   * delivered to this block. If all the notifications have arrived, this
+   * will report the total time take to telemetry.
+   */
+  void RecordContentResponseTime();
+
   /**
    * Record that content didn't respond in time.
    * @return false if this block already timed out, true if not.
@@ -150,6 +165,12 @@ public:
    */
   virtual void DispatchEvent(const InputData& aEvent) const;
 
+  /**
+   * @return true iff this block has received all the information it could
+   *         have gotten from the content thread.
+   */
+  virtual bool HasReceivedAllContentNotifications() const;
+
   /**
    * @return true iff this block has received all the information needed
    *         to properly dispatch the events in the block.
@@ -184,6 +205,7 @@ public:
   virtual const char* Type() = 0;
 
 private:
+  TimeStamp mContentResponseTimer;
   bool mPreventDefault;
   bool mContentResponded;
   bool mContentResponseTimerExpired;
@@ -200,7 +222,6 @@ public:
                   const ScrollWheelInput& aEvent);
 
   bool SetContentResponse(bool aPreventDefault) override;
-  bool IsReadyForHandling() const override;
   bool HasEvents() const override;
   void DropEvents() override;
   void HandleEvents() override;
@@ -320,6 +341,7 @@ public:
                        const PanGestureInput& aEvent);
 
   bool SetContentResponse(bool aPreventDefault) override;
+  bool HasReceivedAllContentNotifications() const override;
   bool IsReadyForHandling() const override;
   bool HasEvents() const override;
   void DropEvents() override;
@@ -398,6 +420,12 @@ public:
    */
   void CopyPropertiesFrom(const TouchBlockState& aOther);
 
+  /*
+   * @return true iff this block has received all the information it could
+   *         have gotten from the content thread.
+   */
+  bool HasReceivedAllContentNotifications() const override;
+
   /**
    * @return true iff this block has received all the information needed
    *         to properly dispatch the events in the block.
diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp
index 66f2b14f99..321d1e183e 100644
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -423,7 +423,7 @@ InputQueue::MaybeRequestContentResponse(const RefPtr& aT
     // need to wait to give content the opportunity to prevent-default the
     // touch events. Either way we schedule a timeout so the main thread stuff
     // can run.
-    ScheduleMainThreadTimeout(aTarget, aBlock->GetBlockId());
+    ScheduleMainThreadTimeout(aTarget, aBlock);
   }
 }
 
@@ -435,7 +435,7 @@ InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
     /* aCopyPropertiesFromCurrent = */ true);
   INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
     block, block->GetBlockId(), aTarget);
-  ScheduleMainThreadTimeout(aTarget, block->GetBlockId());
+  ScheduleMainThreadTimeout(aTarget, block);
   return block->GetBlockId();
 }
 
@@ -562,10 +562,12 @@ InputQueue::IsDragOnScrollbar(bool aHitScrollbar)
 }
 
 void
-InputQueue::ScheduleMainThreadTimeout(const RefPtr& aTarget, uint64_t aInputBlockId) {
+InputQueue::ScheduleMainThreadTimeout(const RefPtr& aTarget,
+                                      CancelableBlockState* aBlock) {
   INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
+  aBlock->StartContentResponseTimer();
   aTarget->PostDelayedTask(
-    NewRunnableMethod(this, &InputQueue::MainThreadTimeout, aInputBlockId),
+    NewRunnableMethod(this, &InputQueue::MainThreadTimeout, aBlock->GetBlockId()),
     gfxPrefs::APZContentResponseTimeout());
 }
 
@@ -597,9 +599,10 @@ InputQueue::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefau
   INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
   for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
-      CancelableBlockState* block = mInputBlockQueue[i].get();
+    CancelableBlockState* block = mInputBlockQueue[i].get();
+    if (block->GetBlockId() == aInputBlockId) {
       success = block->SetContentResponse(aPreventDefault);
+      block->RecordContentResponseTime();
       break;
     }
   }
@@ -616,8 +619,10 @@ InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtrGetGuid()).c_str() : "");
   bool success = false;
   for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
-      success = mInputBlockQueue[i]->SetConfirmedTargetApzc(aTargetApzc);
+    CancelableBlockState* block = mInputBlockQueue[i].get();
+    if (block->GetBlockId() == aInputBlockId) {
+      success = block->SetConfirmedTargetApzc(aTargetApzc);
+      block->RecordContentResponseTime();
       break;
     }
   }
@@ -640,6 +645,7 @@ InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtrGetBlockId() == aInputBlockId) {
       block->SetDragMetrics(aDragMetrics);
       success = block->SetConfirmedTargetApzc(aTargetApzc);
+      block->RecordContentResponseTime();
       break;
     }
   }
@@ -659,6 +665,7 @@ InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArrayAsTouchBlock();
       if (block) {
         success = block->SetAllowedTouchBehaviors(aBehaviors);
+        block->RecordContentResponseTime();
       } else {
         NS_WARNING("input block is not a touch block");
       }
diff --git a/gfx/layers/apz/src/InputQueue.h b/gfx/layers/apz/src/InputQueue.h
index 884290383b..e38d2d20ae 100644
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -10,8 +10,8 @@
 #include "DragTracker.h"
 #include "InputData.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
-#include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "TouchCounter.h"
 
@@ -175,7 +175,8 @@ private:
   bool MaybeHandleCurrentBlock(CancelableBlockState* block,
                                const InputData& aEvent);
 
-  void ScheduleMainThreadTimeout(const RefPtr& aTarget, uint64_t aInputBlockId);
+  void ScheduleMainThreadTimeout(const RefPtr& aTarget,
+                                 CancelableBlockState* aBlock);
   void MainThreadTimeout(const uint64_t& aInputBlockId);
   void ProcessInputBlocks();
   void UpdateActiveApzc(const RefPtr& aNewActive);
diff --git a/gfx/layers/apz/src/OverscrollHandoffState.h b/gfx/layers/apz/src/OverscrollHandoffState.h
index 6ceb9e6f88..699965a6d6 100644
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -8,7 +8,7 @@
 #define mozilla_layers_OverscrollHandoffChain_h
 
 #include 
-#include "nsAutoPtr.h"
+#include "mozilla/RefPtr.h"   // for RefPtr
 #include "nsISupportsImpl.h"  // for NS_INLINE_DECL_THREADSAFE_REFCOUNTING
 #include "APZCTreeManager.h"
 #include "APZUtils.h"         // for CancelAnimationFlags
diff --git a/gfx/layers/apz/src/WheelScrollAnimation.h b/gfx/layers/apz/src/WheelScrollAnimation.h
index c7221c3b61..ae122cae41 100644
--- a/gfx/layers/apz/src/WheelScrollAnimation.h
+++ b/gfx/layers/apz/src/WheelScrollAnimation.h
@@ -34,6 +34,10 @@ public:
     return this;
   }
 
+  CSSPoint GetDestination() const {
+    return CSSPoint::FromAppUnits(mFinalDestination);
+  }
+
 private:
   void InitPreferences(TimeStamp aTime);
 
diff --git a/gfx/layers/apz/test/gtest/APZCBasicTester.h b/gfx/layers/apz/test/gtest/APZCBasicTester.h
index 9debd9106a..57b28da245 100644
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -96,7 +96,7 @@ protected:
 
       // Trigger computation of the overscroll tranform, to make sure
       // no assetions fire during the calculation.
-      apzc->GetOverscrollTransform();
+      apzc->GetOverscrollTransform(AsyncPanZoomController::NORMAL);
 
       if (!apzc->IsOverscrolled()) {
         recoveredFromOverscroll = true;
diff --git a/gfx/layers/apz/test/gtest/APZTestCommon.h b/gfx/layers/apz/test/gtest/APZTestCommon.h
index 141bfd346b..dfb4dd5875 100644
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -247,8 +247,10 @@ public:
                                       const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
     mcc->AdvanceBy(aIncrement);
     bool ret = AdvanceAnimations(mcc->Time());
-    AsyncPanZoomController::SampleContentTransformForFrame(
-      aOutTransform, aScrollOffset);
+    if (aOutTransform) {
+      *aOutTransform = GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL);
+    }
+    aScrollOffset = GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
     return ret;
   }
 
diff --git a/gfx/layers/apz/test/gtest/TestHitTesting.cpp b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
index 6232d9b0f4..de2b17deb5 100644
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -162,6 +162,8 @@ TEST_F(APZHitTestingTester, HitTesting1) {
 
 // A more involved hit testing test that involves css and async transforms.
 TEST_F(APZHitTestingTester, HitTesting2) {
+  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+
   CreateHitTesting2LayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
diff --git a/gfx/layers/apz/test/gtest/TestPanning.cpp b/gfx/layers/apz/test/gtest/TestPanning.cpp
index 8d701d15ae..835079e65b 100644
--- a/gfx/layers/apz/test/gtest/TestPanning.cpp
+++ b/gfx/layers/apz/test/gtest/TestPanning.cpp
@@ -80,6 +80,7 @@ protected:
 
 TEST_F(APZCPanningTester, Pan) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
   DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
 }
 
@@ -93,22 +94,26 @@ TEST_F(APZCPanningTester, Pan) {
 // events.
 TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
   DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
                       | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
 }
 
 TEST_F(APZCPanningTester, PanWithTouchActionNone) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
   DoPanTest(false, false, 0);
 }
 
 TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
   DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
 }
 
 TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
   DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
 }
 
diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp
index ea77b3ee18..b386941f84 100644
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -49,13 +49,6 @@
 #include "mozilla/layers/GrallocTextureClient.h"
 #endif
 
-#ifdef MOZ_WIDGET_ANDROID
-#  include "gfxReusableImageSurfaceWrapper.h"
-#else
-#  include "gfxReusableSharedImageSurfaceWrapper.h"
-#  include "gfxSharedImageSurface.h"
-#endif
-
 #if 0
 #define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
 #else
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
index 5bd07ac5b7..5895a785d5 100644
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -24,7 +24,6 @@
 #include "TextureClientPool.h"
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for gfxContext::AddRef, etc
-#include "gfxReusableSharedImageSurfaceWrapper.h"
 #include "nsExpirationTracker.h"        // for nsExpirationTracker
 #include "nsMathUtils.h"               // for NS_lroundf
 #include "LayersLogging.h"
diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h
index 3d2311040b..3c75c8c0d4 100644
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -35,7 +35,6 @@
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "nsExpirationTracker.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
-#include "gfxReusableSurfaceWrapper.h"
 #include "pratom.h"                     // For PR_ATOMIC_INCREMENT/DECREMENT
 
 namespace mozilla {
diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp
index 4654c623c5..4a0dfc52a9 100644
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -845,11 +845,10 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
 
     hasAsyncTransform = true;
 
-    AsyncTransform asyncTransformWithoutOverscroll;
-    ParentLayerPoint scrollOffset;
-    controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
-                                               scrollOffset);
-    AsyncTransformComponentMatrix overscrollTransform = controller->GetOverscrollTransform();
+    AsyncTransform asyncTransformWithoutOverscroll =
+        controller->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+    AsyncTransformComponentMatrix overscrollTransform =
+        controller->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
     AsyncTransformComponentMatrix asyncTransform =
         AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll)
       * overscrollTransform;
@@ -882,6 +881,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
                                 geckoZoom,
                                 mContentRect);
         } else {
+          ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(
+              AsyncPanZoomController::RESPECT_FORCE_DISABLE);
           // Compute the painted displayport in document-relative CSS pixels.
           CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
               metrics.GetDisplayPort() :
@@ -1032,7 +1033,8 @@ ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
   const FrameMetrics& metrics = aContent.Metrics();
   AsyncPanZoomController* apzc = aContent.GetApzc();
 
-  AsyncTransformComponentMatrix asyncTransform = apzc->GetCurrentAsyncTransform();
+  AsyncTransformComponentMatrix asyncTransform =
+    apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
 
   // |asyncTransform| represents the amount by which we have scrolled and
   // zoomed since the last paint. Because the scrollbar was sized and positioned based
@@ -1147,7 +1149,9 @@ ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
   // the same coordinate space. This requires applying the content transform
   // and then unapplying it after unapplying the async transform.
   if (aScrollbarIsDescendant) {
-    Matrix4x4 asyncUntransform = (asyncTransform * apzc->GetOverscrollTransform()).Inverse().ToUnknownMatrix();
+    AsyncTransformComponentMatrix overscroll =
+        apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+    Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
     Matrix4x4 contentTransform = aContent.GetTransform();
     Matrix4x4 contentUntransform = contentTransform.Inverse();
 
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
index c7d0362272..19a601d134 100755
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -472,10 +472,7 @@ RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
     return;
   }
 
-  AsyncTransform asyncTransformWithoutOverscroll;
-  ParentLayerPoint scrollOffset;
-  controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
-                                           scrollOffset);
+  ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
 
   // Options
   const int verticalPadding = 10;
@@ -659,8 +656,9 @@ RenderLayers(ContainerT* aContainer,
                                                    gfx::Rect(aClipRect.ToUnknownRect()),
                                                    asyncTransform * aContainer->GetEffectiveTransform());
         if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
-          asyncTransform = apzc->GetCurrentAsyncTransformWithOverscroll().ToUnknownMatrix()
-                         * asyncTransform;
+          asyncTransform =
+              apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::RESPECT_FORCE_DISABLE).ToUnknownMatrix()
+            * asyncTransform;
         }
       }
     }
@@ -809,7 +807,7 @@ ContainerRender(ContainerT* aContainer,
     for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
       if (AsyncPanZoomController* apzc = i.GetApzc()) {
         if (!apzc->GetAsyncTransformAppliedToContent()
-            && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
+            && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL)).IsIdentity()) {
           aManager->UnusedApzTransformWarning();
           break;
         }
diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp
index 94cc69d017..119f3d3505 100644
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -21,8 +21,6 @@
 #include "mozilla/layers/TiledContentClient.h"
 #include "gfxPrefs.h"
 
-class gfxReusableSurfaceWrapper;
-
 namespace mozilla {
 using namespace gfx;
 namespace layers {
diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp
index b9a4a9e5fb..d54a1afeb7 100644
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -11,7 +11,6 @@
 #include "GLUploadHelpers.h"
 #include "GLReadTexImageHelper.h"
 #include "gfx2DGlue.h"                  // for ContentForFormat, etc
-#include "gfxReusableSurfaceWrapper.h"  // for gfxReusableSurfaceWrapper
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Logging.h"        // for gfxCriticalError
diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp
index 7a5ace3799..d407326574 100644
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -251,7 +251,7 @@ nsDeviceContext::IsPrinterSurface()
 }
 
 void
-nsDeviceContext::SetDPI()
+nsDeviceContext::SetDPI(double* aScale)
 {
     float dpi = -1.0f;
 
@@ -280,9 +280,21 @@ nsDeviceContext::SetDPI()
             dpi = 96.0f;
         }
 
-        CSSToLayoutDeviceScale scale = mWidget ? mWidget->GetDefaultScale()
-                                               : CSSToLayoutDeviceScale(1.0);
-        double devPixelsPerCSSPixel = scale.scale;
+        double devPixelsPerCSSPixel;
+        if (aScale && *aScale > 0.0) {
+            // if caller provided a scale, we just use it
+            devPixelsPerCSSPixel = *aScale;
+        } else {
+            // otherwise get from the widget, and return it in aScale for
+            // the caller to pass to child contexts if needed
+            CSSToLayoutDeviceScale scale =
+                mWidget ? mWidget->GetDefaultScale()
+                        : CSSToLayoutDeviceScale(1.0);
+            devPixelsPerCSSPixel = scale.scale;
+            if (aScale) {
+                *aScale = devPixelsPerCSSPixel;
+            }
+        }
 
         mAppUnitsPerDevPixelAtUnitFullZoom =
             std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
@@ -637,11 +649,12 @@ nsDeviceContext::CalcPrintingSize()
     return (mWidth > 0 && mHeight > 0);
 }
 
-bool nsDeviceContext::CheckDPIChange() {
+bool nsDeviceContext::CheckDPIChange(double* aScale)
+{
     int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
     int32_t oldInches = mAppUnitsPerPhysicalInch;
 
-    SetDPI();
+    SetDPI(aScale);
 
     return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
         oldInches != mAppUnitsPerPhysicalInch;
diff --git a/gfx/src/nsDeviceContext.h b/gfx/src/nsDeviceContext.h
index 0523abc7c2..03383c18d9 100644
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -219,12 +219,18 @@ public:
     nsresult EndPage();
 
     /**
-     * Check to see if the DPI has changed
+     * Check to see if the DPI has changed, or impose a new DPI scale value.
+     * @param  aScale - If non-null, the default (unzoomed) CSS to device pixel
+     *                  scale factor will be returned here; and if it is > 0.0
+     *                  on input, the given value will be used instead of
+     *                  getting it from the widget (if any). This is used to
+     *                  allow subdocument contexts to inherit the resolution
+     *                  setting of their parent.
      * @return whether there was actually a change in the DPI (whether
      *         AppUnitsPerDevPixel() or AppUnitsPerPhysicalInch()
      *         changed)
      */
-    bool CheckDPIChange();
+    bool CheckDPIChange(double* aScale = nullptr);
 
     /**
      * Set the full zoom factor: all lengths are multiplied by this factor
@@ -249,7 +255,7 @@ private:
     // Private destructor, to discourage deletion outside of Release():
     ~nsDeviceContext();
 
-    void SetDPI();
+    void SetDPI(double* aScale = nullptr);
     void ComputeClientRectUsingScreen(nsRect *outRect);
     void ComputeFullAreaUsingScreen(nsRect *outRect);
     void FindScreen(nsIScreen **outScreen);
diff --git a/gfx/thebes/gfxBaseSharedMemorySurface.h b/gfx/thebes/gfxBaseSharedMemorySurface.h
index a4b28877ef..4d766cb8a7 100644
--- a/gfx/thebes/gfxBaseSharedMemorySurface.h
+++ b/gfx/thebes/gfxBaseSharedMemorySurface.h
@@ -36,7 +36,6 @@ template 
 class gfxBaseSharedMemorySurface : public Base {
     typedef mozilla::ipc::SharedMemory SharedMemory;
     typedef mozilla::ipc::Shmem Shmem;
-    friend class gfxReusableSharedImageSurfaceWrapper;
 
 protected:
     virtual ~gfxBaseSharedMemorySurface()
diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp
index f255f73ba1..fae61a013c 100644
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -1081,6 +1081,9 @@ gfxContext::EnsurePathBuilder()
     Matrix toNewUS = mPathTransform * invTransform;
 
     RefPtr path = mPathBuilder->Finish();
+    if (!path) {
+      gfxCriticalError() << "gfxContext::EnsurePathBuilder failed in PathBuilder::Finish";
+    }
     mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
   }
 
diff --git a/gfx/thebes/gfxDWriteCommon.cpp b/gfx/thebes/gfxDWriteCommon.cpp
index 405a6acd72..3047818bb6 100644
--- a/gfx/thebes/gfxDWriteCommon.cpp
+++ b/gfx/thebes/gfxDWriteCommon.cpp
@@ -8,6 +8,7 @@
 #include 
 
 #include "mozilla/Atomics.h"
+#include "mozilla/gfx/Logging.h"
 
 static mozilla::Atomic sNextFontFileKey;
 static std::unordered_map sFontFileStreams;
@@ -158,6 +159,10 @@ gfxDWriteFontFileLoader::CreateCustomFontFile(FallibleTArray& aFontData
   MOZ_ASSERT(aFontFileStream);
 
   IDWriteFactory *factory = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
+  if (!factory) {
+    gfxCriticalError() << "Failed to get DWrite Factory in CreateCustomFontFile.";
+    return E_FAIL;
+  }
 
   uint64_t fontFileKey = sNextFontFileKey++;
   RefPtr ffsRef = new gfxDWriteFontFileStream(&aFontData, fontFileKey);
diff --git a/gfx/thebes/gfxEnv.h b/gfx/thebes/gfxEnv.h
index 7ee393eeb4..05f71c83e0 100644
--- a/gfx/thebes/gfxEnv.h
+++ b/gfx/thebes/gfxEnv.h
@@ -48,6 +48,10 @@ public:
   // Disabling the crash guard in DriverCrashGuard
   DECL_GFX_ENV_ONCE("MOZ_DISABLE_CRASH_GUARD", DisableCrashGuard);
 
+  // We force present to work around some Windows bugs - disable that if this
+  // environment variable is set.
+  DECL_GFX_ENV_ONCE("MOZ_DISABLE_FORCE_PRESENT", DisableForcePresent);
+
   // Together with paint dumping, only when MOZ_DUMP_PAINTING is defined.
   // Dumping compositor textures is broken pretty badly. For example,
   // on Linux it crashes TextureHost::GetAsSurface() returns null.
diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp
index 2db1028b14..e5fa49beb9 100644
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -325,7 +325,7 @@ GDIFontEntry::TestCharacterMap(uint32_t aCh)
         HFONT hfont = font->GetHFONT();
         HFONT oldFont = (HFONT)SelectObject(dc, hfont);
 
-        wchar_t str[1] = { aCh };
+        wchar_t str[1] = { (wchar_t)aCh };
         WORD glyph[1];
 
         bool hasGlyph = false;
diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp
index f43e802be5..2143e48a12 100644
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -350,13 +350,8 @@ gfxPlatformGtk::GetDPIScale()
 bool
 gfxPlatformGtk::UseImageOffscreenSurfaces()
 {
-    // We want to turn on image offscreen surfaces ONLY for GTK3 builds since
-    // GTK2 theme rendering still requires xlib surfaces.
-#if (MOZ_WIDGET_GTK == 3)
-    return gfxPrefs::UseImageOffscreenSurfaces();
-#else
-    return false;
-#endif
+    return GetDefaultContentBackend() != mozilla::gfx::BackendType::CAIRO ||
+           gfxPrefs::UseImageOffscreenSurfaces();
 }
 
 gfxImageFormat
diff --git a/gfx/thebes/gfxPlatformGtk.h b/gfx/thebes/gfxPlatformGtk.h
index bbfd5b6bc9..51a2d27e5a 100644
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -100,10 +100,6 @@ public:
 
     bool UseXRender() {
 #if defined(MOZ_X11)
-        if (GetDefaultContentBackend() != mozilla::gfx::BackendType::NONE &&
-            GetDefaultContentBackend() != mozilla::gfx::BackendType::CAIRO)
-            return false;
-
         return sUseXRender;
 #else
         return false;
diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
index 83ccd56cc2..142f58f25f 100644
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -437,6 +437,7 @@ private:
   DECL_GFX_PREF(Live, "webgl.disable-angle",                   WebGLDisableANGLE, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-extensions",              WebGLDisableExtensions, bool, false);
   DECL_GFX_PREF(Live, "webgl.dxgl.enabled",                    WebGLDXGLEnabled, bool, false);
+  DECL_GFX_PREF(Live, "webgl.dxgl.needs-finish",               WebGLDXGLNeedsFinish, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
                 WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
diff --git a/gfx/thebes/gfxReusableImageSurfaceWrapper.cpp b/gfx/thebes/gfxReusableImageSurfaceWrapper.cpp
deleted file mode 100644
index 1bbe93a5f3..0000000000
--- a/gfx/thebes/gfxReusableImageSurfaceWrapper.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "gfxReusableImageSurfaceWrapper.h"
-#include "gfxImageSurface.h"
-
-gfxReusableImageSurfaceWrapper::gfxReusableImageSurfaceWrapper(gfxImageSurface* aSurface)
-  : mSurface(aSurface)
-{
-  MOZ_COUNT_CTOR(gfxReusableImageSurfaceWrapper);
-}
-
-gfxReusableImageSurfaceWrapper::~gfxReusableImageSurfaceWrapper()
-{
-  MOZ_COUNT_DTOR(gfxReusableImageSurfaceWrapper);
-}
-
-void
-gfxReusableImageSurfaceWrapper::ReadLock()
-{
-  NS_ASSERT_OWNINGTHREAD(gfxReusableImageSurfaceWrapper);
-  AddRef();
-}
-
-void
-gfxReusableImageSurfaceWrapper::ReadUnlock()
-{
-  Release();
-}
-
-gfxReusableSurfaceWrapper*
-gfxReusableImageSurfaceWrapper::GetWritable(gfxImageSurface** aSurface)
-{
-  NS_ASSERT_OWNINGTHREAD(gfxReusableImageSurfaceWrapper);
-
-  if (mRefCnt == 1) {
-    *aSurface = mSurface;
-    return this;
-  }
-
-  // Something else is reading the surface, copy it
-  gfxImageSurface* copySurface = new gfxImageSurface(mSurface->GetSize(), mSurface->Format(), false);
-  copySurface->CopyFrom(mSurface);
-  *aSurface = copySurface;
-
-  return new gfxReusableImageSurfaceWrapper(copySurface);
-}
-
-const unsigned char*
-gfxReusableImageSurfaceWrapper::GetReadOnlyData() const
-{
-  return mSurface->Data();
-}
-
-gfxImageFormat
-gfxReusableImageSurfaceWrapper::Format()
-{
-  return mSurface->Format();
-}
-
diff --git a/gfx/thebes/gfxReusableImageSurfaceWrapper.h b/gfx/thebes/gfxReusableImageSurfaceWrapper.h
deleted file mode 100644
index f6c340a882..0000000000
--- a/gfx/thebes/gfxReusableImageSurfaceWrapper.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFXMEMCOWSURFACEWRAPPER
-#define GFXMEMCOWSURFACEWRAPPER
-
-#include "gfxReusableSurfaceWrapper.h"
-
-class gfxImageSurface;
-
-/**
- * A cross-thread capable implementation of gfxReusableSurfaceWrapper based
- * on gfxImageSurface.
- */
-class gfxReusableImageSurfaceWrapper : public gfxReusableSurfaceWrapper {
-public:
-  explicit gfxReusableImageSurfaceWrapper(gfxImageSurface* aSurface);
-protected:
-  ~gfxReusableImageSurfaceWrapper();
-
-public:
-  const unsigned char* GetReadOnlyData() const override;
-  gfxImageFormat Format() override;
-  gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface) override;
-  void ReadLock() override;
-  void ReadUnlock() override;
-
-  Type GetType() override
-  {
-    return TYPE_IMAGE;
-  }
-
-private:
-  RefPtr         mSurface;
-};
-
-#endif // GFXMEMCOWSURFACEWRAPPER
diff --git a/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.cpp b/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.cpp
deleted file mode 100644
index 03b25f0180..0000000000
--- a/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "gfxReusableSharedImageSurfaceWrapper.h"
-#include "gfxSharedImageSurface.h"
-#include "mozilla/layers/ISurfaceAllocator.h"
-
-using mozilla::ipc::Shmem;
-using mozilla::layers::ISurfaceAllocator;
-
-gfxReusableSharedImageSurfaceWrapper::gfxReusableSharedImageSurfaceWrapper(ISurfaceAllocator* aAllocator,
-                                                                           gfxSharedImageSurface* aSurface)
-  : mAllocator(aAllocator)
-  , mSurface(aSurface)
-{
-  MOZ_COUNT_CTOR(gfxReusableSharedImageSurfaceWrapper);
-  ReadLock();
-}
-
-gfxReusableSharedImageSurfaceWrapper::~gfxReusableSharedImageSurfaceWrapper()
-{
-  MOZ_COUNT_DTOR(gfxReusableSharedImageSurfaceWrapper);
-  ReadUnlock();
-}
-
-void
-gfxReusableSharedImageSurfaceWrapper::ReadLock()
-{
-  NS_ASSERT_OWNINGTHREAD(gfxReusableSharedImageSurfaceWrapper);
-  mSurface->ReadLock();
-}
-
-void
-gfxReusableSharedImageSurfaceWrapper::ReadUnlock()
-{
-  int32_t readCount = mSurface->ReadUnlock();
-  MOZ_ASSERT(readCount >= 0, "Read count should not be negative");
-
-  if (readCount == 0) {
-    mAllocator->AsShmemAllocator()->DeallocShmem(mSurface->GetShmem());
-  }
-}
-
-gfxReusableSurfaceWrapper*
-gfxReusableSharedImageSurfaceWrapper::GetWritable(gfxImageSurface** aSurface)
-{
-  NS_ASSERT_OWNINGTHREAD(gfxReusableSharedImageSurfaceWrapper);
-
-  int32_t readCount = mSurface->GetReadCount();
-  MOZ_ASSERT(readCount > 0, "A ReadLock must be held when calling GetWritable");
-  if (readCount == 1) {
-    *aSurface = mSurface;
-    return this;
-  }
-
-  // Something else is reading the surface, copy it
-  RefPtr copySurface =
-    gfxSharedImageSurface::CreateUnsafe(mAllocator->AsShmemAllocator(), mSurface->GetSize(), mSurface->Format());
-  copySurface->CopyFrom(mSurface);
-  *aSurface = copySurface;
-
-  // We need to create a new wrapper since this wrapper has an external ReadLock
-  gfxReusableSurfaceWrapper* wrapper = new gfxReusableSharedImageSurfaceWrapper(mAllocator, copySurface);
-
-  // No need to release the ReadLock on the surface, this will happen when
-  // the wrapper is destroyed.
-
-  return wrapper;
-}
-
-const unsigned char*
-gfxReusableSharedImageSurfaceWrapper::GetReadOnlyData() const
-{
-  MOZ_ASSERT(mSurface->GetReadCount() > 0, "Should have read lock");
-  return mSurface->Data();
-}
-
-gfxImageFormat
-gfxReusableSharedImageSurfaceWrapper::Format()
-{
-  return mSurface->Format();
-}
-
-Shmem&
-gfxReusableSharedImageSurfaceWrapper::GetShmem()
-{
-  return mSurface->GetShmem();
-}
-
-/* static */ already_AddRefed
-gfxReusableSharedImageSurfaceWrapper::Open(ISurfaceAllocator* aAllocator, const Shmem& aShmem)
-{
-  RefPtr sharedImage = gfxSharedImageSurface::Open(aShmem);
-  RefPtr wrapper = new gfxReusableSharedImageSurfaceWrapper(aAllocator, sharedImage);
-  wrapper->ReadUnlock();
-  return wrapper.forget();
-}
diff --git a/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.h b/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.h
deleted file mode 100644
index 2c337edc04..0000000000
--- a/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFXSHMCOWSURFACEWRAPPER
-#define GFXSHMCOWSURFACEWRAPPER
-
-#include "gfxReusableSurfaceWrapper.h"
-#include "mozilla/RefPtr.h"
-
-class gfxSharedImageSurface;
-
-namespace mozilla {
-namespace ipc {
-class Shmem;
-} // namespace ipc
-namespace layers {
-class ISurfaceAllocator;
-} // namespace layers
-} // namespace mozilla
-
-/**
- * A cross-process capable implementation of gfxReusableSurfaceWrapper based
- * on gfxSharedImageSurface.
- */
-class gfxReusableSharedImageSurfaceWrapper : public gfxReusableSurfaceWrapper {
-public:
-  gfxReusableSharedImageSurfaceWrapper(mozilla::layers::ISurfaceAllocator* aAllocator,
-                                       gfxSharedImageSurface* aSurface);
-protected:
-  ~gfxReusableSharedImageSurfaceWrapper();
-
-public:
-  const unsigned char* GetReadOnlyData() const override;
-  gfxImageFormat Format() override;
-  gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface) override;
-  void ReadLock() override;
-  void ReadUnlock() override;
-
-  Type GetType() override
-  {
-    return TYPE_SHARED_IMAGE;
-  }
-
-  /**
-   * Returns the shared memory segment that backs the shared image surface.
-   */
-  mozilla::ipc::Shmem& GetShmem();
-
-  /**
-   * Create a gfxReusableSurfaceWrapper from the shared memory segment of a
-   * gfxSharedImageSurface. A ReadLock must be held, which will be adopted by
-   * the returned gfxReusableSurfaceWrapper.
-   */
-  static already_AddRefed
-  Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem);
-
-private:
-  RefPtr mAllocator;
-  RefPtr         mSurface;
-};
-
-#endif // GFXSHMCOWSURFACEWRAPPER
diff --git a/gfx/thebes/gfxReusableSurfaceWrapper.h b/gfx/thebes/gfxReusableSurfaceWrapper.h
deleted file mode 100644
index 831bb1d51d..0000000000
--- a/gfx/thebes/gfxReusableSurfaceWrapper.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFXCOWSURFACEWRAPPER
-#define GFXCOWSURFACEWRAPPER
-
-#include "gfxImageSurface.h"
-#include "nsISupportsImpl.h"
-
-
-/**
- * Provides an interface to implement a cross thread/process wrapper for a
- * gfxImageSurface that has copy-on-write semantics.
- *
- * Only the owner thread can write to the surface and acquire
- * read locks. Destroying a gfxReusableSurfaceWrapper releases
- * a read lock.
- *
- * OMTC Usage:
- * 1) Content creates a writable copy of this surface
- *    wrapper which will be optimized to the same wrapper if there
- *    are no readers.
- * 2) The surface is sent from content to the compositor once
- *    or potentially many times, each increasing a read lock.
- * 3) When the compositor receives the surface, it adopts the
- *    read lock.
- * 4) Once the compositor has processed the surface and uploaded
- *    the content, it then releases the read lock by dereferencing
- *    its wrapper.
- */
-class gfxReusableSurfaceWrapper {
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxReusableSurfaceWrapper)
-public:
-
-  /**
-   * Returns a read-only pointer to the image data.
-   */
-  virtual const unsigned char* GetReadOnlyData() const = 0;
-
-  /**
-   * Returns the image surface format.
-   */
-  virtual gfxImageFormat Format() = 0;
-
-  /**
-   * Returns a writable copy of the image.
-   * If necessary this will copy the wrapper. If there are no contention
-   * the same wrapper will be returned. A ReadLock must be held when
-   * calling this function, and calling it will give up this lock.
-   */
-  virtual gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface) = 0;
-
-  /**
-   * A read only lock count is recorded, any attempts to
-   * call GetWritable() while this count is greater than one will
-   * create a new surface/wrapper pair.
-   *
-   * When a surface's read count falls to zero, its memory will be
-   * deallocated. It is the responsibility of the user to make sure
-   * that all locks are matched with an equal number of unlocks.
-   */
-  virtual void ReadLock() = 0;
-  virtual void ReadUnlock() = 0;
-
-  /**
-   * Types for each implementation of gfxReusableSurfaceWrapper.
-   */
-  enum Type {
-    TYPE_SHARED_IMAGE,
-    TYPE_IMAGE,
-
-    TYPE_MAX
-  };
-
-  /**
-   * Returns a unique ID for each implementation of gfxReusableSurfaceWrapper.
-   */
-  virtual Type GetType() = 0;
-
-protected:
-  // Protected destructor, to discourage deletion outside of Release():
-  virtual ~gfxReusableSurfaceWrapper() {}
-
-  NS_DECL_OWNINGTHREAD
-};
-
-#endif // GFXCOWSURFACEWRAPPER
diff --git a/gfx/thebes/gfxXlibNativeRenderer.cpp b/gfx/thebes/gfxXlibNativeRenderer.cpp
index 450124ccf4..19213b4e77 100644
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp
+++ b/gfx/thebes/gfxXlibNativeRenderer.cpp
@@ -537,11 +537,7 @@ gfxXlibNativeRenderer::Draw(gfxContext* ctx, IntSize size,
     gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32);
     cairo_t* cairo = static_cast
         (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
-    if (!cairo) {
-        return;
-    }
-
-    cairo_surface_t* cairoTarget = cairo_get_group_target(cairo);
+    cairo_surface_t* cairoTarget = cairo ? cairo_get_group_target(cairo) : nullptr;
     cairo_surface_t* tempXlibSurface =
         CreateTempXlibSurface(cairoTarget, drawTarget, size,
                               canDrawOverBackground, flags, screen, visual,
diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build
index ea139d9407..97bf8e2db9 100644
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -38,9 +38,6 @@ EXPORTS += [
     'gfxQuad.h',
     'gfxQuaternion.h',
     'gfxRect.h',
-    'gfxReusableImageSurfaceWrapper.h',
-    'gfxReusableSharedImageSurfaceWrapper.h',
-    'gfxReusableSurfaceWrapper.h',
     'gfxSharedImageSurface.h',
     'gfxSharedQuartzSurface.h',
     'gfxSkipChars.h',
@@ -232,8 +229,6 @@ UNIFIED_SOURCES += [
     'gfxPattern.cpp',
     'gfxPlatformFontList.cpp',
     'gfxRect.cpp',
-    'gfxReusableImageSurfaceWrapper.cpp',
-    'gfxReusableSharedImageSurfaceWrapper.cpp',
     'gfxScriptItemizer.cpp',
     'gfxSkipChars.cpp',
     'gfxSVGGlyphs.cpp',
@@ -271,6 +266,7 @@ FINAL_LIBRARY = 'xul'
 GENERATED_FILES = [
     'DeprecatedPremultiplyTables.h',
 ]
+GENERATED_FILES['DeprecatedPremultiplyTables.h'].script = 'genTables.py:generate'
 
 LOCAL_INCLUDES += [
     '/dom/workers',
@@ -312,5 +308,9 @@ if CONFIG['CLANG_CXX']:
     # Suppress warnings from Skia header files.
     SOURCES['gfxPlatform.cpp'].flags += ['-Wno-implicit-fallthrough']
 
+if CONFIG['_MSC_VER']:
+    # This is intended as a temporary hack to support building with VS2015.
+    CXXFLAGS += ['-wd4577']
+
 if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
     DEFINES['OTS_DLL'] = True
diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4
index b60acb3d77..fc9c2f3a47 100644
--- a/js/src/aclocal.m4
+++ b/js/src/aclocal.m4
@@ -8,7 +8,6 @@ builtin(include, ../../build/autoconf/acwinpaths.m4)dnl
 builtin(include, ../../build/autoconf/hooks.m4)dnl
 builtin(include, ../../build/autoconf/config.status.m4)dnl
 builtin(include, ../../build/autoconf/toolchain.m4)dnl
-builtin(include, ../../build/autoconf/wrapper.m4)dnl
 builtin(include, ../../build/autoconf/pkg.m4)dnl
 builtin(include, ../../build/autoconf/nspr.m4)dnl
 builtin(include, ../../build/autoconf/nspr-build.m4)dnl
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 26bd0fbb33..37c2badffb 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -44,7 +44,7 @@ static const JSPropertySpec promise_static_properties[] = {
 PromiseObject*
 PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(IsCallable(executor));
+    MOZ_ASSERT(executor->isCallable());
 
     RootedObject usedProto(cx, proto);
     bool wrappedProto = false;
diff --git a/js/src/jit-test/lib/wasm.js b/js/src/jit-test/lib/wasm.js
index 657fa4434a..9bd0044f52 100644
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -11,3 +11,42 @@ function mismatchError(actual, expect) {
     var str = `type mismatch: expression has type ${actual} but expected ${expect}`;
     return RegExp(str);
 }
+
+function hasI64() {
+    return getBuildConfiguration().x64;
+}
+
+// Assert that the expected value is equal to the int64 value, as passed by
+// Baldr with --wasm-extra-tests {low: int32, high: int32}.
+// - if the expected value is in the int32 range, it can be just a number.
+// - otherwise, an object with the properties "high" and "low".
+function assertEqI64(obj, expect) {
+    assertEq(typeof obj, 'object');
+    assertEq(typeof expect === 'object' || typeof expect === 'number', true);
+
+    let {low, high} = obj;
+    if (typeof expect === 'number') {
+        assertEq(expect, expect | 0, "in int32 range");
+        assertEq(low, expect | 0, "low");
+        assertEq(high, expect < 0 ? -1 : 0), "high";
+    } else {
+        assertEq(typeof expect.low, 'number');
+        assertEq(typeof expect.high, 'number');
+        assertEq(low, expect.low | 0, "low");
+        assertEq(high, expect.high | 0, "high");
+    }
+}
+
+function createI64(val) {
+    let ret;
+    if (typeof val === 'number') {
+        assertEq(val, val|0, "number input to createI64 must be an int32");
+        ret = { low: val, high: val < 0 ? -1 : 0 };
+    } else {
+        assertEq(typeof val, 'string');
+        assertEq(val.slice(0, 2), "0x");
+        val = val.slice(2).padStart(16, '0');
+        ret = { low: parseInt(val.slice(8, 16), 16), high: parseInt(val.slice(0, 8), 16) };
+    }
+    return ret;
+}
diff --git a/js/src/jit-test/tests/basic/bug1263868.js b/js/src/jit-test/tests/basic/bug1263868.js
new file mode 100644
index 0000000000..c73a4b796f
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1263868.js
@@ -0,0 +1,12 @@
+if (!('oomTest' in this))
+    quit();
+function g(f, params) {
+    entryPoints(params);
+}
+function entry1() {};
+s = "g(entry1, {function: entry1});";
+f(s);
+f(s);
+function f(x) {
+    oomTest(() => eval(x));
+}
diff --git a/js/src/jit-test/tests/debug/Frame-eval-31.js b/js/src/jit-test/tests/debug/Frame-eval-31.js
new file mode 100644
index 0000000000..bef94a046a
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-31.js
@@ -0,0 +1,9 @@
+// evalInFrame with non-syntactic scopes.
+
+load(libdir + "asserts.js");
+load(libdir + "evalInFrame.js");
+
+evalReturningScope(`
+  var x = 42;
+  assertEq(evalInFrame(0, "x"), 42);
+`);
diff --git a/js/src/jit-test/tests/debug/bug-1260725.js b/js/src/jit-test/tests/debug/bug-1260725.js
new file mode 100644
index 0000000000..7719c70b51
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug-1260725.js
@@ -0,0 +1,12 @@
+// |jit-test| error: out of memory
+
+if (!('oomTest' in this))
+  throw new Error("out of memory");
+
+var dbg = new Debugger;
+dbg.onNewGlobalObject = function(global) {
+  dbg.memory.takeCensus({});
+};
+oomTest(function() {
+  newGlobal({})
+});
diff --git a/js/src/jit-test/tests/debug/bug1252464.js b/js/src/jit-test/tests/debug/bug1252464.js
new file mode 100644
index 0000000000..48c6f8ec07
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1252464.js
@@ -0,0 +1,15 @@
+// |jit-test| error: ReferenceError
+
+g = newGlobal();
+dbg = Debugger(g);
+hits = 0;
+dbg.onNewScript = function () hits++;
+assertEq(g.eval("eval('2 + 3')"), 5);
+this.gczeal(hits,1);
+dbg = Debugger(g);
+g.h = function () {
+  var env = dbg.getNewestFrame().environment;
+  dbg =  0;
+  assertThrowsInstanceOf;
+}
+g.eval("h()");
diff --git a/js/src/jit-test/tests/debug/bug1264961.js b/js/src/jit-test/tests/debug/bug1264961.js
new file mode 100644
index 0000000000..c0ae7aaa4d
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1264961.js
@@ -0,0 +1,28 @@
+// |jit-test| slow;
+
+if (!('oomTest' in this))
+  quit();
+
+loadFile(`
+  var o = {}
+  var global = this;
+  var p = new Proxy(o, {
+    "deleteProperty": function (await , key) {
+      var g = newGlobal();
+      g.parent = global;
+      g.eval("var dbg = new Debugger(parent); dbg.onEnterFrame = function(frame) {};");
+    }
+  })
+  for (var i=0; i<100; i++);
+  assertEq(delete p.foo, true);
+`);
+function loadFile(lfVarx) {
+    var k = 0;
+    oomTest(function() {
+        // In practice a crash occurs before iteration 4000.
+        if (k++ > 4000)
+          quit();
+        eval(lfVarx);
+    })
+}
+
diff --git a/js/src/jit-test/tests/gc/bug-1261329.js b/js/src/jit-test/tests/gc/bug-1261329.js
new file mode 100644
index 0000000000..6a4a307618
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1261329.js
@@ -0,0 +1,10 @@
+if (!('oomTest' in this))
+    quit();
+
+print = function() {}
+function k() dissrc(print);
+function j() k();
+function h() j();
+function f() h();
+f();
+oomTest(() => f())
diff --git a/js/src/jit-test/tests/tracelogger/bug1257194.js b/js/src/jit-test/tests/tracelogger/bug1257194.js
new file mode 100644
index 0000000000..d956f7b9ad
--- /dev/null
+++ b/js/src/jit-test/tests/tracelogger/bug1257194.js
@@ -0,0 +1,5 @@
+// |jit-test| allow-oom
+var du = new Debugger();
+var obj = du.drainTraceLogger();
+oomAfterAllocations(1);
+du.drainTraceLogger().length;
diff --git a/js/src/jit-test/tests/wasm/basic-const.js b/js/src/jit-test/tests/wasm/basic-const.js
index 9658ec9712..b4490da091 100644
--- a/js/src/jit-test/tests/wasm/basic-const.js
+++ b/js/src/jit-test/tests/wasm/basic-const.js
@@ -1,10 +1,10 @@
 load(libdir + "wasm.js");
 
-if (!wasmIsSupported())
-    quit();
-
 function testConst(type, str, expect) {
-  assertEq(wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), expect);
+    if (type === 'i64')
+        assertEqI64(wasmEvalText('(module (func (result i64) (i64.const ' + str + ')) (export "" 0))')(), expect);
+    else
+        assertEq(wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), expect);
 }
 
 function testConstError(type, str) {
@@ -26,19 +26,59 @@ testConst('i32', '0x80000000', -2147483648);
 testConst('i32', '-0x80000000', -2147483648);
 testConst('i32', '0xffffffff', -1);
 
-//testConst('i64', '0', 0); // TODO: NYI
-//testConst('i64', '-0', 0); // TODO: NYI
-//testConst('i64', '23', 23); // TODO: NYI
-//testConst('i64', '-23', -23); // TODO: NYI
-//testConst('i64', '0x23', 35); // TODO: NYI
-//testConst('i64', '-0x23', -35); // TODO: NYI
-//testConst('i64', '9223372036854775807', 9223372036854775807); // TODO: NYI
-//testConst('i64', '18446744073709551615', -1); // TODO: NYI
-//testConst('i64', '-9223372036854775808', -9223372036854775808); // TODO: NYI
-//testConst('i64', '0x7fffffffffffffff', 9223372036854775807); // TODO: NYI
-//testConst('i64', '0x8000000000000000', -9223372036854775808); // TODO: NYI
-//testConst('i64', '-0x8000000000000000', -9223372036854775808); // TODO: NYI
-//testConst('i64', '0xffffffffffffffff', -1); // TODO: NYI
+if (hasI64()) {
+
+    assertErrorMessage(() => wasmEvalText('(module (func (result i64) (i64.const 0)) (export "" 0))'), TypeError, /cannot .* i64/);
+
+    setJitCompilerOption('wasm.test-mode', 1);
+
+    testConst('i64', '0', 0);
+    testConst('i64', '-0', 0);
+
+    testConst('i64', '23', 23);
+    testConst('i64', '-23', -23);
+
+    testConst('i64', '0x23', 35);
+    testConst('i64', '-0x23', -35);
+
+    testConst('i64', '-0x1', -1);
+    testConst('i64', '-1', -1);
+    testConst('i64', '0xffffffffffffffff', -1);
+
+    testConst('i64', '0xdeadc0de', {low: 0xdeadc0de, high: 0x0});
+    testConst('i64', '0x1337c0de00000000', {low: 0x0, high: 0x1337c0de});
+
+    testConst('i64', '0x0102030405060708', {low: 0x05060708, high: 0x01020304});
+    testConst('i64', '-0x0102030405060708', {low: -0x05060708, high: -0x01020305});
+
+    // INT64_MAX
+    testConst('i64', '9223372036854775807', {low: 0xffffffff, high: 0x7fffffff});
+    testConst('i64', '0x7fffffffffffffff',  {low: 0xffffffff, high: 0x7fffffff});
+
+    // INT64_MAX + 1
+    testConst('i64', '9223372036854775808', {low: 0x00000000, high: 0x80000000});
+    testConst('i64', '0x8000000000000000', {low: 0x00000000, high: 0x80000000});
+
+    // UINT64_MAX
+    testConst('i64', '18446744073709551615', {low: 0xffffffff, high: 0xffffffff});
+
+    // INT64_MIN
+    testConst('i64', '-9223372036854775808', {low: 0x00000000, high: 0x80000000});
+    testConst('i64', '-0x8000000000000000',  {low: 0x00000000, high: 0x80000000});
+
+    // INT64_MIN - 1
+    testConstError('i64', '-9223372036854775809');
+
+    testConstError('i64', '');
+    testConstError('i64', '0.0');
+    testConstError('i64', 'not an i64');
+
+    setJitCompilerOption('wasm.test-mode', 0);
+
+    assertErrorMessage(() => wasmEvalText('(module (func (result i64) (i64.const 0)) (export "" 0))'), TypeError, /cannot .* i64/);
+} else {
+    assertErrorMessage(() => wasmEvalText('(module (func (result i64) (i64.const 0)) (export "" 0))'), TypeError, /NYI/);
+}
 
 testConst('f32', '0.0', 0.0);
 testConst('f32', '-0', -0.0);
@@ -215,12 +255,6 @@ testConstError('i32', 'not an i32');
 testConstError('i32', '4294967296');
 testConstError('i32', '-2147483649');
 
-//testConstError('i64', ''); // TODO: NYI
-//testConstError('i64', '0.0'); // TODO: NYI
-//testConstError('i64', 'not an i64'); // TODO: NYI
-//testConstError('i64', '9223372036854775808'); // TODO: NYI
-//testConstError('i64', '-9223372036854775809'); // TODO: NYI
-
 testConstError('f32', '');
 testConstError('f32', 'not an f32');
 testConstError('f32', 'nan:');
diff --git a/js/src/jit-test/tests/wasm/basic-conversion.js b/js/src/jit-test/tests/wasm/basic-conversion.js
index c79fc962b0..a4afcc9f1a 100644
--- a/js/src/jit-test/tests/wasm/basic-conversion.js
+++ b/js/src/jit-test/tests/wasm/basic-conversion.js
@@ -1,50 +1,56 @@
 load(libdir + "wasm.js");
 
-if (!wasmIsSupported())
-    quit();
-
 function testConversion(resultType, opcode, paramType, op, expect) {
   if (paramType === 'i64') {
     // i64 cannot be imported, so we use a wrapper function.
     assertEq(wasmEvalText(`(module
                             (func (param i64) (result ${resultType}) (${resultType}.${opcode}/i64 (get_local 0)))
-                            (func (result ${resultType}) (call 0 (i64.const ${op})))
-                            (export "" 1))`)(), expect);
+                            (export "" 0))`)(createI64(op)), expect);
     // The same, but now the input is a constant.
     assertEq(wasmEvalText(`(module
                             (func (result ${resultType}) (${resultType}.${opcode}/i64 (i64.const ${op})))
                             (export "" 0))`)(), expect);
   } else if (resultType === 'i64') {
-    assertEq(wasmEvalText(`(module
+    assertEqI64(wasmEvalText(`(module
                             (func (param ${paramType}) (result i64) (i64.${opcode}/${paramType} (get_local 0)))
-                            (func (result i32) (i64.eq (i64.const ${expect}) (call 0 (${paramType}.const ${op}))))
-                            (export "" 1))`)(), 1);
+                            (export "" 0))`)(op), createI64(expect));
     // The same, but now the input is a constant.
-    assertEq(wasmEvalText(`(module
+    assertEqI64(wasmEvalText(`(module
                             (func (result i64) (i64.${opcode}/${paramType} (${paramType}.const ${op})))
-                            (func (result i32) (i64.eq (i64.const ${expect}) (call 0)))
-                            (export "" 1))`)(), 1);
+                            (export "" 0))`)(), createI64(expect));
   } else {
     assertEq(wasmEvalText('(module (func (param ' + paramType + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))) (export "" 0))')(op), expect);
   }
 
-  // TODO: i64 NYI
-  for (var bad of ['i32', 'f32', 'f64']) {
-    if (bad != resultType)
-      assertErrorMessage(() => wasmEvalText('(module (func (param ' + paramType + ') (result ' + bad + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))))'),
-                         TypeError,
-                         mismatchError(resultType, bad)
-                        );
-    if (bad != paramType)
-      assertErrorMessage(() => wasmEvalText('(module (func (param ' + bad + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))))'),
-                         TypeError,
-                         mismatchError(bad, paramType)
-                        );
+  let formerWasmExtraTests = getJitCompilerOptions()['wasm.test-mode'];
+  setJitCompilerOption('wasm.test-mode', 1);
+  for (var bad of ['i32', 'f32', 'f64', 'i64']) {
+      if (bad === 'i64' && !hasI64())
+          continue;
+
+      if (bad != resultType) {
+          assertErrorMessage(
+              () => wasmEvalText(`(module (func (param ${paramType}) (result ${bad}) (${resultType}.${opcode}/${paramType} (get_local 0))))`),
+              TypeError,
+              mismatchError(resultType, bad)
+          );
+      }
+
+      if (bad != paramType) {
+          assertErrorMessage(
+              () => wasmEvalText(`(module (func (param ${bad}) (result ${resultType}) (${resultType}.${opcode}/${paramType} (get_local 0))))`),
+              TypeError,
+              mismatchError(bad, paramType)
+          );
+      }
   }
+  setJitCompilerOption('wasm.test-mode', formerWasmExtraTests);
 }
 
-if (getBuildConfiguration().x64) {
-    testConversion('i32', 'wrap', 'i64', 4294967336, 40);
+if (hasI64()) {
+    setJitCompilerOption('wasm.test-mode', 1);
+
+    testConversion('i32', 'wrap', 'i64', '0x100000028', 40);
     testConversion('i32', 'wrap', 'i64', -10, -10);
     testConversion('i32', 'wrap', 'i64', "0xffffffff7fffffff", 0x7fffffff);
     testConversion('i32', 'wrap', 'i64', "0xffffffff00000000", 0);
@@ -68,28 +74,28 @@ if (getBuildConfiguration().x64) {
     testConversion('f32', 'convert_s', 'i64', 1, 1.0);
     testConversion('f32', 'convert_s', 'i64', -1, -1.0);
     testConversion('f32', 'convert_s', 'i64', 0, 0.0);
-    testConversion('f32', 'convert_s', 'i64', "9223372036854775807", 9223372036854775807.0);
-    testConversion('f32', 'convert_s', 'i64', "-9223372036854775808", -9223372036854775808.0);
-    testConversion('f32', 'convert_s', 'i64', "314159265358979", 314159275180032.0);
+    testConversion('f32', 'convert_s', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
+    testConversion('f32', 'convert_s', 'i64', "0x8000000000000000", -9223372036854775808.0);
+    testConversion('f32', 'convert_s', 'i64', "0x11db9e76a2483", 314159275180032.0);
 
     testConversion('f64', 'convert_s', 'i64', 1, 1.0);
     testConversion('f64', 'convert_s', 'i64', -1, -1.0);
     testConversion('f64', 'convert_s', 'i64', 0, 0.0);
-    testConversion('f64', 'convert_s', 'i64', "9223372036854775807", 9223372036854775807.0);
-    testConversion('f64', 'convert_s', 'i64', "-9223372036854775808", -9223372036854775808.0);
-    testConversion('f64', 'convert_s', 'i64', "4669201609102990", 4669201609102990);
+    testConversion('f64', 'convert_s', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
+    testConversion('f64', 'convert_s', 'i64', "0x8000000000000000", -9223372036854775808.0);
+    testConversion('f64', 'convert_s', 'i64', "0x10969d374b968e", 4669201609102990);
 
     testConversion('f32', 'convert_u', 'i64', 1, 1.0);
     testConversion('f32', 'convert_u', 'i64', 0, 0.0);
-    testConversion('f32', 'convert_u', 'i64', "9223372036854775807", 9223372036854775807.0);
-    testConversion('f32', 'convert_u', 'i64', "-9223372036854775808", 9223372036854775808.0);
+    testConversion('f32', 'convert_u', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
+    testConversion('f32', 'convert_u', 'i64', "0x8000000000000000", 9223372036854775808.0);
     testConversion('f32', 'convert_u', 'i64', -1, 18446744073709551616.0);
     testConversion('f32', 'convert_u', 'i64', "0xffff0000ffff0000", 18446462598732840000.0);
 
     testConversion('f64', 'convert_u', 'i64', 1, 1.0);
     testConversion('f64', 'convert_u', 'i64', 0, 0.0);
-    testConversion('f64', 'convert_u', 'i64', "9223372036854775807", 9223372036854775807.0);
-    testConversion('f64', 'convert_u', 'i64', "-9223372036854775808", 9223372036854775808.0);
+    testConversion('f64', 'convert_u', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
+    testConversion('f64', 'convert_u', 'i64', "0x8000000000000000", 9223372036854775808.0);
     testConversion('f64', 'convert_u', 'i64', -1, 18446744073709551616.0);
     testConversion('f64', 'convert_u', 'i64', "0xffff0000ffff0000", 18446462603027743000.0);
 
@@ -105,10 +111,10 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'trunc_s', 'f64', -1.5, -1);
     testConversion('i64', 'trunc_s', 'f64', -1.99, -1);
     testConversion('i64', 'trunc_s', 'f64', -2.0, -2);
-    testConversion('i64', 'trunc_s', 'f64', 4294967296.1, "4294967296");
-    testConversion('i64', 'trunc_s', 'f64', -4294967296.8, "-4294967296");
-    testConversion('i64', 'trunc_s', 'f64', 9223372036854774784.8, "9223372036854774784");
-    testConversion('i64', 'trunc_s', 'f64', -9223372036854775808.3, "-9223372036854775808");
+    testConversion('i64', 'trunc_s', 'f64', 4294967296.1, "0x100000000");
+    testConversion('i64', 'trunc_s', 'f64', -4294967296.8, "0xffffffff00000000");
+    testConversion('i64', 'trunc_s', 'f64', 9223372036854774784.8, "0x7ffffffffffffc00");
+    testConversion('i64', 'trunc_s', 'f64', -9223372036854775808.3, "0x8000000000000000");
 
     testConversion('i64', 'trunc_u', 'f64', 0.0, 0);
     testConversion('i64', 'trunc_u', 'f64', "-0.0", 0);
@@ -119,10 +125,10 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'trunc_u', 'f64', -0.9, 0);
     testConversion('i64', 'trunc_u', 'f64', 40.1, 40);
     testConversion('i64', 'trunc_u', 'f64', 4294967295, "0xffffffff");
-    testConversion('i64', 'trunc_u', 'f64', 4294967296.1, "4294967296");
-    testConversion('i64', 'trunc_u', 'f64', 1e8, "100000000");
-    testConversion('i64', 'trunc_u', 'f64', 1e16, "10000000000000000");
-    testConversion('i64', 'trunc_u', 'f64', 9223372036854775808, "-9223372036854775808");
+    testConversion('i64', 'trunc_u', 'f64', 4294967296.1, "0x100000000");
+    testConversion('i64', 'trunc_u', 'f64', 1e8, "0x5f5e100");
+    testConversion('i64', 'trunc_u', 'f64', 1e16, "0x2386f26fc10000");
+    testConversion('i64', 'trunc_u', 'f64', 9223372036854775808, "0x8000000000000000");
     testConversion('i64', 'trunc_u', 'f64', 18446744073709549568.1, -2048);
 
     testConversion('i64', 'trunc_s', 'f32', 0.0, 0);
@@ -137,10 +143,10 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'trunc_s', 'f32', -1.5, -1);
     testConversion('i64', 'trunc_s', 'f32', -1.99, -1);
     testConversion('i64', 'trunc_s', 'f32', -2.0, -2);
-    testConversion('i64', 'trunc_s', 'f32', 4294967296.1, "4294967296");
-    testConversion('i64', 'trunc_s', 'f32', -4294967296.8, "-4294967296");
-    testConversion('i64', 'trunc_s', 'f32', 9223371487098961920.0, "9223371487098961920");
-    testConversion('i64', 'trunc_s', 'f32', -9223372036854775808.3, "-9223372036854775808");
+    testConversion('i64', 'trunc_s', 'f32', 4294967296.1, "0x100000000");
+    testConversion('i64', 'trunc_s', 'f32', -4294967296.8, "0xffffffff00000000");
+    testConversion('i64', 'trunc_s', 'f32', 9223371487098961920.0, "0x7fffff8000000000");
+    testConversion('i64', 'trunc_s', 'f32', -9223372036854775808.3, "0x8000000000000000");
 
     testConversion('i64', 'trunc_u', 'f32', 0.0, 0);
     testConversion('i64', 'trunc_u', 'f32', "-0.0", 0);
@@ -150,9 +156,9 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'trunc_u', 'f32', 1.99, 1);
     testConversion('i64', 'trunc_u', 'f32', -0.9, 0);
     testConversion('i64', 'trunc_u', 'f32', 40.1, 40);
-    testConversion('i64', 'trunc_u', 'f32', 1e8, "100000000");
-    testConversion('i64', 'trunc_u', 'f32', 4294967296, "4294967296");
-    testConversion('i64', 'trunc_u', 'f32', 18446742974197923840.0, "-1099511627776");
+    testConversion('i64', 'trunc_u', 'f32', 1e8, "0x5f5e100");
+    testConversion('i64', 'trunc_u', 'f32', 4294967296, "0x100000000");
+    testConversion('i64', 'trunc_u', 'f32', 18446742974197923840.0, "0xffffff0000000000");
 
     // TODO: these should trap.
     testConversion('i64', 'trunc_s', 'f64', 9223372036854775808.0, "0x8000000000000000");
@@ -179,8 +185,10 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'trunc_u', 'f32', "infinity", "0x8000000000000000");
     testConversion('i64', 'trunc_u', 'f32', "-infinity", "0x8000000000000000");
 
-    testConversion('i64', 'reinterpret', 'f64', 40.09999999999968, 4630840390592548000);
-    testConversion('f64', 'reinterpret', 'i64', 4630840390592548000, 40.09999999999968);
+    testConversion('i64', 'reinterpret', 'f64', 40.09999999999968, "0x40440ccccccccca0");
+    testConversion('f64', 'reinterpret', 'i64', "0x40440ccccccccca0", 40.09999999999968);
+
+    setJitCompilerOption('wasm.test-mode', 0);
 } else {
     // Sleeper test: once i64 works on more platforms, remove this if-else.
     try {
diff --git a/js/src/jit-test/tests/wasm/basic-integer.js b/js/src/jit-test/tests/wasm/basic-integer.js
index 5c42eb000f..77c36be015 100644
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -9,45 +9,40 @@ function testUnary(type, opcode, op, expect) {
 }
 
 function testBinary(type, opcode, lhs, rhs, expect) {
-  if (type === 'i64') {
-    // i64 cannot be imported/exported, so we use a wrapper function.
-    assertEq(wasmEvalText(`(module
-                            (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (get_local 1)))
-                            (func (result i32) (i64.eq (call 0 (i64.const ${lhs}) (i64.const ${rhs})) (i64.const ${expect})))
-                            (export "" 1))`)(), 1);
-    // The same, but now the RHS is a constant.
-    assertEq(wasmEvalText(`(module
-                            (func (param i64) (result i64) (i64.${opcode} (get_local 0) (i64.const ${rhs})))
-                            (func (result i32) (i64.eq (call 0 (i64.const ${lhs})) (i64.const ${expect})))
-                            (export "" 1))`)(), 1);
-    // LHS and RHS are constants.
-    assertEq(wasmEvalText(`(module
-                            (func (result i64) (i64.${opcode} (i64.const ${lhs}) (i64.const ${rhs})))
-                            (func (result i32) (i64.eq (call 0) (i64.const ${expect})))
-                            (export "" 1))`)(), 1);
-  } else {
-    assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
-  }
+    if (type === 'i64') {
+        let lobj = createI64(lhs);
+        let robj = createI64(rhs);
+        expect = createI64(expect);
+
+        assertEqI64(wasmEvalText(`(module (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (get_local 1))) (export "" 0))`)(lobj, robj), expect);
+        // The same, but now the RHS is a constant.
+        assertEqI64(wasmEvalText(`(module (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (i64.const ${rhs}))) (export "" 0))`)(lobj, robj), expect);
+        // LHS and RHS are constants.
+        assertEqI64(wasmEvalText(`(module (func (param i64) (param i64) (result i64) (i64.${opcode} (i64.const ${lhs}) (i64.const ${rhs}))) (export "" 0))`)(lobj, robj), expect);
+    } else {
+        assertEq(wasmEvalText(`(module (func (param ${type}) (param ${type}) (result ${type}) (${type}.${opcode} (get_local 0) (get_local 1))) (export "" 0))`)(lhs, rhs), expect);
+    }
 }
 
 function testComparison(type, opcode, lhs, rhs, expect) {
-  if (type === 'i64') {
-    // i64 cannot be imported/exported, so we use a wrapper function.
-    assertEq(wasmEvalText(`(module
-                            (func (param i64) (param i64) (result i32) (i64.${opcode} (get_local 0) (get_local 1)))
-                            (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
-                            (export "" 1))`)(), expect);
-    // Also test if, for the compare-and-branch path.
-    assertEq(wasmEvalText(`(module
-                            (func (param i64) (param i64) (result i32)
-                              (if (i64.${opcode} (get_local 0) (get_local 1))
-                                (i32.const 1)
-                                (i32.const 0)))
-                            (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
-                            (export "" 1))`)(), expect);
-  } else {
-    assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
-  }
+    if (type === 'i64') {
+        let lobj = createI64(lhs);
+        let robj = createI64(rhs);
+
+        assertEq(wasmEvalText(`(module
+                                (func (param i64) (param i64) (result i32) (i64.${opcode} (get_local 0) (get_local 1)))
+                                (export "" 0))`)(lobj, robj), expect);
+
+        // Also test if, for the compare-and-branch path.
+        assertEq(wasmEvalText(`(module
+                                (func (param i64) (param i64) (result i32)
+                                 (if (i64.${opcode} (get_local 0) (get_local 1))
+                                  (i32.const 1)
+                                  (i32.const 0)))
+                                  (export "" 0))`)(lobj, robj), expect);
+    } else {
+        assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+    }
 }
 
 testUnary('i32', 'clz', 40, 26);
@@ -95,7 +90,10 @@ testComparison('i32', 'ge_u', 40, 40, 1);
 //testUnary('i64', 'popcnt', 40, 0); // TODO: NYI
 //testUnary('i64', 'eqz', 40, 0); // TODO: NYI
 
-if (getBuildConfiguration().x64) {
+if (hasI64()) {
+
+    setJitCompilerOption('wasm.test-mode', 1);
+
     testBinary('i64', 'add', 40, 2, 42);
     testBinary('i64', 'add', "0x1234567887654321", -1, "0x1234567887654320");
     testBinary('i64', 'add', "0xffffffffffffffff", 1, 0);
@@ -157,15 +155,17 @@ if (getBuildConfiguration().x64) {
     testComparison('i64', 'eq', "0x400012345678", "0x400012345678", 1);
     testComparison('i64', 'ne', "0x400012345678", "0x400012345678", 0);
     testComparison('i64', 'ne', "0x400012345678", "0x500012345678", 1);
-    testComparison('i64', 'eq', "0xffffffffffffffff", "-1", 1);
+    testComparison('i64', 'eq', "0xffffffffffffffff", -1, 1);
     testComparison('i64', 'lt_s', "0x8000000012345678", "0x1", 1);
     testComparison('i64', 'lt_u', "0x8000000012345678", "0x1", 0);
-    testComparison('i64', 'le_s', "-1", "0", 1);
-    testComparison('i64', 'le_u', "-1", "-1", 1);
-    testComparison('i64', 'gt_s', "1", "0x8000000000000000", 1);
-    testComparison('i64', 'gt_u', "1", "0x8000000000000000", 0);
-    testComparison('i64', 'ge_s', "1", "0x8000000000000000", 1);
-    testComparison('i64', 'ge_u', "1", "0x8000000000000000", 0);
+    testComparison('i64', 'le_s', -1, 0, 1);
+    testComparison('i64', 'le_u', -1, -1, 1);
+    testComparison('i64', 'gt_s', 1, "0x8000000000000000", 1);
+    testComparison('i64', 'gt_u', 1, "0x8000000000000000", 0);
+    testComparison('i64', 'ge_s', 1, "0x8000000000000000", 1);
+    testComparison('i64', 'ge_u', 1, "0x8000000000000000", 0);
+
+    setJitCompilerOption('wasm.test-mode', 0);
 } else {
     // Sleeper test: once i64 works on more platforms, remove this if-else.
     try {
diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js
index eb2131c3e0..84854617a5 100644
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -87,11 +87,22 @@ wasmEvalText('(module (func (result i32) (param i32) (i32.const 42)))');
 wasmEvalText('(module (func (param f32)))');
 wasmEvalText('(module (func (param f64)))');
 
-var hasI64 = getBuildConfiguration().x64;
-if (!hasI64) {
+if (!hasI64()) {
     assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
     assertErrorMessage(() => wasmEvalText('(module (func (result i64)))'), TypeError, /NYI/);
     assertErrorMessage(() => wasmEvalText('(module (func (result i32) (i32.wrap/i64 (i64.add (i64.const 1) (i64.const 2)))))'), TypeError, /NYI/);
+} else {
+    assertErrorMessage(() => wasmEvalText('(module (func (param i64) (result i32) (i32.const 123)) (export "" 0))'), TypeError, /i64 argument/);
+    assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i64) (i64.const 123)) (export "" 0))'), TypeError, /i64 return type/);
+
+    setJitCompilerOption('wasm.test-mode', 1);
+
+    assertEqI64(wasmEvalText('(module (func (result i64) (i64.const 123)) (export "" 0))')(), {low: 123, high: 0});
+    assertEqI64(wasmEvalText('(module (func (param i64) (result i64) (get_local 0)) (export "" 0))')({ low: 0x7fffffff, high: 0x12340000}),
+                {low: 0x7fffffff, high: 0x12340000});
+    assertEqI64(wasmEvalText('(module (func (param i64) (result i64) (i64.add (get_local 0) (i64.const 1))) (export "" 0))')({ low: 0xffffffff, high: 0x12340000}), {low: 0x0, high: 0x12340001});
+
+    setJitCompilerOption('wasm.test-mode', 0);
 }
 
 // ----------------------------------------------------------------------------
@@ -260,7 +271,7 @@ wasmEvalText('(module (func (local i32) (local f32) (set_local 1 (get_local 1)))
 assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (i32.const 42))) (export "" 0))')(), 42);
 assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (get_local 0))) (export "" 0))')(), 0);
 
-if (!hasI64)
+if (!hasI64())
     assertErrorMessage(() => wasmEvalText('(module (func (local i64)))'), TypeError, /NYI/);
 
 assertEq(wasmEvalText('(module (func (param $a i32) (result i32) (get_local $a)) (export "" 0))')(), 0);
@@ -347,6 +358,70 @@ assertEq(wasmEvalText(code.replace('BODY', '(call 1)'), imports)(), 4);
 
 assertEq(wasmEvalText(`(module (import "evalcx" "" (param i32) (result i32)) (func (result i32) (call_import 0 (i32.const 0))) (export "" 0))`, {evalcx})(), 0);
 
+if (typeof evaluate === 'function')
+    evaluate(`Wasm.instantiateModule(wasmTextToBinary('(module)')) `, { fileName: null });
+
+if (hasI64()) {
+    assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /i64 argument/);
+    assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /i64 return type/);
+
+    setJitCompilerOption('wasm.test-mode', 1);
+
+    let imp = {
+        param(i64) {
+            assertEqI64(i64, {
+                low: 0x9abcdef0,
+                high: 0x12345678
+            });
+            return 42;
+        },
+        result(i32) {
+            return {
+                low: 0xabcdef01,
+                high: 0x12345678 + i32
+            }
+        },
+        paramAndResult(i64) {
+            assertEqI64(i64, {
+                low: 0x9abcdef0,
+                high: 0x12345678
+            });
+            i64.low = 1337;
+            return i64;
+        }
+    }
+
+    assertEq(wasmEvalText(`(module
+        (import "param" "" (param i64) (result i32))
+        (func (result i32) (call_import 0 (i64.const 0x123456789abcdef0)))
+        (export "" 0))`, imp)(), 42);
+
+    assertEqI64(wasmEvalText(`(module
+        (import "result" "" (param i32) (result i64))
+        (func (result i64) (call_import 0 (i32.const 3)))
+        (export "" 0))`, imp)(), { low: 0xabcdef01, high: 0x1234567b });
+
+    // Ensure the ion exit is never taken.
+    let ionThreshold = 2 * getJitCompilerOptions()['ion.warmup.trigger'];
+    assertEqI64(wasmEvalText(`(module
+        (import "paramAndResult" "" (param i64) (result i64))
+        (func (result i64) (local i32) (local i64)
+         (set_local 0 (i32.const 0))
+         (loop $out $in
+             (set_local 1 (call_import 0 (i64.const 0x123456789abcdef0)))
+             (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+             (if (i32.le_s (get_local 0) (i32.const ${ionThreshold})) (br $in))
+         )
+         (get_local 1)
+        )
+    (export "" 0))`, imp)(), { low: 1337, high: 0x12345678 });
+
+    setJitCompilerOption('wasm.test-mode', 0);
+} else {
+    assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /NYI/);
+    assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /NYI/);
+}
+
 var {v2i, i2i, i2v} = wasmEvalText(`(module
     (type (func (result i32)))
     (type (func (param i32) (result i32)))
@@ -407,13 +482,6 @@ for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math
     assertThrowsInstanceOf(() => i2v(bad, 0), RangeError);
 }
 
-if (hasI64) {
-    assertErrorMessage(() => wasmEvalText('(module (func (param i64) (result i32) (i32.const 123)) (export "" 0))'), TypeError, /i64 argument/);
-    assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i64) (i64.const 123)) (export "" 0))'), TypeError, /i64 return type/);
-    assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /i64 argument/);
-    assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /i64 return type/);
-}
-
 var {v2i, i2i, i2v} = wasmEvalText(`(module
     (type $a (func (result i32)))
     (type $b (func (param i32) (result i32)))
@@ -582,26 +650,28 @@ testSelect('f64', 13.37, 19.89);
 testSelect('f64', 'infinity', '-0');
 testSelect('f64', 'nan', Math.pow(2, -31));
 
-if (!hasI64) {
+if (!hasI64()) {
     assertErrorMessage(() => wasmEvalText('(module (func (select (i64.const 0) (i64.const 1) (i32.const 0))))'), TypeError, /NYI/);
 } else {
+
+    setJitCompilerOption('wasm.test-mode', 1);
+
     var f = wasmEvalText(`
     (module
-     (func (result i32) (param i32)
-      (i64.gt_s
-       (select
-        (i64.const ${Math.pow(2, 31) + 1})
-        (i64.const ${-Math.pow(2, 31) - 1})
-        (get_local 0)
-       )
-       (i64.const ${Math.pow(2, 31)})
+     (func (result i64) (param i32)
+      (select
+       (i64.const 0xc0010ff08badf00d)
+       (i64.const 0x12345678deadc0de)
+       (get_local 0)
       )
      )
      (export "" 0)
-    )
-    `, imports);
+    )`, imports);
 
-    assertEq(f(0), 0);
-    assertEq(f(1), 1);
-    assertEq(f(-1), 1);
+    assertEqI64(f(0),  { low: 0xdeadc0de, high: 0x12345678});
+    assertEqI64(f(1),  { low: 0x8badf00d, high: 0xc0010ff0});
+    assertEqI64(f(-1), { low: 0x8badf00d, high: 0xc0010ff0});
+
+    setJitCompilerOption('wasm.test-mode', 0);
 }
+
diff --git a/js/src/js.msg b/js/src/js.msg
index 62b004c846..3bfa3d4087 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -166,6 +166,7 @@ MSG_DEF(JSMSG_BAD_BYTECODE,            1, JSEXN_INTERNALERR, "unimplemented Java
 MSG_DEF(JSMSG_BUFFER_TOO_SMALL,        0, JSEXN_INTERNALERR, "buffer too small")
 MSG_DEF(JSMSG_BUILD_ID_NOT_AVAILABLE,  0, JSEXN_INTERNALERR, "build ID is not available")
 MSG_DEF(JSMSG_BYTECODE_TOO_BIG,        2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")
+MSG_DEF(JSMSG_ERR_DURING_THROW,        0, JSEXN_INTERNALERR, "an internal error occurred while throwing an exception")
 MSG_DEF(JSMSG_NEED_DIET,               1, JSEXN_INTERNALERR, "{0} too large")
 MSG_DEF(JSMSG_OUT_OF_MEMORY,           0, JSEXN_INTERNALERR, "out of memory")
 MSG_DEF(JSMSG_OVER_RECURSED,           0, JSEXN_INTERNALERR, "too much recursion")
@@ -237,7 +238,7 @@ MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 0, JSEXN_SYNTAXERR, "applying the 'dele
 MSG_DEF(JSMSG_DEPRECATED_EXPR_CLOSURE, 0, JSEXN_NONE, "expression closures are deprecated")
 MSG_DEF(JSMSG_DEPRECATED_FLAGS_ARG,    0, JSEXN_NONE, "flags argument of String.prototype.{search,match,replace} is deprecated")
 MSG_DEF(JSMSG_DEPRECATED_FOR_EACH,     0, JSEXN_NONE, "JavaScript 1.6's for-each-in loops are deprecated; consider using ES6 for-of instead")
-MSG_DEF(JSMSG_DEPRECATED_OCTAL,        0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated")
+MSG_DEF(JSMSG_DEPRECATED_OCTAL,        0, JSEXN_SYNTAXERR, "\"0\"-prefixed octal literals and octal escape sequences are deprecated; for octal literals use the \"0o\" prefix instead")
 MSG_DEF(JSMSG_DEPRECATED_PRAGMA,       1, JSEXN_NONE, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead")
 MSG_DEF(JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, 1, JSEXN_NONE, "redeclaration of block-scoped function `{0}' is deprecated")
 MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME,   1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 970a666922..5b2f48dcba 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6289,7 +6289,7 @@ void AutoFilename::setScriptSource(js::ScriptSource* p)
 void AutoFilename::setUnowned(const char* filename)
 {
     MOZ_ASSERT(!get());
-    filename_.as() = filename;
+    filename_.as() = filename ? filename : "";
 }
 
 void AutoFilename::setOwned(UniqueChars&& filename)
@@ -6559,8 +6559,11 @@ JS::SetOutOfMemoryCallback(JSRuntime* rt, OutOfMemoryCallback cb, void* data)
 JS_PUBLIC_API(bool)
 JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp, unsigned maxFrameCount)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     JSCompartment* compartment = cx->compartment();
-    MOZ_ASSERT(compartment);
     Rooted frame(cx);
     if (!compartment->savedStacks().saveCurrentStack(cx, &frame, maxFrameCount))
         return false;
@@ -6573,9 +6576,12 @@ JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
                    JS::HandleString asyncCause, JS::MutableHandleObject stackp,
                    unsigned maxFrameCount)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
     JSCompartment* compartment = cx->compartment();
-    MOZ_ASSERT(compartment);
     Rooted frame(cx);
     if (!compartment->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause,
                                                    &frame, maxFrameCount))
diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp
index 0de3cdcb04..6b474a4089 100644
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -667,7 +667,7 @@ js::ReportUncaughtException(JSContext* cx)
     cx->clearPendingException();
 
     ErrorReport err(cx);
-    if (!err.init(cx, exn)) {
+    if (!err.init(cx, exn, js::ErrorReport::WithSideEffects)) {
         cx->clearPendingException();
         return false;
     }
@@ -773,9 +773,11 @@ ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
 }
 
 bool
-ErrorReport::init(JSContext* cx, HandleValue exn)
+ErrorReport::init(JSContext* cx, HandleValue exn,
+                  SniffingBehavior sniffingBehavior)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
+    MOZ_ASSERT(!reportp);
 
     if (exn.isObject()) {
         // Because ToString below could error and an exception object could become
@@ -783,6 +785,12 @@ ErrorReport::init(JSContext* cx, HandleValue exn)
         exnObject = &exn.toObject();
         reportp = ErrorFromException(cx, exnObject);
 
+        if (!reportp && sniffingBehavior == NoSideEffects) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                                 JSMSG_ERR_DURING_THROW);
+            return false;
+        }
+
         // Let's see if the exception is from add-on code, if so, it should be reported
         // to telementry.
         //ReportAddonExceptionToTelementry(cx);
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index cf8efa7fb3..818a5a1855 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1325,7 +1325,41 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport)
     explicit ErrorReport(JSContext* cx);
     ~ErrorReport();
 
-    bool init(JSContext* cx, JS::HandleValue exn);
+    enum SniffingBehavior {
+        WithSideEffects,
+        NoSideEffects
+    };
+
+    /**
+     * Generate a JSErrorReport from the provided thrown value.
+     *
+     * If the value is a (possibly wrapped) Error object, the JSErrorReport will
+     * be exactly initialized from the Error object's information, without
+     * observable side effects. (The Error object's JSErrorReport is reused, if
+     * it has one.)
+     *
+     * Otherwise various attempts are made to derive JSErrorReport information
+     * from |exn| and from the current execution state.  This process is
+     * *definitely* inconsistent with any standard, and particulars of the
+     * behavior implemented here generally shouldn't be relied upon.
+     *
+     * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
+     * attempts *may* invoke user-configurable behavior when |exn| is an object:
+     * converting |exn| to a string, detecting and getting properties on |exn|,
+     * accessing |exn|'s prototype chain, and others are possible.  Users *must*
+     * tolerate |ErrorReport::init| potentially having arbitrary effects.  Any
+     * exceptions thrown by these operations will be caught and silently
+     * ignored, and "default" values will be substituted into the JSErrorReport.
+     *
+     * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
+     * *will not* invoke any observable side effects.  The JSErrorReport will
+     * simply contain fewer, less precise details.
+     *
+     * Unlike some functions involved in error handling, this function adheres
+     * to the usual JSAPI return value error behavior.
+     */
+    bool init(JSContext* cx, JS::HandleValue exn,
+              SniffingBehavior sniffingBehavior);
 
     JSErrorReport* report()
     {
diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp
index 4546eb7294..bb29a7aec1 100644
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -825,10 +825,12 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
     }
     const JSCodeSpec* cs = &CodeSpec[op];
     ptrdiff_t len = (ptrdiff_t) cs->length;
-    Sprint(sp, "%05u:", loc);
-    if (lines)
-        Sprint(sp, "%4u", PCToLineNumber(script, pc));
-    Sprint(sp, "  %s", CodeName[op]);
+    if (Sprint(sp, "%05u:", loc) == -1)
+        return 0;
+    if (lines && Sprint(sp, "%4u", PCToLineNumber(script, pc)) == -1)
+        return 0;
+    if (Sprint(sp, "  %s", CodeName[op]) == -1)
+        return 0;
 
     int i;
     switch (JOF_TYPE(cs->format)) {
@@ -843,9 +845,12 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
               for(i = 0; i < trynotes->length; i++) {
                   JSTryNote note = trynotes->vector[i];
                   if (note.kind == JSTRY_CATCH && note.start == loc + 1) {
-                      Sprint(sp, " %u (%+d)",
-                             (unsigned int) (loc+note.length+1),
-                             (int) (note.length+1));
+                      if (Sprint(sp, " %u (%+d)",
+                                 (unsigned int) (loc+note.length+1),
+                                 (int) (note.length+1)) == -1)
+                      {
+                          return 0;
+                      }
                       break;
                   }
               }
@@ -854,7 +859,8 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
 
       case JOF_JUMP: {
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
-        Sprint(sp, " %u (%+d)", loc + (int) off, (int) off);
+        if (Sprint(sp, " %u (%+d)", loc + (int) off, (int) off) == -1)
+            return 0;
         break;
       }
 
@@ -865,7 +871,8 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
         ScopeCoordinate sc(pc);
-        Sprint(sp, " %s (hops = %u, slot = %u)", bytes.ptr(), sc.hops(), sc.slot());
+        if (Sprint(sp, " %s (hops = %u, slot = %u)", bytes.ptr(), sc.hops(), sc.slot()) == -1)
+            return 0;
         break;
       }
 
@@ -874,7 +881,8 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        Sprint(sp, " %s", bytes.ptr());
+        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+            return 0;
         break;
       }
 
@@ -883,14 +891,16 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        Sprint(sp, " %s", bytes.ptr());
+        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+            return 0;
         break;
       }
 
       case JOF_OBJECT: {
         /* Don't call obj.toSource if analysis/inference is active. */
         if (script->zone()->types.activeAnalysis) {
-            Sprint(sp, " object");
+            if (Sprint(sp, " object") == -1)
+                return 0;
             break;
         }
 
@@ -900,7 +910,8 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
             RootedValue v(cx, ObjectValue(*obj));
             if (!ToDisassemblySource(cx, v, &bytes))
                 return 0;
-            Sprint(sp, " %s", bytes.ptr());
+            if (Sprint(sp, " %s", bytes.ptr()) == -1)
+                return 0;
         }
         break;
       }
@@ -911,7 +922,8 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
         RootedValue v(cx, ObjectValue(*obj));
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        Sprint(sp, " %s", bytes.ptr());
+        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+            return 0;
         break;
       }
 
@@ -925,10 +937,12 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
         pc2 += JUMP_OFFSET_LEN;
         high = GET_JUMP_OFFSET(pc2);
         pc2 += JUMP_OFFSET_LEN;
-        Sprint(sp, " defaultOffset %d low %d high %d", int(off), low, high);
+        if (Sprint(sp, " defaultOffset %d low %d high %d", int(off), low, high) == -1)
+            return 0;
         for (i = low; i <= high; i++) {
             off = GET_JUMP_OFFSET(pc2);
-            Sprint(sp, "\n\t%d: %d", i, int(off));
+            if (Sprint(sp, "\n\t%d: %d", i, int(off)) == -1)
+                return 0;
             pc2 += JUMP_OFFSET_LEN;
         }
         len = 1 + pc2 - pc;
@@ -936,15 +950,18 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
       }
 
       case JOF_QARG:
-        Sprint(sp, " %u", GET_ARGNO(pc));
+        if (Sprint(sp, " %u", GET_ARGNO(pc)) == -1)
+            return 0;
         break;
 
       case JOF_LOCAL:
-        Sprint(sp, " %u", GET_LOCALNO(pc));
+        if (Sprint(sp, " %u", GET_LOCALNO(pc)) == -1)
+            return 0;
         break;
 
       case JOF_UINT32:
-        Sprint(sp, " %u", GET_UINT32(pc));
+        if (Sprint(sp, " %u", GET_UINT32(pc)) == -1)
+            return 0;
         break;
 
       case JOF_UINT16:
@@ -968,7 +985,8 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
         MOZ_ASSERT(op == JSOP_INT32);
         i = GET_INT32(pc);
       print_int:
-        Sprint(sp, " %d", i);
+        if (Sprint(sp, " %d", i) == -1)
+            return 0;
         break;
 
       default: {
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 533a2afd03..d2734d0006 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2410,10 +2410,10 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
         return false;
     }
 
-#define LINE_BUF_LEN 512
+    const size_t lineBufLen = 512;
     unsigned len, line1, line2, bupline;
     FILE* file;
-    char linebuf[LINE_BUF_LEN];
+    char linebuf[lineBufLen];
     static const char sep[] = ";-------------------------";
 
     bool ok = true;
@@ -2449,7 +2449,7 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
         /* burn the leading lines */
         line2 = PCToLineNumber(script, pc);
         for (line1 = 0; line1 < line2 - 1; line1++) {
-            char* tmp = fgets(linebuf, LINE_BUF_LEN, file);
+            char* tmp = fgets(linebuf, lineBufLen, file);
             if (!tmp) {
                 JS_ReportError(cx, "failed to read %s fully", script->filename());
                 ok = false;
@@ -2464,14 +2464,20 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
             if (line2 < line1) {
                 if (bupline != line2) {
                     bupline = line2;
-                    Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2);
+                    if (Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2) == -1) {
+                        ok = false;
+                        goto bail;
+                    }
                 }
             } else {
                 if (bupline && line1 == line2)
-                    Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2);
+                    if (Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2) == -1) {
+                        ok = false;
+                        goto bail;
+                    }
                 bupline = 0;
                 while (line1 < line2) {
-                    if (!fgets(linebuf, LINE_BUF_LEN, file)) {
+                    if (!fgets(linebuf, lineBufLen, file)) {
                         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                                              JSSMSG_UNEXPECTED_EOF,
                                              script->filename());
@@ -2479,7 +2485,10 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
                         goto bail;
                     }
                     line1++;
-                    Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf);
+                    if (Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf) == -1) {
+                        ok = false;
+                        goto bail;
+                    }
                 }
             }
 
@@ -2498,7 +2507,6 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
     }
     args.rval().setUndefined();
     return ok;
-#undef LINE_BUF_LEN
 }
 
 #endif /* DEBUG */
@@ -4866,7 +4874,13 @@ class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
         RootedString displayId(cx, JS_GetFunctionDisplayId(function));
         if (displayId) {
             UniqueChars displayIdStr(JS_EncodeStringToUTF8(cx, displayId));
-            oom = !displayIdStr || !log.append(mozilla::Move(displayIdStr));
+            if (!displayIdStr) {
+                // We report OOM in buildResult.
+                cx->recoverFromOutOfMemory();
+                oom = true;
+                return;
+            }
+            oom = !log.append(mozilla::Move(displayIdStr));
             return;
         }
 
@@ -6966,18 +6980,20 @@ Shell(JSContext* cx, OptionParser* op, char** envp)
 
 static void
 SetOutputFile(const char* const envVar,
-              FILE* defaultOut,
-              RCFile** outFile)
+              RCFile* defaultOut,
+              RCFile** outFileP)
 {
+    RCFile* outFile;
+
     const char* outPath = getenv(envVar);
     FILE* newfp;
-    if (outPath && *outPath && (newfp = fopen(outPath, "w"))) {
-        *outFile = js_new(newfp);
-        (*outFile)->acquire();
-    } else {
-        *outFile = js_new(defaultOut);
-        (*outFile)->acquire();
-    }
+    if (outPath && *outPath && (newfp = fopen(outPath, "w")))
+        outFile = js_new(newfp);
+    else
+        outFile = defaultOut;
+
+    outFile->acquire();
+    *outFileP = outFile;
 }
 
 /* Pretend we can always preserve wrappers for dummy DOM objects. */
@@ -7038,8 +7054,15 @@ main(int argc, char** argv, char** envp)
     setlocale(LC_ALL, "");
 #endif
 
-    SetOutputFile("JS_STDERR", stderr, &gErrFile);
-    SetOutputFile("JS_STDOUT", stdout, &gOutFile);
+    // Special-case stdout and stderr. We bump their refcounts to prevent them
+    // from getting closed and then having some printf fail somewhere.
+    RCFile rcStdout(stdout);
+    rcStdout.acquire();
+    RCFile rcStderr(stderr);
+    rcStderr.acquire();
+
+    SetOutputFile("JS_STDOUT", &rcStdout, &gOutFile);
+    SetOutputFile("JS_STDERR", &rcStderr, &gErrFile);
 
     OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
 
diff --git a/js/src/threading/posix/Mutex.cpp b/js/src/threading/posix/Mutex.cpp
index bef5b2102a..a9caf6563d 100644
--- a/js/src/threading/posix/Mutex.cpp
+++ b/js/src/threading/posix/Mutex.cpp
@@ -18,8 +18,21 @@ js::Mutex::Mutex()
   if (!platformData_)
     oom.crash("js::Mutex::Mutex");
 
-  int r = pthread_mutex_init(&platformData()->ptMutex, NULL);
+  pthread_mutexattr_t* attrp = nullptr;
+
+#ifdef DEBUG
+  pthread_mutexattr_t attr;
+  MOZ_ALWAYS_TRUE(pthread_mutexattr_init(&attr) == 0);
+  MOZ_ALWAYS_TRUE(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) == 0);
+  attrp = &attr;
+#endif
+
+  int r = pthread_mutex_init(&platformData()->ptMutex, attrp);
   MOZ_RELEASE_ASSERT(r == 0);
+
+#ifdef DEBUG
+  MOZ_ALWAYS_TRUE(pthread_mutexattr_destroy(&attr) == 0);
+#endif
 }
 
 js::Mutex::~Mutex()
diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
index b53c2f8c0f..76844cc1f2 100644
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1028,6 +1028,7 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
             ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));
             uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32());
             MOZ_ASSERT(buf.dataPointer() != nullptr);
+            MOZ_ASSERT(offset <= INT32_MAX);
 
             if (buf.forInlineTypedObject()) {
                 // The data is inline with an InlineTypedObject associated with the
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index adbffe9a75..965b0b3697 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -252,111 +252,6 @@ ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id)
     return true;
 }
 
-/*
- * A range of all the Debugger.Frame objects for a particular AbstractFramePtr.
- *
- * FIXME This checks only current debuggers, so it relies on a hack in
- * Debugger::removeDebuggeeGlobal to make sure only current debuggers
- * have Frame objects with .live === true.
- */
-class Debugger::FrameRange
-{
-    AbstractFramePtr frame;
-
-    /* The debuggers in |fp|'s compartment, or nullptr if there are none. */
-    GlobalObject::DebuggerVector* debuggers;
-
-    /*
-     * The index of the front Debugger.Frame's debugger in debuggers.
-     * nextDebugger < debuggerCount if and only if the range is not empty.
-     */
-    size_t debuggerCount, nextDebugger;
-
-    /*
-     * If the range is not empty, this is front Debugger.Frame's entry in its
-     * debugger's frame table.
-     */
-    FrameMap::Ptr entry;
-
-  public:
-    /*
-     * Return a range containing all Debugger.Frame instances referring to
-     * |fp|. |global| is |fp|'s global object; if nullptr or omitted, we
-     * compute it ourselves from |fp|.
-     *
-     * We keep an index into the compartment's debugger list, and a
-     * FrameMap::Ptr into the current debugger's frame map. Thus, if the set of
-     * debuggers in |fp|'s compartment changes, this range becomes invalid.
-     * Similarly, if stack frames are added to or removed from frontDebugger(),
-     * then the range's front is invalid until popFront is called.
-     */
-    explicit FrameRange(AbstractFramePtr frame, GlobalObject* global = nullptr)
-      : frame(frame)
-    {
-        nextDebugger = 0;
-
-        /* Find our global, if we were not given one. */
-        if (!global)
-            global = &frame.script()->global();
-
-        /* The frame and global must match. */
-        MOZ_ASSERT(&frame.script()->global() == global);
-
-        /* Find the list of debuggers we'll iterate over. There may be none. */
-        debuggers = global->getDebuggers();
-        if (debuggers) {
-            debuggerCount = debuggers->length();
-            findNext();
-        } else {
-            debuggerCount = 0;
-        }
-    }
-
-    bool empty() const {
-        return nextDebugger >= debuggerCount;
-    }
-
-    NativeObject* frontFrame() const {
-        MOZ_ASSERT(!empty());
-        return entry->value();
-    }
-
-    Debugger* frontDebugger() const {
-        MOZ_ASSERT(!empty());
-        return (*debuggers)[nextDebugger];
-    }
-
-    /*
-     * Delete the front frame from its Debugger's frame map. After this call,
-     * the range's front is invalid until popFront is called.
-     */
-    void removeFrontFrame() const {
-        MOZ_ASSERT(!empty());
-        frontDebugger()->frames.remove(entry);
-    }
-
-    void popFront() {
-        MOZ_ASSERT(!empty());
-        nextDebugger++;
-        findNext();
-    }
-
-  private:
-    /*
-     * Either make this range refer to the first appropriate Debugger.Frame at
-     * or after nextDebugger, or make it empty.
-     */
-    void findNext() {
-        while (!empty()) {
-            Debugger* dbg = (*debuggers)[nextDebugger];
-            entry = dbg->frames.lookup(frame);
-            if (entry)
-                break;
-            nextDebugger++;
-        }
-    }
-};
-
 class AutoRestoreCompartmentDebugMode
 {
     JSCompartment* comp_;
@@ -849,22 +744,22 @@ DebuggerFrame_freeScriptFrameIterData(FreeOp* fop, JSObject* obj);
 /* static */ bool
 Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool frameOk)
 {
-    Handle global = cx->global();
+    mozilla::DebugOnly> debuggeeGlobal = cx->global();
+
+    auto frameMapsGuard = MakeScopeExit([&] {
+        // Clean up all Debugger.Frame instances.
+        removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
+    });
 
     // The onPop handler and associated clean up logic should not run multiple
     // times on the same frame. If slowPathOnLeaveFrame has already been
     // called, the frame will not be present in the Debugger frame maps.
-    FrameRange frameRange(frame, global);
-    if (frameRange.empty())
+    Rooted frames(cx, DebuggerFrameVector(cx));
+    if (!getDebuggerFrames(frame, &frames))
+        return false;
+    if (frames.empty())
         return frameOk;
 
-    auto frameMapsGuard = MakeScopeExit([&] {
-        // Clean up all Debugger.Frame instances. This call creates a fresh
-        // FrameRange, as one debugger's onPop handler could have caused another
-        // debugger to create its own Debugger.Frame instance.
-        removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
-    });
-
     /* Save the frame's completion value. */
     JSTrapStatus status;
     RootedValue value(cx);
@@ -875,18 +770,9 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode
     // invoking JS will only trigger the same condition. See
     // slowPathOnExceptionUnwind.
     if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
-        /* Build a list of the recipients. */
-        AutoObjectVector frames(cx);
-        for (; !frameRange.empty(); frameRange.popFront()) {
-            if (!frames.append(frameRange.frontFrame())) {
-                cx->clearPendingException();
-                return false;
-            }
-        }
-
         /* For each Debugger.Frame, fire its onPop handler, if any. */
-        for (JSObject** p = frames.begin(); p != frames.end(); p++) {
-            RootedNativeObject frameobj(cx, &(*p)->as());
+        for (size_t i = 0; i < frames.length(); i++) {
+            HandleNativeObject frameobj = frames[i];
             Debugger* dbg = Debugger::fromChildJSObject(frameobj);
             EnterDebuggeeNoExecute nx(cx, *dbg);
 
@@ -915,7 +801,7 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode
                  * At this point, we are back in the debuggee compartment, and any error has
                  * been wrapped up as a completion value.
                  */
-                MOZ_ASSERT(cx->compartment() == global->compartment());
+                MOZ_ASSERT(cx->compartment() == debuggeeGlobal->compartment());
                 MOZ_ASSERT(!cx->isExceptionPending());
 
                 /* JSTRAP_CONTINUE means "make no change". */
@@ -1867,15 +1753,9 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
      * Build list of Debugger.Frame instances referring to this frame with
      * onStep handlers.
      */
-    AutoObjectVector frames(cx);
-    for (FrameRange r(iter.abstractFramePtr()); !r.empty(); r.popFront()) {
-        NativeObject* frame = r.frontFrame();
-        if (!frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
-            !frames.append(frame))
-        {
-            return JSTRAP_ERROR;
-        }
-    }
+    Rooted frames(cx, DebuggerFrameVector(cx));
+    if (!getDebuggerFrames(iter.abstractFramePtr(), &frames))
+        return JSTRAP_ERROR;
 
 #ifdef DEBUG
     /*
@@ -1909,9 +1789,12 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
     }
 #endif
 
-    /* Call all the onStep handlers we found. */
-    for (JSObject** p = frames.begin(); p != frames.end(); p++) {
-        RootedNativeObject frame(cx, &(*p)->as());
+    // Call onStep for frames that have the handler set.
+    for (size_t i = 0; i < frames.length(); i++) {
+        HandleNativeObject frame = frames[i];
+        if (frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
+            continue;
+
         Debugger* dbg = Debugger::fromChildJSObject(frame);
         EnterDebuggeeNoExecute nx(cx, *dbg);
 
@@ -2329,8 +2212,7 @@ Debugger::updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObs
                 // Debugger.Frame lifetimes are managed by the debug epilogue,
                 // so in general it's unsafe to unmark a frame if it has a
                 // Debugger.Frame associated with it.
-                FrameRange r(iter.abstractFramePtr());
-                MOZ_ASSERT(r.empty());
+                MOZ_ASSERT(!inFrameMaps(iter.abstractFramePtr()));
 #endif
                 iter.abstractFramePtr().unsetIsDebuggee();
             }
@@ -2455,6 +2337,31 @@ Debugger::updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionOb
     return true;
 }
 
+template 
+/* static */ void
+Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn)
+{
+    GlobalObject* global = &frame.script()->global();
+    if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
+        for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
+            Debugger* dbg = *p;
+            if (FrameMap::Ptr entry = dbg->frames.lookup(frame))
+                fn(entry->value());
+        }
+    }
+}
+
+/* static */ bool
+Debugger::getDebuggerFrames(AbstractFramePtr frame, MutableHandle frames)
+{
+    bool hadOOM = false;
+    forEachDebuggerFrame(frame, [&](NativeObject* frameobj) {
+        if (!hadOOM && !frames.append(frameobj))
+            hadOOM = true;
+    });
+    return !hadOOM;
+}
+
 /* static */ bool
 Debugger::updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
                                        IsObserving observing)
@@ -4775,6 +4682,8 @@ Debugger::drainTraceLogger(JSContext* cx, unsigned argc, Value* vp)
 
     RootedObject array(cx, NewDenseEmptyArray(cx));
     JSAtom* dataAtom = Atomize(cx, "data", strlen("data"));
+    if (!array)
+        return false;
     if (!dataAtom)
         return false;
     RootedId dataId(cx, AtomToId(dataAtom));
@@ -4921,6 +4830,8 @@ Debugger::drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp)
                                          &num);
 
     RootedObject array(cx, NewDenseEmptyArray(cx));
+    if (!array)
+        return false;
     RootedId fileNameId(cx, AtomToId(cx->names().fileName));
     RootedId lineNumberId(cx, AtomToId(cx->names().lineNumber));
     RootedId columnNumberId(cx, AtomToId(cx->names().columnNumber));
@@ -5949,10 +5860,10 @@ Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePt
     auto removeFromDebuggerFramesOnExit = MakeScopeExit([&] {
         // Remove any remaining old entries on exit, as the 'from' frame will
         // be gone. On success, the range will be empty.
-        for (Debugger::FrameRange r(from); !r.empty(); r.popFront()) {
-            r.frontFrame()->setPrivate(nullptr);
-            r.removeFrontFrame();
-        }
+        Debugger::forEachDebuggerFrame(from, [&](NativeObject* frameobj) {
+            frameobj->setPrivate(nullptr);
+            Debugger::fromChildJSObject(frameobj)->frames.remove(from);
+        });
 
         // Rekey missingScopes to maintain Debugger.Environment identity and
         // forward liveScopes to point to the new frame.
@@ -5960,10 +5871,21 @@ Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePt
     });
 
     // Forward live Debugger.Frame objects.
-    for (Debugger::FrameRange r(from); !r.empty(); r.popFront()) {
-        RootedNativeObject frameobj(cx, r.frontFrame());
-        Debugger* dbg = r.frontDebugger();
-        MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
+    Rooted frames(cx, DebuggerFrameVector(cx));
+    if (!getDebuggerFrames(from, &frames))
+        return false;
+
+    // If during the loop below we hit an OOM, we must also rollback any of
+    // the frames that were successfully replaced. For OSR frames, OOM here
+    // means those frames will pop from the OSR trampoline, which does not
+    // call Debugger::onLeaveFrame.
+    auto removeToDebuggerFramesOnExit = MakeScopeExit([&] {
+        removeFromFrameMapsAndClearBreakpointsIn(cx, to);
+    });
+
+    for (size_t i = 0; i < frames.length(); i++) {
+        HandleNativeObject frameobj = frames[i];
+        Debugger* dbg = Debugger::fromChildJSObject(frameobj);
 
         // Update frame object's ScriptFrameIter::data pointer.
         DebuggerFrame_freeScriptFrameIterData(cx->runtime()->defaultFreeOp(), frameobj);
@@ -5972,8 +5894,8 @@ Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePt
             return false;
         frameobj->setPrivate(data);
 
-        // Remove the old frame.
-        r.removeFrontFrame();
+        // Remove old frame.
+        dbg->frames.remove(from);
 
         // Add the frame object with |to| as key.
         if (!dbg->frames.putNew(to, frameobj)) {
@@ -5982,32 +5904,32 @@ Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePt
         }
     }
 
+    // All frames successfuly replaced, cancel the rollback.
+    removeToDebuggerFramesOnExit.release();
+
     return true;
 }
 
 /* static */ bool
 Debugger::inFrameMaps(AbstractFramePtr frame)
 {
-    FrameRange r(frame);
-    return !r.empty();
+    bool foundAny = false;
+    forEachDebuggerFrame(frame, [&](NativeObject* frameobj) { foundAny = true; });
+    return foundAny;
 }
 
 /* static */ void
 Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame)
 {
-    Handle global = cx->global();
-
-    for (FrameRange r(frame, global); !r.empty(); r.popFront()) {
-        RootedNativeObject frameobj(cx, r.frontFrame());
-        Debugger* dbg = r.frontDebugger();
-        MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
+    forEachDebuggerFrame(frame, [&](NativeObject* frameobj) {
+        Debugger* dbg = Debugger::fromChildJSObject(frameobj);
 
         FreeOp* fop = cx->runtime()->defaultFreeOp();
         DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
         DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
 
         dbg->frames.remove(frame);
-    }
+    });
 
     /*
      * If this is an eval frame, then from the debugger's perspective the
diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h
index 09da10819c..e833e084bb 100644
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -478,7 +478,6 @@ class Debugger : private mozilla::LinkedListElement
     uint32_t traceLoggerScriptedCallsLastDrainedSize;
     uint32_t traceLoggerScriptedCallsLastDrainedIteration;
 
-    class FrameRange;
     class ScriptQuery;
     class ObjectQuery;
 
@@ -625,6 +624,18 @@ class Debugger : private mozilla::LinkedListElement
     static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
                                              IsObserving observing);
 
+    template 
+    static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);
+
+    /*
+     * Return a vector containing all Debugger.Frame instances referring to
+     * |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
+     * compute it ourselves from |frame|.
+     */
+    using DebuggerFrameVector = GCVector;
+    static bool getDebuggerFrames(AbstractFramePtr frame,
+                                  MutableHandle frames);
+
   public:
     static bool ensureExecutionObservabilityOfOsrFrame(JSContext* cx, InterpreterFrame* frame);
 
diff --git a/js/src/vm/SavedStacks-inl.h b/js/src/vm/SavedStacks-inl.h
index dd05760b4e..29b5657571 100644
--- a/js/src/vm/SavedStacks-inl.h
+++ b/js/src/vm/SavedStacks-inl.h
@@ -22,7 +22,8 @@
 inline void
 js::AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack)
 {
-    MOZ_ASSERT_IF(stack, js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack));
+    if (stack)
+        MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack));
 }
 
 #endif // vm_SavedStacksInl_h
diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp
index bf4e6d20a4..362f9bf023 100644
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -660,8 +660,13 @@ public:
                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        // Note that obj might be null here, since we're doing this
-        // before UnwrapSavedFrame.
+
+        MOZ_RELEASE_ASSERT(cx->compartment());
+        if (obj)
+            MOZ_RELEASE_ASSERT(obj->compartment());
+
+        // Note that obj might be null here, since we're doing this before
+        // UnwrapSavedFrame.
         if (obj && cx->compartment() != obj->compartment())
         {
             JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
@@ -691,7 +696,7 @@ UnwrapSavedFrame(JSContext* cx, HandleObject obj, SavedFrameSelfHosted selfHoste
     if (!savedFrameObj)
         return nullptr;
 
-    MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
+    MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
     js::RootedSavedFrame frame(cx, &savedFrameObj->as());
     return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
 }
@@ -700,6 +705,10 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
@@ -715,7 +724,11 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
                   SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
     MOZ_ASSERT(linep);
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
@@ -731,7 +744,11 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
     MOZ_ASSERT(columnp);
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
@@ -747,6 +764,10 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
                                  SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
@@ -760,11 +781,21 @@ GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, Mutable
 
 JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
-                        SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
+                        SavedFrameSelfHosted unused_ /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
+    // This function is always called with self-hosted frames excluded by
+    // GetValueIfNotCached in dom/bindings/Exceptions.cpp. However, we want
+    // to include them because our Promise implementation causes us to have
+    // the async cause on a self-hosted frame. So we just ignore the
+    // parameter and always include self-hosted frames.
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, SavedFrameSelfHosted::Include,
+                                                    skippedAsync));
     if (!frame) {
         asyncCausep.set(nullptr);
         return SavedFrameResult::AccessDenied;
@@ -779,6 +810,10 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
                          SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
@@ -808,6 +843,10 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
@@ -836,6 +875,10 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
 JS_PUBLIC_API(bool)
 BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent)
 {
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    MOZ_RELEASE_ASSERT(cx->compartment());
+
     js::StringBuffer sb(cx);
 
     // Enter a new block to constrain the scope of possibly entering the stack's
@@ -1023,10 +1066,12 @@ bool
 SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
 {
     MOZ_ASSERT(initialized());
+    MOZ_RELEASE_ASSERT(cx->compartment());
     assertSameCompartment(cx, this);
 
     if (creatingSavedFrame ||
         cx->isExceptionPending() ||
+        !cx->global() ||
         !cx->global()->isStandardClassResolved(JSProto_Object))
     {
         frame.set(nullptr);
@@ -1043,11 +1088,12 @@ SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString
                             MutableHandleSavedFrame adoptedStack, unsigned maxFrameCount)
 {
     MOZ_ASSERT(initialized());
+    MOZ_RELEASE_ASSERT(cx->compartment());
     assertSameCompartment(cx, this);
 
     RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
-    MOZ_ASSERT(asyncStackObj);
-    MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
+    MOZ_RELEASE_ASSERT(asyncStackObj);
+    MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
     RootedSavedFrame frame(cx, &asyncStackObj->as());
 
     return adoptAsyncStack(cx, frame, asyncCause, adoptedStack, maxFrameCount);
diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp
index 8cff9f711c..eadc5f3124 100644
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1825,7 +1825,9 @@ class DebugScopeProxy : public BaseProxyHandler
         }
 
         /* The rest of the internal scopes do not have unaliased vars. */
-        MOZ_ASSERT(scope->is() || scope->is() ||
+        MOZ_ASSERT(!IsSyntacticScope(scope) ||
+                   scope->is() ||
+                   scope->is() ||
                    scope->as().isForEval());
         return true;
     }
diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp
index c6cec1fd0f..a83178485c 100644
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -535,10 +535,14 @@ TraceLoggerThread::log(uint32_t id)
         return;
 
     MOZ_ASSERT(traceLoggerState);
-    if (!events.hasSpaceForAdd()) {
+
+    // We request for 3 items to add, since if we don't have enough room
+    // we record the time it took to make more place. To log this information
+    // we need 2 extra free entries.
+    if (!events.hasSpaceForAdd(3)) {
         uint64_t start = rdtsc() - traceLoggerState->startupTime;
 
-        if (!events.ensureSpaceBeforeAdd()) {
+        if (!events.ensureSpaceBeforeAdd(3)) {
             if (graph.get())
                 graph->log(events);
 
@@ -570,7 +574,7 @@ TraceLoggerThread::log(uint32_t id)
         // Log the time it took to flush the events as being from the
         // Tracelogger.
         if (graph.get()) {
-            MOZ_ASSERT(events.capacity() > 2);
+            MOZ_ASSERT(events.hasSpaceForAdd(2));
             EventEntry& entryStart = events.pushUninitialized();
             entryStart.time = start;
             entryStart.textId = TraceLogger_Internal;
diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h
index 5ef2cc4a54..97ac7b8b59 100644
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -120,7 +120,9 @@ class TypedArrayObject : public NativeObject
         return tarr->getFixedSlot(BUFFER_SLOT);
     }
     static Value byteOffsetValue(TypedArrayObject* tarr) {
-        return tarr->getFixedSlot(BYTEOFFSET_SLOT);
+        Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
+        MOZ_ASSERT(v.toInt32() >= 0);
+        return v;
     }
     static Value byteLengthValue(TypedArrayObject* tarr) {
         return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
diff --git a/js/src/vm/UbiNodeCensus.cpp b/js/src/vm/UbiNodeCensus.cpp
index 4e9265fb2b..f6c5efb734 100644
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -963,7 +963,7 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
 {
     if (breakdownValue.isUndefined()) {
         // Construct the default type, { by: 'count' }
-        CountTypePtr simple(js_new());
+        CountTypePtr simple(cx->new_());
         return simple;
     }
 
@@ -1021,14 +1021,14 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
                 return nullptr;
         }
 
-        CountTypePtr simple(js_new(labelUnique,
-                                                ToBoolean(countValue),
-                                                ToBoolean(bytesValue)));
+        CountTypePtr simple(cx->new_(labelUnique,
+                                                  ToBoolean(countValue),
+                                                  ToBoolean(bytesValue)));
         return simple;
     }
 
     if (StringEqualsAscii(by, "bucket"))
-        return CountTypePtr(js_new());
+        return CountTypePtr(cx->new_());
 
     if (StringEqualsAscii(by, "objectClass")) {
         CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then));
@@ -1039,7 +1039,7 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
         if (!otherType)
             return nullptr;
 
-        return CountTypePtr(js_new(thenType, otherType));
+        return CountTypePtr(cx->new_(thenType, otherType));
     }
 
     if (StringEqualsAscii(by, "coarseType")) {
@@ -1056,10 +1056,10 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
         if (!otherType)
             return nullptr;
 
-        return CountTypePtr(js_new(objectsType,
-                                                 scriptsType,
-                                                 stringsType,
-                                                 otherType));
+        return CountTypePtr(cx->new_(objectsType,
+                                                   scriptsType,
+                                                   stringsType,
+                                                   otherType));
     }
 
     if (StringEqualsAscii(by, "internalType")) {
@@ -1067,7 +1067,7 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
         if (!thenType)
             return nullptr;
 
-        return CountTypePtr(js_new(thenType));
+        return CountTypePtr(cx->new_(thenType));
     }
 
     if (StringEqualsAscii(by, "allocationStack")) {
@@ -1078,7 +1078,7 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
         if (!noStackType)
             return nullptr;
 
-        return CountTypePtr(js_new(thenType, noStackType));
+        return CountTypePtr(cx->new_(thenType, noStackType));
     }
 
     if (StringEqualsAscii(by, "filename")) {
@@ -1090,7 +1090,7 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
         if (!noFilenameType)
             return nullptr;
 
-        return CountTypePtr(js_new(Move(thenType), Move(noFilenameType)));
+        return CountTypePtr(cx->new_(Move(thenType), Move(noFilenameType)));
     }
 
     // We didn't recognize the breakdown type; complain.
@@ -1114,22 +1114,40 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
 //   other:   { by: "internalType" }
 // }
 static CountTypePtr
-GetDefaultBreakdown()
+GetDefaultBreakdown(JSContext* cx)
 {
-    CountTypePtr byClass(js_new());
-    CountTypePtr byClassElse(js_new());
-    CountTypePtr objects(js_new(byClass, byClassElse));
+    CountTypePtr byClass(cx->new_());
+    if (!byClass)
+        return nullptr;
 
-    CountTypePtr scripts(js_new());
-    CountTypePtr strings(js_new());
+    CountTypePtr byClassElse(cx->new_());
+    if (!byClassElse)
+        return nullptr;
 
-    CountTypePtr byType(js_new());
-    CountTypePtr other(js_new(byType));
+    CountTypePtr objects(cx->new_(byClass, byClassElse));
+    if (!objects)
+        return nullptr;
 
-    return CountTypePtr(js_new(objects,
-                                             scripts,
-                                             strings,
-                                             other));
+    CountTypePtr scripts(cx->new_());
+    if (!scripts)
+        return nullptr;
+
+    CountTypePtr strings(cx->new_());
+    if (!strings)
+        return nullptr;
+
+    CountTypePtr byType(cx->new_());
+    if (!byType)
+        return nullptr;
+
+    CountTypePtr other(cx->new_(byType));
+    if (!other)
+        return nullptr;
+
+    return CountTypePtr(cx->new_(objects,
+                                               scripts,
+                                               strings,
+                                               other));
 }
 
 bool
@@ -1140,7 +1158,7 @@ ParseCensusOptions(JSContext* cx, Census& census, HandleObject options, CountTyp
         return false;
 
     outResult = breakdown.isUndefined()
-        ? GetDefaultBreakdown()
+        ? GetDefaultBreakdown(cx)
         : ParseBreakdown(cx, breakdown);
     return !!outResult;
 }
diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp
index dac625cef5..d1f7523a25 100644
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -21,6 +21,7 @@
 #include "AccessCheck.h"
 
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/Promise.h"
 
@@ -205,6 +206,26 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aFallbackMessage,
     mIsMuted = aReport->isMuted;
 }
 
+void
+xpc::ErrorReport::Init(JSContext* aCx, mozilla::dom::Exception* aException,
+                       bool aIsChrome, uint64_t aWindowID)
+{
+    mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
+                          : NS_LITERAL_CSTRING("content javascript");
+    mWindowID = aWindowID;
+
+    aException->GetErrorMessage(mErrorMsg);
+
+    aException->GetFilename(aCx, mFileName);
+    if (mFileName.IsEmpty()) {
+      mFileName.SetIsVoid(true);
+    }
+    aException->GetLineNumber(aCx, &mLineNumber);
+    aException->GetColumnNumber(&mColumn);
+
+    mFlags = JSREPORT_EXCEPTION;
+}
+
 static LazyLogModule gJSDiagnostics("JSDiagnostics");
 
 void
diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h
index 5f35924fad..dc333007cf 100644
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -30,6 +30,12 @@ class nsIPrincipal;
 class nsScriptNameSpaceManager;
 class nsIMemoryReporterCallback;
 
+namespace mozilla {
+namespace dom {
+class Exception;
+}
+}
+
 typedef void (* xpcGCCallback)(JSGCStatus status);
 
 namespace xpc {
@@ -515,6 +521,8 @@ class ErrorReport {
 
     void Init(JSErrorReport* aReport, const char* aFallbackMessage,
               bool aIsChrome, uint64_t aWindowID);
+    void Init(JSContext* aCx, mozilla::dom::Exception* aException,
+              bool aIsChrome, uint64_t aWindowID);
     // Log the error report to the console.  Which console will depend on the
     // window id it was initialized with.
     void LogToConsole();
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
index 61d42c868c..4206882a7b 100644
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1694,7 +1694,7 @@ nsPresContext::UIResolutionChangedSync()
 {
   if (!mPendingUIResolutionChanged) {
     mPendingUIResolutionChanged = true;
-    UIResolutionChangedInternal();
+    UIResolutionChangedInternalScale(0.0);
   }
 }
 
@@ -1706,7 +1706,12 @@ nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument,
   if (shell) {
     nsPresContext* pc = shell->GetPresContext();
     if (pc) {
-      pc->UIResolutionChangedInternal();
+      // For subdocuments, we want to apply the parent's scale, because there
+      // are cases where the subdoc's device context is connected to a widget
+      // that has an out-of-date resolution (it's on a different screen, but
+      // currently hidden, and will not be updated until shown): bug 1249279.
+      double scale = *static_cast(aData);
+      pc->UIResolutionChangedInternalScale(scale);
     }
   }
   return true;
@@ -1735,10 +1740,16 @@ NotifyChildrenUIResolutionChanged(nsIDOMWindow* aWindow)
 
 void
 nsPresContext::UIResolutionChangedInternal()
+{
+  UIResolutionChangedInternalScale(0.0);
+}
+
+void
+nsPresContext::UIResolutionChangedInternalScale(double aScale)
 {
   mPendingUIResolutionChanged = false;
 
-  mDeviceContext->CheckDPIChange();
+  mDeviceContext->CheckDPIChange(&aScale);
   if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) {
     AppUnitsPerDevPixelChanged();
   }
@@ -1747,7 +1758,7 @@ nsPresContext::UIResolutionChangedInternal()
   NotifyChildrenUIResolutionChanged(mDocument->GetWindow());
 
   mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
-                                   nullptr);
+                                   &aScale);
 }
 
 void
diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
index 8d645b1b19..3990ba1383 100644
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1101,8 +1101,17 @@ protected:
   friend class nsRunnableMethod;
   void ThemeChangedInternal();
   void SysColorChangedInternal();
+
+  // update device context's resolution from the widget
   void UIResolutionChangedInternal();
 
+  // if aScale > 0.0, use it as resolution scale factor to the device context
+  // (otherwise get it from the widget)
+  void UIResolutionChangedInternalScale(double aScale);
+
+  // aData here is a pointer to a double that holds the CSS to device-pixel
+  // scale factor from the parent, which will be applied to the subdocument's
+  // device context instead of retrieving a scale from the widget.
   static bool
   UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData);
 
diff --git a/layout/style/CSSStyleSheet.h b/layout/style/CSSStyleSheet.h
index 7ffd198b1a..1f65b1220a 100644
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -231,6 +231,9 @@ public:
      return null */
   nsIURI* GetOriginalURI() const { return mInner->mOriginalSheetURI; }
 
+  // Whether the sheet is for an inline