[WebKitLegacy] Media playback pauses on scroll
[WebKit-https.git] / Source / WebCore / html / MediaElementSession.cpp
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(VIDEO)
29
30 #include "MediaElementSession.h"
31
32 #include "Document.h"
33 #include "DocumentLoader.h"
34 #include "Frame.h"
35 #include "FrameView.h"
36 #include "HTMLAudioElement.h"
37 #include "HTMLMediaElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLVideoElement.h"
40 #include "HitTestResult.h"
41 #include "Logging.h"
42 #include "Page.h"
43 #include "PlatformMediaSessionManager.h"
44 #include "RenderMedia.h"
45 #include "RenderView.h"
46 #include "ScriptController.h"
47 #include "Settings.h"
48 #include "SourceBuffer.h"
49 #include <wtf/text/StringBuilder.h>
50
51 #if PLATFORM(IOS_FAMILY)
52 #include "AudioSession.h"
53 #include "RuntimeApplicationChecks.h"
54 #include <wtf/spi/darwin/dyldSPI.h>
55 #endif
56
57 namespace WebCore {
58
59 static const Seconds clientDataBufferingTimerThrottleDelay { 100_ms };
60 static const Seconds elementMainContentCheckInterval { 250_ms };
61
62 static bool isElementRectMostlyInMainFrame(const HTMLMediaElement&);
63 static bool isElementLargeEnoughForMainContent(const HTMLMediaElement&, MediaSessionMainContentPurpose);
64 static bool isElementMainContentForPurposesOfAutoplay(const HTMLMediaElement&, bool shouldHitTestMainFrame);
65
66 #if !RELEASE_LOG_DISABLED
67 static String restrictionNames(MediaElementSession::BehaviorRestrictions restriction)
68 {
69     StringBuilder restrictionBuilder;
70 #define CASE(restrictionType) \
71     if (restriction & MediaElementSession::restrictionType) { \
72         if (!restrictionBuilder.isEmpty()) \
73             restrictionBuilder.appendLiteral(", "); \
74         restrictionBuilder.append(#restrictionType); \
75     } \
76
77     CASE(NoRestrictions)
78     CASE(RequireUserGestureForLoad)
79     CASE(RequireUserGestureForVideoRateChange)
80     CASE(RequireUserGestureForAudioRateChange)
81     CASE(RequireUserGestureForFullscreen)
82     CASE(RequirePageConsentToLoadMedia)
83     CASE(RequirePageConsentToResumeMedia)
84     CASE(RequireUserGestureToShowPlaybackTargetPicker)
85     CASE(WirelessVideoPlaybackDisabled)
86     CASE(RequireUserGestureToAutoplayToExternalDevice)
87     CASE(MetadataPreloadingNotPermitted)
88     CASE(AutoPreloadingNotPermitted)
89     CASE(InvisibleAutoplayNotPermitted)
90     CASE(OverrideUserGestureRequirementForMainContent)
91     CASE(RequireUserGestureToControlControlsManager)
92     CASE(RequirePlaybackToControlControlsManager)
93     CASE(RequireUserGestureForVideoDueToLowPowerMode)
94
95     return restrictionBuilder.toString();
96 }
97 #endif
98
99 static bool pageExplicitlyAllowsElementToAutoplayInline(const HTMLMediaElement& element)
100 {
101     Document& document = element.document();
102     Page* page = document.page();
103     return document.isMediaDocument() && !document.ownerElement() && page && page->allowsMediaDocumentInlinePlayback();
104 }
105
106 MediaElementSession::MediaElementSession(HTMLMediaElement& element)
107     : PlatformMediaSession(element)
108     , m_element(element)
109     , m_restrictions(NoRestrictions)
110 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
111     , m_targetAvailabilityChangedTimer(*this, &MediaElementSession::targetAvailabilityChangedTimerFired)
112 #endif
113     , m_mainContentCheckTimer(*this, &MediaElementSession::mainContentCheckTimerFired)
114     , m_clientDataBufferingTimer(*this, &MediaElementSession::clientDataBufferingTimerFired)
115 #if !RELEASE_LOG_DISABLED
116     , m_logIdentifier(element.logIdentifier())
117 #endif
118 {
119 }
120
121 void MediaElementSession::registerWithDocument(Document& document)
122 {
123 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
124     document.addPlaybackTargetPickerClient(*this);
125 #else
126     UNUSED_PARAM(document);
127 #endif
128 }
129
130 void MediaElementSession::unregisterWithDocument(Document& document)
131 {
132 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
133     document.removePlaybackTargetPickerClient(*this);
134 #else
135     UNUSED_PARAM(document);
136 #endif
137 }
138
139 void MediaElementSession::clientWillBeginAutoplaying()
140 {
141     PlatformMediaSession::clientWillBeginAutoplaying();
142     m_elementIsHiddenBecauseItWasRemovedFromDOM = false;
143     updateClientDataBuffering();
144 }
145
146 bool MediaElementSession::clientWillBeginPlayback()
147 {
148     if (!PlatformMediaSession::clientWillBeginPlayback())
149         return false;
150
151     m_elementIsHiddenBecauseItWasRemovedFromDOM = false;
152     updateClientDataBuffering();
153     return true;
154 }
155
156 bool MediaElementSession::clientWillPausePlayback()
157 {
158     if (!PlatformMediaSession::clientWillPausePlayback())
159         return false;
160
161     updateClientDataBuffering();
162     return true;
163 }
164
165 void MediaElementSession::visibilityChanged()
166 {
167     scheduleClientDataBufferingCheck();
168
169     if (m_element.elementIsHidden() && !m_element.isFullscreen())
170         m_elementIsHiddenUntilVisibleInViewport = true;
171     else if (m_element.isVisibleInViewport())
172         m_elementIsHiddenUntilVisibleInViewport = false;
173 }
174
175 void MediaElementSession::isVisibleInViewportChanged()
176 {
177     scheduleClientDataBufferingCheck();
178
179     if (m_element.isFullscreen() || m_element.isVisibleInViewport())
180         m_elementIsHiddenUntilVisibleInViewport = false;
181 }
182
183 void MediaElementSession::inActiveDocumentChanged()
184 {
185     m_elementIsHiddenBecauseItWasRemovedFromDOM = !m_element.inActiveDocument();
186     scheduleClientDataBufferingCheck();
187 }
188
189 void MediaElementSession::scheduleClientDataBufferingCheck()
190 {
191     if (!m_clientDataBufferingTimer.isActive())
192         m_clientDataBufferingTimer.startOneShot(clientDataBufferingTimerThrottleDelay);
193 }
194
195 void MediaElementSession::clientDataBufferingTimerFired()
196 {
197     INFO_LOG(LOGIDENTIFIER, "visible = ", m_element.elementIsHidden());
198
199     updateClientDataBuffering();
200
201 #if PLATFORM(IOS_FAMILY)
202     PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
203 #endif
204
205     if (state() != Playing || !m_element.elementIsHidden())
206         return;
207
208     PlatformMediaSessionManager::SessionRestrictions restrictions = PlatformMediaSessionManager::sharedManager().restrictions(mediaType());
209     if ((restrictions & PlatformMediaSessionManager::BackgroundTabPlaybackRestricted) == PlatformMediaSessionManager::BackgroundTabPlaybackRestricted)
210         pauseSession();
211 }
212
213 void MediaElementSession::updateClientDataBuffering()
214 {
215     if (m_clientDataBufferingTimer.isActive())
216         m_clientDataBufferingTimer.stop();
217
218     m_element.setShouldBufferData(dataBufferingPermitted());
219 }
220
221 void MediaElementSession::addBehaviorRestriction(BehaviorRestrictions restrictions)
222 {
223     if (restrictions & ~m_restrictions)
224         INFO_LOG(LOGIDENTIFIER, "adding ", restrictionNames(restrictions & ~m_restrictions));
225
226     m_restrictions |= restrictions;
227
228     if (restrictions & OverrideUserGestureRequirementForMainContent)
229         m_mainContentCheckTimer.startRepeating(elementMainContentCheckInterval);
230 }
231
232 void MediaElementSession::removeBehaviorRestriction(BehaviorRestrictions restriction)
233 {
234     if (restriction & RequireUserGestureToControlControlsManager) {
235         m_mostRecentUserInteractionTime = MonotonicTime::now();
236         if (auto page = m_element.document().page())
237             page->setAllowsPlaybackControlsForAutoplayingAudio(true);
238     }
239
240     if (!(m_restrictions & restriction))
241         return;
242
243     INFO_LOG(LOGIDENTIFIER, "removing ", restrictionNames(m_restrictions & restriction));
244     m_restrictions &= ~restriction;
245 }
246
247 #if PLATFORM(MAC)
248 static bool needsArbitraryUserGestureAutoplayQuirk(const Document& document)
249 {
250     if (!document.settings().needsSiteSpecificQuirks())
251         return false;
252
253     auto loader = makeRefPtr(document.loader());
254     return loader && loader->allowedAutoplayQuirks().contains(AutoplayQuirk::ArbitraryUserGestures);
255 }
256 #endif // PLATFORM(MAC)
257
258 SuccessOr<MediaPlaybackDenialReason> MediaElementSession::playbackPermitted() const
259 {
260     if (m_element.isSuspended()) {
261         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because element is suspended");
262         return MediaPlaybackDenialReason::InvalidState;
263     }
264
265     auto& document = m_element.document();
266     auto* page = document.page();
267     if (!page || page->mediaPlaybackIsSuspended())
268         return MediaPlaybackDenialReason::PageConsentRequired;
269
270     if (document.isMediaDocument() && !document.ownerElement())
271         return { };
272
273     if (pageExplicitlyAllowsElementToAutoplayInline(m_element))
274         return { };
275
276     if (requiresFullscreenForVideoPlayback() && !fullscreenPermitted()) {
277         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because of fullscreen restriction");
278         return MediaPlaybackDenialReason::FullscreenRequired;
279     }
280
281     if (m_restrictions & OverrideUserGestureRequirementForMainContent && updateIsMainContent())
282         return { };
283
284 #if ENABLE(MEDIA_STREAM)
285     if (m_element.hasMediaStreamSrcObject()) {
286         if (document.isCapturing())
287             return { };
288         if (document.mediaState() & MediaProducer::IsPlayingAudio)
289             return { };
290     }
291 #endif
292
293 #if PLATFORM(MAC)
294     // FIXME <https://webkit.org/b/175856>: Make this dependent on a runtime flag for desktop autoplay restrictions.
295     const auto& topDocument = document.topDocument();
296     if (topDocument.mediaState() & MediaProducer::HasUserInteractedWithMediaElement && topDocument.settings().needsSiteSpecificQuirks())
297         return { };
298
299     if (document.hasHadUserInteraction() && needsArbitraryUserGestureAutoplayQuirk(document))
300         return { };
301 #endif
302
303     if (m_restrictions & RequireUserGestureForVideoRateChange && m_element.isVideo() && !document.processingUserGestureForMedia()) {
304         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because a user gesture is required for video rate change restriction");
305         return MediaPlaybackDenialReason::UserGestureRequired;
306     }
307
308     if (m_restrictions & RequireUserGestureForAudioRateChange && (!m_element.isVideo() || m_element.hasAudio()) && !m_element.muted() && m_element.volume() && !document.processingUserGestureForMedia()) {
309         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because a user gesture is required for audio rate change restriction");
310         return MediaPlaybackDenialReason::UserGestureRequired;
311     }
312
313     if (m_restrictions & RequireUserGestureForVideoDueToLowPowerMode && m_element.isVideo() && !document.processingUserGestureForMedia()) {
314         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because of video low power mode restriction");
315         return MediaPlaybackDenialReason::UserGestureRequired;
316     }
317
318     return { };
319 }
320
321 bool MediaElementSession::autoplayPermitted() const
322 {
323     const Document& document = m_element.document();
324     if (document.pageCacheState() != Document::NotInPageCache)
325         return false;
326     if (document.activeDOMObjectsAreSuspended())
327         return false;
328
329     if (!hasBehaviorRestriction(MediaElementSession::InvisibleAutoplayNotPermitted))
330         return true;
331
332     // If the media element is audible, allow autoplay even when not visible as pausing it would be observable by the user.
333     if ((!m_element.isVideo() || m_element.hasAudio()) && !m_element.muted() && m_element.volume())
334         return true;
335
336     auto* renderer = m_element.renderer();
337     if (!renderer) {
338         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because element has no renderer");
339         return false;
340     }
341     if (renderer->style().visibility() != Visibility::Visible) {
342         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because element is not visible");
343         return false;
344     }
345     if (renderer->view().frameView().isOffscreen()) {
346         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because frame is offscreen");
347         return false;
348     }
349     if (renderer->visibleInViewportState() != VisibleInViewportState::Yes) {
350         ALWAYS_LOG(LOGIDENTIFIER, "Returning FALSE because element is not visible in the viewport");
351         return false;
352     }
353     return true;
354 }
355
356 bool MediaElementSession::dataLoadingPermitted() const
357 {
358     if (m_restrictions & OverrideUserGestureRequirementForMainContent && updateIsMainContent())
359         return true;
360
361     if (m_restrictions & RequireUserGestureForLoad && !m_element.document().processingUserGestureForMedia()) {
362         INFO_LOG(LOGIDENTIFIER, "returning FALSE");
363         return false;
364     }
365
366     return true;
367 }
368
369 bool MediaElementSession::dataBufferingPermitted() const
370 {
371     if (isSuspended())
372         return false;
373
374     if (state() == PlatformMediaSession::Playing)
375         return true;
376
377     if (shouldOverrideBackgroundLoadingRestriction())
378         return true;
379
380     if (m_elementIsHiddenUntilVisibleInViewport || m_elementIsHiddenBecauseItWasRemovedFromDOM || m_element.elementIsHidden())
381         return false;
382
383     return true;
384 }
385
386 bool MediaElementSession::fullscreenPermitted() const
387 {
388     if (m_restrictions & RequireUserGestureForFullscreen && !m_element.document().processingUserGestureForMedia()) {
389         INFO_LOG(LOGIDENTIFIER, "returning FALSE");
390         return false;
391     }
392
393     return true;
394 }
395
396 bool MediaElementSession::pageAllowsDataLoading() const
397 {
398     Page* page = m_element.document().page();
399     if (m_restrictions & RequirePageConsentToLoadMedia && page && !page->canStartMedia()) {
400         INFO_LOG(LOGIDENTIFIER, "returning FALSE");
401         return false;
402     }
403
404     return true;
405 }
406
407 bool MediaElementSession::pageAllowsPlaybackAfterResuming() const
408 {
409     Page* page = m_element.document().page();
410     if (m_restrictions & RequirePageConsentToResumeMedia && page && !page->canStartMedia()) {
411         INFO_LOG(LOGIDENTIFIER, "returning FALSE");
412         return false;
413     }
414
415     return true;
416 }
417
418 bool MediaElementSession::canShowControlsManager(PlaybackControlsPurpose purpose) const
419 {
420     if (m_element.isSuspended() || !m_element.inActiveDocument()) {
421         INFO_LOG(LOGIDENTIFIER, "returning FALSE: isSuspended()");
422         return false;
423     }
424
425     if (m_element.isFullscreen()) {
426         INFO_LOG(LOGIDENTIFIER, "returning TRUE: is fullscreen");
427         return true;
428     }
429
430     if (m_element.muted()) {
431         INFO_LOG(LOGIDENTIFIER, "returning FALSE: muted");
432         return false;
433     }
434
435     if (m_element.document().isMediaDocument() && (m_element.document().frame() && m_element.document().frame()->isMainFrame())) {
436         INFO_LOG(LOGIDENTIFIER, "returning TRUE: is media document");
437         return true;
438     }
439
440     if (client().presentationType() == Audio) {
441         if (!hasBehaviorRestriction(RequireUserGestureToControlControlsManager) || m_element.document().processingUserGestureForMedia()) {
442             INFO_LOG(LOGIDENTIFIER, "returning TRUE: audio element with user gesture");
443             return true;
444         }
445
446         if (m_element.isPlaying() && allowsPlaybackControlsForAutoplayingAudio()) {
447             INFO_LOG(LOGIDENTIFIER, "returning TRUE: user has played media before");
448             return true;
449         }
450
451         INFO_LOG(LOGIDENTIFIER, "returning FALSE: audio element is not suitable");
452         return false;
453     }
454
455     if (purpose == PlaybackControlsPurpose::ControlsManager && !isElementRectMostlyInMainFrame(m_element)) {
456         INFO_LOG(LOGIDENTIFIER, "returning FALSE: not in main frame");
457         return false;
458     }
459
460     if (!m_element.hasAudio() && !m_element.hasEverHadAudio()) {
461         INFO_LOG(LOGIDENTIFIER, "returning FALSE: no audio");
462         return false;
463     }
464
465     if (!playbackPermitted()) {
466         INFO_LOG(LOGIDENTIFIER, "returning FALSE: playback not permitted");
467         return false;
468     }
469
470     if (!hasBehaviorRestriction(RequireUserGestureToControlControlsManager) || m_element.document().processingUserGestureForMedia()) {
471         INFO_LOG(LOGIDENTIFIER, "returning TRUE: no user gesture required");
472         return true;
473     }
474
475     if (purpose == PlaybackControlsPurpose::ControlsManager && hasBehaviorRestriction(RequirePlaybackToControlControlsManager) && !m_element.isPlaying()) {
476         INFO_LOG(LOGIDENTIFIER, "returning FALSE: needs to be playing");
477         return false;
478     }
479
480     if (!m_element.hasEverNotifiedAboutPlaying()) {
481         INFO_LOG(LOGIDENTIFIER, "returning FALSE: hasn't fired playing notification");
482         return false;
483     }
484
485 #if ENABLE(FULLSCREEN_API)
486     // Elements which are not descendents of the current fullscreen element cannot be main content.
487     auto* fullscreenElement = m_element.document().webkitCurrentFullScreenElement();
488     if (fullscreenElement && !m_element.isDescendantOf(*fullscreenElement)) {
489         INFO_LOG(LOGIDENTIFIER, "returning FALSE: outside of full screen");
490         return false;
491     }
492 #endif
493
494     // Only allow the main content heuristic to forbid videos from showing up if our purpose is the controls manager.
495     if (purpose == PlaybackControlsPurpose::ControlsManager && m_element.isVideo()) {
496         if (!m_element.renderer()) {
497             INFO_LOG(LOGIDENTIFIER, "returning FALSE: no renderer");
498             return false;
499         }
500
501         if (!m_element.hasVideo() && !m_element.hasEverHadVideo()) {
502             INFO_LOG(LOGIDENTIFIER, "returning FALSE: no video");
503             return false;
504         }
505
506         if (isLargeEnoughForMainContent(MediaSessionMainContentPurpose::MediaControls)) {
507             INFO_LOG(LOGIDENTIFIER, "returning TRUE: is main content");
508             return true;
509         }
510     }
511
512     if (purpose == PlaybackControlsPurpose::NowPlaying) {
513         INFO_LOG(LOGIDENTIFIER, "returning TRUE: potentially plays audio");
514         return true;
515     }
516
517     INFO_LOG(LOGIDENTIFIER, "returning FALSE: no user gesture");
518     return false;
519 }
520
521 bool MediaElementSession::isLargeEnoughForMainContent(MediaSessionMainContentPurpose purpose) const
522 {
523     return isElementLargeEnoughForMainContent(m_element, purpose);
524 }
525
526 bool MediaElementSession::isMainContentForPurposesOfAutoplayEvents() const
527 {
528     return isElementMainContentForPurposesOfAutoplay(m_element, false);
529 }
530
531 MonotonicTime MediaElementSession::mostRecentUserInteractionTime() const
532 {
533     return m_mostRecentUserInteractionTime;
534 }
535
536 bool MediaElementSession::wantsToObserveViewportVisibilityForMediaControls() const
537 {
538     return isLargeEnoughForMainContent(MediaSessionMainContentPurpose::MediaControls);
539 }
540
541 bool MediaElementSession::wantsToObserveViewportVisibilityForAutoplay() const
542 {
543     return m_element.isVideo();
544 }
545
546 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
547 void MediaElementSession::showPlaybackTargetPicker()
548 {
549     INFO_LOG(LOGIDENTIFIER);
550
551     auto& document = m_element.document();
552     if (m_restrictions & RequireUserGestureToShowPlaybackTargetPicker && !document.processingUserGestureForMedia()) {
553         INFO_LOG(LOGIDENTIFIER, "returning early because of permissions");
554         return;
555     }
556
557     if (!document.page()) {
558         INFO_LOG(LOGIDENTIFIER, "returning early because page is NULL");
559         return;
560     }
561
562 #if !PLATFORM(IOS_FAMILY)
563     if (m_element.readyState() < HTMLMediaElementEnums::HAVE_METADATA) {
564         INFO_LOG(LOGIDENTIFIER, "returning early because element is not playable");
565         return;
566     }
567 #endif
568
569     auto& audioSession = AudioSession::sharedSession();
570     document.showPlaybackTargetPicker(*this, is<HTMLVideoElement>(m_element), audioSession.routeSharingPolicy(), audioSession.routingContextUID());
571 }
572
573 bool MediaElementSession::hasWirelessPlaybackTargets() const
574 {
575     INFO_LOG(LOGIDENTIFIER, "returning ", m_hasPlaybackTargets);
576
577     return m_hasPlaybackTargets;
578 }
579
580 bool MediaElementSession::wirelessVideoPlaybackDisabled() const
581 {
582     if (!m_element.document().settings().allowsAirPlayForMediaPlayback()) {
583         INFO_LOG(LOGIDENTIFIER, "returning TRUE because of settings");
584         return true;
585     }
586
587     if (m_element.hasAttributeWithoutSynchronization(HTMLNames::webkitwirelessvideoplaybackdisabledAttr)) {
588         INFO_LOG(LOGIDENTIFIER, "returning TRUE because of attribute");
589         return true;
590     }
591
592 #if PLATFORM(IOS_FAMILY)
593     auto& legacyAirplayAttributeValue = m_element.attributeWithoutSynchronization(HTMLNames::webkitairplayAttr);
594     if (equalLettersIgnoringASCIICase(legacyAirplayAttributeValue, "deny")) {
595         INFO_LOG(LOGIDENTIFIER, "returning TRUE because of legacy attribute");
596         return true;
597     }
598     if (equalLettersIgnoringASCIICase(legacyAirplayAttributeValue, "allow")) {
599         INFO_LOG(LOGIDENTIFIER, "returning FALSE because of legacy attribute");
600         return false;
601     }
602 #endif
603
604     auto player = m_element.player();
605     if (!player)
606         return true;
607
608     bool disabled = player->wirelessVideoPlaybackDisabled();
609     INFO_LOG(LOGIDENTIFIER, "returning ", disabled, " because media engine says so");
610     
611     return disabled;
612 }
613
614 void MediaElementSession::setWirelessVideoPlaybackDisabled(bool disabled)
615 {
616     if (disabled)
617         addBehaviorRestriction(WirelessVideoPlaybackDisabled);
618     else
619         removeBehaviorRestriction(WirelessVideoPlaybackDisabled);
620
621     auto player = m_element.player();
622     if (!player)
623         return;
624
625     INFO_LOG(LOGIDENTIFIER, disabled);
626     player->setWirelessVideoPlaybackDisabled(disabled);
627 }
628
629 void MediaElementSession::setHasPlaybackTargetAvailabilityListeners(bool hasListeners)
630 {
631     INFO_LOG(LOGIDENTIFIER, hasListeners);
632
633 #if PLATFORM(IOS_FAMILY)
634     m_hasPlaybackTargetAvailabilityListeners = hasListeners;
635     PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
636 #else
637     UNUSED_PARAM(hasListeners);
638     m_element.document().playbackTargetPickerClientStateDidChange(*this, m_element.mediaState());
639 #endif
640 }
641
642 void MediaElementSession::setPlaybackTarget(Ref<MediaPlaybackTarget>&& device)
643 {
644     m_playbackTarget = WTFMove(device);
645     client().setWirelessPlaybackTarget(*m_playbackTarget.copyRef());
646 }
647
648 void MediaElementSession::targetAvailabilityChangedTimerFired()
649 {
650     client().wirelessRoutesAvailableDidChange();
651 }
652
653 void MediaElementSession::externalOutputDeviceAvailableDidChange(bool hasTargets)
654 {
655     if (m_hasPlaybackTargets == hasTargets)
656         return;
657
658     INFO_LOG(LOGIDENTIFIER, hasTargets);
659
660     m_hasPlaybackTargets = hasTargets;
661     m_targetAvailabilityChangedTimer.startOneShot(0_s);
662 }
663
664 bool MediaElementSession::isPlayingToWirelessPlaybackTarget() const
665 {
666 #if !PLATFORM(IOS_FAMILY)
667     if (!m_playbackTarget || !m_playbackTarget->hasActiveRoute())
668         return false;
669 #endif
670
671     return client().isPlayingToWirelessPlaybackTarget();
672 }
673
674 void MediaElementSession::setShouldPlayToPlaybackTarget(bool shouldPlay)
675 {
676     INFO_LOG(LOGIDENTIFIER, shouldPlay);
677     m_shouldPlayToPlaybackTarget = shouldPlay;
678     client().setShouldPlayToPlaybackTarget(shouldPlay);
679 }
680
681 void MediaElementSession::mediaStateDidChange(MediaProducer::MediaStateFlags state)
682 {
683     m_element.document().playbackTargetPickerClientStateDidChange(*this, state);
684 }
685 #endif
686
687 MediaPlayer::Preload MediaElementSession::effectivePreloadForElement() const
688 {
689     MediaPlayer::Preload preload = m_element.preloadValue();
690
691     if (pageExplicitlyAllowsElementToAutoplayInline(m_element))
692         return preload;
693
694     if (m_restrictions & MetadataPreloadingNotPermitted)
695         return MediaPlayer::None;
696
697     if (m_restrictions & AutoPreloadingNotPermitted) {
698         if (preload > MediaPlayer::MetaData)
699             return MediaPlayer::MetaData;
700     }
701
702     return preload;
703 }
704
705 bool MediaElementSession::requiresFullscreenForVideoPlayback() const
706 {
707     if (pageExplicitlyAllowsElementToAutoplayInline(m_element))
708         return false;
709
710     if (is<HTMLAudioElement>(m_element))
711         return false;
712
713     if (m_element.document().isMediaDocument()) {
714         ASSERT(is<HTMLVideoElement>(m_element));
715         const HTMLVideoElement& videoElement = *downcast<const HTMLVideoElement>(&m_element);
716         if (m_element.readyState() < HTMLVideoElement::HAVE_METADATA || !videoElement.hasEverHadVideo())
717             return false;
718     }
719
720     if (m_element.isTemporarilyAllowingInlinePlaybackAfterFullscreen())
721         return false;
722
723     if (!m_element.document().settings().allowsInlineMediaPlayback())
724         return true;
725
726     if (!m_element.document().settings().inlineMediaPlaybackRequiresPlaysInlineAttribute())
727         return false;
728
729 #if PLATFORM(IOS_FAMILY)
730     if (IOSApplication::isIBooks())
731         return !m_element.hasAttributeWithoutSynchronization(HTMLNames::webkit_playsinlineAttr) && !m_element.hasAttributeWithoutSynchronization(HTMLNames::playsinlineAttr);
732     if (dyld_get_program_sdk_version() < DYLD_IOS_VERSION_10_0)
733         return !m_element.hasAttributeWithoutSynchronization(HTMLNames::webkit_playsinlineAttr);
734 #endif
735
736     if (m_element.document().isMediaDocument() && m_element.document().ownerElement())
737         return false;
738
739     return !m_element.hasAttributeWithoutSynchronization(HTMLNames::playsinlineAttr);
740 }
741
742 bool MediaElementSession::allowsAutomaticMediaDataLoading() const
743 {
744     if (pageExplicitlyAllowsElementToAutoplayInline(m_element))
745         return true;
746
747     if (m_element.document().settings().mediaDataLoadsAutomatically())
748         return true;
749
750     return false;
751 }
752
753 void MediaElementSession::mediaEngineUpdated()
754 {
755     INFO_LOG(LOGIDENTIFIER);
756
757 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
758     if (m_restrictions & WirelessVideoPlaybackDisabled)
759         setWirelessVideoPlaybackDisabled(true);
760     if (m_playbackTarget)
761         client().setWirelessPlaybackTarget(*m_playbackTarget.copyRef());
762     if (m_shouldPlayToPlaybackTarget)
763         client().setShouldPlayToPlaybackTarget(true);
764 #endif
765     
766 }
767
768 void MediaElementSession::resetPlaybackSessionState()
769 {
770     m_mostRecentUserInteractionTime = MonotonicTime();
771     addBehaviorRestriction(RequireUserGestureToControlControlsManager | RequirePlaybackToControlControlsManager);
772 }
773
774 bool MediaElementSession::allowsPictureInPicture() const
775 {
776     return m_element.document().settings().allowsPictureInPictureMediaPlayback();
777 }
778
779 #if PLATFORM(IOS_FAMILY)
780 bool MediaElementSession::requiresPlaybackTargetRouteMonitoring() const
781 {
782     return m_hasPlaybackTargetAvailabilityListeners && !m_element.elementIsHidden();
783 }
784 #endif
785
786 #if ENABLE(MEDIA_SOURCE)
787 size_t MediaElementSession::maximumMediaSourceBufferSize(const SourceBuffer& buffer) const
788 {
789     // A good quality 1080p video uses 8,000 kbps and stereo audio uses 384 kbps, so assume 95% for video and 5% for audio.
790     const float bufferBudgetPercentageForVideo = .95;
791     const float bufferBudgetPercentageForAudio = .05;
792
793     size_t maximum = buffer.document().settings().maximumSourceBufferSize();
794
795     // Allow a SourceBuffer to buffer as though it is audio-only even if it doesn't have any active tracks (yet).
796     size_t bufferSize = static_cast<size_t>(maximum * bufferBudgetPercentageForAudio);
797     if (buffer.hasVideo())
798         bufferSize += static_cast<size_t>(maximum * bufferBudgetPercentageForVideo);
799
800     // FIXME: we might want to modify this algorithm to:
801     // - decrease the maximum size for background tabs
802     // - decrease the maximum size allowed for inactive elements when a process has more than one
803     //   element, eg. so a page with many elements which are played one at a time doesn't keep
804     //   everything buffered after an element has finished playing.
805
806     return bufferSize;
807 }
808 #endif
809
810 static bool isElementMainContentForPurposesOfAutoplay(const HTMLMediaElement& element, bool shouldHitTestMainFrame)
811 {
812     Document& document = element.document();
813     if (!document.hasLivingRenderTree() || document.activeDOMObjectsAreStopped() || element.isSuspended() || !element.hasAudio() || !element.hasVideo())
814         return false;
815
816     // Elements which have not yet been laid out, or which are not yet in the DOM, cannot be main content.
817     auto* renderer = element.renderer();
818     if (!renderer)
819         return false;
820
821     if (!isElementLargeEnoughForMainContent(element, MediaSessionMainContentPurpose::Autoplay))
822         return false;
823
824     // Elements which are hidden by style, or have been scrolled out of view, cannot be main content.
825     // But elements which have audio & video and are already playing should not stop playing because
826     // they are scrolled off the page.
827     if (renderer->style().visibility() != Visibility::Visible)
828         return false;
829     if (renderer->visibleInViewportState() != VisibleInViewportState::Yes && !element.isPlaying())
830         return false;
831
832     // Main content elements must be in the main frame.
833     if (!document.frame() || !document.frame()->isMainFrame())
834         return false;
835
836     auto& mainFrame = document.frame()->mainFrame();
837     if (!mainFrame.view() || !mainFrame.view()->renderView())
838         return false;
839
840     if (!shouldHitTestMainFrame)
841         return true;
842
843     RenderView& mainRenderView = *mainFrame.view()->renderView();
844
845     // Hit test the area of the main frame where the element appears, to determine if the element is being obscured.
846     IntRect rectRelativeToView = element.clientRect();
847     ScrollPosition scrollPosition = mainFrame.view()->documentScrollPositionRelativeToViewOrigin();
848     IntRect rectRelativeToTopDocument(rectRelativeToView.location() + scrollPosition, rectRelativeToView.size());
849     HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AllowChildFrameContent | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowUserAgentShadowContent);
850     HitTestResult result(rectRelativeToTopDocument.center());
851
852     // Elements which are obscured by other elements cannot be main content.
853     mainRenderView.hitTest(request, result);
854     result.setToNonUserAgentShadowAncestor();
855     RefPtr<Element> hitElement = result.targetElement();
856     if (hitElement != &element)
857         return false;
858
859     return true;
860 }
861
862 static bool isElementRectMostlyInMainFrame(const HTMLMediaElement& element)
863 {
864     if (!element.renderer())
865         return false;
866
867     auto documentFrame = makeRefPtr(element.document().frame());
868     if (!documentFrame)
869         return false;
870
871     auto mainFrameView = documentFrame->mainFrame().view();
872     if (!mainFrameView)
873         return false;
874
875     IntRect mainFrameRectAdjustedForScrollPosition = IntRect(-mainFrameView->documentScrollPositionRelativeToViewOrigin(), mainFrameView->contentsSize());
876     IntRect elementRectInMainFrame = element.clientRect();
877     auto totalElementArea = elementRectInMainFrame.area<RecordOverflow>();
878     if (totalElementArea.hasOverflowed())
879         return false;
880
881     elementRectInMainFrame.intersect(mainFrameRectAdjustedForScrollPosition);
882
883     return elementRectInMainFrame.area().unsafeGet() > totalElementArea.unsafeGet() / 2;
884 }
885
886 static bool isElementLargeRelativeToMainFrame(const HTMLMediaElement& element)
887 {
888     static const double minimumPercentageOfMainFrameAreaForMainContent = 0.9;
889     auto* renderer = element.renderer();
890     if (!renderer)
891         return false;
892
893     auto documentFrame = makeRefPtr(element.document().frame());
894     if (!documentFrame)
895         return false;
896
897     if (!documentFrame->mainFrame().view())
898         return false;
899
900     auto& mainFrameView = *documentFrame->mainFrame().view();
901     auto maxVisibleClientWidth = std::min(renderer->clientWidth().toInt(), mainFrameView.visibleWidth());
902     auto maxVisibleClientHeight = std::min(renderer->clientHeight().toInt(), mainFrameView.visibleHeight());
903
904     return maxVisibleClientWidth * maxVisibleClientHeight > minimumPercentageOfMainFrameAreaForMainContent * mainFrameView.visibleWidth() * mainFrameView.visibleHeight();
905 }
906
907 static bool isElementLargeEnoughForMainContent(const HTMLMediaElement& element, MediaSessionMainContentPurpose purpose)
908 {
909     static const double elementMainContentAreaMinimum = 400 * 300;
910     static const double maximumAspectRatio = purpose == MediaSessionMainContentPurpose::MediaControls ? 3 : 1.8;
911     static const double minimumAspectRatio = .5; // Slightly smaller than 9:16.
912
913     // Elements which have not yet been laid out, or which are not yet in the DOM, cannot be main content.
914     auto* renderer = element.renderer();
915     if (!renderer)
916         return false;
917
918     double width = renderer->clientWidth();
919     double height = renderer->clientHeight();
920     double area = width * height;
921     double aspectRatio = width / height;
922
923     if (area < elementMainContentAreaMinimum)
924         return false;
925
926     if (aspectRatio >= minimumAspectRatio && aspectRatio <= maximumAspectRatio)
927         return true;
928
929     return isElementLargeRelativeToMainFrame(element);
930 }
931
932 void MediaElementSession::mainContentCheckTimerFired()
933 {
934     if (!hasBehaviorRestriction(OverrideUserGestureRequirementForMainContent))
935         return;
936
937     updateIsMainContent();
938 }
939
940 bool MediaElementSession::updateIsMainContent() const
941 {
942     if (m_element.isSuspended())
943         return false;
944
945     bool wasMainContent = m_isMainContent;
946     m_isMainContent = isElementMainContentForPurposesOfAutoplay(m_element, true);
947
948     if (m_isMainContent != wasMainContent)
949         m_element.updateShouldPlay();
950
951     return m_isMainContent;
952 }
953
954 bool MediaElementSession::allowsNowPlayingControlsVisibility() const
955 {
956     auto page = m_element.document().page();
957     return page && !page->isVisibleAndActive();
958 }
959
960 bool MediaElementSession::allowsPlaybackControlsForAutoplayingAudio() const
961 {
962     auto page = m_element.document().page();
963     return page && page->allowsPlaybackControlsForAutoplayingAudio();
964 }
965
966 }
967
968 #endif // ENABLE(VIDEO)