Hulu.com gets stuck in a "loading" state when seeking
https://bugs.webkit.org/show_bug.cgi?id=209916
<rdar://problem/55041979>

Reviewed by Eric Carlson.

Hulu.com depends on the "canplay" event firing after a seek to hide their loading indicator.
The HTML spec says that "canplay" should only be fired when moving from the HAVE_CURRENT_DATA
to HAVE_FUTURE_DATA or greater, but when seeking within the buffered range, the readyState
never drops below HAVE_FUTURE_DATA. To work around this behavior, add a quirk for Hulu.com
that always fires "canplay" after a seek completes, so long as the readyState is HAVE_FUTURE_DATA
or higher.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::seekTask):
(WebCore::HTMLMediaElement::finishSeek):
* page/Quirks.cpp:
(WebCore::Quirks::needsCanPlayAfterSeekedQuirk const):
* page/Quirks.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@259404 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index fee93dd..d8d0009 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,25 @@
+2020-04-02  Jer Noble  <jer.noble@apple.com>
+
+        Hulu.com gets stuck in a "loading" state when seeking
+        https://bugs.webkit.org/show_bug.cgi?id=209916
+        <rdar://problem/55041979>
+
+        Reviewed by Eric Carlson.
+
+        Hulu.com depends on the "canplay" event firing after a seek to hide their loading indicator.
+        The HTML spec says that "canplay" should only be fired when moving from the HAVE_CURRENT_DATA
+        to HAVE_FUTURE_DATA or greater, but when seeking within the buffered range, the readyState
+        never drops below HAVE_FUTURE_DATA. To work around this behavior, add a quirk for Hulu.com
+        that always fires "canplay" after a seek completes, so long as the readyState is HAVE_FUTURE_DATA
+        or higher.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::seekTask):
+        (WebCore::HTMLMediaElement::finishSeek):
+        * page/Quirks.cpp:
+        (WebCore::Quirks::needsCanPlayAfterSeekedQuirk const):
+        * page/Quirks.h:
+
 2020-04-02  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Avoid null deref after inserting a text field with a list attribute
diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp
index 3e8d038..d75c480 100644
--- a/Source/WebCore/html/HTMLMediaElement.cpp
+++ b/Source/WebCore/html/HTMLMediaElement.cpp
@@ -3053,6 +3053,9 @@
             scheduleEvent(eventNames().seekingEvent);
             scheduleTimeupdateEvent(false);
             scheduleEvent(eventNames().seekedEvent);
+
+            if (document().quirks().needsCanPlayAfterSeekedQuirk() && m_readyState > HAVE_CURRENT_DATA)
+                scheduleEvent(eventNames().canplayEvent);
         }
         clearSeeking();
         return;
@@ -3101,6 +3104,9 @@
     // 17 - Queue a task to fire a simple event named seeked at the element.
     scheduleEvent(eventNames().seekedEvent);
 
+    if (document().quirks().needsCanPlayAfterSeekedQuirk() && m_readyState > HAVE_CURRENT_DATA)
+        scheduleEvent(eventNames().canplayEvent);
+
     if (m_mediaSession)
         m_mediaSession->clientCharacteristicsChanged();
 
diff --git a/Source/WebCore/page/Quirks.cpp b/Source/WebCore/page/Quirks.cpp
index 5f06b83..458583c 100644
--- a/Source/WebCore/page/Quirks.cpp
+++ b/Source/WebCore/page/Quirks.cpp
@@ -721,4 +721,19 @@
 #endif
 }
 
+bool Quirks::needsCanPlayAfterSeekedQuirk() const
+{
+    if (!needsQuirks())
+        return false;
+
+    if (m_needsCanPlayAfterSeekedQuirk)
+        return *m_needsCanPlayAfterSeekedQuirk;
+
+    auto domain = m_document->securityOrigin().domain().convertToASCIILowercase();
+
+    m_needsCanPlayAfterSeekedQuirk = domain == "hulu.com" || domain.endsWith(".hulu.com");
+
+    return m_needsCanPlayAfterSeekedQuirk.value();
+}
+
 }
diff --git a/Source/WebCore/page/Quirks.h b/Source/WebCore/page/Quirks.h
index d6f8574..727f5d2 100644
--- a/Source/WebCore/page/Quirks.h
+++ b/Source/WebCore/page/Quirks.h
@@ -93,6 +93,8 @@
 
     bool shouldDisableElementFullscreenQuirk() const;
 
+    bool needsCanPlayAfterSeekedQuirk() const;
+
 private:
     bool needsQuirks() const;
 
@@ -114,6 +116,7 @@
 #if ENABLE(TOUCH_EVENTS)
     mutable Optional<bool> m_shouldDispatchSimulatedMouseEventsQuirk;
 #endif
+    mutable Optional<bool> m_needsCanPlayAfterSeekedQuirk;
 };
 
 }