Title: [214721] trunk
Revision
214721
Author
cdu...@apple.com
Date
2017-04-01 17:55:20 -0700 (Sat, 01 Apr 2017)

Log Message

We should pause silent WebAudio rendering in background tabs
https://bugs.webkit.org/show_bug.cgi?id=170299
<rdar://problem/31289132>

Reviewed by Eric Carlson.

Source/WebCore:

We should pause silent WebAudio rendering in background tabs since it uses CPU and is
not observable by the user. Such silent WebAudio rendering seems to be used by
doubleclick ads.

Test: webaudio/silent-audio-interrupted-in-background.html

* Modules/webaudio/AudioContext.cpp:
(WebCore::AudioContext::lazyInitialize):
(WebCore::AudioContext::uninitialize):
Have AudioContext register / unregister itself with the Document to get
visibility change notifications, similarly to what HTMLMediaElement was
already doing.

(WebCore::AudioContext::visibilityStateChanged):
Begin / End session interruption whenever the page visiblity changes.

* Modules/webaudio/AudioContext.h:
* WebCore.xcodeproj/project.pbxproj:

* dom/Document.cpp:
(WebCore::Document::registerForVisibilityStateChangedCallbacks):
(WebCore::Document::unregisterForVisibilityStateChangedCallbacks):
(WebCore::Document::visibilityStateChanged):
* dom/Document.h:
* dom/Element.h:
* dom/VisibilityChangeClient.h: Added.
(WebCore::VisibilityChangeClient::~VisibilityChangeClient):
* html/HTMLMediaElement.h:
Introduce a new VisibilityChangeClient interface and have both AudioContext
and HTMLMediaElement subclass it. Previously, the visibilityStateChanged()
function was on Element but this prevented AudioContext from registering
itself since AudioContext is not an Element.

LayoutTests:

Add layout test coverage.

* webaudio/silent-audio-interrupted-in-background-expected.txt: Added.
* webaudio/silent-audio-interrupted-in-background.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (214720 => 214721)


--- trunk/LayoutTests/ChangeLog	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/LayoutTests/ChangeLog	2017-04-02 00:55:20 UTC (rev 214721)
@@ -1,3 +1,16 @@
+2017-04-01  Chris Dumez  <cdu...@apple.com>
+
+        We should pause silent WebAudio rendering in background tabs
+        https://bugs.webkit.org/show_bug.cgi?id=170299
+        <rdar://problem/31289132>
+
+        Reviewed by Eric Carlson.
+
+        Add layout test coverage.
+
+        * webaudio/silent-audio-interrupted-in-background-expected.txt: Added.
+        * webaudio/silent-audio-interrupted-in-background.html: Added.
+
 2017-04-01  Alexey Proskuryakov  <a...@apple.com>
 
         Rolling back http://trac.webkit.org/r214663 - memory corruption

Modified: trunk/LayoutTests/platform/ios/TestExpectations (214720 => 214721)


--- trunk/LayoutTests/platform/ios/TestExpectations	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/LayoutTests/platform/ios/TestExpectations	2017-04-02 00:55:20 UTC (rev 214721)
@@ -311,6 +311,7 @@
 webkit.org/b/165799 svg/animations/animations-paused-page-non-visible.html [ Skip ]
 webkit.org/b/165799 svg/animations/animations-paused-in-background-page-iframe.html [ Skip ]
 webkit.org/b/165799 svg/animations/animations-paused-in-background-page.html [ Skip ]
+webkit.org/b/165799 webaudio/silent-audio-interrupted-in-background.html [ Skip ]
 
 # AutoFill button is not supported
 fast/forms/auto-fill-button/mouse-down-input-mouse-release-auto-fill-button.html

Added: trunk/LayoutTests/webaudio/silent-audio-interrupted-in-background-expected.txt (0 => 214721)


--- trunk/LayoutTests/webaudio/silent-audio-interrupted-in-background-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/silent-audio-interrupted-in-background-expected.txt	2017-04-02 00:55:20 UTC (rev 214721)
@@ -0,0 +1,14 @@
+Tests that silent WebAudio rendering gets interrupted in hidden pages.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS context.state became 'running'
+* Setting page visibility to hidden
+PASS context.state became 'interrupted'
+* Setting page visibility to visible
+PASS context.state became 'running'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/webaudio/silent-audio-interrupted-in-background.html (0 => 214721)


--- trunk/LayoutTests/webaudio/silent-audio-interrupted-in-background.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/silent-audio-interrupted-in-background.html	2017-04-02 00:55:20 UTC (rev 214721)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests that silent WebAudio rendering gets interrupted in hidden pages.");
+jsTestIsAsync = true;
+
+document._onvisibilitychange_ = function() {
+    if (document.hidden) {
+        shouldBecomeEqual("context.state", "'interrupted'", showPage);
+    } else {
+        shouldBecomeEqual("context.state", "'running'", finishJSTest);
+    }
+}
+
+function showPage()
+{
+    debug("* Setting page visibility to visible");
+    if (window.testRunner)
+        testRunner.setPageVisibility("visible");
+}
+
+function hidePage()
+{
+    debug("* Setting page visibility to hidden");
+    if (window.testRunner)
+        testRunner.setPageVisibility("hidden");
+}
+
+_onload_ = function() {
+    context = new (window.AudioContext || window.webkitAudioContext)();
+
+    audioElement = document.createElement("audio");
+    audioElement.loop = true;
+    audioElement.muted = true;
+    audioElement.src = ""
+    source = context.createMediaElementSource(audioElement);
+
+    var gainNode = context.createGain();
+    source.connect(gainNode);
+    gainNode.connect(context.destination);
+    gainNode.gain.value = 0; // Mute.
+
+    shouldBecomeEqual("context.state", "'running'", hidePage);
+}
+</script>
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (214720 => 214721)


--- trunk/Source/WebCore/ChangeLog	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/ChangeLog	2017-04-02 00:55:20 UTC (rev 214721)
@@ -1,3 +1,44 @@
+2017-04-01  Chris Dumez  <cdu...@apple.com>
+
+        We should pause silent WebAudio rendering in background tabs
+        https://bugs.webkit.org/show_bug.cgi?id=170299
+        <rdar://problem/31289132>
+
+        Reviewed by Eric Carlson.
+
+        We should pause silent WebAudio rendering in background tabs since it uses CPU and is
+        not observable by the user. Such silent WebAudio rendering seems to be used by
+        doubleclick ads.
+
+        Test: webaudio/silent-audio-interrupted-in-background.html
+
+        * Modules/webaudio/AudioContext.cpp:
+        (WebCore::AudioContext::lazyInitialize):
+        (WebCore::AudioContext::uninitialize):
+        Have AudioContext register / unregister itself with the Document to get
+        visibility change notifications, similarly to what HTMLMediaElement was
+        already doing.
+
+        (WebCore::AudioContext::visibilityStateChanged):
+        Begin / End session interruption whenever the page visiblity changes.
+
+        * Modules/webaudio/AudioContext.h:
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * dom/Document.cpp:
+        (WebCore::Document::registerForVisibilityStateChangedCallbacks):
+        (WebCore::Document::unregisterForVisibilityStateChangedCallbacks):
+        (WebCore::Document::visibilityStateChanged):
+        * dom/Document.h:
+        * dom/Element.h:
+        * dom/VisibilityChangeClient.h: Added.
+        (WebCore::VisibilityChangeClient::~VisibilityChangeClient):
+        * html/HTMLMediaElement.h:
+        Introduce a new VisibilityChangeClient interface and have both AudioContext
+        and HTMLMediaElement subclass it. Previously, the visibilityStateChanged()
+        function was on Element but this prevented AudioContext from registering
+        itself since AudioContext is not an Element.
+
 2017-04-01  Dan Bernstein  <m...@apple.com>
 
         [Cocoa] A couple of UI strings use three periods instead of an ellipsis

Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp (214720 => 214721)


--- trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp	2017-04-02 00:55:20 UTC (rev 214721)
@@ -54,6 +54,7 @@
 #include "HRTFDatabaseLoader.h"
 #include "HRTFPanner.h"
 #include "JSDOMPromise.h"
+#include "Logging.h"
 #include "NetworkingContext.h"
 #include "OfflineAudioCompletionEvent.h"
 #include "OfflineAudioDestinationNode.h"
@@ -104,6 +105,8 @@
 const unsigned MaxPeriodicWaveLength = 4096;
 
 namespace WebCore {
+
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(document()->page() && document()->page()->isAlwaysOnLoggingAllowed(), Media, "%p - AudioContext::" fmt, this, ##__VA_ARGS__)
     
 bool AudioContext::isSampleRateRangeGood(float sampleRate)
 {
@@ -215,6 +218,7 @@
 
         if (!isOfflineContext()) {
             document()->addAudioProducer(this);
+            document()->registerForVisibilityStateChangedCallbacks(this);
 
             // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
             // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
@@ -259,6 +263,7 @@
 
     if (!isOfflineContext()) {
         document()->removeAudioProducer(this);
+        document()->unregisterForVisibilityStateChangedCallbacks(this);
 
         ASSERT(s_hardwareContextCount);
         --s_hardwareContextCount;
@@ -363,6 +368,25 @@
     return emptyString();
 }
 
+void AudioContext::visibilityStateChanged()
+{
+    // Do not suspend if audio is audible.
+    if (mediaState() == MediaProducer::IsPlayingAudio)
+        return;
+
+    if (document()->hidden()) {
+        if (state() == State::Running) {
+            RELEASE_LOG_IF_ALLOWED("visibilityStateChanged() Suspending playback after going to the background");
+            m_mediaSession->beginInterruption(PlatformMediaSession::EnteringBackground);
+        }
+    } else {
+        if (state() == State::Interrupted) {
+            RELEASE_LOG_IF_ALLOWED("visibilityStateChanged() Resuming playback after entering foreground");
+            m_mediaSession->endInterruption(PlatformMediaSession::MayResumePlaying);
+        }
+    }
+}
+
 ExceptionOr<Ref<AudioBuffer>> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
 {
     auto audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);

Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (214720 => 214721)


--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h	2017-04-02 00:55:20 UTC (rev 214721)
@@ -35,6 +35,7 @@
 #include "MediaCanStartListener.h"
 #include "MediaProducer.h"
 #include "PlatformMediaSession.h"
+#include "VisibilityChangeClient.h"
 #include <atomic>
 #include <wtf/HashSet.h>
 #include <wtf/MainThread.h>
@@ -75,7 +76,7 @@
 // AudioContext is the cornerstone of the web audio API and all AudioNodes are created from it.
 // For thread safety between the audio thread and the main thread, it has a rendering graph locking mechanism. 
 
-class AudioContext : public ActiveDOMObject, public ThreadSafeRefCounted<AudioContext>, public EventTargetWithInlineData, public MediaCanStartListener, public MediaProducer, private PlatformMediaSessionClient {
+class AudioContext : public ActiveDOMObject, public ThreadSafeRefCounted<AudioContext>, public EventTargetWithInlineData, public MediaCanStartListener, public MediaProducer, private PlatformMediaSessionClient, private VisibilityChangeClient {
 public:
     // Create an AudioContext for rendering to the audio hardware.
     static RefPtr<AudioContext> create(Document&);
@@ -319,6 +320,8 @@
     String sourceApplicationIdentifier() const override;
     bool canProduceAudio() const final { return true; }
 
+    void visibilityStateChanged() final;
+
     // EventTarget
     void refEventTarget() override { ref(); }
     void derefEventTarget() override { deref(); }

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (214720 => 214721)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-04-02 00:55:20 UTC (rev 214721)
@@ -3282,6 +3282,7 @@
 		83120C711C56F3FB001CB112 /* HTMLDataElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 834B86A71C56E83A00F3F0E3 /* HTMLDataElement.h */; };
 		832B843419D8E55100B26055 /* SVGAnimateElementBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 832B843319D8E55100B26055 /* SVGAnimateElementBase.h */; };
 		832B843619D8E57400B26055 /* SVGAnimateElementBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832B843519D8E57400B26055 /* SVGAnimateElementBase.cpp */; };
+		83407FC11E8D9C1700E048D3 /* VisibilityChangeClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 83407FC01E8D9C1200E048D3 /* VisibilityChangeClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		834476EE1DA5BC57002B6ED2 /* JSScrollToOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E9B3001DA5A51E00FFE8F6 /* JSScrollToOptions.cpp */; };
 		834476EF1DA5BC5E002B6ED2 /* JSScrollToOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E9B3011DA5A51E00FFE8F6 /* JSScrollToOptions.h */; };
 		8348BFAB1B85729800912F36 /* ClassCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8348BFA91B85729500912F36 /* ClassCollection.cpp */; };
@@ -11261,6 +11262,7 @@
 		8329DCC21C7A6AE300730B33 /* HTMLHyperlinkElementUtils.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLHyperlinkElementUtils.idl; sourceTree = "<group>"; };
 		832B843319D8E55100B26055 /* SVGAnimateElementBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGAnimateElementBase.h; sourceTree = "<group>"; };
 		832B843519D8E57400B26055 /* SVGAnimateElementBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGAnimateElementBase.cpp; sourceTree = "<group>"; };
+		83407FC01E8D9C1200E048D3 /* VisibilityChangeClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VisibilityChangeClient.h; sourceTree = "<group>"; };
 		8348BFA91B85729500912F36 /* ClassCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassCollection.cpp; sourceTree = "<group>"; };
 		8348BFAA1B85729500912F36 /* ClassCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClassCollection.h; sourceTree = "<group>"; };
 		834B86A61C56E83A00F3F0E3 /* HTMLDataElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLDataElement.idl; sourceTree = "<group>"; };
@@ -25687,6 +25689,7 @@
 				BCDF317A11F8D683003C5BF8 /* UserTypingGestureIndicator.h */,
 				CEF418CC1179678C009D112C /* ViewportArguments.cpp */,
 				CEF418CD1179678C009D112C /* ViewportArguments.h */,
+				83407FC01E8D9C1200E048D3 /* VisibilityChangeClient.h */,
 				419BC2DC1685329900D64D6D /* VisitedLinkState.cpp */,
 				419BC2DD1685329900D64D6D /* VisitedLinkState.h */,
 				31C0FF1B0E4CEB6E007D6FE5 /* WebKitAnimationEvent.cpp */,
@@ -29641,6 +29644,7 @@
 				26F9A83918A046AC00AEB88A /* ViewportConfiguration.h in Headers */,
 				3FFFF9AE159D9B060020BBD5 /* ViewportStyleResolver.h in Headers */,
 				93309E20099E64920056E581 /* VisiblePosition.h in Headers */,
+				83407FC11E8D9C1700E048D3 /* VisibilityChangeClient.h in Headers */,
 				A883DF280F3D045D00F19BF6 /* VisibleSelection.h in Headers */,
 				93309E1E099E64920056E581 /* VisibleUnits.h in Headers */,
 				419BC2DF1685329900D64D6D /* VisitedLinkState.h in Headers */,

Modified: trunk/Source/WebCore/dom/Document.cpp (214720 => 214721)


--- trunk/Source/WebCore/dom/Document.cpp	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/dom/Document.cpp	2017-04-02 00:55:20 UTC (rev 214721)
@@ -1530,21 +1530,21 @@
     updateTitleFromTitleElement();
 }
 
-void Document::registerForVisibilityStateChangedCallbacks(Element* element)
+void Document::registerForVisibilityStateChangedCallbacks(VisibilityChangeClient* client)
 {
-    m_visibilityStateCallbackElements.add(element);
+    m_visibilityStateCallbackClients.add(client);
 }
 
-void Document::unregisterForVisibilityStateChangedCallbacks(Element* element)
+void Document::unregisterForVisibilityStateChangedCallbacks(VisibilityChangeClient* client)
 {
-    m_visibilityStateCallbackElements.remove(element);
+    m_visibilityStateCallbackClients.remove(client);
 }
 
 void Document::visibilityStateChanged()
 {
     dispatchEvent(Event::create(eventNames().visibilitychangeEvent, false, false));
-    for (auto* element : m_visibilityStateCallbackElements)
-        element->visibilityStateChanged();
+    for (auto* client : m_visibilityStateCallbackClients)
+        client->visibilityStateChanged();
 }
 
 auto Document::visibilityState() const -> VisibilityState

Modified: trunk/Source/WebCore/dom/Document.h (214720 => 214721)


--- trunk/Source/WebCore/dom/Document.h	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/dom/Document.h	2017-04-02 00:55:20 UTC (rev 214721)
@@ -163,6 +163,7 @@
 class Text;
 class TextResourceDecoder;
 class TreeWalker;
+class VisibilityChangeClient;
 class VisitedLinkState;
 class XPathEvaluator;
 class XPathExpression;
@@ -1006,8 +1007,8 @@
     void userInterfaceLayoutDirectionChanged();
 #endif
 
-    void registerForVisibilityStateChangedCallbacks(Element*);
-    void unregisterForVisibilityStateChangedCallbacks(Element*);
+    void registerForVisibilityStateChangedCallbacks(VisibilityChangeClient*);
+    void unregisterForVisibilityStateChangedCallbacks(VisibilityChangeClient*);
 
 #if ENABLE(VIDEO)
     void registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement&);
@@ -1520,7 +1521,7 @@
     HashSet<HTMLMediaElement*> m_userInterfaceLayoutDirectionChangedElements;
 #endif
 
-    HashSet<Element*> m_visibilityStateCallbackElements;
+    HashSet<VisibilityChangeClient*> m_visibilityStateCallbackClients;
 #if ENABLE(VIDEO)
     HashSet<HTMLMediaElement*> m_allowsMediaDocumentInlinePlaybackElements;
 #endif

Modified: trunk/Source/WebCore/dom/Element.h (214720 => 214721)


--- trunk/Source/WebCore/dom/Element.h	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/dom/Element.h	2017-04-02 00:55:20 UTC (rev 214721)
@@ -400,9 +400,6 @@
     virtual void didBecomeFullscreenElement() { }
     virtual void willStopBeingFullscreenElement() { }
 
-    // Use Document::registerForVisibilityStateChangedCallbacks() to subscribe to this.
-    virtual void visibilityStateChanged() { }
-
 #if ENABLE(VIDEO_TRACK)
     virtual void captionPreferencesChanged() { }
 #endif

Added: trunk/Source/WebCore/dom/VisibilityChangeClient.h (0 => 214721)


--- trunk/Source/WebCore/dom/VisibilityChangeClient.h	                        (rev 0)
+++ trunk/Source/WebCore/dom/VisibilityChangeClient.h	2017-04-02 00:55:20 UTC (rev 214721)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+class VisibilityChangeClient {
+public:
+    virtual ~VisibilityChangeClient() { }
+
+    virtual void visibilityStateChanged() = 0;
+};
+
+}

Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (214720 => 214721)


--- trunk/Source/WebCore/html/HTMLMediaElement.h	2017-04-02 00:17:55 UTC (rev 214720)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h	2017-04-02 00:55:20 UTC (rev 214721)
@@ -37,6 +37,7 @@
 #include "MediaElementSession.h"
 #include "MediaProducer.h"
 #include "UserInterfaceLayoutDirection.h"
+#include "VisibilityChangeClient.h"
 
 #if ENABLE(VIDEO_TRACK)
 #include "AudioTrack.h"
@@ -111,6 +112,7 @@
     , private MediaCanStartListener
     , private MediaPlayerClient
     , private MediaProducer
+    , private VisibilityChangeClient
 #if ENABLE(VIDEO_TRACK)
     , private AudioTrackClient
     , private TextTrackClient
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to