Merge default into GECKOVIEW_64_RELBRANCH. a=merge GECKOVIEW_64_RELBRANCH
authorJulien Cristau <jcristau@mozilla.com>
Wed, 05 Dec 2018 16:28:28 -0500
branchGECKOVIEW_64_RELBRANCH
changeset 501458 f95d41399777
parent 501452 26d700d8010a (current diff)
parent 501457 319052b37637 (diff)
child 501459 6891b272d3fd
push id1871
push userjcristau@mozilla.com
push dateWed, 05 Dec 2018 21:37:07 +0000
treeherdermozilla-release@6891b272d3fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0
Merge default into GECKOVIEW_64_RELBRANCH. a=merge
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3809,31 +3809,49 @@ const DOMEventHandler = {
   clearPendingIcon(aBrowser) {
     let tab = gBrowser.getTabForBrowser(aBrowser);
     tab.removeAttribute("pendingicon");
   },
 
   setIconFromLink(aBrowser, aPageURL, aOriginalURL, aCanUseForTab, aExpiration, aIconURL) {
     let tab = gBrowser.getTabForBrowser(aBrowser);
     if (!tab) {
-      return false;
+      return;
+    }
+
+    if (aCanUseForTab) {
+      this.clearPendingIcon(aBrowser);
+    }
+
+    let iconURI;
+    try {
+      iconURI = Services.io.newURI(aIconURL);
+    } catch (ex) {
+      Cu.reportError(ex);
+      return;
+    }
+    if (iconURI.scheme != "data") {
+      try {
+        Services.scriptSecurityManager.checkLoadURIWithPrincipal(
+          aBrowser.contentPrincipal, iconURI, Services.scriptSecurityManager.ALLOW_CHROME);
+      } catch (ex) {
+        return;
+      }
     }
     try {
       PlacesUIUtils.loadFavicon(aBrowser, Services.scriptSecurityManager.getSystemPrincipal(),
                                 makeURI(aPageURL), makeURI(aOriginalURL),
-                                aExpiration, makeURI(aIconURL));
+                                aExpiration, iconURI);
     } catch (ex) {
       Cu.reportError(ex);
     }
 
     if (aCanUseForTab) {
-      this.clearPendingIcon(aBrowser);
       gBrowser.setIcon(tab, aIconURL, aOriginalURL);
     }
-    return true;
   },
 
   addSearch(aBrowser, aEngine, aURL) {
     let tab = gBrowser.getTabForBrowser(aBrowser);
     if (!tab)
       return;
 
     BrowserSearch.addEngine(aBrowser, aEngine, makeURI(aURL));
--- a/browser/modules/FaviconLoader.jsm
+++ b/browser/modules/FaviconLoader.jsm
@@ -373,16 +373,22 @@ class IconLoader {
   }
 
   async load(iconInfo) {
     if (this._loader) {
       this._loader.cancel();
     }
 
     if (LOCAL_FAVICON_SCHEMES.includes(iconInfo.iconUri.scheme)) {
+      // We need to do a manual security check because the channel won't do
+      // it for us.
+      try {
+        Services.scriptSecurityManager.checkLoadURIWithPrincipal(
+          iconInfo.node.nodePrincipal, iconInfo.iconUri, Services.scriptSecurityManager.ALLOW_CHROME);
+      } catch (ex) { return; }
       this.mm.sendAsyncMessage("Link:SetIcon", {
         pageURL: iconInfo.pageUri.spec,
         originalURL: iconInfo.iconUri.spec,
         canUseForTab: !iconInfo.isRichIcon,
         expiration: undefined,
         iconURL: iconInfo.iconUri.spec,
       });
       return;
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -2,28 +2,59 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAboutRedirector.h"
 #include "nsNetUtil.h"
 #include "nsAboutProtocolUtils.h"
+#include "nsBaseChannel.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsIProtocolHandler.h"
 
 NS_IMPL_ISUPPORTS(nsAboutRedirector, nsIAboutModule)
 
 struct RedirEntry
 {
   const char* id;
   const char* url;
   uint32_t flags;
 };
 
+class CrashChannel final : public nsBaseChannel
+{
+public:
+  explicit CrashChannel(nsIURI* aURI)
+  {
+    SetURI(aURI);
+  }
+
+  nsresult OpenContentStream(bool async, nsIInputStream **stream,
+                             nsIChannel** channel) override
+  {
+    nsAutoCString spec;
+    mURI->GetSpec(spec);
+
+    if (spec.EqualsASCII("about:crashparent") && XRE_IsParentProcess()) {
+      MOZ_CRASH("Crash via about:crashparent");
+    }
+
+    if (spec.EqualsASCII("about:crashcontent") && XRE_IsContentProcess()) {
+      MOZ_CRASH("Crash via about:crashcontent");
+    }
+
+    NS_WARNING("Unhandled about:crash* URI or wrong process");
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+protected:
+  virtual ~CrashChannel() = default;
+};
+
 /*
   Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome
   privileges. This is potentially dangerous. Please use
   URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below
   unless your about: page really needs chrome privileges. Security review is
   required before adding new map entries without
   URI_SAFE_FOR_UNTRUSTED_CONTENT.
 
@@ -132,22 +163,20 @@ static const RedirEntry kRedirMap[] = {
   {
     "printpreview", "about:blank",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::URI_CAN_LOAD_IN_CHILD
   },
   {
     "crashparent", "about:blank",
-    nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT
   },
   {
     "crashcontent", "about:blank",
-    nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD
   }
 };
 static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
 
 NS_IMETHODIMP
@@ -161,22 +190,20 @@ nsAboutRedirector::NewChannel(nsIURI* aU
 
   nsAutoCString path;
   nsresult rv = NS_GetAboutModuleName(aURI, path);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (XRE_IsParentProcess() && path.EqualsASCII("crashparent")) {
-    MOZ_CRASH("Crash via about:crashparent");
-  }
-
-  if (XRE_IsContentProcess() && path.EqualsASCII("crashcontent")) {
-    MOZ_CRASH("Crash via about:crashcontent");
+  if (path.EqualsASCII("crashparent") || path.EqualsASCII("crashcontent")) {
+    nsCOMPtr<nsIChannel> channel = new CrashChannel(aURI);
+    channel.forget(aResult);
+    return NS_OK;
   }
 
   for (int i = 0; i < kRedirTotal; i++) {
     if (!strcmp(path.get(), kRedirMap[i].id)) {
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
       NS_ENSURE_SUCCESS(rv, rv);
--- a/docshell/test/mochitest/mochitest.ini
+++ b/docshell/test/mochitest/mochitest.ini
@@ -110,8 +110,9 @@ support-files = file_bug675587.html
 [test_close_onpagehide_by_window_close.html]
 [test_forceinheritprincipal_overrule_owner.html]
 [test_framedhistoryframes.html]
 skip-if = toolkit == 'android' # bug 784321
 support-files = file_framedhistoryframes.html
 [test_pushState_after_document_open.html]
 [test_windowedhistoryframes.html]
 [test_triggeringprincipal_location_seturi.html]
+[test_bug1507702.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1507702.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1507702
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1507702</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <link rel="icon" href="about:crashparent"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1507702">Mozilla Bug 1507702</a>
+<img src="about:crashparent">
+<img src="about:crashcontent">
+<iframe src="about:crashparent"></iframe>
+<iframe src="about:crashcontent"></iframe>
+<script>
+  let urls = ["about:crashparent", "about:crashcontent"];
+  async function testFetch() {
+    const url = urls.shift();
+    if (!url) {
+      return Promise.resolve();
+    }
+
+    let threw;
+    try {
+      await fetch(url);
+      threw = false;
+    } catch (e) {
+      threw = true;
+    };
+
+    ok(threw === true, "fetch should reject");
+    return testFetch();
+  }
+
+  document.body.onload = async () => {
+    for (const url of ["about:crashparent", "about:crashcontent"]) {
+      SimpleTest.doesThrow(() => {
+        top.location.href = url;
+      }, "navigation should throw");
+
+      SimpleTest.doesThrow(() => {
+        location.href = url;
+      }, "navigation should throw");
+    }
+
+    await testFetch();
+    SimpleTest.finish();
+  };
+
+  SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -135,23 +135,19 @@ WebGLContext::WebGLContext()
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
     mDisableExtensions = false;
     mIsMesa = false;
-    mEmitContextLostErrorOnce = false;
     mWebGLError = 0;
-    mUnderlyingGLError = 0;
     mVRReady = false;
 
-    mContextLostErrorSet = false;
-
     mViewportX = 0;
     mViewportY = 0;
     mViewportWidth = 0;
     mViewportHeight = 0;
 
     mDitherEnabled = 1;
     mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
     mScissorTestEnabled = 0;
@@ -161,17 +157,16 @@ WebGLContext::WebGLContext()
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::AddWebGLContext(this);
     }
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
     mLoseContextOnMemoryPressure = false;
     mCanLoseContextInForeground = true;
-    mRestoreWhenVisible = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
 
     mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
     if (mMaxWarnings < -1) {
         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
@@ -314,29 +309,16 @@ WebGLContext::Invalidate()
 
     SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
 
     mInvalidated = true;
     mCanvasElement->InvalidateCanvasContent(nullptr);
 }
 
 void
-WebGLContext::OnVisibilityChange()
-{
-    if (gl) // Context not lost.
-        return;
-
-    if (!mRestoreWhenVisible || mLastLossWasSimulated) {
-        return;
-    }
-
-    ForceRestoreContext();
-}
-
-void
 WebGLContext::OnMemoryPressure()
 {
     bool shouldLoseContext = mLoseContextOnMemoryPressure;
 
     if (!mCanLoseContextInForeground &&
         ProcessPriorityManager::CurrentProcessIsForeground())
     {
         shouldLoseContext = false;
@@ -1526,78 +1508,57 @@ WebGLContext::Has64BitTimestamps() const
     // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
     return gl->IsSupported(GLFeature::sync);
 }
 
 static bool
 CheckContextLost(GLContext* gl, bool* const out_isGuilty)
 {
     MOZ_ASSERT(gl);
-    MOZ_ASSERT(out_isGuilty);
-
-    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
-
-    GLenum resetStatus = LOCAL_GL_NO_ERROR;
-    if (gl->IsSupported(GLFeature::robustness)) {
-        gl->MakeCurrent();
-        resetStatus = gl->fGetGraphicsResetStatus();
-    } else if (isEGL) {
-        // Simulate a ARB_robustness guilty context loss for when we
-        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
-        // but we can't make any distinction.
-        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
-            resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
-        }
-    }
-
+
+    const auto resetStatus = gl->fGetGraphicsResetStatus();
     if (resetStatus == LOCAL_GL_NO_ERROR) {
         *out_isGuilty = false;
         return false;
     }
 
     // Assume guilty unless we find otherwise!
     bool isGuilty = true;
     switch (resetStatus) {
     case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+    case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
         // Either nothing wrong, or not our fault.
         isGuilty = false;
         break;
     case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
         NS_WARNING("WebGL content on the page definitely caused the graphics"
                    " card to reset.");
         break;
     case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
         NS_WARNING("WebGL content on the page might have caused the graphics"
                    " card to reset");
-        // If we can't tell, assume guilty.
+        // If we can't tell, assume not-guilty.
+        // Todo: Implement max number of "unknown" resets per document or time.
+        isGuilty = false;
         break;
     default:
-        MOZ_ASSERT(false, "Unreachable.");
-        // If we do get here, let's pretend to be guilty as an escape plan.
+        gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
+                           << gfx::hexa(resetStatus);
         break;
     }
 
     if (isGuilty) {
         NS_WARNING("WebGL context on this page is considered guilty, and will"
                    " not be restored.");
     }
 
     *out_isGuilty = isGuilty;
     return true;
 }
 
-bool
-WebGLContext::TryToRestoreContext()
-{
-    if (NS_FAILED(SetDimensions(mRequestedSize.width, mRequestedSize.height)))
-        return false;
-
-    return true;
-}
-
 void
 WebGLContext::RunContextLossTimer()
 {
     mContextLossHandler.RunTimer();
 }
 
 class UpdateContextLossStatusTask : public CancelableRunnable
 {
@@ -1716,40 +1677,38 @@ WebGLContext::UpdateContextLossStatus()
         if (!mAllowContextRestore)
             return;
 
         // If we're only simulated-lost, we shouldn't auto-restore, and
         // instead we should wait for restoreContext() to be called.
         if (mLastLossWasSimulated)
             return;
 
-        // Restore when the app is visible
-        if (mRestoreWhenVisible)
-            return;
-
         ForceRestoreContext();
         return;
     }
 
     if (mContextStatus == ContextStatus::LostAwaitingRestore) {
         // Context is lost, but we should try to restore it.
 
+        if (mAllowContextRestore) {
+            if (NS_FAILED(SetDimensions(mRequestedSize.width,
+                                        mRequestedSize.height)))
+            {
+                // Assume broken forever.
+                mAllowContextRestore = false;
+            }
+        }
         if (!mAllowContextRestore) {
             // We might decide this after thinking we'd be OK restoring
             // the context, so downgrade.
             mContextStatus = ContextStatus::Lost;
             return;
         }
 
-        if (!TryToRestoreContext()) {
-            // Failed to restore. Try again later.
-            mContextLossHandler.RunTimer();
-            return;
-        }
-
         // Revival!
         mContextStatus = ContextStatus::NotLost;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
                 mCanvasElement->OwnerDoc(),
                 static_cast<nsIContent*>(mCanvasElement),
                 NS_LITERAL_STRING("webglcontextrestored"),
@@ -1759,28 +1718,27 @@ WebGLContext::UpdateContextLossStatus()
             RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
             event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"),
                              CanBubble::eYes,
                              Cancelable::eYes);
             event->SetTrusted(true);
             mOffscreenCanvas->DispatchEvent(*event);
         }
 
-        mEmitContextLostErrorOnce = true;
         return;
     }
 }
 
 void
 WebGLContext::ForceLoseContext(bool simulateLosing)
 {
     printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
     MOZ_ASSERT(gl);
     mContextStatus = ContextStatus::LostAwaitingEvent;
-    mContextLostErrorSet = false;
+    mWebGLError = LOCAL_GL_CONTEXT_LOST_WEBGL;
 
     // Burn it all!
     DestroyResourcesAndContext();
     mLastLossWasSimulated = simulateLosing;
 
     // Queue up a task, since we know the status changed.
     EnqueueUpdateContextLossStatus();
 }
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -348,17 +348,16 @@ protected:
 public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
                                                            nsICanvasRenderingContextInternal)
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
-    virtual void OnVisibilityChange() override;
     virtual void OnMemoryPressure() override;
 
     // nsICanvasRenderingContextInternal
     virtual int32_t GetWidth() override { return DrawingBufferWidth(); }
     virtual int32_t GetHeight() override { return DrawingBufferHeight(); }
 
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
@@ -1479,33 +1478,28 @@ protected:
     bool mInvalidated;
     bool mCapturedFrameInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mDisableExtensions;
     bool mIsMesa;
     bool mLoseContextOnMemoryPressure;
     bool mCanLoseContextInForeground;
-    bool mRestoreWhenVisible;
     bool mShouldPresent;
     bool mDisableFragHighP;
     bool mVRReady;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture = 0;
     GLenum mDefaultFB_DrawBuffer0 = 0;
     GLenum mDefaultFB_ReadBuffer = 0;
 
-    // glGetError sources:
-    bool mEmitContextLostErrorOnce;
     mutable GLenum mWebGLError;
-    mutable GLenum mUnderlyingGLError;
-    GLenum GetAndFlushUnderlyingGLErrors() const;
 
     bool mBypassShaderValidation;
 
     webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
 
     // some GL constants
     uint32_t mGLMaxTextureUnits = 0;
 
@@ -1921,17 +1915,16 @@ protected:
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
     GLfloat mLineWidth = 0.0;
 
     WebGLContextLossHandler mContextLossHandler;
     bool mAllowContextRestore;
     bool mLastLossWasSimulated;
     ContextStatus mContextStatus = ContextStatus::NotLost;
-    bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
 
     mutable int mAlreadyGeneratedWarnings;
     int mMaxWarnings;
     bool mAlreadyWarnedAboutFakeVertexAttrib0;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -225,18 +225,16 @@ DoSetsIntersect(const std::set<T>& a, co
                           std::back_inserter(intersection));
     return bool(intersection.size());
 }
 
 const webgl::CachedDrawFetchLimits*
 ValidateDraw(WebGLContext* const webgl, const GLenum mode,
              const uint32_t instanceCount)
 {
-    MOZ_ASSERT(webgl->gl->IsCurrent());
-
     if (!webgl->BindCurFBForDraw())
         return nullptr;
 
     switch (mode) {
     case LOCAL_GL_TRIANGLES:
     case LOCAL_GL_TRIANGLE_STRIP:
     case LOCAL_GL_TRIANGLE_FAN:
     case LOCAL_GL_POINTS:
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -905,63 +905,53 @@ WebGLContext::CreateTexture()
 
     GLuint tex = 0;
     gl->fGenTextures(1, &tex);
 
     RefPtr<WebGLTexture> globj = new WebGLTexture(this, tex);
     return globj.forget();
 }
 
-static GLenum
-GetAndClearError(GLenum* errorVar)
-{
-    MOZ_ASSERT(errorVar);
-    GLenum ret = *errorVar;
-    *errorVar = LOCAL_GL_NO_ERROR;
-    return ret;
-}
-
 GLenum
 WebGLContext::GetError()
 {
     const FuncScope funcScope(*this, "getError");
+
     /* WebGL 1.0: Section 5.14.3: Setting and getting state:
      *   If the context's webgl context lost flag is set, returns
      *   CONTEXT_LOST_WEBGL the first time this method is called.
      *   Afterward, returns NO_ERROR until the context has been
      *   restored.
      *
      * WEBGL_lose_context:
      *   [When this extension is enabled: ] loseContext and
      *   restoreContext are allowed to generate INVALID_OPERATION errors
      *   even when the context is lost.
      */
 
-    if (IsContextLost()) {
-        if (mEmitContextLostErrorOnce) {
-            mEmitContextLostErrorOnce = false;
-            return LOCAL_GL_CONTEXT_LOST_WEBGL;
-        }
-        // Don't return yet, since WEBGL_lose_contexts contradicts the
-        // original spec, and allows error generation while lost.
-    }
-
-    GLenum err = GetAndClearError(&mWebGLError);
-    if (err != LOCAL_GL_NO_ERROR)
+    auto err = mWebGLError;
+    mWebGLError = 0;
+    if (IsContextLost() || err) // Must check IsContextLost in all flow paths.
         return err;
 
-    if (IsContextLost())
-        return LOCAL_GL_NO_ERROR;
-
     // Either no WebGL-side error, or it's already been cleared.
     // UnderlyingGL-side errors, now.
-
-    GetAndFlushUnderlyingGLErrors();
-
-    err = GetAndClearError(&mUnderlyingGLError);
+    err = gl->fGetError();
+    if (gl->IsContextLost()) {
+        UpdateContextLossStatus();
+        return GetError();
+    }
+    MOZ_ASSERT(err != LOCAL_GL_CONTEXT_LOST);
+
+    if (err) {
+        GenerateWarning("Driver error unexpected by WebGL: 0x%04x", err);
+        // This might be:
+        // - INVALID_OPERATION from ANGLE due to incomplete RBAB implementation for DrawElements
+        //   with DYNAMIC_DRAW index buffer.
+    }
     return err;
 }
 
 JS::Value
 WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
 {
     const FuncScope funcScope(*this, "getProgramParameter");
     if (IsContextLost())
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -627,30 +627,16 @@ EnumString(const GLenum val)
 void
 WebGLContext::ErrorInvalidEnumArg(const char* argName, GLenum val) const
 {
     nsCString enumName;
     EnumName(val, &enumName);
     ErrorInvalidEnum("Bad `%s`: %s", argName, enumName.BeginReading());
 }
 
-GLenum
-WebGLContext::GetAndFlushUnderlyingGLErrors() const
-{
-    // Get and clear GL error in ALL cases.
-    GLenum error = gl->fGetError();
-
-    // Only store in mUnderlyingGLError if is hasn't already recorded an
-    // error.
-    if (!mUnderlyingGLError)
-        mUnderlyingGLError = error;
-
-    return error;
-}
-
 #ifdef DEBUG
 // For NaNs, etc.
 static bool
 IsCacheCorrect(float cached, float actual)
 {
     if (IsNaN(cached)) {
         // GL is allowed to do anything it wants for NaNs, so if we're shadowing
         // a NaN, then whatever `actual` is might be correct.
@@ -694,17 +680,17 @@ AssertUintParamCorrect(gl::GLContext*, G
 {
 }
 #endif
 
 void
 WebGLContext::AssertCachedBindings() const
 {
 #ifdef DEBUG
-    GetAndFlushUnderlyingGLErrors();
+    gl::GLContext::LocalErrorScope errorScope(*gl);
 
     if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound);
     }
 
     GLint stencilBits = 0;
     if (GetStencilBits(&stencilBits)) { // Depends on current draw framebuffer.
@@ -734,27 +720,27 @@ WebGLContext::AssertCachedBindings() con
     bound = mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound);
 
     MOZ_ASSERT(mBoundVertexArray);
     WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer;
     bound = curBuff ? curBuff->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound);
 
-    MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
+    MOZ_ASSERT(!gl::GLContext::IsBadCallError(errorScope.GetError()));
 #endif
 
     // We do not check the renderbuffer binding, because we never rely on it matching.
 }
 
 void
 WebGLContext::AssertCachedGlobalState() const
 {
 #ifdef DEBUG
-    GetAndFlushUnderlyingGLErrors();
+    gl::GLContext::LocalErrorScope errorScope(*gl);
 
     ////////////////
 
     // Draw state
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DITHER) == mDitherEnabled);
     MOZ_ASSERT_IF(IsWebGL2(),
                   gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
@@ -802,17 +788,17 @@ WebGLContext::AssertCachedGlobalState() 
         AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ROW_LENGTH  , mPixelStore_UnpackRowLength);
         AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_ROWS   , mPixelStore_UnpackSkipRows);
         AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_PIXELS , mPixelStore_UnpackSkipPixels);
         AssertUintParamCorrect(gl, LOCAL_GL_PACK_ROW_LENGTH    , mPixelStore_PackRowLength);
         AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_ROWS     , mPixelStore_PackSkipRows);
         AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_PIXELS   , mPixelStore_PackSkipPixels);
     }
 
-    MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
+    MOZ_ASSERT(!gl::GLContext::IsBadCallError(errorScope.GetError()));
 #endif
 }
 
 const char*
 InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims)
 {
     switch (dims) {
     case WebGLTexDimensions::Tex2D:
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -322,17 +322,16 @@ WebGLContext::InitAndValidateGL(FailureR
                                      error);
         *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_1", reason };
         return false;
     }
 
     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
-    mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
 
     // These are the default values, see 6.2 State tables in the
     // OpenGL ES 2.0.25 spec.
     mColorWriteMask = 0x0f;
     mDriverColorMask = mColorWriteMask;
     mColorClearValue[0] = 0.f;
     mColorClearValue[1] = 0.f;
     mColorClearValue[2] = 0.f;
@@ -378,19 +377,17 @@ WebGLContext::InitAndValidateGL(FailureR
 
     mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
 
     // Bindings, etc.
     mActiveTexture = 0;
     mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
     mDefaultFB_ReadBuffer = LOCAL_GL_BACK;
 
-    mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
-    mUnderlyingGLError = LOCAL_GL_NO_ERROR;
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
     mBound2DArrayTextures.Clear();
     mBoundSamplers.Clear();
 
     mBoundArrayBuffer = nullptr;
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -374,16 +374,18 @@ GLContext::LoadFeatureSymbols(const char
     }
     return true;
 };
 
 bool
 GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
 {
     mWorkAroundDriverBugs = gfxPrefs::WorkAroundDriverBugs();
+    if (!MakeCurrent(true))
+        return false;
 
     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 } },
         { (PRFuncPtr*) &mSymbols.fBlendColor, { "BlendColor", nullptr } },
@@ -507,22 +509,45 @@ GLContext::InitWithPrefixImpl(const char
         { (PRFuncPtr*) &mSymbols.fDeleteShader, { "DeleteShader", "DeleteShaderARB", nullptr } },
 
         END_SYMBOLS
     };
 
     if (!LoadGLSymbols(this, prefix, trygl, coreSymbols, "GL"))
         return false;
 
+    {
+        const SymLoadStruct symbols[] = {
+            { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatus",
+                                                                "GetGraphicsResetStatusARB",
+                                                                "GetGraphicsResetStatusKHR",
+                                                                "GetGraphicsResetStatusEXT",
+                                                                nullptr } },
+            END_SYMBOLS
+        };
+        (void)LoadGLSymbols(this, prefix, trygl, symbols, nullptr);
+
+        auto err = fGetError();
+        if (err == LOCAL_GL_CONTEXT_LOST) {
+            MOZ_ASSERT(mSymbols.fGetGraphicsResetStatus);
+            const auto status = fGetGraphicsResetStatus();
+            if (status) {
+                printf_stderr("Unflushed glGetGraphicsResetStatus: 0x%04x\n", status);
+            }
+            err = fGetError();
+            MOZ_ASSERT(!err);
+        }
+        if (err) {
+            MOZ_ASSERT(false);
+            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");
@@ -1006,33 +1031,16 @@ GLContext::LoadMoreSymbols(const char* p
                 const bool isDisabled = (resetStrategy == LOCAL_GL_NO_RESET_NOTIFICATION);
                 printf_stderr("Strategy: %s (0x%04x)",
                               (isDisabled ? "disabled" : "unrecognized"),
                               resetStrategy);
             }
             MarkUnsupported(GLFeature::robustness);
         }
     }
-    if (IsSupported(GLFeature::robustness)) {
-        const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatus",
-                                                                "GetGraphicsResetStatusARB",
-                                                                "GetGraphicsResetStatusKHR",
-                                                                "GetGraphicsResetStatusEXT",
-                                                                nullptr } },
-            END_SYMBOLS
-        };
-        if (fnLoadForFeature(symbols, GLFeature::robustness)) {
-            const auto status = mSymbols.fGetGraphicsResetStatus();
-            MOZ_ALWAYS_TRUE(!status);
-
-            const auto err = mSymbols.fGetError();
-            MOZ_ALWAYS_TRUE(!err);
-        }
-    }
 
     if (IsSupported(GLFeature::sync)) {
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fFenceSync,      { "FenceSync",      nullptr } },
             { (PRFuncPtr*) &mSymbols.fIsSync,         { "IsSync",         nullptr } },
             { (PRFuncPtr*) &mSymbols.fDeleteSync,     { "DeleteSync",     nullptr } },
             { (PRFuncPtr*) &mSymbols.fClientWaitSync, { "ClientWaitSync", nullptr } },
             { (PRFuncPtr*) &mSymbols.fWaitSync,       { "WaitSync",       nullptr } },
@@ -2046,20 +2054,17 @@ GLContext::MarkDestroyed()
         return;
 
     // Null these before they're naturally nulled after dtor, as we want GLContext to
     // still be alive in *their* dtors.
     mScreen = nullptr;
     mBlitHelper = nullptr;
     mReadTexImageHelper = nullptr;
 
-    if (!MakeCurrent()) {
-        NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
-    }
-
+    mContextLost = true;
     mSymbols = {};
 }
 
 #ifdef MOZ_GL_DEBUG
 /* static */ void
 GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)
 {
   int somethingOnTheStack;
@@ -2938,28 +2943,28 @@ GetBytesPerTexel(GLenum format, GLenum t
 
     gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
     return 0;
 }
 
 bool
 GLContext::MakeCurrent(bool aForce) const
 {
-    if (MOZ_UNLIKELY( IsDestroyed() ))
+    if (MOZ_UNLIKELY( IsContextLost() ))
         return false;
 
     if (MOZ_LIKELY( !aForce )) {
         bool isCurrent;
         if (mUseTLSIsCurrent) {
             isCurrent = (sCurrentContext.get() == reinterpret_cast<uintptr_t>(this));
         } else {
             isCurrent = IsCurrentImpl();
         }
         if (MOZ_LIKELY( isCurrent )) {
-            MOZ_ASSERT(IsCurrentImpl());
+            MOZ_ASSERT(IsCurrentImpl() || !MakeCurrentImpl()); // Might have lost context.
             return true;
         }
     }
 
     if (!MakeCurrentImpl())
         return false;
 
     sCurrentContext.set(reinterpret_cast<uintptr_t>(this));
@@ -2972,52 +2977,169 @@ GLContext::ResetSyncCallCount(const char
     if (ShouldSpew()) {
         printf_stderr("On %s, mSyncGLCallCount = %" PRIu64 "\n",
                        resetReason, mSyncGLCallCount);
     }
 
     mSyncGLCallCount = 0;
 }
 
+// -
+
+GLenum
+GLContext::GetError() const
+{
+    if (mContextLost)
+        return LOCAL_GL_CONTEXT_LOST;
+
+    if (mImplicitMakeCurrent) {
+        (void)MakeCurrent();
+    }
+
+    const auto fnGetError = [&]() {
+        const auto ret = mSymbols.fGetError();
+        if (ret == LOCAL_GL_CONTEXT_LOST) {
+            OnContextLostError();
+            mTopError = ret; // Promote to top!
+        }
+        return ret;
+    };
+
+    auto ret = fnGetError();
+
+    {
+        auto flushedErr = ret;
+        uint32_t i = 1;
+        while (flushedErr && !mContextLost) {
+            if (i == 100) {
+                gfxCriticalError() << "Flushing glGetError still "
+                                   << gfx::hexa(flushedErr) << " after " << i
+                                   << " calls.";
+                break;
+            }
+            flushedErr = fnGetError();
+            i += 1;
+        }
+    }
+
+    if (mTopError) {
+        ret = mTopError;
+        mTopError = 0;
+    }
+
+    if (mDebugFlags & DebugFlagTrace) {
+        const auto errStr = GLErrorToString(ret);
+        printf_stderr("[gl:%p] GetError() -> %s\n", this, errStr);
+    }
+    return ret;
+}
+
+GLenum
+GLContext::fGetGraphicsResetStatus() const
+{
+    OnSyncCall();
+
+    GLenum ret = 0;
+    if (mSymbols.fGetGraphicsResetStatus) {
+        if (mImplicitMakeCurrent) {
+            (void)MakeCurrent();
+        }
+        ret = mSymbols.fGetGraphicsResetStatus();
+    } else {
+        if (!MakeCurrent(true)) {
+            ret = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+        }
+    }
+
+    if (mDebugFlags & DebugFlagTrace) {
+        printf_stderr("[gl:%p] GetGraphicsResetStatus() -> 0x%04x\n", this, ret);
+    }
+
+    return ret;
+}
+
+void
+GLContext::OnContextLostError() const
+{
+    if (mDebugFlags & DebugFlagTrace) {
+        printf_stderr("[gl:%p] CONTEXT_LOST\n", this);
+    }
+    mContextLost = true;
+}
+
+// --
+
+/*static*/ const char*
+GLContext::GLErrorToString(const GLenum err)
+{
+    switch (err) {
+    case LOCAL_GL_NO_ERROR:
+        return "GL_NO_ERROR";
+    case LOCAL_GL_INVALID_ENUM:
+        return "GL_INVALID_ENUM";
+    case LOCAL_GL_INVALID_VALUE:
+        return "GL_INVALID_VALUE";
+    case LOCAL_GL_INVALID_OPERATION:
+        return "GL_INVALID_OPERATION";
+    case LOCAL_GL_STACK_OVERFLOW:
+        return "GL_STACK_OVERFLOW";
+    case LOCAL_GL_STACK_UNDERFLOW:
+        return "GL_STACK_UNDERFLOW";
+    case LOCAL_GL_OUT_OF_MEMORY:
+        return "GL_OUT_OF_MEMORY";
+    case LOCAL_GL_TABLE_TOO_LARGE:
+        return "GL_TABLE_TOO_LARGE";
+    case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
+        return "GL_INVALID_FRAMEBUFFER_OPERATION";
+    case LOCAL_GL_CONTEXT_LOST:
+        return "GL_CONTEXT_LOST";
+    }
+
+    return "<unknown>";
+}
+
 // --
 
 void
 GLContext::BeforeGLCall_Debug(const char* const funcName) const
 {
     MOZ_ASSERT(mDebugFlags);
 
-    FlushErrors();
-
     if (mDebugFlags & DebugFlagTrace) {
         printf_stderr("[gl:%p] > %s\n", this, funcName);
     }
+
+    MOZ_ASSERT(!mDebugErrorScope);
+    mDebugErrorScope.reset(new LocalErrorScope(*this));
 }
 
 void
 GLContext::AfterGLCall_Debug(const char* const funcName) const
 {
     MOZ_ASSERT(mDebugFlags);
 
     // calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
     // the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
     // tend to be meaningless
     mSymbols.fFinish();
-    GLenum err = FlushErrors();
+
+    const auto err = mDebugErrorScope->GetError();
+    mDebugErrorScope = nullptr;
+    if (!mTopError) {
+        mTopError = err;
+    }
 
     if (mDebugFlags & DebugFlagTrace) {
-        printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName,
-                      GLErrorToString(err), err);
+        printf_stderr("[gl:%p] < %s [%s]\n", this, funcName,
+                      GLErrorToString(err));
     }
 
-    if (err != LOCAL_GL_NO_ERROR &&
-        !mLocalErrorScopeStack.size())
-    {
-        printf_stderr("[gl:%p] %s: Generated unexpected %s error."
-                      " (0x%04x)\n", this, funcName,
-                      GLErrorToString(err), err);
+    if (err && !mLocalErrorScopeStack.size()) {
+        printf_stderr("[gl:%p] %s: Generated unexpected %s error.\n", this, funcName,
+                      GLErrorToString(err));
 
         if (mDebugFlags & DebugFlagAbortOnError) {
             MOZ_CRASH("Unexpected error with MOZ_GL_DEBUG_ABORT_ON_ERROR. (Run"
                       " with MOZ_GL_DEBUG_ABORT_ON_ERROR=0 to disable)");
         }
     }
 }
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -335,17 +335,16 @@ public:
      * Get the default framebuffer for this context.
      */
     virtual GLuint GetDefaultFramebuffer() {
         return 0;
     }
 
 protected:
     bool mIsOffscreen;
-    mutable bool mContextLost = 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;
 
@@ -548,113 +547,79 @@ private:
     /**
      * Is this feature supported using the core (unsuffixed) symbols?
      */
     bool IsFeatureProvidedByCoreSymbols(GLFeature feature);
 
 public:
 // -----------------------------------------------------------------------------
 // Error handling
-    static const char* GLErrorToString(GLenum aError) {
-        switch (aError) {
-            case LOCAL_GL_INVALID_ENUM:
-                return "GL_INVALID_ENUM";
-            case LOCAL_GL_INVALID_VALUE:
-                return "GL_INVALID_VALUE";
-            case LOCAL_GL_INVALID_OPERATION:
-                return "GL_INVALID_OPERATION";
-            case LOCAL_GL_STACK_OVERFLOW:
-                return "GL_STACK_OVERFLOW";
-            case LOCAL_GL_STACK_UNDERFLOW:
-                return "GL_STACK_UNDERFLOW";
-            case LOCAL_GL_OUT_OF_MEMORY:
-                return "GL_OUT_OF_MEMORY";
-            case LOCAL_GL_TABLE_TOO_LARGE:
-                return "GL_TABLE_TOO_LARGE";
-            case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
-                return "GL_INVALID_FRAMEBUFFER_OPERATION";
-            default:
-                return "";
-        }
-    }
-
 private:
+    mutable bool mContextLost = false;
     mutable GLenum mTopError = 0;
 
-    GLenum RawGetError() const {
-        return mSymbols.fGetError();
-    }
-
-    GLenum RawGetErrorAndClear() const {
-        GLenum err = RawGetError();
-
-        if (err)
-            while (RawGetError()) {}
-
-        return err;
-    }
-
-    GLenum FlushErrors() const {
-        GLenum err = RawGetErrorAndClear();
-        if (!mTopError)
-            mTopError = err;
-        return err;
-    }
+protected:
+    void OnContextLostError() const;
+
+public:
+    static const char* GLErrorToString(GLenum aError);
+
+    static bool IsBadCallError(const GLenum err) {
+        return !(err == 0 ||
+                 err == LOCAL_GL_CONTEXT_LOST);
+    }
+
+    class LocalErrorScope;
+private:
+    mutable std::stack<const LocalErrorScope*> mLocalErrorScopeStack;
+    mutable UniquePtr<LocalErrorScope> mDebugErrorScope;
 
     ////////////////////////////////////
     // Use this safer option.
 
 public:
-    class LocalErrorScope;
-
-private:
-    std::stack<const LocalErrorScope*> mLocalErrorScopeStack;
-
-public:
     class LocalErrorScope {
-        GLContext& mGL;
+        const GLContext& mGL;
         GLenum mOldTop;
         bool mHasBeenChecked;
 
     public:
-        explicit LocalErrorScope(GLContext& gl)
+        explicit LocalErrorScope(const GLContext& gl)
             : mGL(gl)
             , mHasBeenChecked(false)
         {
             mGL.mLocalErrorScopeStack.push(this);
-
-            mGL.FlushErrors();
-
-            mOldTop = mGL.mTopError;
-            mGL.mTopError = LOCAL_GL_NO_ERROR;
+            mOldTop = mGL.GetError();
         }
 
+        /// Never returns CONTEXT_LOST.
         GLenum GetError() {
             MOZ_ASSERT(!mHasBeenChecked);
             mHasBeenChecked = true;
 
-            const GLenum ret = mGL.fGetError();
-
-            while (mGL.fGetError()) {}
-
+            const auto ret = mGL.GetError();
+            if (ret == LOCAL_GL_CONTEXT_LOST)
+                return 0;
             return ret;
         }
 
         ~LocalErrorScope() {
             MOZ_ASSERT(mHasBeenChecked);
 
-            MOZ_ASSERT(mGL.fGetError() == LOCAL_GL_NO_ERROR);
+            MOZ_ASSERT(!IsBadCallError(mGL.GetError()));
 
             MOZ_ASSERT(mGL.mLocalErrorScopeStack.top() == this);
             mGL.mLocalErrorScopeStack.pop();
 
             mGL.mTopError = mOldTop;
         }
     };
 
+    // -
+
     bool GetPotentialInteger(GLenum pname, GLint* param) {
         LocalErrorScope localError(*this);
 
         fGetIntegerv(pname, param);
 
         GLenum err = localError.GetError();
         MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_INVALID_ENUM);
         return err == LOCAL_GL_NO_ERROR;
@@ -709,30 +674,32 @@ private:
 
     void BeforeGLCall_Debug(const char* funcName) const;
     void AfterGLCall_Debug(const char* funcName) const;
     static void OnImplicitMakeCurrentFailure(const char* funcName);
 
     bool BeforeGLCall(const char* const funcName) const {
         if (mImplicitMakeCurrent) {
             if (MOZ_UNLIKELY( !MakeCurrent() )) {
-                OnImplicitMakeCurrentFailure(funcName);
+                if (!mContextLost) {
+                    OnImplicitMakeCurrentFailure(funcName);
+                }
                 return false;
             }
         }
         MOZ_ASSERT(IsCurrentImpl());
 
-        if (mDebugFlags) {
+        if (MOZ_UNLIKELY( mDebugFlags )) {
             BeforeGLCall_Debug(funcName);
         }
         return true;
     }
 
     void AfterGLCall(const char* const funcName) const {
-        if (mDebugFlags) {
+        if (MOZ_UNLIKELY( mDebugFlags )) {
             AfterGLCall_Debug(funcName);
         }
     }
 
     GLContext* TrackingContext()
     {
         GLContext* tip = this;
         while (tip->mSharedContext)
@@ -796,27 +763,27 @@ public:
 
     void ResetSyncCallCount(const char* resetReason) const;
 
 // -----------------------------------------------------------------------------
 // GL official entry points
 public:
     // We smash all errors together, so you never have to loop on this. We
     // guarantee that immediately after this call, there are no errors left.
+    // Always returns the top-most error, except if followed by CONTEXT_LOST, then return
+    // that instead.
+    GLenum GetError() const;
+
     GLenum fGetError() {
-        GLenum err = LOCAL_GL_CONTEXT_LOST;
-        BEFORE_GL_CALL;
-
-        FlushErrors();
-        err = mTopError;
-        mTopError = LOCAL_GL_NO_ERROR;
-
-        AFTER_GL_CALL;
-        return err;
-    }
+        return GetError();
+    }
+
+    GLenum fGetGraphicsResetStatus() const;
+
+    // -
 
     void fActiveTexture(GLenum texture) {
         BEFORE_GL_CALL;
         mSymbols.fActiveTexture(texture);
         AFTER_GL_CALL;
     }
 
     void fAttachShader(GLuint program, GLuint shader) {
@@ -2295,26 +2262,16 @@ public:
         TRACKING_CONTEXT(DeletedRenderbuffers(this, n, names));
     }
 
     void fDeleteTextures(GLsizei n, const GLuint* names) {
         raw_fDeleteTextures(n, names);
         TRACKING_CONTEXT(DeletedTextures(this, n, names));
     }
 
-    GLenum fGetGraphicsResetStatus() {
-        GLenum ret = 0;
-        BEFORE_GL_CALL;
-        ASSERT_SYMBOL_PRESENT(fGetGraphicsResetStatus);
-        ret = mSymbols.fGetGraphicsResetStatus();
-        OnSyncCall();
-        AFTER_GL_CALL;
-        return ret;
-    }
-
 
 // -----------------------------------------------------------------------------
 // Extension ARB_sync (GL)
 public:
     GLsync fFenceSync(GLenum condition, GLbitfield flags) {
         GLsync ret = 0;
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fFenceSync);
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -432,18 +432,17 @@ GLContextEGL::MakeCurrentImpl() const
         surface = mFallbackSurface;
     }
 
     const bool succeeded = mEgl->fMakeCurrent(EGL_DISPLAY(), surface, surface,
                                               mContext);
     if (!succeeded) {
         const auto eglError = mEgl->fGetError();
         if (eglError == LOCAL_EGL_CONTEXT_LOST) {
-            mContextLost = true;
-            NS_WARNING("EGL context has been lost.");
+            OnContextLostError();
         } else {
             NS_WARNING("Failed to make GL context current!");
 #ifdef DEBUG
             printf_stderr("EGL Error: 0x%04x\n", eglError);
 #endif
         }
     }
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -783,17 +783,16 @@ private:
   DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
   DECL_GFX_PREF(Live, "webgl.max-contexts",                    WebGLMaxContexts, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.max-contexts-per-principal",      WebGLMaxContextsPerPrincipal, uint32_t, 16);
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-samples",                    WebGLMsaaSamples, uint32_t, 4);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
-  DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
   DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.perf.max-warnings",                    WebGLMaxPerfWarnings, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.perf.max-acceptable-fb-status-invals", WebGLMaxAcceptableFBStatusInvals, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.perf.spew-frame-allocs",          WebGLSpewFrameAllocs, bool, true);
 
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4775,17 +4775,16 @@ pref("webgl.min_capability_mode", false)
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
 pref("webgl.default-no-alpha", false);
 pref("webgl.force-layers-readback", false);
 pref("webgl.force-index-validation", 0);
 pref("webgl.lose-context-on-memory-pressure", false);
 pref("webgl.can-lose-context-in-foreground", true);
-pref("webgl.restore-context-when-visible", true);
 #ifdef ANDROID
 pref("webgl.max-contexts", 16);
 pref("webgl.max-contexts-per-principal", 8);
 #else
 pref("webgl.max-contexts", 32);
 pref("webgl.max-contexts-per-principal", 16);
 #endif
 pref("webgl.max-warnings-per-context", 32);
--- a/widget/GfxDriverInfo.cpp
+++ b/widget/GfxDriverInfo.cpp
@@ -315,16 +315,50 @@ const GfxDeviceFamily* GfxDriverInfo::Ge
       APPEND_DEVICE(0x990b);
       break;
     case Bug1207665:
       APPEND_DEVICE(0xa001); /* Intel Media Accelerator 3150 */
       APPEND_DEVICE(0xa002);
       APPEND_DEVICE(0xa011);
       APPEND_DEVICE(0xa012);
       break;
+    case NvidiaBlockWebRender:
+      /* GT218 */
+      APPEND_DEVICE(0x0a60);
+      APPEND_DEVICE(0x0a62);
+      APPEND_DEVICE(0x0a63);
+      APPEND_DEVICE(0x0a64);
+      APPEND_DEVICE(0x0a65);
+      APPEND_DEVICE(0x0a66);
+      APPEND_DEVICE(0x0a67);
+      APPEND_DEVICE(0x0a7b);
+      APPEND_DEVICE(0x10c0);
+      APPEND_DEVICE(0x10c3);
+      APPEND_DEVICE(0x10c5);
+      APPEND_DEVICE(0x10d8);
+      /* GT218M */
+      APPEND_DEVICE(0x0a68);
+      APPEND_DEVICE(0x0a69);
+      APPEND_DEVICE(0x0a6a);
+      APPEND_DEVICE(0x0a6c);
+      APPEND_DEVICE(0x0a6e);
+      APPEND_DEVICE(0x0a6f);
+      APPEND_DEVICE(0x0a70);
+      APPEND_DEVICE(0x0a71);
+      APPEND_DEVICE(0x0a72);
+      APPEND_DEVICE(0x0a73);
+      APPEND_DEVICE(0x0a74);
+      APPEND_DEVICE(0x0a75);
+      APPEND_DEVICE(0x0a76);
+      APPEND_DEVICE(0x0a7a);
+      /* GT218GL */
+      APPEND_DEVICE(0x0a78);
+      /* GT218GLM */
+      APPEND_DEVICE(0x0a7c);
+      break;
     // This should never happen, but we get a warning if we don't handle this.
     case DeviceFamilyMax:
       NS_WARNING("Invalid DeviceFamily id");
       break;
   }
 
   return deviceFamily;
 }
--- a/widget/GfxDriverInfo.h
+++ b/widget/GfxDriverInfo.h
@@ -94,16 +94,17 @@ enum DeviceFamily {
   Geforce7300GT,
   Nvidia310M,
   Nvidia8800GTS,
   Bug1137716,
   Bug1116812,
   Bug1155608,
   Bug1207665,
   Bug1447141,
+  NvidiaBlockWebRender,
   DeviceFamilyMax
 };
 
 enum DeviceVendor {
   VendorAll, // There is an assumption that this is the first enum
   VendorIntel,
   VendorNVIDIA,
   VendorAMD,
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -1415,42 +1415,38 @@ GfxInfo::GetGfxDriverInfo()
     APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_DX_P010, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_P010_NVIDIA");
 
     ////////////////////////////////////
     // FEATURE_WEBRENDER
 
-    APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows,
-      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), GfxDriverInfo::allDevices,
+    // We are blocking all non-Nvidia cards in gfxPlatform.cpp where we check
+    // for the WEBRENDER_QUALIFIED feature. However we also want to block some
+    // specific Nvidia cards for being too low-powered, so we do that here.
+    APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows10,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA),
+      (GfxDeviceFamily*)GfxDriverInfo::GetDeviceFamily(NvidiaBlockWebRender),
       nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
-      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_WIN_INTEL");
-    APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows,
-      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
-      nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
-      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_WIN_ATI");
-    APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows,
-      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAMD), GfxDriverInfo::allDevices,
-      nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
-      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_WIN_AMD");
+      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_NVIDIA_BLOCKED");
 
-    // Allow Nvidia on Windows 10
+    // Block all windows versions other than windows 10
     APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows7,
-      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
-      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_NVIDIA_7");
+      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_WINDOWS_7");
     APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows8,
-      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
-      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_NVIDIA_8");
+      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_WINDOWS_8");
     APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows8_1,
-      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
-      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_NVIDIA_8_1");
+      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_UNQUALIFIED_WEBRENDER_WINDOWS_8_1");
   }
   return *sDriverInfo;
 }
 
 nsresult
 GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
                               int32_t *aStatus,
                               nsAString & aSuggestedDriverVersion,