ASSERTION FAILED: m_wrapper on webgl/max-active-contexts-webglcontextlost-prevent-default.html
https://bugs.webkit.org/show_bug.cgi?id=209863
<rdar://problem/61164936>

Reviewed by Darin Adler.

The HTMLCanvasElement JS wrapper needs to stay alive as long as JS events may need to be fired.
When the canvas has a WebGL context, the WebGL context may cause contextlost / contextrestored
/ contextchanged events at any point, unless the context is unrecoverably lost. To fix the
issue, we now override virtualHasPendingActivity() in HTMLCanvasElement and return true if
it has a WebGL context that is not unrecoverably lost and if relevant WebGL event listeners
are registed.

No new tests, covered by existing test.

* html/HTMLCanvasElement.cpp:
(WebCore::HTMLCanvasElement::~HTMLCanvasElement):
(WebCore::HTMLCanvasElement::virtualHasPendingActivity const):
(WebCore::HTMLCanvasElement::stop):
(WebCore::HTMLCanvasElement::eventListenersDidChange):
* html/HTMLCanvasElement.h:
* html/canvas/WebGLRenderingContextBase.cpp:
(WebCore::WebGLRenderingContextBase::isContextUnrecoverablyLost const):
* html/canvas/WebGLRenderingContextBase.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@259364 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 0c4d86c..ded931a 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,30 @@
+2020-04-01  Chris Dumez  <cdumez@apple.com>
+
+        ASSERTION FAILED: m_wrapper on webgl/max-active-contexts-webglcontextlost-prevent-default.html
+        https://bugs.webkit.org/show_bug.cgi?id=209863
+        <rdar://problem/61164936>
+
+        Reviewed by Darin Adler.
+
+        The HTMLCanvasElement JS wrapper needs to stay alive as long as JS events may need to be fired.
+        When the canvas has a WebGL context, the WebGL context may cause contextlost / contextrestored
+        / contextchanged events at any point, unless the context is unrecoverably lost. To fix the
+        issue, we now override virtualHasPendingActivity() in HTMLCanvasElement and return true if
+        it has a WebGL context that is not unrecoverably lost and if relevant WebGL event listeners
+        are registed.
+
+        No new tests, covered by existing test.
+
+        * html/HTMLCanvasElement.cpp:
+        (WebCore::HTMLCanvasElement::~HTMLCanvasElement):
+        (WebCore::HTMLCanvasElement::virtualHasPendingActivity const):
+        (WebCore::HTMLCanvasElement::stop):
+        (WebCore::HTMLCanvasElement::eventListenersDidChange):
+        * html/HTMLCanvasElement.h:
+        * html/canvas/WebGLRenderingContextBase.cpp:
+        (WebCore::WebGLRenderingContextBase::isContextUnrecoverablyLost const):
+        * html/canvas/WebGLRenderingContextBase.h:
+
 2020-04-01  Jer Noble  <jer.noble@apple.com>
 
         CRASH in MediaPlayerPrivateMediaSourceAVFObjC::addAudioRenderer(), uncaught ObjC exception
diff --git a/Source/WebCore/html/HTMLCanvasElement.cpp b/Source/WebCore/html/HTMLCanvasElement.cpp
index e6a2268..ef2b564 100644
--- a/Source/WebCore/html/HTMLCanvasElement.cpp
+++ b/Source/WebCore/html/HTMLCanvasElement.cpp
@@ -955,4 +955,28 @@
     return "HTMLCanvasElement";
 }
 
+bool HTMLCanvasElement::virtualHasPendingActivity() const
+{
+    if (isContextStopped())
+        return false;
+
+#if ENABLE(WEBGL)
+    if (is<WebGLRenderingContextBase>(m_context.get())) {
+        // WebGL rendering context may fire contextlost / contextchange / contextrestored events at any point.
+        return m_hasRelevantWebGLEventListener && !downcast<WebGLRenderingContextBase>(*m_context).isContextUnrecoverablyLost();
+    }
+#endif
+
+    return false;
+}
+
+void HTMLCanvasElement::eventListenersDidChange()
+{
+#if ENABLE(WEBGL)
+    m_hasRelevantWebGLEventListener = hasEventListeners(eventNames().webglcontextchangedEvent)
+        || hasEventListeners(eventNames().webglcontextlostEvent)
+        || hasEventListeners(eventNames().webglcontextrestoredEvent);
+#endif
+}
+
 }
diff --git a/Source/WebCore/html/HTMLCanvasElement.h b/Source/WebCore/html/HTMLCanvasElement.h
index 2e62891..50e7c2c 100644
--- a/Source/WebCore/html/HTMLCanvasElement.h
+++ b/Source/WebCore/html/HTMLCanvasElement.h
@@ -133,7 +133,13 @@
     HTMLCanvasElement(const QualifiedName&, Document&);
 
     bool isHTMLCanvasElement() const final { return true; }
+
+    // ActiveDOMObject.
     const char* activeDOMObjectName() const final;
+    bool virtualHasPendingActivity() const final;
+
+    // EventTarget.
+    void eventListenersDidChange() final;
 
     void parseAttribute(const QualifiedName&, const AtomString&) final;
     RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&) final;
@@ -171,6 +177,9 @@
     // m_hasCreatedImageBuffer means we tried to malloc the buffer. We didn't necessarily get it.
     mutable bool m_hasCreatedImageBuffer { false };
     mutable bool m_didClearImageBuffer { false };
+#if ENABLE(WEBGL)
+    bool m_hasRelevantWebGLEventListener { false };
+#endif
 
     mutable RefPtr<Image> m_presentedImage;
     mutable RefPtr<Image> m_copiedImage; // FIXME: This is temporary for platforms that have to copy the image buffer to render (and for CSSCanvasValue).
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp b/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
index 94292e3..4be9016 100644
--- a/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
+++ b/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
@@ -5502,6 +5502,11 @@
         m_restoreTimer.startOneShot(0_s);
 }
 
+bool WebGLRenderingContextBase::isContextUnrecoverablyLost() const
+{
+    return m_contextLost && !m_restoreAllowed;
+}
+
 PlatformLayer* WebGLRenderingContextBase::platformLayer() const
 {
     return (!isContextLost() && !m_isPendingPolicyResolution) ? m_context->platformLayer() : 0;
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContextBase.h b/Source/WebCore/html/canvas/WebGLRenderingContextBase.h
index d70cb25..77b2fd9 100644
--- a/Source/WebCore/html/canvas/WebGLRenderingContextBase.h
+++ b/Source/WebCore/html/canvas/WebGLRenderingContextBase.h
@@ -359,6 +359,8 @@
     
     unsigned getMaxVertexAttribs() const { return m_maxVertexAttribs; }
 
+    bool isContextUnrecoverablyLost() const;
+
     // Instanced Array helper functions.
     void drawArraysInstanced(GCGLenum mode, GCGLint first, GCGLsizei count, GCGLsizei primcount);
     void drawElementsInstanced(GCGLenum mode, GCGLsizei count, GCGLenum type, long long offset, GCGLsizei primcount);