Pages using WebGLRenderingContext fail to enter the back/forward cache
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Sep 2019 22:42:20 +0000 (22:42 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Sep 2019 22:42:20 +0000 (22:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202318
<rdar://problem/55783612>

Reviewed by Tim Horton.

Source/WebCore:

Allow pages with a WebGLRenderingContext to enter the back/forward cache by updating the
implementation to use SuspendableTimers to fire JS events. This guarantees that no events
will be fired (and thus no JS will run) while in the page cache.

This was preventing some of the pages on weather.com and facebook.com from entering the
back/forward cache.

Test: fast/canvas/webgl/canvas-webgl-page-cache.html

* dom/DocumentEventQueue.cpp:
* html/canvas/WebGLRenderingContextBase.cpp:
(WebCore::WebGLRenderingContextBase::WebGLRenderingContextBase):
(WebCore::WebGLRenderingContextBase::canSuspendForDocumentSuspension const):
(WebCore::WebGLRenderingContextBase::suspend):
(WebCore::WebGLRenderingContextBase::resume):
(WebCore::WebGLRenderingContextBase::dispatchContextLostEvent):
(WebCore::WebGLRenderingContextBase::maybeRestoreContext):
(WebCore::WebGLRenderingContextBase::dispatchContextChangedNotification):
(WebCore::WebGLRenderingContextBase::dispatchContextChangedEvent):
* html/canvas/WebGLRenderingContextBase.h:
* page/DOMTimer.cpp:
(WebCore::DOMTimer::DOMTimer):
* page/DOMTimer.h:
* page/SuspendableTimer.cpp:
(WebCore::SuspendableTimerBase::SuspendableTimerBase):
(WebCore::SuspendableTimerBase::hasPendingActivity const):
(WebCore::SuspendableTimerBase::stop):
(WebCore::SuspendableTimerBase::suspend):
(WebCore::SuspendableTimerBase::resume):
(WebCore::SuspendableTimerBase::canSuspendForDocumentSuspension const):
(WebCore::SuspendableTimerBase::didStop):
(WebCore::SuspendableTimerBase::cancel):
(WebCore::SuspendableTimerBase::startRepeating):
(WebCore::SuspendableTimerBase::startOneShot):
(WebCore::SuspendableTimerBase::repeatInterval const):
(WebCore::SuspendableTimerBase::augmentFireInterval):
(WebCore::SuspendableTimerBase::augmentRepeatInterval):
(WebCore::SuspendableTimer::activeDOMObjectName const):
* page/SuspendableTimer.h:

Source/WebKitLegacy/win:

* WebView.cpp:
(WindowCloseTimer::WindowCloseTimer):
(WindowCloseTimer::contextDestroyed):
* WebView.h:

LayoutTests:

Add layout test coverage.

* fast/canvas/webgl/canvas-webgl-page-cache-expected.txt: Added.
* fast/canvas/webgl/canvas-webgl-page-cache.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250464 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/canvas/webgl/canvas-webgl-page-cache-expected.txt [new file with mode: 0644]
LayoutTests/fast/canvas/webgl/canvas-webgl-page-cache.html [new file with mode: 0644]
LayoutTests/http/tests/security/navigate-when-restoring-cached-page.html
Source/WebCore/ChangeLog
Source/WebCore/dom/DocumentEventQueue.cpp
Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
Source/WebCore/html/canvas/WebGLRenderingContextBase.h
Source/WebCore/page/DOMTimer.cpp
Source/WebCore/page/DOMTimer.h
Source/WebCore/page/SuspendableTimer.cpp
Source/WebCore/page/SuspendableTimer.h
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebView.cpp
Source/WebKitLegacy/win/WebView.h

index cc3506f..d9ce9ec 100644 (file)
@@ -1,3 +1,16 @@
+2019-09-27  Chris Dumez  <cdumez@apple.com>
+
+        Pages using WebGLRenderingContext fail to enter the back/forward cache
+        https://bugs.webkit.org/show_bug.cgi?id=202318
+        <rdar://problem/55783612>
+
+        Reviewed by Tim Horton.
+
+        Add layout test coverage.
+
+        * fast/canvas/webgl/canvas-webgl-page-cache-expected.txt: Added.
+        * fast/canvas/webgl/canvas-webgl-page-cache.html: Added.
+
 2019-09-27  Andres Gonzalez  <andresg_22@apple.com>
 
         Support accessibility for <figure> element on iOS.
diff --git a/LayoutTests/fast/canvas/webgl/canvas-webgl-page-cache-expected.txt b/LayoutTests/fast/canvas/webgl/canvas-webgl-page-cache-expected.txt
new file mode 100644 (file)
index 0000000..3825537
--- /dev/null
@@ -0,0 +1,14 @@
+Tests that a page with a WebGLRenderingContext is able to enter the back/forward cache.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Canvas did not crash on resize.
+pageshow - not from page cache
+pagehide - entering page cache
+pageshow - from page cache
+PASS Page entered page cache
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/canvas/webgl/canvas-webgl-page-cache.html b/LayoutTests/fast/canvas/webgl/canvas-webgl-page-cache.html
new file mode 100644 (file)
index 0000000..cba3279
--- /dev/null
@@ -0,0 +1,50 @@
+<!-- webkit-test-runner [ enablePageCache=true ] -->
+<html>
+<head>
+<style>
+#example {
+  width: 100%;
+  height: 100%;
+}
+</style>
+<script src="../../../resources/js-test.js"></script>
+<script src="resources/webgl-test-utils.js"> </script>
+<script>
+description('Tests that a page with a WebGLRenderingContext is able to enter the back/forward cache.');
+jsTestIsAsync = true;
+
+window.addEventListener("pageshow", function(event) {
+  debug("pageshow - " + (event.persisted ? "" : "not ") + "from page cache");
+  if (event.persisted) {
+      testPassed("Page entered page cache");
+      finishJSTest();
+  }
+});
+
+window.addEventListener("pagehide", function(event) {
+    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering page cache");
+    if (!event.persisted) {
+        testFailed("Page failed to enter page cache");
+        finishJSTest();
+    }
+});
+
+function start()
+{
+    canvas = document.getElementById('example');
+    gl = WebGLTestUtils.create3DContext(canvas);
+
+    setTimeout(() => {
+        window.location = "../../history/resources/page-cache-helper.html";
+    }, 0);
+}
+
+testPassed("Canvas did not crash on resize.");
+</script>
+</head>
+
+<body onload="start()">
+    <canvas id="example"></canvas>
+</body>
+</html>
+
index 9051db8..7e34f5c 100644 (file)
@@ -60,9 +60,10 @@ function runTest()
             return;
           
           clearInterval(interval_id);
-          
-          canvas = w.document.body.appendChild(document.createElement('canvas'));
-          context = canvas.getContext('webgl');
+
+          // Prevent entering PageCache.
+          peerConnection = new w.RTCPeerConnection();
+          peerConnection.addEventListener('onicecandidate', (e) => { });
           
           child_frame = w.document.body.appendChild(document.createElement('iframe'));
           child_frame.contentWindow.onunload = () => {
index 90f18ef..d89ecc4 100644 (file)
@@ -1,3 +1,51 @@
+2019-09-27  Chris Dumez  <cdumez@apple.com>
+
+        Pages using WebGLRenderingContext fail to enter the back/forward cache
+        https://bugs.webkit.org/show_bug.cgi?id=202318
+        <rdar://problem/55783612>
+
+        Reviewed by Tim Horton.
+
+        Allow pages with a WebGLRenderingContext to enter the back/forward cache by updating the
+        implementation to use SuspendableTimers to fire JS events. This guarantees that no events
+        will be fired (and thus no JS will run) while in the page cache.
+
+        This was preventing some of the pages on weather.com and facebook.com from entering the
+        back/forward cache.
+
+        Test: fast/canvas/webgl/canvas-webgl-page-cache.html
+
+        * dom/DocumentEventQueue.cpp:
+        * html/canvas/WebGLRenderingContextBase.cpp:
+        (WebCore::WebGLRenderingContextBase::WebGLRenderingContextBase):
+        (WebCore::WebGLRenderingContextBase::canSuspendForDocumentSuspension const):
+        (WebCore::WebGLRenderingContextBase::suspend):
+        (WebCore::WebGLRenderingContextBase::resume):
+        (WebCore::WebGLRenderingContextBase::dispatchContextLostEvent):
+        (WebCore::WebGLRenderingContextBase::maybeRestoreContext):
+        (WebCore::WebGLRenderingContextBase::dispatchContextChangedNotification):
+        (WebCore::WebGLRenderingContextBase::dispatchContextChangedEvent):
+        * html/canvas/WebGLRenderingContextBase.h:
+        * page/DOMTimer.cpp:
+        (WebCore::DOMTimer::DOMTimer):
+        * page/DOMTimer.h:
+        * page/SuspendableTimer.cpp:
+        (WebCore::SuspendableTimerBase::SuspendableTimerBase):
+        (WebCore::SuspendableTimerBase::hasPendingActivity const):
+        (WebCore::SuspendableTimerBase::stop):
+        (WebCore::SuspendableTimerBase::suspend):
+        (WebCore::SuspendableTimerBase::resume):
+        (WebCore::SuspendableTimerBase::canSuspendForDocumentSuspension const):
+        (WebCore::SuspendableTimerBase::didStop):
+        (WebCore::SuspendableTimerBase::cancel):
+        (WebCore::SuspendableTimerBase::startRepeating):
+        (WebCore::SuspendableTimerBase::startOneShot):
+        (WebCore::SuspendableTimerBase::repeatInterval const):
+        (WebCore::SuspendableTimerBase::augmentFireInterval):
+        (WebCore::SuspendableTimerBase::augmentRepeatInterval):
+        (WebCore::SuspendableTimer::activeDOMObjectName const):
+        * page/SuspendableTimer.h:
+
 2019-09-27  Adrian Perez de Castro  <aperez@igalia.com>
 
         [GTK][WPE] Fixes for non-unified builds after r249714
index 03703b6..ceaebcd 100644 (file)
 
 namespace WebCore {
     
-class DocumentEventQueue::Timer final : public SuspendableTimer {
+class DocumentEventQueue::Timer final : public SuspendableTimerBase {
 public:
     Timer(DocumentEventQueue& eventQueue)
-        : SuspendableTimer(eventQueue.m_document)
+        : SuspendableTimerBase(&eventQueue.m_document)
         , m_eventQueue(eventQueue)
     {
     }
index 6eea91f..2d150c7 100644 (file)
@@ -632,13 +632,18 @@ std::unique_ptr<WebGLRenderingContextBase> WebGLRenderingContextBase::create(Can
 
 WebGLRenderingContextBase::WebGLRenderingContextBase(CanvasBase& canvas, WebGLContextAttributes attributes)
     : GPUBasedCanvasRenderingContext(canvas)
-    , m_dispatchContextLostEventTimer(*this, &WebGLRenderingContextBase::dispatchContextLostEvent)
-    , m_restoreTimer(*this, &WebGLRenderingContextBase::maybeRestoreContext)
+    , m_dispatchContextLostEventTimer(canvas.scriptExecutionContext(), *this, &WebGLRenderingContextBase::dispatchContextLostEvent)
+    , m_dispatchContextChangedEventTimer(canvas.scriptExecutionContext(), *this, &WebGLRenderingContextBase::dispatchContextChangedEvent)
+    , m_restoreTimer(canvas.scriptExecutionContext(), *this, &WebGLRenderingContextBase::maybeRestoreContext)
     , m_attributes(attributes)
     , m_numGLErrorsToConsoleAllowed(maxGLErrorsAllowedToConsole)
     , m_isPendingPolicyResolution(true)
     , m_checkForContextLossHandlingTimer(*this, &WebGLRenderingContextBase::checkForContextLossHandling)
 {
+    m_dispatchContextLostEventTimer.suspendIfNeeded();
+    m_dispatchContextChangedEventTimer.suspendIfNeeded();
+    m_restoreTimer.suspendIfNeeded();
+
     registerWithWebGLStateTracker();
     m_checkForContextLossHandlingTimer.startOneShot(checkContextLossHandlingDelay);
 }
@@ -646,13 +651,18 @@ WebGLRenderingContextBase::WebGLRenderingContextBase(CanvasBase& canvas, WebGLCo
 WebGLRenderingContextBase::WebGLRenderingContextBase(CanvasBase& canvas, Ref<GraphicsContext3D>&& context, WebGLContextAttributes attributes)
     : GPUBasedCanvasRenderingContext(canvas)
     , m_context(WTFMove(context))
-    , m_dispatchContextLostEventTimer(*this, &WebGLRenderingContextBase::dispatchContextLostEvent)
-    , m_restoreTimer(*this, &WebGLRenderingContextBase::maybeRestoreContext)
+    , m_dispatchContextLostEventTimer(canvas.scriptExecutionContext(), *this, &WebGLRenderingContextBase::dispatchContextLostEvent)
+    , m_dispatchContextChangedEventTimer(canvas.scriptExecutionContext(), *this, &WebGLRenderingContextBase::dispatchContextChangedEvent)
+    , m_restoreTimer(canvas.scriptExecutionContext(), *this, &WebGLRenderingContextBase::maybeRestoreContext)
     , m_generatedImageCache(4)
     , m_attributes(attributes)
     , m_numGLErrorsToConsoleAllowed(maxGLErrorsAllowedToConsole)
     , m_checkForContextLossHandlingTimer(*this, &WebGLRenderingContextBase::checkForContextLossHandling)
 {
+    m_dispatchContextLostEventTimer.suspendIfNeeded();
+    m_dispatchContextChangedEventTimer.suspendIfNeeded();
+    m_restoreTimer.suspendIfNeeded();
+
     m_contextGroup = WebGLContextGroup::create();
     m_contextGroup->addContext(*this);
     
@@ -5197,8 +5207,17 @@ const char* WebGLRenderingContextBase::activeDOMObjectName() const
 
 bool WebGLRenderingContextBase::canSuspendForDocumentSuspension() const
 {
-    // FIXME: We should try and do better here.
-    return false;
+    return true;
+}
+
+void WebGLRenderingContextBase::suspend(ReasonForSuspension)
+{
+    m_isSuspended = true;
+}
+
+void WebGLRenderingContextBase::resume()
+{
+    m_isSuspended = false;
 }
 
 bool WebGLRenderingContextBase::getBooleanParameter(GC3Denum pname)
@@ -6151,6 +6170,7 @@ void WebGLRenderingContextBase::restoreStatesAfterVertexAttrib0Simulation()
 
 void WebGLRenderingContextBase::dispatchContextLostEvent()
 {
+    RELEASE_ASSERT(!m_isSuspended);
     auto* canvas = htmlCanvas();
     if (!canvas)
         return;
@@ -6164,6 +6184,7 @@ void WebGLRenderingContextBase::dispatchContextLostEvent()
 
 void WebGLRenderingContextBase::maybeRestoreContext()
 {
+    RELEASE_ASSERT(!m_isSuspended);
     ASSERT(m_contextLost);
     if (!m_contextLost)
         return;
@@ -6545,6 +6566,13 @@ void WebGLRenderingContextBase::recycleContext()
 
 void WebGLRenderingContextBase::dispatchContextChangedNotification()
 {
+    if (!m_dispatchContextChangedEventTimer.isActive())
+        m_dispatchContextChangedEventTimer.startOneShot(0_s);
+}
+
+void WebGLRenderingContextBase::dispatchContextChangedEvent()
+{
+    RELEASE_ASSERT(!m_isSuspended);
     auto* canvas = htmlCanvas();
     if (!canvas)
         return;
index 79fd4e7..70db6f8 100644 (file)
@@ -32,6 +32,7 @@
 #include "GPUBasedCanvasRenderingContext.h"
 #include "GraphicsContext3D.h"
 #include "ImageBuffer.h"
+#include "SuspendableTimer.h"
 #include "Timer.h"
 #include "WebGLAny.h"
 #include "WebGLBuffer.h"
@@ -391,6 +392,8 @@ protected:
     void stop() override;
     const char* activeDOMObjectName() const override;
     bool canSuspendForDocumentSuspension() const override;
+    void suspend(ReasonForSuspension) override;
+    void resume() override;
 
     void addSharedObject(WebGLSharedObject&);
     void addContextObject(WebGLContextObject&);
@@ -451,9 +454,10 @@ protected:
     // likely that there's no JavaScript on the stack, but that might be dependent
     // on how exactly the platform discovers that the context was lost. For better
     // portability we always defer the dispatch of the event.
-    Timer m_dispatchContextLostEventTimer;
+    SuspendableTimer m_dispatchContextLostEventTimer;
+    SuspendableTimer m_dispatchContextChangedEventTimer;
     bool m_restoreAllowed { false };
-    Timer m_restoreTimer;
+    SuspendableTimer m_restoreTimer;
 
     bool m_needsUpdate;
     bool m_markedCanvasDirty;
@@ -806,10 +810,6 @@ protected:
     bool validateSimulatedVertexAttrib0(GC3Duint numVertex);
     void restoreStatesAfterVertexAttrib0Simulation();
 
-    void dispatchContextLostEvent();
-    // Helper for restoration after context lost.
-    void maybeRestoreContext();
-
     // Wrapper for GraphicsContext3D::synthesizeGLError that sends a message to the JavaScript console.
     enum ConsoleDisplayPreference { DisplayInConsole, DontDisplayInConsole };
     void synthesizeGLError(GC3Denum, const char* functionName, const char* description, ConsoleDisplayPreference = DisplayInConsole);
@@ -842,6 +842,11 @@ protected:
     template <typename T> unsigned getMaxIndex(const RefPtr<JSC::ArrayBuffer> elementArrayBuffer, GC3Dintptr uoffset, GC3Dsizei n);
 
 private:
+    void dispatchContextLostEvent();
+    void dispatchContextChangedEvent();
+    // Helper for restoration after context lost.
+    void maybeRestoreContext();
+
     bool validateArrayBufferType(const char* functionName, GC3Denum type, Optional<JSC::TypedArrayType>);
     void registerWithWebGLStateTracker();
     void checkForContextLossHandling();
@@ -850,6 +855,7 @@ private:
 
     WebGLStateTracker::Token m_trackerToken;
     Timer m_checkForContextLossHandlingTimer;
+    bool m_isSuspended { false };
 };
 
 template <typename T>
index 7f57537..dd1e9db 100644 (file)
@@ -161,7 +161,7 @@ private:
 bool NestedTimersMap::isTrackingNestedTimers = false;
 
 DOMTimer::DOMTimer(ScriptExecutionContext& context, std::unique_ptr<ScheduledAction> action, Seconds interval, bool singleShot)
-    : SuspendableTimer(context)
+    : SuspendableTimerBase(&context)
     , m_nestingLevel(context.timerNestingLevel())
     , m_action(WTFMove(action))
     , m_originalInterval(interval)
index b9755a7..7d4801f 100644 (file)
@@ -40,7 +40,7 @@ class Document;
 class HTMLPlugInElement;
 class ScheduledAction;
 
-class DOMTimer final : public RefCounted<DOMTimer>, public SuspendableTimer {
+class DOMTimer final : public RefCounted<DOMTimer>, public SuspendableTimerBase {
     WTF_MAKE_NONCOPYABLE(DOMTimer);
     WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -72,7 +72,7 @@ private:
     bool isDOMTimersThrottlingEnabled(Document&) const;
     void updateThrottlingStateIfNecessary(const DOMTimerFireState&);
 
-    // SuspendableTimer
+    // SuspendableTimerBase
     void fired() override;
     void didStop() override;
     WEBCORE_EXPORT Optional<MonotonicTime> alignedFireTime(MonotonicTime) const override;
index 76ed197..e7e517b 100644 (file)
 
 namespace WebCore {
 
-SuspendableTimer::SuspendableTimer(ScriptExecutionContext& context)
-    : ActiveDOMObject(&context)
+SuspendableTimerBase::SuspendableTimerBase(ScriptExecutionContext* context)
+    : ActiveDOMObject(context)
 {
 }
 
-SuspendableTimer::~SuspendableTimer() = default;
+SuspendableTimerBase::~SuspendableTimerBase() = default;
 
-bool SuspendableTimer::hasPendingActivity() const
+bool SuspendableTimerBase::hasPendingActivity() const
 {
     return isActive();
 }
 
-void SuspendableTimer::stop()
+void SuspendableTimerBase::stop()
 {
     if (!m_suspended)
         TimerBase::stop();
@@ -56,7 +56,7 @@ void SuspendableTimer::stop()
     didStop();
 }
 
-void SuspendableTimer::suspend(ReasonForSuspension)
+void SuspendableTimerBase::suspend(ReasonForSuspension)
 {
     ASSERT(!m_suspended);
     m_suspended = true;
@@ -69,7 +69,7 @@ void SuspendableTimer::suspend(ReasonForSuspension)
     }
 }
 
-void SuspendableTimer::resume()
+void SuspendableTimerBase::resume()
 {
     ASSERT(m_suspended);
     m_suspended = false;
@@ -78,16 +78,16 @@ void SuspendableTimer::resume()
         start(m_savedNextFireInterval, m_savedRepeatInterval);
 }
 
-bool SuspendableTimer::canSuspendForDocumentSuspension() const
+bool SuspendableTimerBase::canSuspendForDocumentSuspension() const
 {
     return true;
 }
 
-void SuspendableTimer::didStop()
+void SuspendableTimerBase::didStop()
 {
 }
 
-void SuspendableTimer::cancel()
+void SuspendableTimerBase::cancel()
 {
     if (!m_suspended)
         TimerBase::stop();
@@ -95,7 +95,7 @@ void SuspendableTimer::cancel()
         m_suspended = false;
 }
 
-void SuspendableTimer::startRepeating(Seconds repeatInterval)
+void SuspendableTimerBase::startRepeating(Seconds repeatInterval)
 {
     if (!m_suspended)
         TimerBase::startRepeating(repeatInterval);
@@ -106,7 +106,7 @@ void SuspendableTimer::startRepeating(Seconds repeatInterval)
     }
 }
 
-void SuspendableTimer::startOneShot(Seconds interval)
+void SuspendableTimerBase::startOneShot(Seconds interval)
 {
     if (!m_suspended)
         TimerBase::startOneShot(interval);
@@ -117,7 +117,7 @@ void SuspendableTimer::startOneShot(Seconds interval)
     }
 }
 
-Seconds SuspendableTimer::repeatInterval() const
+Seconds SuspendableTimerBase::repeatInterval() const
 {
     if (!m_suspended)
         return TimerBase::repeatInterval();
@@ -126,7 +126,7 @@ Seconds SuspendableTimer::repeatInterval() const
     return 0_s;
 }
 
-void SuspendableTimer::augmentFireInterval(Seconds delta)
+void SuspendableTimerBase::augmentFireInterval(Seconds delta)
 {
     if (!m_suspended)
         TimerBase::augmentFireInterval(delta);
@@ -139,7 +139,7 @@ void SuspendableTimer::augmentFireInterval(Seconds delta)
     }
 }
 
-void SuspendableTimer::augmentRepeatInterval(Seconds delta)
+void SuspendableTimerBase::augmentRepeatInterval(Seconds delta)
 {
     if (!m_suspended)
         TimerBase::augmentRepeatInterval(delta);
@@ -153,4 +153,9 @@ void SuspendableTimer::augmentRepeatInterval(Seconds delta)
     }
 }
 
+const char* SuspendableTimer::activeDOMObjectName() const
+{
+    return "SuspendableTimer";
+}
+
 } // namespace WebCore
index 3c3d49e..f3ef123 100644 (file)
 #include "ActiveDOMObject.h"
 #include "Timer.h"
 
+#include <wtf/Function.h>
 #include <wtf/Seconds.h>
 
 namespace WebCore {
 
-class SuspendableTimer : private TimerBase, public ActiveDOMObject {
+class SuspendableTimerBase : private TimerBase, public ActiveDOMObject {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    explicit SuspendableTimer(ScriptExecutionContext&);
-    virtual ~SuspendableTimer();
+    explicit SuspendableTimerBase(ScriptExecutionContext*);
+    virtual ~SuspendableTimerBase();
 
     // A hook for derived classes to perform cleanup.
     virtual void didStop();
 
-    // Part of TimerBase interface used by SuspendableTimer clients, modified to work when suspended.
+    // Part of TimerBase interface used by SuspendableTimerBase clients, modified to work when suspended.
     bool isActive() const { return TimerBase::isActive() || (m_suspended && m_savedIsActive); }
     bool isSuspended() const { return m_suspended; }
 
@@ -77,4 +78,31 @@ private:
     bool m_savedIsActive { false };
 };
 
+class SuspendableTimer final : public SuspendableTimerBase {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    template <typename TimerFiredClass, typename TimerFiredBaseClass>
+    SuspendableTimer(ScriptExecutionContext* context, TimerFiredClass& object, void (TimerFiredBaseClass::*function)())
+        : SuspendableTimerBase(context)
+        , m_function(std::bind(function, &object))
+    {
+    }
+
+    SuspendableTimer(ScriptExecutionContext* context, WTF::Function<void ()>&& function)
+        : SuspendableTimerBase(context)
+        , m_function(WTFMove(function))
+    {
+    }
+
+private:
+    void fired() final
+    {
+        m_function();
+    }
+
+    const char* activeDOMObjectName() const final;
+
+    WTF::Function<void ()> m_function;
+};
+
 } // namespace WebCore
index 1775764..dce9dc3 100644 (file)
@@ -1,3 +1,16 @@
+2019-09-27  Chris Dumez  <cdumez@apple.com>
+
+        Pages using WebGLRenderingContext fail to enter the back/forward cache
+        https://bugs.webkit.org/show_bug.cgi?id=202318
+        <rdar://problem/55783612>
+
+        Reviewed by Tim Horton.
+
+        * WebView.cpp:
+        (WindowCloseTimer::WindowCloseTimer):
+        (WindowCloseTimer::contextDestroyed):
+        * WebView.h:
+
 2019-09-25  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         Unreviewed build fix for Windows ports.
index 06c7025..eeb49ae 100644 (file)
@@ -1455,7 +1455,7 @@ void WebView::frameRect(RECT* rect)
     ::GetWindowRect(m_viewWindow, rect);
 }
 
-class WindowCloseTimer final : public WebCore::SuspendableTimer {
+class WindowCloseTimer final : public WebCore::SuspendableTimerBase {
 public:
     static WindowCloseTimer* create(WebView*);
 
@@ -1466,7 +1466,7 @@ private:
     void contextDestroyed() override;
     const char* activeDOMObjectName() const override { return "WindowCloseTimer"; }
 
-    // SuspendableTimer API.
+    // SuspendableTimerBase API.
     void fired() override;
 
     WebView* m_webView;
@@ -1491,7 +1491,7 @@ WindowCloseTimer* WindowCloseTimer::create(WebView* webView)
 }
 
 WindowCloseTimer::WindowCloseTimer(ScriptExecutionContext& context, WebView* webView)
-    : SuspendableTimer(context)
+    : SuspendableTimerBase(&context)
     , m_webView(webView)
 {
     ASSERT_ARG(webView, webView);
@@ -1499,7 +1499,7 @@ WindowCloseTimer::WindowCloseTimer(ScriptExecutionContext& context, WebView* web
 
 void WindowCloseTimer::contextDestroyed()
 {
-    SuspendableTimer::contextDestroyed();
+    SuspendableTimerBase::contextDestroyed();
     delete this;
 }
 
index 3581743..23480b6 100644 (file)
@@ -680,7 +680,7 @@ protected:
 
     static bool s_allowSiteSpecificHacks;
 
-    WebCore::SuspendableTimer* m_closeWindowTimer { nullptr };
+    WebCore::SuspendableTimerBase* m_closeWindowTimer { nullptr };
     std::unique_ptr<TRACKMOUSEEVENT> m_mouseOutTracker;
 
     HWND m_topLevelParent { nullptr };