| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef AutoplayExperimentHelper_h | |
| 6 #define AutoplayExperimentHelper_h | |
| 7 | |
| 8 #include "core/page/PageVisibilityState.h" | |
| 9 #include "platform/Timer.h" | |
| 10 #include "platform/geometry/IntRect.h" | |
| 11 | |
| 12 namespace blink { | |
| 13 | |
| 14 class Document; | |
| 15 class AutoplayExperimentTest; | |
| 16 | |
| 17 // These values are used for a histogram. Do not reorder. | |
| 18 enum AutoplayMetrics { | |
| 19 // Media element with autoplay seen. | |
| 20 AutoplayMediaFound = 0, | |
| 21 // Autoplay enabled and user stopped media play at any point. | |
| 22 AutoplayPaused = 1, | |
| 23 // Autoplay enabled but user bailed out on media play early. | |
| 24 AutoplayBailout = 2, | |
| 25 // Autoplay disabled but user manually started media. | |
| 26 AutoplayManualStart = 3, | |
| 27 // Autoplay was (re)enabled through a user-gesture triggered load() | |
| 28 AutoplayEnabledThroughLoad = 4, | |
| 29 // Autoplay disabled by sandbox flags. | |
| 30 AutoplayDisabledBySandbox = 5, | |
| 31 | |
| 32 // These metrics indicate "no gesture detected, but the gesture | |
| 33 // requirement was overridden by experiment". They do not include cases | |
| 34 // where no user gesture is required (!m_userGestureRequiredForPlay). | |
| 35 | |
| 36 // User gesture requirement was bypassed, and playback started, during | |
| 37 // initial load via the autoplay flag. If playback was deferred due to | |
| 38 // visibility requirements, then this does not apply. | |
| 39 GesturelessPlaybackStartedByAutoplayFlagImmediately = 6, | |
| 40 | |
| 41 // User gesture requirement was bypassed, and playback started, when | |
| 42 // the play() method was called. If playback is deferred due to | |
| 43 // visibility requirements, then this does not apply. | |
| 44 GesturelessPlaybackStartedByPlayMethodImmediately = 7, | |
| 45 | |
| 46 // User gesture requirement was bypassed, and playback started, after | |
| 47 // an element with the autoplay flag moved into the viewport. Playback | |
| 48 // was deferred earlier due to visibility requirements. | |
| 49 GesturelessPlaybackStartedByAutoplayFlagAfterScroll = 8, | |
| 50 | |
| 51 // User gesture requirement was bypassed, and playback started, after | |
| 52 // an element on which the play() method was called was moved into the | |
| 53 // viewport. Playback had been deferred due to visibility requirements. | |
| 54 GesturelessPlaybackStartedByPlayMethodAfterScroll = 9, | |
| 55 | |
| 56 // play() failed to play due to gesture requirement. | |
| 57 PlayMethodFailed = 10, | |
| 58 | |
| 59 // Some play, whether user initiated or not, started. | |
| 60 AnyPlaybackStarted = 11, | |
| 61 // Some play, whether user initiated or not, paused. | |
| 62 AnyPlaybackPaused = 12, | |
| 63 // Some playback, whether user initiated or not, bailed out early. | |
| 64 AnyPlaybackBailout = 13, | |
| 65 // Some playback, whether user initiated or not, played to completion. | |
| 66 AnyPlaybackComplete = 14, | |
| 67 | |
| 68 // Number of audio elements detected that reach the resource fetch algorithm. | |
| 69 AnyAudioElement = 15, | |
| 70 // Numer of video elements detected that reach the resource fetch algorithm. | |
| 71 AnyVideoElement = 16, | |
| 72 | |
| 73 // User gesture was bypassed, and playback started, and media played to | |
| 74 // completion without a user-initiated pause. | |
| 75 AutoplayComplete = 17, | |
| 76 | |
| 77 // Autoplay started after the gesture requirement was removed by a | |
| 78 // user gesture load(). | |
| 79 GesturelessPlaybackEnabledByLoad = 18, | |
| 80 | |
| 81 // Gestureless playback started after the gesture requirement was removed | |
| 82 // because src is media stream. | |
| 83 GesturelessPlaybackEnabledByStream = 19, | |
| 84 | |
| 85 // Gestureless playback was started, but was never overridden. This | |
| 86 // includes the case in which no gesture was ever required. | |
| 87 GesturelessPlaybackNotOverridden = 20, | |
| 88 | |
| 89 // Gestureless playback was enabled by a user gesture play() call. | |
| 90 GesturelessPlaybackEnabledByPlayMethod = 21, | |
| 91 | |
| 92 // This enum value must be last. | |
| 93 NumberOfAutoplayMetrics, | |
| 94 }; | |
| 95 | |
| 96 class CORE_EXPORT AutoplayExperimentHelper final | |
| 97 : public GarbageCollectedFinalized<AutoplayExperimentHelper> { | |
| 98 friend class AutoplayExperimentTest; | |
| 99 | |
| 100 public: | |
| 101 // For easier testing, collect all the things we care about here. | |
| 102 class Client : public GarbageCollectedFinalized<Client> { | |
| 103 public: | |
| 104 virtual ~Client() {} | |
| 105 | |
| 106 // HTMLMediaElement | |
| 107 virtual double currentTime() const = 0; | |
| 108 virtual double duration() const = 0; | |
| 109 virtual bool paused() const = 0; | |
| 110 virtual bool ended() const = 0; | |
| 111 virtual bool muted() const = 0; | |
| 112 virtual void setMuted(bool) = 0; | |
| 113 virtual void playInternal() = 0; | |
| 114 virtual void pauseInternal() = 0; | |
| 115 virtual bool isLockedPendingUserGesture() const = 0; | |
| 116 virtual void unlockUserGesture() = 0; | |
| 117 virtual void recordAutoplayMetric(AutoplayMetrics) = 0; | |
| 118 virtual bool shouldAutoplay() = 0; | |
| 119 virtual bool isHTMLVideoElement() const = 0; | |
| 120 virtual bool isHTMLAudioElement() const = 0; | |
| 121 | |
| 122 // Document | |
| 123 virtual bool isLegacyViewportType() = 0; | |
| 124 virtual PageVisibilityState pageVisibilityState() const = 0; | |
| 125 virtual String autoplayExperimentMode() const = 0; | |
| 126 | |
| 127 // Frame | |
| 128 virtual bool isCrossOrigin() const = 0; | |
| 129 virtual bool isAutoplayAllowedPerSettings() const = 0; | |
| 130 | |
| 131 // LayoutObject | |
| 132 virtual void setRequestPositionUpdates(bool) = 0; | |
| 133 virtual IntRect absoluteBoundingBoxRect() const = 0; | |
| 134 | |
| 135 DEFINE_INLINE_VIRTUAL_TRACE() {} | |
| 136 }; | |
| 137 | |
| 138 static AutoplayExperimentHelper* create(Client* client) { | |
| 139 return new AutoplayExperimentHelper(client); | |
| 140 } | |
| 141 | |
| 142 ~AutoplayExperimentHelper(); | |
| 143 | |
| 144 void becameReadyToPlay(); | |
| 145 void playMethodCalled(); | |
| 146 void pauseMethodCalled(); | |
| 147 void loadMethodCalled(); | |
| 148 void mutedChanged(); | |
| 149 void positionChanged(const IntRect&); | |
| 150 void updatePositionNotificationRegistration(); | |
| 151 void recordSandboxFailure(); | |
| 152 void loadingStarted(); | |
| 153 void playbackStarted(); | |
| 154 void playbackStopped(); | |
| 155 void initialPlayWithUserGesture(); | |
| 156 | |
| 157 // Returns true if and only if any experiment is enabled (i.e., |m_mode| | |
| 158 // is not ExperimentOff). | |
| 159 bool isExperimentEnabled(); | |
| 160 | |
| 161 // Remove the user gesture requirement, and record why. If there is no | |
| 162 // gesture requirement, then this does nothing. | |
| 163 void unlockUserGesture(AutoplayMetrics); | |
| 164 | |
| 165 // Set the reason that we're overridding the user gesture. If there is no | |
| 166 // gesture requirement, then this does nothing. | |
| 167 void setDeferredOverrideReason(AutoplayMetrics); | |
| 168 | |
| 169 // Return true if and only if the user gesture requirement is currently | |
| 170 // overridden by the experiment, permitting playback. | |
| 171 bool isGestureRequirementOverridden() const; | |
| 172 | |
| 173 // Return true if and only if playback is queued but hasn't started yet, | |
| 174 // such as if the element doesn't meet visibility requirements. | |
| 175 bool isPlaybackDeferred() const; | |
| 176 | |
| 177 // Set the position to the current view's position, and | |
| 178 void triggerAutoplayViewportCheckForTesting(); | |
| 179 | |
| 180 enum Mode { | |
| 181 // Do not enable the autoplay experiment. | |
| 182 ExperimentOff = 0, | |
| 183 // Enable gestureless autoplay for video elements. | |
| 184 ForVideo = 1 << 0, | |
| 185 // Enable gestureless autoplay for audio elements. | |
| 186 ForAudio = 1 << 1, | |
| 187 // Restrict gestureless autoplay to media that is in a visible page. | |
| 188 IfPageVisible = 1 << 2, | |
| 189 // Restrict gestureless autoplay to media that is entirely visible in | |
| 190 // the viewport. | |
| 191 IfViewport = 1 << 3, | |
| 192 // Restrict gestureless autoplay to media that is partially visible in | |
| 193 // the viewport. | |
| 194 IfPartialViewport = 1 << 4, | |
| 195 // Restrict gestureless autoplay to audio-less or muted media. | |
| 196 IfMuted = 1 << 5, | |
| 197 // Restrict gestureless autoplay to sites which contain the | |
| 198 // viewport tag. | |
| 199 IfMobile = 1 << 6, | |
| 200 // Restrict gestureless autoplay to sites which are from the same origin | |
| 201 // as the top-level frame. | |
| 202 IfSameOrigin = 1 << 7, | |
| 203 // Extend IfSameOrigin to allow autoplay of cross-origin elements if | |
| 204 // they're muted. This has no effect on same-origin or if IfSameOrigin | |
| 205 // isn't enabled. | |
| 206 OrMuted = 1 << 8, | |
| 207 // If gestureless autoplay is allowed, then mute the media before | |
| 208 // starting to play. | |
| 209 PlayMuted = 1 << 9, | |
| 210 }; | |
| 211 | |
| 212 DEFINE_INLINE_TRACE() { visitor->trace(m_client); } | |
| 213 | |
| 214 private: | |
| 215 explicit AutoplayExperimentHelper(Client*); | |
| 216 | |
| 217 // Register to receive position updates, if we haven't already. If we | |
| 218 // have, then this does nothing. | |
| 219 void registerForPositionUpdatesIfNeeded(); | |
| 220 | |
| 221 // Un-register for position updates, if we are currently registered. | |
| 222 void unregisterForPositionUpdatesIfNeeded(); | |
| 223 | |
| 224 // Modifiers for checking isEligible(). | |
| 225 enum EligibilityMode { | |
| 226 // Perform all normal eligibility checks. | |
| 227 Normal = 0, | |
| 228 | |
| 229 // Perform normal eligibility checks, but skip checking if autoplay has | |
| 230 // actually been requested. In other words, don't fail just becase | |
| 231 // nobody has called play() and/or set the autoplay attribute. | |
| 232 IgnorePendingPlayback = 1 | |
| 233 }; | |
| 234 | |
| 235 // Return true if any only if this player meets (most) of the eligibility | |
| 236 // requirements for the experiment to override the need for a user | |
| 237 // gesture. This includes everything except the visibility test. | |
| 238 // |mode| modifies the eligibility check, as described above. | |
| 239 bool isEligible(EligibilityMode = Normal) const; | |
| 240 | |
| 241 // Return false if and only if m_element is not visible, and we care | |
| 242 // that it must be visible. | |
| 243 bool meetsVisibilityRequirements() const; | |
| 244 | |
| 245 // Set the muted flag on the media if we're in an experiment mode that | |
| 246 // requires it, else do nothing. | |
| 247 void muteIfNeeded(); | |
| 248 | |
| 249 // Maybe override the requirement for a user gesture, and start playing | |
| 250 // autoplay media. Returns true if only if it starts playback. | |
| 251 bool maybeStartPlaying(); | |
| 252 | |
| 253 // Configure internal state to record that the autoplay experiment is | |
| 254 // going to start playback. This doesn't actually start playback, since | |
| 255 // there are several different cases. | |
| 256 void prepareToAutoplay(AutoplayMetrics); | |
| 257 | |
| 258 // Record that an attempt to play without a user gesture has happened. | |
| 259 // This does not assume whether or not the play attempt will succeed. | |
| 260 // This method takes no action after it is called once. | |
| 261 void autoplayMediaEncountered(); | |
| 262 | |
| 263 // If we are about to enter a paused state, call this to record | |
| 264 // autoplay metrics. | |
| 265 void recordMetricsBeforePause(); | |
| 266 | |
| 267 // Process a timer for checking visibility. | |
| 268 void viewportTimerFired(TimerBase*); | |
| 269 | |
| 270 Client& client() const { return *m_client; } | |
| 271 | |
| 272 bool isLockedPendingUserGesture() const; | |
| 273 | |
| 274 inline bool enabled(Mode mode) const { | |
| 275 return static_cast<int>(m_mode) & static_cast<int>(mode); | |
| 276 } | |
| 277 | |
| 278 Mode fromString(const String& mode); | |
| 279 | |
| 280 void recordAutoplayMetric(AutoplayMetrics); | |
| 281 | |
| 282 // Could stopping at this point be considered a bailout of playback? | |
| 283 // (as in, "The user really didn't want to play this"). | |
| 284 bool isBailout() const; | |
| 285 | |
| 286 // Returns true if and only if the experiment requires some sort of viewport | |
| 287 // visibility check for autoplay. | |
| 288 bool requiresViewportVisibility() const; | |
| 289 | |
| 290 Member<Client> m_client; | |
| 291 | |
| 292 Mode m_mode; | |
| 293 | |
| 294 // Autoplay experiment state. | |
| 295 // True if we've received a play() without a pause(). | |
| 296 bool m_playPending : 1; | |
| 297 | |
| 298 // Are we registered with the view for position updates? | |
| 299 bool m_registeredWithLayoutObject : 1; | |
| 300 | |
| 301 // According to our last position update, are we in the viewport? | |
| 302 bool m_wasInViewport : 1; | |
| 303 | |
| 304 // Have we counted this autoplay media in the metrics yet? This is set when | |
| 305 // a media element tries to autoplay, and we record that via the | |
| 306 // AutoplayMediaFound metric. | |
| 307 bool m_autoplayMediaEncountered : 1; | |
| 308 | |
| 309 // Have we recorded a metric about the cause of the initial playback of | |
| 310 // this media yet? | |
| 311 bool m_playbackStartedMetricRecorded : 1; | |
| 312 | |
| 313 // Is the current playback the result of autoplay? If so, then this flag | |
| 314 // records that the pause / stop should be counted in the autoplay metrics. | |
| 315 bool m_waitingForAutoplayPlaybackStop : 1; | |
| 316 | |
| 317 // Did we record that this media element exists in the metrics yet? This is | |
| 318 // independent of whether it autoplays; we just want to know how many | |
| 319 // elements exist for the Any{Audio|Video}Element metrics. | |
| 320 bool m_recordedElement : 1; | |
| 321 | |
| 322 // According to our last position update, where was our element? | |
| 323 IntRect m_lastLocation; | |
| 324 IntRect m_lastVisibleRect; | |
| 325 | |
| 326 // When was m_lastLocation set? | |
| 327 double m_lastLocationUpdateTime; | |
| 328 | |
| 329 Timer<AutoplayExperimentHelper> m_viewportTimer; | |
| 330 | |
| 331 // Reason that autoplay would be allowed to proceed without a user gesture. | |
| 332 AutoplayMetrics m_autoplayDeferredMetric; | |
| 333 }; | |
| 334 | |
| 335 inline AutoplayExperimentHelper::Mode& operator|=( | |
| 336 AutoplayExperimentHelper::Mode& a, | |
| 337 const AutoplayExperimentHelper::Mode& b) { | |
| 338 a = static_cast<AutoplayExperimentHelper::Mode>(static_cast<int>(a) | | |
| 339 static_cast<int>(b)); | |
| 340 return a; | |
| 341 } | |
| 342 | |
| 343 } // namespace blink | |
| 344 | |
| 345 #endif // AutoplayExperimentHelper_h | |
| OLD | NEW |