Merge autoland to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Thu, 25 Oct 2018 01:00:19 +0300
changeset 442848 ddadc29de671917f66478e62a6accd4764892d25
parent 442831 8ca56f27dc5864f2c8be0c6aa498cb24ebb7e65e (current diff)
parent 442847 13b39a6aa15fa631096b96d4351c2b1d08a4626e (diff)
child 442867 418b398baaeeb9e9b5652f0be376aa07bfe3f769
child 442902 524c546c7ad5a2a73f56bb6858ffcac4f82a6088
push id34925
push userrgurzau@mozilla.com
push dateWed, 24 Oct 2018 22:00:55 +0000
treeherdermozilla-central@ddadc29de671 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
ddadc29de671 / 65.0a1 / 20181024221315 / files
nightly linux64
ddadc29de671 / 65.0a1 / 20181024221315 / files
nightly mac
ddadc29de671 / 65.0a1 / 20181024221315 / files
nightly win32
ddadc29de671 / 65.0a1 / 20181024221315 / files
nightly win64
ddadc29de671 / 65.0a1 / 20181024221315 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -733,16 +733,30 @@ async function sanitizeSessionPrincipals
   }).catch(() => []);
 
   let serviceWorkers = serviceWorkerManager.getAllRegistrations();
   for (let i = 0; i < serviceWorkers.length; i++) {
     let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
     principals.push(sw.principal);
   }
 
+  // Let's take the list of unique hosts+OA from cookies.
+  let enumerator = Services.cookies.enumerator;
+  let hosts = new Set();
+  for (let cookie of enumerator) {
+    hosts.add(cookie.host + ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
+  }
+
+  hosts.forEach(host => {
+    // Cookies and permissions are handled by origin/host. Doesn't matter if we
+    // use http: or https: schema here.
+    principals.push(
+      Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://" + host));
+  });
+
   await maybeSanitizeSessionPrincipals(principals);
 }
 
 // This method receives a list of principals and it checks if some of them need
 // to be sanitize.
 async function maybeSanitizeSessionPrincipals(principals) {
   let promises = [];
 
@@ -756,17 +770,18 @@ async function maybeSanitizeSessionPrinc
   }
 
   return Promise.all(promises);
 }
 
 async function sanitizeSessionPrincipal(principal) {
   await new Promise(resolve => {
     Services.clearData.deleteDataFromPrincipal(principal, true /* user request */,
-                                               Ci.nsIClearDataService.CLEAR_DOM_STORAGES,
+                                               Ci.nsIClearDataService.CLEAR_DOM_STORAGES |
+                                               Ci.nsIClearDataService.CLEAR_COOKIES,
                                                resolve);
   });
 }
 
 function sanitizeNewTabSegregation() {
   let identity = ContextualIdentityService.getPrivateIdentity("userContextIdInternal.thumbnail");
   if (identity) {
     Services.obs.notifyObservers(null, "clear-origin-attributes-data",
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -596,18 +596,26 @@ WebGLContext::CreateAndInitGL(bool force
     // --
 
     typedef decltype(gl::GLContextProviderEGL::CreateOffscreen) fnCreateOffscreenT;
     const auto fnCreate = [&](fnCreateOffscreenT* const pfnCreateOffscreen,
                               const char* const info)
     {
         const gfx::IntSize dummySize(1, 1);
         nsCString failureId;
-        const RefPtr<GLContext> gl = pfnCreateOffscreen(dummySize, surfaceCaps, flags,
-                                                        &failureId);
+        RefPtr<GLContext> gl = pfnCreateOffscreen(dummySize, surfaceCaps, flags,
+                                                  &failureId);
+        if (gl && gl->IsCoreProfile() &&
+            !(flags & gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE) &&
+            !gl->IsSupported(gl::GLFeature::gpu_shader5))
+        {
+            // See comment on "constant-index-expression" in WebGLShaderValidator.cpp.
+            const auto compatFlags = flags | gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
+            gl = pfnCreateOffscreen(dummySize, surfaceCaps, compatFlags, &failureId);
+        }
         if (!gl) {
             out_failReasons->push_back(WebGLContext::FailureReason(failureId, info));
         }
         return gl;
     };
 
     const auto newGL = [&]() -> RefPtr<gl::GLContext> {
         if (tryNativeGL) {
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -300,33 +300,32 @@ WebGLContext::ValidateUniformMatrixArray
     return true;
 }
 
 bool
 WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
 {
     MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");
 
+    if (!gl->MakeCurrent(true)) {
+        MOZ_ASSERT(false);
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_MAKECURRENT",
+                            "Failed to MakeCurrent for init." };
+        return false;
+    }
+
     // Unconditionally create a new format usage authority. This is
     // important when restoring contexts and extensions need to add
     // formats back into the authority.
     mFormatUsage = CreateFormatUsage(gl);
-    if (!mFormatUsage) {
-        *out_failReason = { "FEATURE_FAILURE_WEBGL_FORMAT",
-                            "Failed to create mFormatUsage." };
-        return false;
-    }
+    MOZ_RELEASE_ASSERT(mFormatUsage);
 
-    GLenum error = gl->fGetError();
-    if (error != LOCAL_GL_NO_ERROR) {
-        const nsPrintfCString reason("GL error 0x%x occurred during OpenGL context"
-                                     " initialization, before WebGL initialization!",
-                                     error);
-        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_1", reason };
-        return false;
+    {
+        const auto error = gl->fGetError();
+        MOZ_ALWAYS_TRUE(!error);
     }
 
     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
     mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
 
     // These are the default values, see 6.2 State tables in the
@@ -599,23 +598,19 @@ WebGLContext::InitAndValidateGL(FailureR
     // Mesa can only be detected with the GL_VERSION string, of the form
     // "2.1 Mesa 7.11.0"
     const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
     mIsMesa = strstr(versionStr, "Mesa");
 
     // Notice that the point of calling fGetError here is not only to check for
     // errors, but also to reset the error flags so that a subsequent WebGL
     // getError call will give the correct result.
-    error = gl->fGetError();
-    if (error != LOCAL_GL_NO_ERROR) {
-        const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
-                                     " initialization!",
-                                     error);
-        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_2", reason };
-        return false;
+    {
+        const auto error = gl->fGetError();
+        MOZ_ALWAYS_TRUE(!error);
     }
 
     if (IsWebGL2() &&
         !InitWebGL2(out_failReason))
     {
         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLShaderValidator.h"
 
+#include <algorithm>
 #include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/Preferences.h"
 #include "MurmurHash3.h"
 #include "nsPrintfCString.h"
 #include <string>
 #include <vector>
@@ -96,22 +97,32 @@ ChooseValidatorCompileOptions(const ShBu
     return options;
 }
 
 } // namespace webgl
 
 ////////////////////////////////////////
 
 static ShShaderOutput
-ShaderOutput(gl::GLContext* gl)
+ShaderOutput(gl::GLContext* const gl)
 {
     if (gl->IsGLES()) {
         return SH_ESSL_OUTPUT;
     } else {
         uint32_t version = gl->ShadingLanguageVersion();
+
+        // Version 130 starts to require integral constant expressions for loop indices,
+        // instead of "constant-index-expression".
+        // Both version 400 and gpu_shader5 remove this restrictions.
+        // gpu_shader5 went core in 400, so we can just check for the GLFeature.
+        // If we're compiling for webglsl1, even for webgl2, we need gpu_shader5, or GLSL_COMPAT.
+        if (!gl->IsSupported(gl::GLFeature::gpu_shader5)) {
+            version = std::min<uint32_t>(version, 120);
+        }
+
         switch (version) {
         case 100: return SH_GLSL_COMPATIBILITY_OUTPUT;
         case 120: return SH_GLSL_COMPATIBILITY_OUTPUT;
         case 130: return SH_GLSL_130_OUTPUT;
         case 140: return SH_GLSL_140_OUTPUT;
         case 150: return SH_GLSL_150_CORE_OUTPUT;
         case 330: return SH_GLSL_330_CORE_OUTPUT;
         case 400: return SH_GLSL_400_CORE_OUTPUT;
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -11111,17 +11111,16 @@ subsuite = webgl1-ext
 subsuite = webgl1-ext
 skip-if = (os == 'android')
 [generated/test_conformance__glsl__bugs__qualcomm-loop-with-continue-crash.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__bugs__sampler-array-struct-function-arg.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__bugs__sampler-array-using-loop-index.html]
 subsuite = webgl1-ext
-fail-if = (os == 'linux')
 [generated/test_conformance__glsl__bugs__sampler-struct-function-arg.html]
 subsuite = webgl1-ext
 skip-if = (os == 'linux') || (os == 'android')
 [generated/test_conformance__glsl__bugs__sequence-operator-evaluation-order.html]
 subsuite = webgl1-ext
 skip-if = (os == 'android')
 [generated/test_conformance__glsl__bugs__sketchfab-lighting-shader-crash.html]
 subsuite = webgl1-ext
@@ -11407,17 +11406,17 @@ subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__empty-declaration.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__empty_main.vert.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__expression-list-in-declarator-initializer.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__fragcolor-fragdata-invariant.html]
 subsuite = webgl1-ext
-fail-if = (os == 'mac')
+fail-if = (os == 'linux') || (os == 'mac')
 [generated/test_conformance__glsl__misc__gl_position_unset.vert.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__global-variable-init.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__glsl-function-nodes.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__glsl-long-variable-names.html]
 subsuite = webgl1-ext
@@ -11587,16 +11586,17 @@ subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shader-with-while-loop.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shader-without-precision.frag.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shaders-with-constant-expression-loop-conditions.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
 subsuite = webgl1-ext
+fail-if = (os == 'linux')
 [generated/test_conformance__glsl__misc__shaders-with-mis-matching-uniforms.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shaders-with-mis-matching-varyings.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shaders-with-missing-varyings.html]
 subsuite = webgl1-ext
 [generated/test_conformance__glsl__misc__shaders-with-name-conflicts.html]
 subsuite = webgl1-ext
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -188,19 +188,17 @@ skip-if = (os == 'linux') || (os == 'mac
 [generated/test_conformance__glsl__constructors__glsl-construct-ivec4.html]
 # Assume crashes like ivec3
 skip-if = (os == 'linux') || (os == 'mac')
 
 [generated/test_conformance__glsl__constructors__glsl-construct-mat2.html]
 # Crashes on Linux ASAN
 skip-if = ((os == 'linux') && asan)
 
-[generated/test_conformance__glsl__bugs__sampler-array-using-loop-index.html]
-# Testfail on Linux after removing SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX.
-# Only happen on tryserver
+[generated/test_conformance__glsl__misc__shaders-with-invariance.html]
 fail-if = (os == 'linux')
 
 [generated/test_conformance__misc__type-conversion-test.html]
 fail-if = (os == 'linux')
 # Resets device on Android 2.3.
 # Crashes on desktop Linux.
 skip-if = (os == 'android') || (os == 'linux')
 
@@ -355,17 +353,17 @@ fail-if = (os == 'mac') || (verify && de
 skip-if = (os == 'win')
 [generated/test_2_conformance__rendering__rendering-stencil-large-viewport.html]
 # same as webgl1 test
 fail-if = (os == 'mac')
 skip-if = (os == 'win')
 
 [generated/test_conformance__glsl__misc__fragcolor-fragdata-invariant.html]
 # [unexpected fragment shader compile status] (expected: true) Declaring both gl_FragColor and gl_FragData invariant should succeed.
-fail-if = (os == 'mac')
+fail-if = (os == 'linux') || (os == 'mac')
 
 ########################################################################
 # "tst-linux{32,64}-spot-NNN" Slaves:
 #   Android 2.3 and Linux.
 # Android: os == 'android'. (Not enough info to separate out 2.3)
 # Linux: os == 'linux'.
 [generated/test_conformance__glsl__bugs__temp-expressions-should-not-crash.html]
 # Coincidentally enough, crashes on Linux and Android 4.0.
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -92,16 +92,17 @@ static const char* const sExtensionNames
     "GL_ARB_compatibility",
     "GL_ARB_copy_buffer",
     "GL_ARB_depth_texture",
     "GL_ARB_draw_buffers",
     "GL_ARB_draw_instanced",
     "GL_ARB_framebuffer_object",
     "GL_ARB_framebuffer_sRGB",
     "GL_ARB_geometry_shader4",
+    "GL_ARB_gpu_shader5",
     "GL_ARB_half_float_pixel",
     "GL_ARB_instanced_arrays",
     "GL_ARB_internalformat_query",
     "GL_ARB_invalidate_subdata",
     "GL_ARB_map_buffer_range",
     "GL_ARB_occlusion_query2",
     "GL_ARB_pixel_buffer_object",
     "GL_ARB_robust_buffer_access_behavior",
@@ -134,16 +135,17 @@ static const char* const sExtensionNames
     "GL_EXT_draw_instanced",
     "GL_EXT_draw_range_elements",
     "GL_EXT_frag_depth",
     "GL_EXT_framebuffer_blit",
     "GL_EXT_framebuffer_multisample",
     "GL_EXT_framebuffer_object",
     "GL_EXT_framebuffer_sRGB",
     "GL_EXT_gpu_shader4",
+    "GL_EXT_gpu_shader5",
     "GL_EXT_multisampled_render_to_texture",
     "GL_EXT_occlusion_query_boolean",
     "GL_EXT_packed_depth_stencil",
     "GL_EXT_read_format_bgra",
     "GL_EXT_robustness",
     "GL_EXT_sRGB",
     "GL_EXT_sRGB_write_control",
     "GL_EXT_shader_texture_lod",
@@ -167,16 +169,17 @@ static const char* const sExtensionNames
     "GL_KHR_robust_buffer_access_behavior",
     "GL_KHR_robustness",
     "GL_KHR_texture_compression_astc_hdr",
     "GL_KHR_texture_compression_astc_ldr",
     "GL_NV_draw_instanced",
     "GL_NV_fence",
     "GL_NV_framebuffer_blit",
     "GL_NV_geometry_program4",
+    "GL_NV_gpu_shader5",
     "GL_NV_half_float",
     "GL_NV_instanced_arrays",
     "GL_NV_primitive_restart",
     "GL_NV_texture_barrier",
     "GL_NV_transform_feedback",
     "GL_NV_transform_feedback2",
     "GL_OES_EGL_image",
     "GL_OES_EGL_image_external",
@@ -373,16 +376,20 @@ GLContext::LoadFeatureSymbols(const char
         return false;
     }
     return true;
 };
 
 bool
 GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
 {
+    // see bug 929506 comment 29. wglGetProcAddress requires a current context.
+    if (!MakeCurrent(true))
+        return false;
+
     mWorkAroundDriverBugs = gfxPrefs::WorkAroundDriverBugs();
 
     const SymLoadStruct coreSymbols[] = {
         { (PRFuncPtr*) &mSymbols.fActiveTexture, { "ActiveTexture", "ActiveTextureARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fAttachShader, { "AttachShader", "AttachShaderARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBindAttribLocation, { "BindAttribLocation", "BindAttribLocationARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBindBuffer, { "BindBuffer", "BindBufferARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBindTexture, { "BindTexture", "BindTextureARB", nullptr } },
@@ -509,20 +516,16 @@ GLContext::InitWithPrefixImpl(const char
         END_SYMBOLS
     };
 
     if (!LoadGLSymbols(this, prefix, trygl, coreSymbols, "GL"))
         return false;
 
     ////////////////
 
-    if (!MakeCurrent()) {
-        return false;
-    }
-
     const std::string versionStr = (const char*)fGetString(LOCAL_GL_VERSION);
     if (versionStr.find("OpenGL ES") == 0) {
         mProfile = ContextProfile::OpenGLES;
     }
 
     uint32_t majorVer, minorVer;
     if (!ParseVersion(versionStr, &majorVer, &minorVer)) {
         MOZ_ASSERT(false, "Failed to parse GL_VERSION");
@@ -2046,21 +2049,19 @@ GLContext::MarkDestroyed()
         return;
 
     // Null these before they're naturally nulled after dtor, as we want GLContext to
     // still be alive in *their* dtors.
     mScreen = nullptr;
     mBlitHelper = nullptr;
     mReadTexImageHelper = nullptr;
 
-    if (!MakeCurrent()) {
-        NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
-    }
-
+    mIsDestroyed = true;
     mSymbols = {};
+    (void)MakeCurrent(true); // Clear current context.
 }
 
 #ifdef MOZ_GL_DEBUG
 /* static */ void
 GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)
 {
   int somethingOnTheStack;
   const void* someStackPtr = &somethingOnTheStack;
@@ -2936,36 +2937,36 @@ GetBytesPerTexel(GLenum format, GLenum t
         return 2;
     }
 
     gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
     return 0;
 }
 
 bool
-GLContext::MakeCurrent(bool aForce) const
+GLContext::MakeCurrent(const bool aForce) const
 {
-    if (MOZ_UNLIKELY( IsDestroyed() ))
-        return false;
-
-    if (MOZ_LIKELY( !aForce )) {
-        bool isCurrent;
+    if (MOZ_LIKELY( !aForce & !IsDestroyed() )) {
+        bool isCurrent = false;
         if (mUseTLSIsCurrent) {
             isCurrent = (sCurrentContext.get() == reinterpret_cast<uintptr_t>(this));
-        } else {
+        }
+        if (MOZ_UNLIKELY( !isCurrent )) {
             isCurrent = IsCurrentImpl();
         }
         if (MOZ_LIKELY( isCurrent )) {
             MOZ_ASSERT(IsCurrentImpl());
             return true;
         }
     }
 
-    if (!MakeCurrentImpl())
+    if (MOZ_UNLIKELY( !MakeCurrentImpl() )) {
+        ClearGetCurrentContextTLS();
         return false;
+    }
 
     sCurrentContext.set(reinterpret_cast<uintptr_t>(this));
     return true;
 }
 
 void
 GLContext::ResetSyncCallCount(const char* resetReason) const
 {
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -98,16 +98,17 @@ enum class GLFeature {
     framebuffer_multisample,
     framebuffer_object,
     framebuffer_object_EXT_OES,
     get_integer_indexed,
     get_integer64_indexed,
     get_query_object_i64v,
     get_query_object_iv,
     gpu_shader4,
+    gpu_shader5,
     instanced_arrays,
     instanced_non_arrays,
     internalformat_query,
     invalidate_framebuffer,
     map_buffer_range,
     occlusion_query,
     occlusion_query_boolean,
     occlusion_query2,
@@ -195,16 +196,20 @@ class GLContext
     : public GLLibraryLoader
     , public GenericAtomicRefCounted
     , public SupportsWeakPtr<GLContext>
 {
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
     static MOZ_THREAD_LOCAL(uintptr_t) sCurrentContext;
 
+    static void ClearGetCurrentContextTLS() {
+        sCurrentContext.set(0);
+    }
+
     bool mImplicitMakeCurrent = false;
     bool mUseTLSIsCurrent;
 
     class TlsScope final {
         const WeakPtr<GLContext> mGL;
         const bool mWasTlsOk;
     public:
         explicit TlsScope(GLContext* const gl)
@@ -336,16 +341,17 @@ public:
      */
     virtual GLuint GetDefaultFramebuffer() {
         return 0;
     }
 
 protected:
     bool mIsOffscreen;
     mutable bool mContextLost = false;
+    bool mIsDestroyed = false;
 
     /**
      * mVersion store the OpenGL's version, multiplied by 100. For example, if
      * the context is an OpenGL 2.1 context, mVersion value will be 210.
      */
     uint32_t mVersion = 0;
     ContextProfile mProfile = ContextProfile::Unknown;
 
@@ -393,16 +399,17 @@ public:
         ARB_compatibility,
         ARB_copy_buffer,
         ARB_depth_texture,
         ARB_draw_buffers,
         ARB_draw_instanced,
         ARB_framebuffer_object,
         ARB_framebuffer_sRGB,
         ARB_geometry_shader4,
+        ARB_gpu_shader5,
         ARB_half_float_pixel,
         ARB_instanced_arrays,
         ARB_internalformat_query,
         ARB_invalidate_subdata,
         ARB_map_buffer_range,
         ARB_occlusion_query2,
         ARB_pixel_buffer_object,
         ARB_robust_buffer_access_behavior,
@@ -435,16 +442,17 @@ public:
         EXT_draw_instanced,
         EXT_draw_range_elements,
         EXT_frag_depth,
         EXT_framebuffer_blit,
         EXT_framebuffer_multisample,
         EXT_framebuffer_object,
         EXT_framebuffer_sRGB,
         EXT_gpu_shader4,
+        EXT_gpu_shader5,
         EXT_multisampled_render_to_texture,
         EXT_occlusion_query_boolean,
         EXT_packed_depth_stencil,
         EXT_read_format_bgra,
         EXT_robustness,
         EXT_sRGB,
         EXT_sRGB_write_control,
         EXT_shader_texture_lod,
@@ -468,16 +476,17 @@ public:
         KHR_robust_buffer_access_behavior,
         KHR_robustness,
         KHR_texture_compression_astc_hdr,
         KHR_texture_compression_astc_ldr,
         NV_draw_instanced,
         NV_fence,
         NV_framebuffer_blit,
         NV_geometry_program4,
+        NV_gpu_shader5,
         NV_half_float,
         NV_instanced_arrays,
         NV_primitive_restart,
         NV_texture_barrier,
         NV_transform_feedback,
         NV_transform_feedback2,
         OES_EGL_image,
         OES_EGL_image_external,
@@ -3369,18 +3378,17 @@ protected:
 public:
     virtual bool Init() = 0;
 
     virtual bool SetupLookupFunction() = 0;
 
     virtual void ReleaseSurface() {}
 
     bool IsDestroyed() const {
-        // MarkDestroyed will mark all these as null.
-        return mSymbols.fUseProgram == nullptr;
+        return mIsDestroyed;
     }
 
     GLContext* GetSharedContext() { return mSharedContext; }
 
     /**
      * Returns true if the thread on which this context was created is the currently
      * executing thread.
      */
--- a/gfx/gl/GLContextCGL.h
+++ b/gfx/gl/GLContextCGL.h
@@ -19,17 +19,17 @@ typedef void NSOpenGLContext;
 
 namespace mozilla {
 namespace gl {
 
 class GLContextCGL : public GLContext
 {
     friend class GLContextProviderCGL;
 
-    NSOpenGLContext* mContext;
+    NSOpenGLContext* const mContext;
 
 public:
     MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextCGL, override)
     GLContextCGL(CreateContextFlags flags, const SurfaceCaps& caps,
                  NSOpenGLContext* context, bool isOffscreen);
 
     ~GLContextCGL();
 
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -317,16 +317,28 @@ static const FeatureInfo sFeatureInfoArr
         GLESVersion::ES3,
         GLContext::Extension_None,
         {
             GLContext::EXT_gpu_shader4,
             GLContext::Extensions_End
         }
     },
     {
+        "gpu_shader5",
+        GLVersion::GL4,
+        GLESVersion::NONE,
+        GLContext::Extension_None,
+        {
+            GLContext::ARB_gpu_shader5,
+            GLContext::EXT_gpu_shader5,
+            GLContext::NV_gpu_shader5,
+            GLContext::Extensions_End
+        }
+    },
+    {
         "instanced_arrays",
         GLVersion::GL3_3,
         GLESVersion::ES3,
         GLContext::Extension_None,
         {
             GLContext::ARB_instanced_arrays,
             GLContext::NV_instanced_arrays,
             GLContext::ANGLE_instanced_arrays,
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -82,16 +82,17 @@ private:
                  GLXContext aContext,
                  bool aDeleteDrawable,
                  bool aDoubleBuffered,
                  gfxXlibSurface* aPixmap);
 
     GLXContext mContext;
     Display* mDisplay;
     GLXDrawable mDrawable;
+    Maybe<GLXDrawable> mOverrideDrawable;
     bool mDeleteDrawable;
     bool mDoubleBuffered;
 
     GLXLibrary* mGLX;
 
     RefPtr<gfxXlibSurface> mPixmap;
     bool mOwnsContext = true;
 };
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -71,46 +71,39 @@ GLContextCGL::GLContextCGL(CreateContext
     : GLContext(flags, caps, nullptr, isOffscreen)
     , mContext(context)
 {
 }
 
 GLContextCGL::~GLContextCGL()
 {
     MarkDestroyed();
-
-    if (mContext) {
-        if ([NSOpenGLContext currentContext] == mContext) {
-            // Clear the current context before releasing. If we don't do
-            // this, the next time we call [NSOpenGLContext currentContext],
-            // "invalid context" will be printed to the console.
-            [NSOpenGLContext clearCurrentContext];
-        }
-        [mContext release];
-    }
+    [mContext release];
 }
 
 bool
 GLContextCGL::Init()
 {
-    if (!InitWithPrefix("gl", true))
-        return false;
-
-    return true;
+    return InitWithPrefix("gl", true);
 }
 
 CGLContextObj
 GLContextCGL::GetCGLContext() const
 {
     return static_cast<CGLContextObj>([mContext CGLContextObj]);
 }
 
 bool
 GLContextCGL::MakeCurrentImpl() const
 {
+    if (IsDestroyed()) {
+        [NSOpenGLContext clearCurrentContext];
+        return false;
+    }
+
     if (mContext) {
         [mContext makeCurrentContext];
         MOZ_ASSERT(IsCurrentImpl());
         // Use non-blocking swap in "ASAP mode".
         // ASAP mode means that rendering is iterated as fast as possible.
         // ASAP mode is entered when layout.frame_rate=0 (requires restart).
         // If swapInt is 1, then glSwapBuffers will block and wait for a vblank signal.
         // When we're iterating as fast as possible, however, we want a non-blocking
--- a/gfx/gl/GLContextProviderEAGL.mm
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -27,45 +27,36 @@ GLContextEAGL::GLContextEAGL(CreateConte
                              bool isOffscreen)
     : GLContext(flags, caps, sharedContext, isOffscreen)
     , mContext(context)
 {
 }
 
 GLContextEAGL::~GLContextEAGL()
 {
-    MakeCurrent();
+    if (MakeCurrent()) {
+        if (mBackbufferFB) {
+            fDeleteFramebuffers(1, &mBackbufferFB);
+        }
 
-    if (mBackbufferFB) {
-        fDeleteFramebuffers(1, &mBackbufferFB);
-    }
-
-    if (mBackbufferRB) {
-        fDeleteRenderbuffers(1, &mBackbufferRB);
+        if (mBackbufferRB) {
+            fDeleteRenderbuffers(1, &mBackbufferRB);
+        }
     }
 
-    MarkDestroyed();
+    mLayer = nil;
 
-    if (mLayer) {
-      mLayer = nil;
-    }
-
-    if (mContext) {
-      [EAGLContext setCurrentContext:nil];
-      [mContext release];
-    }
+    MarkDestroyed();
+    [mContext release];
 }
 
 bool
 GLContextEAGL::Init()
 {
-    if (!InitWithPrefix("gl", true))
-        return false;
-
-    return true;
+    return InitWithPrefix("gl", true);
 }
 
 bool
 GLContextEAGL::AttachToWindow(nsIWidget* aWidget)
 {
     // This should only be called once
     MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB);
 
@@ -107,22 +98,21 @@ GLContextEAGL::RecreateRB()
                              LOCAL_GL_RENDERBUFFER, mBackbufferRB);
 
     return LOCAL_GL_FRAMEBUFFER_COMPLETE == fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
 }
 
 bool
 GLContextEAGL::MakeCurrentImpl() const
 {
-    if (mContext) {
-        if(![EAGLContext setCurrentContext:mContext]) {
-            return false;
-        }
+    if (IsDestroyed()) {
+        [EAGLContext setCurrentContext:nil];
+        return false;
     }
-    return true;
+    return [EAGLContext setCurrentContext:mContext];
 }
 
 bool
 GLContextEAGL::IsCurrentImpl() const
 {
     return [EAGLContext currentContext] == mContext;
 }
 
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -153,29 +153,28 @@ is_power_of_two(int v)
 
     if (v == 0)
         return true;
 
     return (v & (v-1)) == 0;
 }
 
 static void
-DestroySurface(EGLSurface oldSurface) {
-    auto* egl = gl::GLLibraryEGL::Get();
+DestroySurface(const EGLSurface surf)
+{
+    if (!surf)
+        return;
 
-    if (oldSurface != EGL_NO_SURFACE) {
-        // TODO: This breaks TLS MakeCurrent caching.
-        egl->fMakeCurrent(EGL_DISPLAY(),
-                          EGL_NO_SURFACE, EGL_NO_SURFACE,
-                          EGL_NO_CONTEXT);
-        egl->fDestroySurface(EGL_DISPLAY(), oldSurface);
+    const auto& egl = gl::GLLibraryEGL::Get();
+
+    // TODO: This breaks TLS MakeCurrent caching.
+    MOZ_ALWAYS_TRUE( egl->fDestroySurface(EGL_DISPLAY(), surf) );
 #if defined(MOZ_WAYLAND)
-        DeleteWaylandGLSurface(oldSurface);
+    DeleteWaylandGLSurface(surf);
 #endif
-    }
 }
 
 static EGLSurface
 CreateFallbackSurface(const EGLConfig& config)
 {
     nsCString discardFailureId;
     if (!GLLibraryEGL::EnsureInitialized(false, &discardFailureId)) {
         gfxCriticalNote << "Failed to load EGL library 3!";
@@ -279,17 +278,17 @@ GLContextEGLFactory::Create(EGLNativeWin
     if (aWebRender) {
         flags |= CreateContextFlags::PREFER_ES3;
     }
     SurfaceCaps caps = SurfaceCaps::Any();
     RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, caps, false, config,
                                                             surface, &discardFailureId);
     if (!gl) {
         gfxCriticalNote << "Failed to create EGLContext!";
-        mozilla::gl::DestroySurface(surface);
+        DestroySurface(surface);
         return nullptr;
     }
 
     gl->MakeCurrent();
     gl->SetIsDoubleBuffered(doubleBuffered);
     if (aWebRender && egl->IsANGLE()) {
         MOZ_ASSERT(doubleBuffered);
         egl->fSwapInterval(EGL_DISPLAY(), 0);
@@ -321,19 +320,19 @@ GLContextEGL::~GLContextEGL()
         return;
     }
 
 #ifdef DEBUG
     printf_stderr("Destroying context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
 #endif
 
     mEgl->fDestroyContext(EGL_DISPLAY(), mContext);
-
-    mozilla::gl::DestroySurface(mSurface);
-    mozilla::gl::DestroySurface(mFallbackSurface);
+    DestroySurface(mSurface);
+    MOZ_ASSERT(!mFallbackSurface || mFallbackSurface != mSurface);
+    DestroySurface(mFallbackSurface);
 }
 
 bool
 GLContextEGL::Init()
 {
 #if defined(ANDROID)
     // We can't use LoadApitraceLibrary here because the GLContext
     // expects its own handle to the GL library
@@ -346,23 +345,17 @@ GLContextEGL::Init()
                 return false;
             }
 #endif
         }
 
     SetupLookupFunction();
     if (!InitWithPrefix("gl", true))
         return false;
-
-    bool current = MakeCurrent();
-    if (!current) {
-        gfx::LogFailure(NS_LITERAL_CSTRING(
-            "Couldn't get device attachments for device."));
-        return false;
-    }
+    MOZ_ASSERT(IsCurrent());
 
     static_assert(sizeof(GLint) >= sizeof(int32_t), "GLint is smaller than int32_t");
     mMaxTextureImageSize = INT32_MAX;
 
     mShareWithEGLImage = mEgl->HasKHRImageBase() &&
                          mEgl->HasKHRImageTexture2D() &&
                          IsExtensionSupported(OES_EGL_image);
 
@@ -403,36 +396,45 @@ GLContextEGL::ReleaseTexImage()
     if (success == LOCAL_EGL_FALSE)
         return false;
 
     mBound = false;
     return true;
 }
 
 void
-GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf) {
+GLContextEGL::SetEGLSurfaceOverride(const EGLSurface surf)
+{
+    MOZ_ASSERT(!surf || surf != mSurface);
+
     if (Screen()) {
         /* Blit `draw` to `read` if we need to, before we potentially juggle
           * `read` around. If we don't, we might attach a different `read`,
           * and *then* hit AssureBlitted, which will blit a dirty `draw` onto
           * the wrong `read`!
           */
         Screen()->AssureBlitted();
     }
 
     mSurfaceOverride = surf;
-    DebugOnly<bool> ok = MakeCurrent(true);
-    MOZ_ASSERT(ok);
+    MOZ_ALWAYS_TRUE( MakeCurrent(true) );
 }
 
 bool
 GLContextEGL::MakeCurrentImpl() const
 {
-    EGLSurface surface = (mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride
-                                                              : mSurface;
+    if (IsDestroyed()) {
+        MOZ_ALWAYS_TRUE( mEgl->fMakeCurrent(EGL_DISPLAY(), nullptr, nullptr, nullptr) );
+        return false;
+    }
+
+    auto surface = mSurface;
+    if (mSurfaceOverride) {
+        surface = mSurfaceOverride;
+    }
     if (!surface) {
         surface = mFallbackSurface;
     }
 
     const bool succeeded = mEgl->fMakeCurrent(EGL_DISPLAY(), surface, surface,
                                               mContext);
     if (!succeeded) {
         const auto eglError = mEgl->fGetError();
@@ -452,17 +454,18 @@ GLContextEGL::MakeCurrentImpl() const
 
 bool
 GLContextEGL::IsCurrentImpl() const
 {
     return mEgl->fGetCurrentContext() == mContext;
 }
 
 bool
-GLContextEGL::RenewSurface(CompositorWidget* aWidget) {
+GLContextEGL::RenewSurface(CompositorWidget* const aWidget)
+{
     if (!mOwnsContext) {
         return false;
     }
     // unconditionally release the surface and create a new one. Don't try to optimize this away.
     // If we get here, then by definition we know that we want to get a new surface.
     ReleaseSurface();
     MOZ_ASSERT(aWidget);
 
@@ -474,24 +477,23 @@ GLContextEGL::RenewSurface(CompositorWid
             return false;
         }
     }
 
     return MakeCurrent(true);
 }
 
 void
-GLContextEGL::ReleaseSurface() {
-    if (mOwnsContext) {
-        mozilla::gl::DestroySurface(mSurface);
-    }
-    if (mSurface == mSurfaceOverride) {
-        mSurfaceOverride = EGL_NO_SURFACE;
-    }
-    mSurface = EGL_NO_SURFACE;
+GLContextEGL::ReleaseSurface()
+{
+    if (!mOwnsContext)
+        return;
+
+    DestroySurface(mSurface);
+    mSurface = nullptr;
 }
 
 bool
 GLContextEGL::SetupLookupFunction()
 {
     mLookupFunc = mEgl->GetLookupFunction();
     return true;
 }
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -582,30 +582,22 @@ GLContextGLX::~GLContextGLX()
 {
     MarkDestroyed();
 
     // Wrapped context should not destroy glxContext/Surface
     if (!mOwnsContext) {
         return;
     }
 
-    // see bug 659842 comment 76
-#ifdef DEBUG
-    bool success =
-#endif
-    mGLX->fMakeCurrent(mDisplay, X11None, nullptr);
-    MOZ_ASSERT(success,
-               "glXMakeCurrent failed to release GL context before we call "
-               "glXDestroyContext!");
-
     mGLX->fDestroyContext(mDisplay, mContext);
 
     if (mDeleteDrawable) {
         mGLX->fDestroyPixmap(mDisplay, mDrawable);
     }
+    MOZ_ASSERT(!mOverrideDrawable);
 }
 
 
 bool
 GLContextGLX::Init()
 {
     SetupLookupFunction();
     if (!InitWithPrefix("gl", true)) {
@@ -624,17 +616,26 @@ bool
 GLContextGLX::MakeCurrentImpl() const
 {
     if (mGLX->IsMesa()) {
         // Read into the event queue to ensure that Mesa receives a
         // DRI2InvalidateBuffers event before drawing. See bug 1280653.
         Unused << XPending(mDisplay);
     }
 
-    const bool succeeded = mGLX->fMakeCurrent(mDisplay, mDrawable, mContext);
+    if (IsDestroyed()) {
+        MOZ_ALWAYS_TRUE( mGLX->fMakeCurrent(mDisplay, X11None, nullptr) );
+        return false; // Did not MakeCurrent mContext, but that's what we wanted!
+    }
+
+    auto drawable = mDrawable;
+    if (mOverrideDrawable) {
+        drawable = mOverrideDrawable.ref();
+    }
+    const bool succeeded = mGLX->fMakeCurrent(mDisplay, drawable, mContext);
     NS_ASSERTION(succeeded, "Failed to make GL context current!");
 
     if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
         // Many GLX implementations default to blocking until the next
         // VBlank when calling glXSwapBuffers. We want to run unthrottled
         // in ASAP mode. See bug 1280744.
         const bool isASAP = (gfxPrefs::LayoutFrameRate() == 0);
         mGLX->fSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1);
@@ -689,26 +690,28 @@ GLContextGLX::GetWSIInfo(nsCString* cons
 
     out->AppendLiteral("\nExtensions: ");
     out->Append(sGLXLibrary.fQueryExtensionsString(display, screen));
 }
 
 bool
 GLContextGLX::OverrideDrawable(GLXDrawable drawable)
 {
-    if (Screen())
+    if (Screen()) {
         Screen()->AssureBlitted();
-    Bool result = mGLX->fMakeCurrent(mDisplay, drawable, mContext);
-    return result;
+    }
+    mOverrideDrawable = Some(drawable);
+    return MakeCurrent(true);
 }
 
 bool
 GLContextGLX::RestoreDrawable()
 {
-    return mGLX->fMakeCurrent(mDisplay, mDrawable, mContext);
+    mOverrideDrawable = Nothing();
+    return MakeCurrent(true);
 }
 
 GLContextGLX::GLContextGLX(
                   CreateContextFlags flags,
                   const SurfaceCaps& caps,
                   bool isOffscreen,
                   Display* aDisplay,
                   GLXDrawable aDrawable,
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -158,17 +158,16 @@ WGLLibrary::EnsureInitialized()
 
     // create rendering context
     mDummyGlrc = mSymbols.fCreateContext(mRootDc);
     if (!mDummyGlrc)
         return false;
 
     const auto curCtx = mSymbols.fGetCurrentContext();
     const auto curDC = mSymbols.fGetCurrentDC();
-
     if (!mSymbols.fMakeCurrent(mRootDc, mDummyGlrc)) {
         NS_WARNING("wglMakeCurrent failed");
         return false;
     }
     const auto resetContext = MakeScopeExit([&]() {
         mSymbols.fMakeCurrent(curDC, curCtx);
     });
 
@@ -294,17 +293,16 @@ GLContextWGL::GLContextWGL(CreateContext
       mPBuffer(aPbuffer),
       mPixelFormat(aPixelFormat)
 {
 }
 
 GLContextWGL::~GLContextWGL()
 {
     MarkDestroyed();
-
     (void)sWGLLib.mSymbols.fDeleteContext(mContext);
 
     if (mPBuffer) {
         (void)sWGLLib.mSymbols.fReleasePbufferDC(mPBuffer, mDC);
         (void)sWGLLib.mSymbols.fDestroyPbuffer(mPBuffer);
     }
     if (mWnd) {
         (void)ReleaseDC(mWnd, mDC);
@@ -313,30 +311,28 @@ GLContextWGL::~GLContextWGL()
 }
 
 bool
 GLContextWGL::Init()
 {
     if (!mDC || !mContext)
         return false;
 
-    // see bug 929506 comment 29. wglGetProcAddress requires a current context.
-    if (!sWGLLib.mSymbols.fMakeCurrent(mDC, mContext))
-        return false;
-
     SetupLookupFunction();
-    if (!InitWithPrefix("gl", true))
-        return false;
-
-    return true;
+    return InitWithPrefix("gl", true);
 }
 
 bool
 GLContextWGL::MakeCurrentImpl() const
 {
+    if (IsDestroyed()) {
+        MOZ_ALWAYS_TRUE( sWGLLib.mSymbols.fMakeCurrent(0, 0) );
+        return false;
+    }
+
     const bool succeeded = sWGLLib.mSymbols.fMakeCurrent(mDC, mContext);
     NS_ASSERTION(succeeded, "Failed to make GL context current!");
     return succeeded;
 }
 
 bool
 GLContextWGL::IsCurrentImpl() const
 {
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/1501518.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- Add a few levels of blur to put as at an earlier render pass than the rest of the browser UI.
+     We then trigger a render pass with a larger border and a small rectangle. The border gets drawn
+     in this pass because it goes into the texture cache, and the rectangle gets drawn in this pass
+     because it gets an additional blur. A large item drawn to the texture cache in a pass where
+     the other objects are small will trigger the allocation of a render target that's smaller than
+     the large item, which is fine but triggered a panic before this bug was fixed.
+-->
+<div style="filter: blur(1px);">
+  <div style="filter: blur(1px);">
+    <div style="filter: blur(1px);">
+      <div style="background: green; height: 50px; width: 1000px; border: 10px dotted black;"></div>
+      <div style="background: blue; height: 50px; width: 50px; filter: blur(1px);"</div>
+    </div>
+  </div>
+</div>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -164,8 +164,9 @@ load 1325159-1.html
 load 1331683.html
 skip-if(Android) pref(dom.disable_open_during_load,false) load 1343666.html
 load 1408078-1.html
 load 1464243.html
 load 1467847-1.html
 load 1468020.html
 load 1470440.html
 load 1478035.html
+load 1501518.html
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -155,24 +155,24 @@ constexpr uint8_t ControllerEmbeddingFla
 
 enum BYOBRequestSlots {
     BYOBRequestSlot_Controller,
     BYOBRequestSlot_View,
     BYOBRequestSlotCount
 };
 
 template<class T>
-MOZ_ALWAYS_INLINE bool
+bool
 Is(const HandleValue v)
 {
     return v.isObject() && v.toObject().is<T>();
 }
 
 template<class T>
-MOZ_ALWAYS_INLINE bool
+bool
 IsMaybeWrapped(const HandleValue v)
 {
     if (!v.isObject()) {
         return false;
     }
     JSObject* obj = &v.toObject();
     if (obj->is<T>()) {
         return true;
@@ -300,17 +300,17 @@ JSObject::is<ReadableStreamReader>() con
 
 /**
  * Checks that |obj| is an unwrapped instance of T or throws an error.
  *
  * This overload must only be used if the caller can ensure that failure to
  * unwrap is the only possible source of exceptions.
  */
 template<class T>
-static MOZ_ALWAYS_INLINE T*
+static T*
 ToUnwrapped(JSContext* cx, JSObject* obj)
 {
     if (IsWrapper(obj)) {
         obj = CheckedUnwrap(obj);
         if (!obj) {
             ReportAccessDenied(cx);
             return nullptr;
         }
@@ -318,17 +318,17 @@ ToUnwrapped(JSContext* cx, JSObject* obj
 
     return &obj->as<T>();
 }
 
 /**
  * Checks that |obj| is an unwrapped instance of T or throws an error.
  */
 template<class T>
-static MOZ_ALWAYS_INLINE T*
+static T*
 ToUnwrapped(JSContext* cx, JSObject* obj, const char* description)
 {
     if (IsWrapper(obj)) {
         obj = CheckedUnwrap(obj);
         if (!obj) {
             ReportAccessDenied(cx);
             return nullptr;
         }
@@ -346,17 +346,17 @@ ToUnwrapped(JSContext* cx, JSObject* obj
 
 /**
  * Checks that |obj| is an unwrapped instance of T or throws an error.
  *
  * If the caller can ensure that failure to unwrap is the only possible
  * source of exceptions, it may omit the error messages.
  */
 template<class T>
-static MOZ_ALWAYS_INLINE T*
+static T*
 ToUnwrapped(JSContext* cx, JSObject* obj, const char* className, const char* methodName)
 {
     if (IsWrapper(obj)) {
         obj = CheckedUnwrap(obj);
         if (!obj) {
             ReportAccessDenied(cx);
             return nullptr;
         }
@@ -377,17 +377,17 @@ ToUnwrapped(JSContext* cx, JSObject* obj
  *
  * Throws exceptions if the value isn't an object, cannot be unwrapped, or
  * isn't an instance of the expected type.
  *
  * If the caller can ensure that failure to unwrap is the only possible
  * source of exceptions, it may omit the error messages.
  */
 template<class T>
-static MOZ_ALWAYS_INLINE T*
+static T*
 ToUnwrapped(JSContext* cx,
             HandleValue val,
             const char* className = "",
             const char* methodName = "")
 {
     if (!val.isObject()) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                    className, methodName, InformalValueTypeName(val));
@@ -404,17 +404,17 @@ ToUnwrapped(JSContext* cx,
  * compartment.
  *
  * If the stream cannot be unwrapped, which can only happen if it's a dead
  * wrapper object, a nullptr is returned. If a JSContext is available, this
  * case will also report an exception. The JSContext isn't mandatory to make
  * use easier for call sites that don't otherwise need a JSContext and can
  * provide useful defaults in case the stream is a dead wrapper.
  */
-MOZ_ALWAYS_INLINE static MOZ_MUST_USE ReadableStream*
+static MOZ_MUST_USE ReadableStream*
 StreamFromReader(JSContext *maybeCx, const ReadableStreamReader* reader)
 {
     MOZ_ASSERT(ReaderHasStream(reader));
     JSObject* streamObj = &reader->getFixedSlot(ReaderSlot_Stream).toObject();
     if (IsProxy(streamObj)) {
         if (JS_IsDeadWrapper(streamObj)) {
             if (maybeCx) {
                 JS_ReportErrorNumberASCII(maybeCx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
@@ -439,17 +439,17 @@ StreamFromReader(JSContext *maybeCx, con
  * object from the currently active compartment.
  *
  * If the reader cannot be unwrapped, which can only happen if it's a dead
  * wrapper object, a nullptr is returned. If a JSContext is available, an
  * exception is reported, too. The JSContext isn't mandatory to make use
  * easier for call sites that don't otherwise need a JSContext and can provide
  * useful defaults in case the reader is a dead object wrapper.
  */
-MOZ_ALWAYS_INLINE static MOZ_MUST_USE ReadableStreamReader*
+static MOZ_MUST_USE ReadableStreamReader*
 ReaderFromStream(JSContext* maybeCx, const ReadableStream* stream)
 {
     JSObject* readerObj = &stream->getFixedSlot(StreamSlot_Reader).toObject();
     if (IsProxy(readerObj)) {
         if (JS_IsDeadWrapper(readerObj)) {
             if (maybeCx) {
                 JS_ReportErrorNumberASCII(maybeCx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
             }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1500255.js
@@ -0,0 +1,10 @@
+
+setJitCompilerOption("offthread-compilation.enable", 0);
+setJitCompilerOption("ion.warmup.trigger", 0);
+
+foo();
+
+function foo() {
+    Array.prototype.__proto__ = null;
+    Array.prototype[1] = 'bar';
+}
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4088,17 +4088,19 @@ SetPropIRGenerator::tryAttachAddOrUpdate
 
     // Don't attach if we're adding to an array with non-writable length.
     bool isAdd = (index >= aobj->length());
     if (isAdd && !aobj->lengthIsWritable()) {
         return false;
     }
 
     // Indexed properties on the prototype chain aren't handled by the helper.
-    if (ObjectMayHaveExtraIndexedProperties(aobj->staticPrototype())) {
+    if ((aobj->staticPrototype() != nullptr) &&
+        ObjectMayHaveExtraIndexedProperties(aobj->staticPrototype()))
+    {
         return false;
     }
 
     // Ensure we are still talking about an array class.
     writer.guardClass(objId, GuardClassKind::Array);
 
     // The helper we are going to call only applies to non-dense elements.
     writer.guardIndexGreaterThanDenseInitLength(objId, indexId);
--- a/taskcluster/ci/test/misc.yml
+++ b/taskcluster/ci/test/misc.yml
@@ -6,20 +6,16 @@ geckoview-junit:
     loopback-video: true
     e10s: true
     target: geckoview-androidTest.apk
     max-run-time: 3600
     tier:
         by-test-platform:
             android-em-7.0-x86/opt: 3
             default: default
-    run-on-projects:
-        by-test-platform:
-            android-em-7.0-x86/opt: ['mozilla-central']
-            default: built-projects
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16-ccov/debug: 4
             android-em-4.3-arm7-api-16/debug: 4
             android-em-4.3-arm7-api-16/opt: 2
             default: 1
     mozharness:
         script: android_emulator_unittest.py
--- a/taskcluster/ci/test/mochitest.yml
+++ b/taskcluster/ci/test/mochitest.yml
@@ -202,20 +202,16 @@ mochitest-clipboard:
         by-test-platform:
             windows10-64-ccov/debug: 7200
             macosx64-ccov/debug: 7200
             default: 3600
     e10s:
         by-test-platform:
             linux32/debug: both
             default: true
-    run-on-projects:
-        by-test-platform:
-            android-em-7.0-x86/opt: ['mozilla-central']
-            default: built-projects
     mozharness:
         mochitest-flavor: plain
         extra-options:
             by-test-platform:
                 android-em.*:
                     # note that Android runs fewer suites than other platforms
                     - --test-suite=mochitest-plain-clipboard
                 default:
@@ -246,20 +242,17 @@ mochitest-devtools-chrome:
             default: default
     # Bug 1296086: high number of intermittents observed with software GL and large instances
     allow-software-gl-layers: false
 
 mochitest-gpu:
     description: "Mochitest GPU run"
     suite: mochitest/gpu
     treeherder-symbol: M(gpu)
-    run-on-projects:
-        by-test-platform:
-            android-em-7.0-x86/opt: ['mozilla-central']
-            default: built-projects
+    run-on-projects: built-projects
     loopback-video: true
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     virtualization: virtual-with-gpu
     e10s:
         by-test-platform:
--- a/taskcluster/ci/test/reftest.yml
+++ b/taskcluster/ci/test/reftest.yml
@@ -31,20 +31,17 @@ job-defaults:
                     - unittests/mac_unittest.py
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
 
 crashtest:
     description: "Crashtest run"
     suite: reftest/crashtest
     treeherder-symbol: R(C)
-    run-on-projects:
-        by-test-platform:
-            android-em-7.0-x86/opt: ['mozilla-central']
-            default: built-projects
+    run-on-projects: built-projects
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     virtualization:
         by-test-platform:
             windows10-64-qr/.*: virtual-with-gpu
             default: virtual
@@ -63,20 +60,17 @@ crashtest:
             android-em.*: 7200
             default: 3600
 
 jsreftest:
     description: "JS Reftest run"
     suite: reftest/jsreftest
     schedules-component: jsreftest  # scheduling for this reftest is different from the others..
     treeherder-symbol: R(J)
-    run-on-projects:
-        by-test-platform:
-            android-em-7.0-x86/opt: ['mozilla-central']
-            default: built-projects
+    run-on-projects: built-projects
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16/debug: 100
             android-em-7.0-x86/opt: 3
--- a/testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
+++ b/testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
@@ -16,17 +16,17 @@ def printstatus(name, returncode):
     print the status of a command exit code, formatted for tbpl.
 
     Note that mozlog structured action "process_exit" should be used
     instead of that in new code.
     """
     print("TEST-INFO | %s: %s" % (name, strstatus(returncode)))
 
 
-def dump_screen(utilityPath, log):
+def dump_screen(utilityPath, log, prefix='mozilla-test-fail-screenshot_'):
     """dumps a screenshot of the entire screen to a directory specified by
     the MOZ_UPLOAD_DIR environment variable.
 
     :param utilityPath: Path of utility programs. This is typically a path
         to either the objdir's bin directory or a path to the host utilities.
     :param log: Reference to logger.
     """
 
@@ -47,33 +47,33 @@ def dump_screen(utilityPath, log):
     parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None)
     if not parent_dir:
         log.info('Failed to retrieve MOZ_UPLOAD_DIR env var')
         return
 
     # Run the capture
     try:
         tmpfd, imgfilename = tempfile.mkstemp(
-            prefix='mozilla-test-fail-screenshot_',
+            prefix=prefix,
             suffix='.png', dir=parent_dir
         )
         os.close(tmpfd)
         if is_structured_log:
             log.process_start(utilityname)
         returncode = subprocess.call(utility + [imgfilename])
         if is_structured_log:
             log.process_exit(utilityname, returncode)
         else:
             printstatus(utilityname, returncode)
     except OSError as err:
         log.info("Failed to start %s for screenshot: %s"
                  % (utility[0], err.strerror))
 
 
-def dump_device_screen(device, log):
+def dump_device_screen(device, log, prefix='mozilla-test-fail-screenshot_'):
     """dumps a screenshot of a real device's entire screen to a directory
     specified by the MOZ_UPLOAD_DIR environment variable. Cloned from
     mozscreenshot.dump_screen.
 
     :param device: Reference to an ADBAndroid object which provides the
         interface to interact with Android devices.
     :param log: Reference to logger.
     """
@@ -90,18 +90,18 @@ def dump_device_screen(device, log):
     # Run the capture
     try:
         # Android 6.0 and later support mktemp.  See
         # https://android.googlesource.com/platform/system/core/
         # +/master/shell_and_utilities/README.md#android-6_0-marshmallow
         # We can use mktemp on real devices since we do not test on
         # real devices older than Android 6.0. Note we must create the
         # file without an extension due to limitations in mktemp.
-        filename = device.shell_output('mktemp -p %s mozilla-test-fail-screenshot_XXXXXX' %
-                                       device.test_root)
+        filename = device.shell_output('mktemp -p %s %sXXXXXX' %
+                                       (device.test_root, prefix))
         pngfilename = filename + '.png'
         device.mv(filename, pngfilename)
         if is_structured_log:
             log.process_start(utilityname)
         device.shell_output('%s -p %s' % (utilityname, pngfilename))
         if is_structured_log:
             log.process_exit(utilityname, 0)
         else:
--- a/testing/mozharness/configs/android/android_common.py
+++ b/testing/mozharness/configs/android/android_common.py
@@ -47,16 +47,20 @@ config = {
     "hostutils_manifest_path": "testing/config/tooltool-manifests/linux64/hostutils.manifest",
     "avds_dir": "/builds/worker/workspace/build/.android",
     # "log_format": "%(levelname)8s - %(message)s",
     "log_tbpl_level": "info",
     "log_raw_level": "info",
     "minidump_stackwalk_path": "/usr/local/bin/linux64-minidump_stackwalk",
     "marionette_address": "localhost:2828",
     "marionette_test_manifest": "unit-tests.ini",
+    # To take device screenshots at timed intervals (each time in seconds, relative
+    # to the start of the run-tests step) specify screenshot_times. For example, to
+    # take 4 screenshots at one minute intervals you could specify:
+    # "screenshot_times": [60, 120, 180, 240],
 
     "suite_definitions": {
         "mochitest": {
             "run_filename": "runtestsremote.py",
             "testsdir": "mochitest",
             "options": [
                 "--app=%(app)s",
                 "--remote-webserver=%(remote_webserver)s",
--- a/testing/mozharness/mozharness/mozilla/testing/android.py
+++ b/testing/mozharness/mozharness/mozilla/testing/android.py
@@ -8,18 +8,19 @@
 import datetime
 import glob
 import os
 import re
 import signal
 import subprocess
 import time
 import tempfile
+from threading import Timer
 from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
-from mozharness.base.script import PostScriptAction
+from mozharness.base.script import PreScriptAction, PostScriptAction
 
 
 class AndroidMixin(object):
     """
        Mixin class used by Android test scripts.
     """
 
     def __init__(self, **kwargs):
@@ -189,17 +190,17 @@ class AndroidMixin(object):
         if not boot_ok:
             self.warning('Unable to verify Android boot completion')
             return False
         return True
 
     def _verify_emulator_and_restart_on_fail(self):
         emulator_ok = self._verify_emulator()
         if not emulator_ok:
-            self.screenshot("emulator-startup-screenshot-")
+            self.device_screenshot("screenshot-emulator-start")
             self.kill_processes(self.config["emulator_process_name"])
             subprocess.check_call(['ps', '-ef'])
             # remove emulator tmp files
             for dir in glob.glob("/tmp/android-*"):
                 self.rmtree(dir)
             time.sleep(5)
             self.emulator_proc = self._launch_emulator()
         return emulator_ok
@@ -330,33 +331,35 @@ class AndroidMixin(object):
         out = self.device.get_prop('sys.boot_completed', timeout=30)
         if out.strip() == '1':
             return True
         return False
 
     def shell_output(self, cmd):
         return self.device.shell_output(cmd, timeout=30)
 
-    def screenshot(self, prefix):
+    def device_screenshot(self, prefix):
         """
-           Save a screenshot of the entire screen to the blob upload directory.
+           On emulator, save a screenshot of the entire screen to the upload directory;
+           otherwise, save a screenshot of the device to the upload directory.
+
+           :param prefix specifies a filename prefix for the screenshot
         """
-        dirs = self.query_abs_dirs()
-        utility = os.path.join(self.xre_path, "screentopng")
-        if not os.path.exists(utility):
-            self.warning("Unable to take screenshot: %s does not exist" % utility)
-            return
-        try:
-            tmpfd, filename = tempfile.mkstemp(prefix=prefix, suffix='.png',
-                                               dir=dirs['abs_blob_upload_dir'])
-            os.close(tmpfd)
-            self.info("Taking screenshot with %s; saving to %s" % (utility, filename))
-            subprocess.call([utility, filename], env=self.query_env())
-        except OSError, err:
-            self.warning("Failed to take screenshot: %s" % err.strerror)
+        from mozscreenshot import dump_screen, dump_device_screen
+        reset_dir = False
+        if not os.environ.get("MOZ_UPLOAD_DIR", None):
+            dirs = self.query_abs_dirs()
+            os.environ["MOZ_UPLOAD_DIR"] = dirs['abs_blob_upload_dir']
+            reset_dir = True
+        if self.is_emulator:
+            dump_screen(self.xre_path, self, prefix=prefix)
+        else:
+            dump_device_screen(self.device, self, prefix=prefix)
+        if reset_dir:
+            del os.environ["MOZ_UPLOAD_DIR"]
 
     def download_hostutils(self, xre_dir):
         """
            Download and install hostutils from tooltool.
         """
         xre_path = None
         self.rmtree(xre_dir)
         self.mkdir_p(xre_dir)
@@ -489,19 +492,40 @@ class AndroidMixin(object):
                            % max_restarts, EXIT_STATUS_DICT[TBPL_RETRY])
 
         self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir'])
         self.dump_perf_info()
         self.logcat_start()
         # Get a post-boot device process list for diagnostics
         self.info(self.shell_output('ps'))
 
+    @PreScriptAction('run-tests')
+    def timed_screenshots(self, action, success=None):
+        """
+        If configured, start screenshot timers.
+        """
+        if not self.is_android:
+            return
+
+        def take_screenshot(seconds):
+            self.device_screenshot("screenshot-%ss-" % str(seconds))
+            self.info("timed (%ss) screenshot complete" % str(seconds))
+
+        self.timers = []
+        for seconds in self.config.get("screenshot_times", []):
+            self.info("screenshot requested %s seconds from now" % str(seconds))
+            t = Timer(int(seconds), take_screenshot, [seconds])
+            t.start()
+            self.timers.append(t)
+
     @PostScriptAction('run-tests')
     def stop_device(self, action, success=None):
         """
         Stop logcat and kill the emulator, if necessary.
         """
         if not self.is_android:
             return
 
+        for t in self.timers:
+            t.cancel()
         self.logcat_stop()
         if self.is_emulator:
             self.kill_processes(self.config["emulator_process_name"])
--- a/toolkit/components/antitracking/test/browser/browser_blockingServiceWorkers.js
+++ b/toolkit/components/antitracking/test/browser/browser_blockingServiceWorkers.js
@@ -9,23 +9,25 @@ AntiTracking.runTest("ServiceWorkers",
   },
   null,
   async _ => {
     await new Promise(resolve => {
       Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
     });
   },
   [["dom.serviceWorkers.exemptFromPerDomainMax", true],
+   ["dom.ipc.processCount", 1],
    ["dom.serviceWorkers.enabled", true],
    ["dom.serviceWorkers.testing.enabled", true]]);
 
 AntiTracking.runTest("ServiceWorkers and Storage Access API",
   async _ => {
     await SpecialPowers.pushPrefEnv({"set": [
        ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+       ["dom.ipc.processCount", 1],
        ["dom.serviceWorkers.enabled", true],
        ["dom.serviceWorkers.testing.enabled", true],
     ]});
 
     /* import-globals-from storageAccessAPIHelpers.js */
     await noStorageAccessInitially();
 
     await navigator.serviceWorker.register("empty.js").then(
@@ -41,16 +43,17 @@ AntiTracking.runTest("ServiceWorkers and
       _ => { ok(false, "ServiceWorker cannot be used! " + _); }).then(
       reg => reg.unregister(),
       _ => { ok(false, "unregister failed"); }).
       catch(e => ok(false, "Promise rejected: " + e));
   },
   async _ => {
     await SpecialPowers.pushPrefEnv({"set": [
        ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+       ["dom.ipc.processCount", 1],
        ["dom.serviceWorkers.enabled", true],
        ["dom.serviceWorkers.testing.enabled", true],
     ]});
 
     /* import-globals-from storageAccessAPIHelpers.js */
     await noStorageAccessInitially();
 
     await navigator.serviceWorker.register("empty.js").then(
@@ -72,11 +75,12 @@ AntiTracking.runTest("ServiceWorkers and
       catch(e => ok(false, "Promise rejected: " + e));
   },
   async _ => {
     await new Promise(resolve => {
       Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
     });
   },
   [["dom.serviceWorkers.exemptFromPerDomainMax", true],
+   ["dom.ipc.processCount", 1],
    ["dom.serviceWorkers.enabled", true],
    ["dom.serviceWorkers.testing.enabled", true]],
   false, false);
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -1703,16 +1703,30 @@ telemetry:
     kind: boolean
     notification_emails:
       - jrediger@mozilla.com
       - telemetry-client-dev@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - 'main'
 
+  keyed_scalars_exceed_limit:
+    bug_numbers:
+      - 1451813
+    description: >
+      The number of times keyed scalars exceeded the number of keys limit, keyed by scalar name.
+    expires: "never"
+    kind: uint
+    keyed: true
+    notification_emails:
+      - chutten@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
 telemetry.discarded:
   accumulations:
     bug_numbers:
       - 1369041
     description: >
       Number of discarded accumulations to histograms in child processes
     expires: "never"
     kind: uint
--- a/toolkit/components/telemetry/core/TelemetryScalar.cpp
+++ b/toolkit/components/telemetry/core/TelemetryScalar.cpp
@@ -682,33 +682,33 @@ internal_ScalarAllocate(uint32_t aScalar
 /**
  * The implementation for the keyed scalar type.
  */
 class KeyedScalar
 {
 public:
   typedef mozilla::Pair<nsCString, nsCOMPtr<nsIVariant>> KeyValuePair;
 
-  explicit KeyedScalar(uint32_t aScalarKind)
-    : mScalarKind(aScalarKind)
+  explicit KeyedScalar(const BaseScalarInfo& info)
+    : mScalarInfo(info)
     , mMaximumNumberOfKeys(kMaximumNumberOfKeys)
   { };
   ~KeyedScalar() = default;
 
   // Set, Add and SetMaximum functions as described in the Telemetry IDL.
   // These methods implicitly instantiate a Scalar[*] for each key.
-  ScalarResult SetValue(const nsAString& aKey, nsIVariant* aValue);
-  ScalarResult AddValue(const nsAString& aKey, nsIVariant* aValue);
-  ScalarResult SetMaximum(const nsAString& aKey, nsIVariant* aValue);
+  ScalarResult SetValue(const StaticMutexAutoLock& locker, const nsAString& aKey, nsIVariant* aValue);
+  ScalarResult AddValue(const StaticMutexAutoLock& locker, const nsAString& aKey, nsIVariant* aValue);
+  ScalarResult SetMaximum(const StaticMutexAutoLock& locker, const nsAString& aKey, nsIVariant* aValue);
 
   // Convenience methods used by the C++ API.
-  void SetValue(const nsAString& aKey, uint32_t aValue);
-  void SetValue(const nsAString& aKey, bool aValue);
-  void AddValue(const nsAString& aKey, uint32_t aValue);
-  void SetMaximum(const nsAString& aKey, uint32_t aValue);
+  void SetValue(const StaticMutexAutoLock& locker, const nsAString& aKey, uint32_t aValue);
+  void SetValue(const StaticMutexAutoLock& locker, const nsAString& aKey, bool aValue);
+  void AddValue(const StaticMutexAutoLock& locker, const nsAString& aKey, uint32_t aValue);
+  void SetMaximum(const StaticMutexAutoLock& locker, const nsAString& aKey, uint32_t aValue);
 
   // GetValue is used to get the key-value pairs stored in the keyed scalar
   // when persisting it to JS.
   nsresult GetValue(nsTArray<KeyValuePair>& aValues) const;
 
   // To measure the memory stats.
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
@@ -717,104 +717,120 @@ public:
   {
     mMaximumNumberOfKeys = aMaximumNumberOfKeys;
   };
 
 private:
   typedef nsClassHashtable<nsCStringHashKey, ScalarBase> ScalarKeysMapType;
 
   ScalarKeysMapType mScalarKeys;
-  const uint32_t mScalarKind;
+  const BaseScalarInfo& mScalarInfo;
   uint32_t mMaximumNumberOfKeys;
 
-  ScalarResult GetScalarForKey(const nsAString& aKey, ScalarBase** aRet);
+  ScalarResult GetScalarForKey(const StaticMutexAutoLock& locker, const nsAString& aKey, ScalarBase** aRet);
 };
 
 ScalarResult
-KeyedScalar::SetValue(const nsAString& aKey, nsIVariant* aValue)
+KeyedScalar::SetValue(const StaticMutexAutoLock& locker, const nsAString& aKey, nsIVariant* aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
   if (sr != ScalarResult::Ok) {
     return sr;
   }
 
   return scalar->SetValue(aValue);
 }
 
 ScalarResult
-KeyedScalar::AddValue(const nsAString& aKey, nsIVariant* aValue)
+KeyedScalar::AddValue(const StaticMutexAutoLock& locker, const nsAString& aKey, nsIVariant* aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
   if (sr != ScalarResult::Ok) {
     return sr;
   }
 
   return scalar->AddValue(aValue);
 }
 
 ScalarResult
-KeyedScalar::SetMaximum(const nsAString& aKey, nsIVariant* aValue)
+KeyedScalar::SetMaximum(const StaticMutexAutoLock& locker, const nsAString& aKey, nsIVariant* aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
   if (sr != ScalarResult::Ok) {
     return sr;
   }
 
   return scalar->SetMaximum(aValue);
 }
 
 void
-KeyedScalar::SetValue(const nsAString& aKey, uint32_t aValue)
+KeyedScalar::SetValue(const StaticMutexAutoLock& locker, const nsAString& aKey, uint32_t aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
+
   if (sr != ScalarResult::Ok) {
-    MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
+    // Bug 1451813 - We now report which scalars exceed the key limit in telemetry.keyed_scalars_exceed_limit.
+    if(sr != ScalarResult::TooManyKeys) {
+      MOZ_ASSERT(false, "Key too long to be recorded in the scalar.");
+    }
     return;
   }
 
   return scalar->SetValue(aValue);
 }
 
 void
-KeyedScalar::SetValue(const nsAString& aKey, bool aValue)
+KeyedScalar::SetValue(const StaticMutexAutoLock& locker, const nsAString& aKey, bool aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
+
   if (sr != ScalarResult::Ok) {
-    MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
+    // Bug 1451813 - We now report which scalars exceed the key limit in telemetry.keyed_scalars_exceed_limit.
+    if (sr != ScalarResult::TooManyKeys) {
+      MOZ_ASSERT(false, "Key too long to be recorded in the scalar.");
+    }
     return;
   }
 
   return scalar->SetValue(aValue);
 }
 
 void
-KeyedScalar::AddValue(const nsAString& aKey, uint32_t aValue)
+KeyedScalar::AddValue(const StaticMutexAutoLock& locker, const nsAString& aKey, uint32_t aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
+
   if (sr != ScalarResult::Ok) {
-    MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
+    // Bug 1451813 - We now report which scalars exceed the key limit in telemetry.keyed_scalars_exceed_limit.
+    if (sr != ScalarResult::TooManyKeys) {
+      MOZ_ASSERT(false, "Key too long to be recorded in the scalar.");
+    }
     return;
   }
 
   return scalar->AddValue(aValue);
 }
 
 void
-KeyedScalar::SetMaximum(const nsAString& aKey, uint32_t aValue)
+KeyedScalar::SetMaximum(const StaticMutexAutoLock& locker, const nsAString& aKey, uint32_t aValue)
 {
   ScalarBase* scalar = nullptr;
-  ScalarResult sr = GetScalarForKey(aKey, &scalar);
+  ScalarResult sr = GetScalarForKey(locker, aKey, &scalar);
+
   if (sr != ScalarResult::Ok) {
-    MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
+    // Bug 1451813 - We now report which scalars exceed the key limit in telemetry.keyed_scalars_exceed_limit.
+    if(sr != ScalarResult::TooManyKeys) {
+      MOZ_ASSERT(false, "Key too long to be recorded in the scalar.");
+    }
     return;
   }
 
   return scalar->SetMaximum(aValue);
 }
 
 /**
  * Get a key-value array with the values for the Keyed Scalar.
@@ -838,23 +854,30 @@ KeyedScalar::GetValue(nsTArray<KeyValueP
 
     // Append it to value list.
     aValues.AppendElement(mozilla::MakePair(nsCString(iter.Key()), scalarValue));
   }
 
   return NS_OK;
 }
 
+// Forward declaration
+nsresult
+internal_GetKeyedScalarByEnum(const StaticMutexAutoLock& lock,
+                              const ScalarKey& aId,
+                              ProcessID aProcessStorage,
+                              KeyedScalar** aRet);
+
 /**
  * Get the scalar for the referenced key.
  * If there's no such key, instantiate a new Scalar object with the
  * same type of the Keyed scalar and create the key.
  */
 ScalarResult
-KeyedScalar::GetScalarForKey(const nsAString& aKey, ScalarBase** aRet)
+KeyedScalar::GetScalarForKey(const StaticMutexAutoLock& locker, const nsAString& aKey, ScalarBase** aRet)
 {
   if (aKey.IsEmpty()) {
     return ScalarResult::KeyIsEmpty;
   }
 
   if (aKey.Length() > kMaximumKeyStringLength) {
     return ScalarResult::KeyTooLong;
   }
@@ -863,20 +886,37 @@ KeyedScalar::GetScalarForKey(const nsASt
 
   ScalarBase* scalar = nullptr;
   if (mScalarKeys.Get(utf8Key, &scalar)) {
     *aRet = scalar;
     return ScalarResult::Ok;
   }
 
   if (mScalarKeys.Count() >= mMaximumNumberOfKeys) {
+    if(aKey.EqualsLiteral("telemetry.keyed_scalars_exceed_limit")) {
+      return ScalarResult::TooManyKeys;
+    }
+
+    KeyedScalar* scalarExceed = nullptr;
+
+    ScalarKey uniqueId{static_cast<uint32_t>(mozilla::Telemetry::ScalarID::TELEMETRY_KEYED_SCALARS_EXCEED_LIMIT), false};
+
+    ProcessID process = ProcessID::Parent;
+    nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId, process, &scalarExceed);
+
+    if (NS_FAILED(rv)) {
+      return ScalarResult::TooManyKeys;
+    }
+
+    scalarExceed->AddValue(locker, NS_ConvertUTF8toUTF16(mScalarInfo.name()), 1);
+
     return ScalarResult::TooManyKeys;
   }
 
-  scalar = internal_ScalarAllocate(mScalarKind);
+  scalar = internal_ScalarAllocate(mScalarInfo.kind);
   if (!scalar) {
     return ScalarResult::InvalidType;
   }
 
   mScalarKeys.Put(utf8Key, scalar);
 
   *aRet = scalar;
   return ScalarResult::Ok;
@@ -1565,17 +1605,17 @@ internal_GetKeyedScalarByEnum(const Stat
   }
 
   // We don't currently support keyed string scalars. Disable them.
   if (info.kind == nsITelemetry::SCALAR_TYPE_STRING) {
     MOZ_ASSERT(false, "Keyed string scalars are not currently supported.");
     return NS_ERROR_INVALID_ARG;
   }
 
-  scalar = new KeyedScalar(info.kind);
+  scalar = new KeyedScalar(info);
   if (!scalar) {
     return NS_ERROR_INVALID_ARG;
   }
 
   scalarStorage->Put(aId.id, scalar);
   *aRet = scalar;
   return NS_OK;
 }
@@ -1652,23 +1692,23 @@ internal_UpdateKeyedScalar(const StaticM
     // Don't throw on expired scalars.
     if (rv == NS_ERROR_NOT_AVAILABLE) {
       return ScalarResult::Ok;
     }
     return ScalarResult::UnknownScalar;
   }
 
   if (aType == ScalarActionType::eAdd) {
-    return scalar->AddValue(aKey, aValue);
+    return scalar->AddValue(lock, aKey, aValue);
   }
   if (aType == ScalarActionType::eSet) {
-    return scalar->SetValue(aKey, aValue);
+    return scalar->SetValue(lock, aKey, aValue);
   }
 
-  return scalar->SetMaximum(aKey, aValue);
+  return scalar->SetMaximum(lock, aKey, aValue);
 }
 
 /**
  * Helper function to convert an array of |DynamicScalarInfo|
  * to |DynamicScalarDefinition| used by the IPC calls.
  */
 void
 internal_DynamicScalarToIPC(const StaticMutexAutoLock& lock,
@@ -2104,24 +2144,24 @@ internal_ApplyKeyedScalarActions(const S
         {
           switch (scalarType)
           {
             case nsITelemetry::SCALAR_TYPE_COUNT:
               if (!upd.mData->is<uint32_t>()) {
                 NS_WARNING("Attempting to set a count scalar to a non-integer.");
                 continue;
               }
-              scalar->SetValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
+              scalar->SetValue(lock, NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
               break;
             case nsITelemetry::SCALAR_TYPE_BOOLEAN:
               if (!upd.mData->is<bool>()) {
                 NS_WARNING("Attempting to set a boolean scalar to a non-boolean.");
                 continue;
               }
-              scalar->SetValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<bool>());
+              scalar->SetValue(lock, NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<bool>());
               break;
             default:
               NS_WARNING("Unsupported type coming from scalar child updates.");
           }
           break;
         }
       case ScalarActionType::eAdd:
         {
@@ -2129,31 +2169,31 @@ internal_ApplyKeyedScalarActions(const S
             NS_WARNING("Attempting to add on a non count scalar.");
             continue;
           }
           // We only support adding on uint32_t.
           if (!upd.mData->is<uint32_t>()) {
             NS_WARNING("Attempting to add to a count scalar with a non-integer.");
             continue;
           }
-          scalar->AddValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
+          scalar->AddValue(lock, NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
           break;
         }
       case ScalarActionType::eSetMaximum:
         {
           if (scalarType != nsITelemetry::SCALAR_TYPE_COUNT) {
             NS_WARNING("Attempting to setMaximum on a non count scalar.");
             continue;
           }
           // We only support SetMaximum on uint32_t.
           if (!upd.mData->is<uint32_t>()) {
             NS_WARNING("Attempting to setMaximum a count scalar to a non-integer.");
             continue;
           }
-          scalar->SetMaximum(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
+          scalar->SetMaximum(lock, NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
           break;
         }
       default:
         NS_WARNING("Unsupported action coming from keyed scalar child updates.");
     }
   }
 }
 
@@ -2433,17 +2473,17 @@ TelemetryScalar::Add(mozilla::Telemetry:
   KeyedScalar* scalar = nullptr;
   nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId,
                                               ProcessID::Parent,
                                               &scalar);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  scalar->AddValue(aKey, aValue);
+  scalar->AddValue(locker, aKey, aValue);
 }
 
 /**
  * Sets the scalar to the given value.
  *
  * @param aName The scalar name.
  * @param aVal The value to set the scalar to.
  * @param aCx The JS context.
@@ -2699,17 +2739,17 @@ TelemetryScalar::Set(mozilla::Telemetry:
   KeyedScalar* scalar = nullptr;
   nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId,
                                               ProcessID::Parent,
                                               &scalar);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  scalar->SetValue(aKey, aValue);
+  scalar->SetValue(locker, aKey, aValue);
 }
 
 /**
  * Sets the scalar to the given boolean value.
  *
  * @param aId The scalar enum id.
  * @param aKey The scalar key.
  * @param aValue The boolean value to set the scalar to.
@@ -2749,17 +2789,17 @@ TelemetryScalar::Set(mozilla::Telemetry:
   KeyedScalar* scalar = nullptr;
   nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId,
                                               ProcessID::Parent,
                                               &scalar);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  scalar->SetValue(aKey, aValue);
+  scalar->SetValue(locker, aKey, aValue);
 }
 
 /**
  * Sets the scalar to the maximum of the current and the passed value.
  *
  * @param aName The scalar name.
  * @param aVal The numeric value to set the scalar to.
  * @param aCx The JS context.
@@ -2920,17 +2960,17 @@ TelemetryScalar::SetMaximum(mozilla::Tel
 
   KeyedScalar* scalar = nullptr;
   nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId, ProcessID::Parent,
                                               &scalar);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  scalar->SetMaximum(aKey, aValue);
+  scalar->SetMaximum(locker, aKey, aValue);
 }
 
 /**
  * Serializes the scalars from the given dataset to a json-style object and resets them.
  * The returned structure looks like:
  *    {"process": {"category1.probe":1,"category1.other_probe":false,...}, ... }.
  *
  * @param aDataset DATASET_RELEASE_CHANNEL_OPTOUT or DATASET_RELEASE_CHANNEL_OPTIN.
@@ -3257,17 +3297,17 @@ TelemetryScalar::SummarizeEvent(const ns
   }
 
   static uint32_t sMaxEventSummaryKeys =
     Preferences::GetUint("toolkit.telemetry.maxEventSummaryKeys", 500);
 
   // Set this each time as it may have been cleared and recreated between calls
   scalar->SetMaximumNumberOfKeys(sMaxEventSummaryKeys);
 
-  scalar->AddValue(NS_ConvertASCIItoUTF16(aUniqueEventName), 1);
+  scalar->AddValue(lock, NS_ConvertASCIItoUTF16(aUniqueEventName), 1);
 }
 
 /**
  * Resets all the stored scalars. This is intended to be only used in tests.
  */
 void
 TelemetryScalar::ClearScalars()
 {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/
 */
 
 const UINT_SCALAR = "telemetry.test.unsigned_int_kind";
 const STRING_SCALAR = "telemetry.test.string_kind";
 const BOOLEAN_SCALAR = "telemetry.test.boolean_kind";
 const KEYED_UINT_SCALAR = "telemetry.test.keyed_unsigned_int";
+const KEYED_EXCEED_SCALAR = "telemetry.keyed_scalars_exceed_limit";
 
 function getProcessScalars(aChannel, aProcessName, aKeyed = false, aClear = false) {
   const scalars = aKeyed ?
     Telemetry.snapshotKeyedScalars(aChannel, aClear)[aProcessName] :
     Telemetry.snapshotScalars(aChannel, aClear)[aProcessName];
   return scalars || {};
 }
 
@@ -529,16 +530,41 @@ add_task(async function test_keyed_max_k
             "The keyed scalar must contain all the 100 keys, and drop the others.");
 
   // Check that all the keys recorded the expected values.
   let expectedValue = 0;
   keyNamesSet.forEach(keyName => {
     Assert.equal(keyedScalars[KEYED_UINT_SCALAR][keyName], expectedValue++,
                  "The key must contain the expected value.");
   });
+
+  // Check that KEYED_EXCEED_SCALAR is in keyedScalars
+  Assert.ok((KEYED_EXCEED_SCALAR in keyedScalars),
+            "We have exceeded maximum number of Keys.");
+
+  // Generate the names for the exceeded keys
+  let keyNamesSet2 = new Set();
+  for (let k = 0; k < 100; k++) {
+    keyNamesSet2.add("key2_" + k);
+  }
+
+  // Add 100 keys to the keyed exceed scalar and set their initial value.
+  valueToSet = 0;
+  keyNamesSet2.forEach(keyName2 => {
+    Telemetry.keyedScalarSet(KEYED_EXCEED_SCALAR, keyName2, valueToSet++);
+  });
+
+  // Check that there are exactly 100 keys in KEYED_EXCEED_SCALAR
+  let snapshot = Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  Assert.equal(100, Object.keys(snapshot.parent[KEYED_UINT_SCALAR]).length,
+             "The keyed scalar must contain all the 100 keys.");
+
+  // Check that KEYED_UINT_SCALAR is in keyedScalars and its value equals 3
+  Assert.ok((KEYED_UINT_SCALAR in keyedScalars[KEYED_EXCEED_SCALAR]), "The keyed Scalar is in the keyed exceeded scalar");
+  Assert.equal(keyedScalars[KEYED_EXCEED_SCALAR][KEYED_UINT_SCALAR], 3, "We have exactly 3 keys over the limit");
 });
 
 add_task(async function test_dynamicScalars_registration() {
   Telemetry.clearScalars();
 
   const TEST_CASES = [
     {
       "category": "telemetry.test",