Bug 1427668 - Lose context if EnsureDefaultFB fails. - r=daoshengmu draft
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 21 Dec 2017 17:14:54 -0800
changeset 716061 796f905d66d9eaa610401d2a4db70773a39cbe32
parent 716060 07a88e32f508aa43187b5544d229f9e090badb7c
child 716062 ad2b61c9f1bd24a7f6f333452c2638680d3c2ec2
push id94316
push userbmo:jgilbert@mozilla.com
push dateFri, 05 Jan 2018 03:14:09 +0000
reviewersdaoshengmu
bugs1427668
milestone59.0a1
Bug 1427668 - Lose context if EnsureDefaultFB fails. - r=daoshengmu MozReview-Commit-ID: 8OqXYXpUv4I
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/canvas/ImageBitmapRenderingContext.cpp
dom/canvas/ImageBitmapRenderingContext.h
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextState.cpp
dom/canvas/nsICanvasRenderingContextInternal.h
gfx/gl/MozFramebuffer.cpp
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1942,28 +1942,16 @@ CanvasRenderingContext2D::TryBasicTarget
   if (!aOutDT) {
     return false;
   }
 
   aOutProvider = new PersistentBufferProviderBasic(aOutDT);
   return true;
 }
 
-int32_t
-CanvasRenderingContext2D::GetWidth() const
-{
-  return mWidth;
-}
-
-int32_t
-CanvasRenderingContext2D::GetHeight() const
-{
-  return mHeight;
-}
-
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
 {
   ClearTarget();
 
   // Zero sized surfaces can cause problems.
   mZero = false;
   if (aHeight == 0) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -421,19 +421,19 @@ public:
 
   bool SwitchRenderingMode(RenderingMode aRenderingMode);
 
   // Eventually this should be deprecated. Keeping for now to keep the binding functional.
   void Demote();
 
   nsresult Redraw();
 
-  virtual int32_t GetWidth() const override;
-  virtual int32_t GetHeight() const override;
   gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
+  virtual int32_t GetWidth() override { return GetSize().width; }
+  virtual int32_t GetHeight() override { return GetSize().height; }
 
   // nsICanvasRenderingContextInternal
   /**
     * Gets the pres shell from either the canvas element or the doc shell
     */
   virtual nsIPresShell *GetPresShell() override {
     if (mCanvasElement) {
       return mCanvasElement->OwnerDoc()->GetShell();
--- a/dom/canvas/ImageBitmapRenderingContext.cpp
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -65,28 +65,16 @@ ImageBitmapRenderingContext::TransferFro
 
   if (!mImage) {
     return;
   }
 
   Redraw(gfxRect(0, 0, mWidth, mHeight));
 }
 
-int32_t
-ImageBitmapRenderingContext::GetWidth() const
-{
-  return mWidth;
-}
-
-int32_t
-ImageBitmapRenderingContext::GetHeight() const
-{
-  return mHeight;
-}
-
 NS_IMETHODIMP
 ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
 {
   mWidth = aWidth;
   mHeight = aHeight;
   return NS_OK;
 }
 
--- a/dom/canvas/ImageBitmapRenderingContext.h
+++ b/dom/canvas/ImageBitmapRenderingContext.h
@@ -45,18 +45,18 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ImageBitmapRenderingContext)
 
   void TransferImageBitmap(ImageBitmap& aImageBitmap);
   void TransferFromImageBitmap(ImageBitmap& aImageBitmap);
 
   // nsICanvasRenderingContextInternal
-  virtual int32_t GetWidth() const override;
-  virtual int32_t GetHeight() const override;
+  virtual int32_t GetWidth() override { return mWidth; }
+  virtual int32_t GetHeight() override { return mHeight; }
 
   NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
 
   NS_IMETHOD InitializeWithDrawTarget(nsIDocShell* aDocShell,
                                       NotNull<gfx::DrawTarget*> aTarget) override;
 
   virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
   NS_IMETHOD GetInputStream(const char* aMimeType,
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -174,17 +174,17 @@ WebGLContext::ValidateInvalidateFramebuf
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (fb) {
         const auto fbStatus = fb->CheckFramebufferStatus(funcName);
         if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
             return false; // Not an error, but don't run forward to driver either.
     } else {
-        if (!EnsureDefaultFB())
+        if (!EnsureDefaultFB(funcName))
             return false;
     }
     DoBindFB(fb, target);
 
     *out_glNumAttachments = attachments.Length();
     *out_glAttachments = attachments.Elements();
 
     if (fb) {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -735,17 +735,17 @@ WebGLContext::CreateAndInitGL(bool force
     out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
                                              "Exhausted GL driver options."));
     return false;
 }
 
 // Fallback for resizes:
 
 bool
-WebGLContext::EnsureDefaultFB() const
+WebGLContext::EnsureDefaultFB(const char* const funcName)
 {
     if (mDefaultFB) {
         MOZ_ASSERT(mDefaultFB->mSize == mRequestedSize);
         return true;
     }
 
     const bool depthStencil = mOptions.depth || mOptions.stencil;
     auto attemptSize = mRequestedSize;
@@ -771,27 +771,31 @@ WebGLContext::EnsureDefaultFB() const
 
         if (mDefaultFB)
             break;
 
         attemptSize.width /= 2;
         attemptSize.height /= 2;
     }
 
-    if (!mDefaultFB)
+    if (!mDefaultFB) {
+        GenerateWarning("%s: Backbuffer resize failed. Losing context.", funcName);
+        ForceLoseContext();
         return false;
+    }
 
     mDefaultFB_IsInvalid = true;
 
     if (mDefaultFB->mSize != mRequestedSize) {
         GenerateWarning("Requested size %dx%d was too large, but resize"
                           " to %dx%d succeeded.",
                         mRequestedSize.width, mRequestedSize.height,
                         mDefaultFB->mSize.width, mDefaultFB->mSize.height);
     }
+    mRequestedSize = mDefaultFB->mSize;
     return true;
 }
 
 void
 WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
 {
     RefPtr<EventTarget> target = mCanvasElement;
     if (!target && mOffscreenCanvas) {
@@ -964,17 +968,16 @@ WebGLContext::SetDimensions(int32_t sign
             text.AppendASCII("\n* ");
             text.Append(cur.info);
         }
         failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(gl);
-    MOZ_ASSERT_IF(mOptions.alpha, gl->Caps().alpha);
 
     if (mOptions.failIfMajorPerformanceCaveat) {
         if (gl->IsWARP()) {
             DestroyResourcesAndContext();
             MOZ_ASSERT(!gl);
 
             failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_WARP");
             const nsLiteralCString text("failIfMajorPerformanceCaveat: Driver is not"
@@ -995,17 +998,19 @@ WebGLContext::SetDimensions(int32_t sign
             ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
 #endif
     }
 
     MOZ_ASSERT(!mDefaultFB);
     mRequestedSize = {width, height};
-    if (!EnsureDefaultFB()) {
+    if (!EnsureDefaultFB("context initialization")) {
+        MOZ_ASSERT(!gl);
+
         failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_BACKBUFFER");
         const nsLiteralCString text("Initializing WebGL backbuffer failed.");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
 
     if (GLContext::ShouldSpew()) {
         printf_stderr("--- WebGL context created: %p\n", gl.get());
@@ -1315,17 +1320,17 @@ WebGLContext::InitializeCanvasRenderer(n
         // the invalidation state to indicate that the canvas is up to date.
         data.mPreTransCallback = WebGLContextUserData::PreTransactionCallback;
         data.mPreTransCallbackData = this;
         data.mDidTransCallback = WebGLContextUserData::DidTransactionCallback;
         data.mDidTransCallbackData = this;
     }
 
     data.mGLContext = gl;
-    data.mSize = DrawingBufferSize();
+    data.mSize = DrawingBufferSize("InitializeCanvasRenderer");
     data.mHasAlpha = mOptions.alpha;
     data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
 
     aRenderer->Initialize(data);
     aRenderer->SetDirty();
     return true;
 }
 
@@ -1956,40 +1961,37 @@ WebGLContext::DidRefresh()
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 gfx::IntSize
-WebGLContext::DrawingBufferSize() const
+WebGLContext::DrawingBufferSize(const char* const funcName)
 {
     const gfx::IntSize zeros{0, 0};
     if (IsContextLost())
         return zeros;
 
-    if (!EnsureDefaultFB())
+    if (!EnsureDefaultFB(funcName))
         return zeros;
 
     return mDefaultFB->mSize;
 }
 
 bool
 WebGLContext::ValidateAndInitFB(const char* const funcName,
                                 const WebGLFramebuffer* const fb)
 {
     if (fb)
         return fb->ValidateAndInitAttachments(funcName);
 
-    if (!EnsureDefaultFB()) {
-        GenerateWarning("%s: Lazy resize failed. Losing context.", funcName);
-        ForceLoseContext();
+    if (!EnsureDefaultFB(funcName))
         return false;
-    }
 
     if (mDefaultFB_IsInvalid) {
         gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
         const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
                                 LOCAL_GL_DEPTH_BUFFER_BIT |
                                 LOCAL_GL_STENCIL_BUFFER_BIT;
         const bool fakeNoAlpha = !mOptions.alpha;
         ForceClearFramebufferWithDefaultValues(bits, fakeNoAlpha);
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -337,18 +337,18 @@ public:
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
 
     virtual void OnVisibilityChange() override;
     virtual void OnMemoryPressure() override;
 
     // nsICanvasRenderingContextInternal
-    virtual int32_t GetWidth() const override { return DrawingBufferWidth(); }
-    virtual int32_t GetHeight() const override { return DrawingBufferHeight(); }
+    virtual int32_t GetWidth() override { return DrawingBufferWidth("get width"); }
+    virtual int32_t GetHeight() override { return DrawingBufferHeight("get height"); }
 
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
                                         NotNull<gfx::DrawTarget*>) override
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
@@ -496,20 +496,24 @@ public:
 
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     nsIDocument* GetOwnerDoc() const;
 
     // WebIDL WebGLRenderingContext API
     void Commit();
     void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
 private:
-    gfx::IntSize DrawingBufferSize() const;
+    gfx::IntSize DrawingBufferSize(const char* funcName);
 public:
-    GLsizei DrawingBufferWidth() const { return DrawingBufferSize().width; }
-    GLsizei DrawingBufferHeight() const { return DrawingBufferSize().height; }
+    GLsizei DrawingBufferWidth(const char* const funcName = "drawingBufferWidth") {
+        return DrawingBufferSize(funcName).width;
+    }
+    GLsizei DrawingBufferHeight(const char* const funcName = "drawingBufferHeight") {
+        return DrawingBufferSize(funcName).height;
+    }
 
     layers::LayersBackend GetCompositorBackendType() const;
 
     void
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
@@ -1985,24 +1989,24 @@ protected:
 
     const bool mAllowFBInvalidation;
 
     bool Has64BitTimestamps() const;
 
     // --
 
     const uint8_t mMsaaSamples;
-    gfx::IntSize mRequestedSize;
+    mutable gfx::IntSize mRequestedSize;
     mutable UniquePtr<gl::MozFramebuffer> mDefaultFB;
     mutable bool mDefaultFB_IsInvalid;
     mutable UniquePtr<gl::MozFramebuffer> mResolvedDefaultFB;
 
     // --
 
-    bool EnsureDefaultFB() const;
+    bool EnsureDefaultFB(const char* funcName);
     bool ValidateAndInitFB(const char* funcName, const WebGLFramebuffer* fb);
     void DoBindFB(const WebGLFramebuffer* fb, GLenum target = LOCAL_GL_FRAMEBUFFER) const;
 
     bool BindCurFBForDraw(const char* funcName);
     bool BindCurFBForColorRead(const char* funcName,
                                const webgl::FormatUsageInfo** out_format,
                                uint32_t* out_width, uint32_t* out_height);
     void DoColorMask(uint8_t bitmask) const;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -357,17 +357,17 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(refValue & stencilMask);
         }
 
         case LOCAL_GL_SAMPLE_BUFFERS:
         case LOCAL_GL_SAMPLES: {
             const auto& fb = mBoundDrawFramebuffer;
             auto samples = [&]() -> Maybe<uint32_t> {
                 if (!fb) {
-                    if (!EnsureDefaultFB())
+                    if (!EnsureDefaultFB(funcName))
                         return Nothing();
                     return Some(mDefaultFB->mSamples);
                 }
 
                 if (!fb->IsCheckFramebufferStatusComplete(funcName))
                     return Some(0);
 
                 DoBindFB(fb, LOCAL_GL_FRAMEBUFFER);
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -88,18 +88,18 @@ public:
   }
 
   void SetOffscreenCanvas(mozilla::dom::OffscreenCanvas* aOffscreenCanvas)
   {
     mOffscreenCanvas = aOffscreenCanvas;
   }
 
   // Dimensions of the canvas, in pixels.
-  virtual int32_t GetWidth() const = 0;
-  virtual int32_t GetHeight() const = 0;
+  virtual int32_t GetWidth() = 0;
+  virtual int32_t GetHeight() = 0;
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
 
   // Initializes with an nsIDocShell and DrawTarget. The size is taken from the
   // DrawTarget.
   NS_IMETHOD InitializeWithDrawTarget(nsIDocShell *aDocShell,
--- a/gfx/gl/MozFramebuffer.cpp
+++ b/gfx/gl/MozFramebuffer.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
 /* 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 "MozFramebuffer.h"
 
 #include "GLContext.h"
+#include "mozilla/gfx/Logging.h"
 #include "ScopedGLHelpers.h"
 
 namespace mozilla {
 namespace gl {
 
 static void
 DeleteByTarget(GLContext* const gl, const GLenum target, const GLuint name)
 {
@@ -56,17 +57,20 @@ MozFramebuffer::Create(GLContext* const 
         gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
         gl->fTexImage2D(colorTarget, 0, LOCAL_GL_RGBA,
                         size.width, size.height, 0,
                         LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
     }
 
     const auto err = errorScope.GetError();
     if (err) {
-        MOZ_ASSERT(err == LOCAL_GL_OUT_OF_MEMORY);
+        if (err != LOCAL_GL_OUT_OF_MEMORY) {
+            gfxCriticalNote << "Unexpected error: " << gfx::hexa(err) << ": "
+                            << GLContext::GLErrorToString(err);
+        }
         DeleteByTarget(gl, colorTarget, colorName);
         return nullptr;
     }
 
     return CreateWith(gl, size, samples, depthStencil, colorTarget, colorName);
 }
 
 UniquePtr<MozFramebuffer>