| OLD | NEW |
| 1 <html> | 1 <html> |
| 2 <video autoplay controls></video> | 2 <video autoplay controls></video> |
| 3 <script src=media-file.js></script> | 3 <script src=media-file.js></script> |
| 4 <script src=video-test.js></script> | 4 <script src=video-test.js></script> |
| 5 <body> | 5 <body> |
| 6 <pre> | 6 <pre> |
| 7 Check if the autoplay gesture override experiment works. There are a lot | 7 Check if the autoplay gesture override experiment works. There are a lot |
| 8 of config options, so this test just runs all of them. | 8 of config options, so this test just runs all of them. |
| 9 | 9 |
| 10 The "results" table contains one row per config tested. | 10 The "results" table contains one row per config tested. |
| 11 == Test Inputs == | 11 == Test Inputs == |
| 12 # - config number, in case you'd like to run just one. | 12 # - config number, in case you'd like to run just one. |
| 13 Flags - autoplay experiment setting being tested. | 13 Flags - autoplay experiment setting being tested. |
| 14 a - "foraudio" | 14 a - "foraudio" |
| 15 v - "forvideo" | 15 v - "forvideo" |
| 16 V - "ifviewport" |
| 17 P - "ifpagevisible" |
| 16 M - "ifmuted" | 18 M - "ifmuted" |
| 17 p - "playmuted" | 19 p - "playmuted" |
| 18 m - "ifmobile" | 20 m - "ifmobile" |
| 19 For example, EM means "enabled-ifmuted". | 21 For example, vM means '-forvideo-ifmuted". |
| 20 Type - audio or video element? | 22 Type - audio or video element? |
| 21 audio - <audio> | 23 audio - <audio> |
| 22 video - <video> | 24 video - <video> |
| 23 Play w/- how is play requested? | 25 Play w/- how is play requested? |
| 24 none - play is not requested. | 26 none - play is not requested. |
| 25 attr - autoplay attribute is set on the element. | 27 attr - autoplay attribute is set on the element. |
| 26 play() - play() called after media is ready to play. | 28 play() - play() called after media is ready to play. |
| 27 Mute - how is media muted? | 29 Mute - how is media muted? |
| 28 no - media is not muted. | 30 no - media is not muted. |
| 29 yes - muted attribute is set on the element. | 31 yes - muted attribute is set on the element. |
| 30 Mobile - is page optimized for mobile? | 32 Mobile - is page optimized for mobile? |
| 31 no - page is not optimized for mobile. | 33 no - page is not optimized for mobile. |
| 32 yes - page is optimized for mobile. | 34 yes - page is optimized for mobile. |
| 35 View - is media in viewport? |
| 36 onscreen - element starts out onscreen. |
| 37 scroll - element starts offscreen, scrolled into view once |
| 38 it is ready to play. |
| 39 offscreen - element starts out offscreen and stays offscreen. |
| 40 obscured - onscreen but page is not visible. |
| 33 | 41 |
| 34 == Test Outputs == | 42 == Test Outputs == |
| 43 Early? - did playback start before element was scrolled onscreen? For |
| 44 tests in which View!=scroll, this is reported as "-". |
| 35 Played? - did playback start by the conclusion of the test? | 45 Played? - did playback start by the conclusion of the test? |
| 36 Muted? - was the media muted? If the media didn't play, then this is | 46 Muted? - was the media muted? If the media didn't play, then this is |
| 37 reported as "-". | 47 reported as "-". |
| 38 | 48 |
| 39 </pre> | 49 </pre> |
| 40 <table id="results"> | 50 <table id="results"> |
| 41 <tr> | 51 <tr> |
| 42 <td>#</td> | 52 <td>#</td> |
| 43 <td>Flags</td> | 53 <td>Flags</td> |
| 44 <td>Type</td> | 54 <td>Type</td> |
| 45 <td>Play w/</td> | 55 <td>Play w/</td> |
| 46 <td>Mute</td> | 56 <td>Mute</td> |
| 47 <td>Mobile</td> | 57 <td>Mobile</td> |
| 58 <td>View</td> |
| 59 <td>Early?</td> |
| 48 <td>Played?</td> | 60 <td>Played?</td> |
| 49 <td>Muted?</td> | 61 <td>Muted?</td> |
| 50 </tr> | 62 </tr> |
| 51 </table> | 63 </table> |
| 52 </body> | 64 </body> |
| 53 | 65 |
| 54 <script> | 66 <script> |
| 55 | 67 |
| 56 // Starting configuration number. This should be zero normally. | 68 // Starting configuration number. This should be zero normally. |
| 57 var configNumber = 0; | 69 var configNumber = 0; |
| 58 | 70 |
| 59 var mediaFile = findMediaFile("video", "content/test"); | 71 var mediaFile = findMediaFile("video", "content/test"); |
| 60 var onscreenParent = document.createElement("div"); | 72 var onscreenParent = document.createElement("div"); |
| 73 // The onscreen parent's height is also used to make sure that the off-screen |
| 74 // parent is, in fact, off-screen. |
| 75 onscreenParent.style.height = "1000px"; |
| 61 document.body.insertBefore(onscreenParent, document.body.firstChild); | 76 document.body.insertBefore(onscreenParent, document.body.firstChild); |
| 62 // When we remove the meta tag from the document, we put it here. | 77 // Is the page optimized for mobile? We can't un-optimize it. |
| 63 var isOptimizedForMobile = false; | 78 var isOptimizedForMobile = false; |
| 79 // Also create another root that's off the bottom of the window. |
| 80 var offscreenParent = document.createElement("div"); |
| 81 document.body.appendChild(offscreenParent); |
| 64 | 82 |
| 65 function didPlaybackStart(video) | 83 function didPlaybackStart(element) |
| 66 { | 84 { |
| 67 // We say that the video started if it's not paused or if it played and | 85 return !element.paused || element.ended; |
| 68 // already ended. | |
| 69 return !video.paused || video.ended; | |
| 70 } | 86 } |
| 71 | 87 |
| 72 function becomeOptimizedForMobile(enable) | 88 function becomeOptimizedForMobile(enable) |
| 73 { | 89 { |
| 74 // If we're in the right state already, then return; | 90 // If we're in the right state already, then return; |
| 75 if (enable == isOptimizedForMobile) | 91 if (enable == isOptimizedForMobile) |
| 76 return; | 92 return; |
| 77 | 93 |
| 78 if (!enable) { | 94 if (!enable) { |
| 79 // We can't transition out of optimized for mobile. | 95 // We can't transition out of optimized for mobile. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 94 // Add a row to the results table. | 110 // Add a row to the results table. |
| 95 var row = document.getElementById("results").insertRow(); | 111 var row = document.getElementById("results").insertRow(); |
| 96 var td = row.insertCell(); | 112 var td = row.insertCell(); |
| 97 | 113 |
| 98 // Add experiment number | 114 // Add experiment number |
| 99 row.insertCell().innerText = (""+spec.experimentNumber); | 115 row.insertCell().innerText = (""+spec.experimentNumber); |
| 100 | 116 |
| 101 // Process experiment type specially. | 117 // Process experiment type specially. |
| 102 var type = spec.experimentType; | 118 var type = spec.experimentType; |
| 103 var smallType = ""; | 119 var smallType = ""; |
| 104 smallType += (type.indexOf("-forvideo")>-1)?"v":""; | 120 smallType += type.includes("-forvideo")?"v":""; |
| 105 smallType += (type.indexOf("-foraudio")>-1)?"a":""; | 121 smallType += type.includes("-foraudio")?"a":""; |
| 106 smallType += (type.indexOf("-ifmuted")>-1)?"M":""; | 122 smallType += type.includes("-ifviewport")?"V":""; |
| 107 smallType += (type.indexOf("-playmuted")>-1)?"p":""; | 123 smallType += type.includes("-ifpagevisible")?"P":""; |
| 108 smallType += (type.indexOf("-ifmobile")>-1)?"m":""; | 124 smallType += type.includes("-ifmuted")?"M":""; |
| 125 smallType += type.includes("-playmuted")?"p":""; |
| 126 smallType += type.includes("-ifmobile")?"m":""; |
| 109 row.insertCell().innerText = smallType; | 127 row.insertCell().innerText = smallType; |
| 110 | 128 |
| 111 // Add remaining fields. | 129 // Add remaining fields. |
| 112 var fields = [ "elementType", "autoplayType", "mutedType", "mobileType", | 130 var fields = [ "elementType", "autoplayType", "mutedType", "mobileType", |
| 113 "played", "muted"]; | 131 "visType", "playedEarly", "played", "muted"]; |
| 114 for(idx in fields) | 132 for(idx in fields) |
| 115 row.insertCell().innerText = spec[fields[idx]].substring(0,7); | 133 row.insertCell().innerText = spec[fields[idx]].substring(0,7); |
| 116 } | 134 } |
| 117 | 135 |
| 118 function configureElementViaScript(element, spec) | 136 function configureElementViaScript(element, spec) |
| 119 { | 137 { |
| 120 if(spec.autoplayType == "play()") | 138 if(spec.autoplayType == "play()") |
| 121 element.play(); | 139 element.play(); |
| 122 } | 140 } |
| 123 | 141 |
| 124 function queueNextExperiment() | 142 function queueNextExperiment() |
| 125 { | 143 { |
| 126 // Start the next config, but let the event queue drain. | 144 // Start the next config, but let the event queue drain. |
| 127 setTimeout(runNextConfig, 0); | 145 setTimeout(runNextConfig, 0); |
| 128 } | 146 } |
| 129 | 147 |
| 130 function checkElementStatus(element) | 148 function checkElementStatus(element) |
| 131 { | 149 { |
| 132 // Update the spec with the results. | 150 // Update the spec with the results. |
| 133 var didStart = didPlaybackStart(element); | 151 var didStart = didPlaybackStart(element); |
| 134 element.spec.played = didStart ? "played" : "no"; | 152 element.spec.played = didStart ? "played" : "no"; |
| 135 element.spec.muted = didStart ? (element.muted ? "muted" : "unmuted") : "-"; | 153 element.spec.muted = didStart ? (element.muted ? "muted" : "unmuted") : "-"; |
| 136 | 154 |
| 137 addResultsRow(element.spec); | 155 addResultsRow(element.spec); |
| 138 element.remove(); | 156 element.remove(); |
| 139 | 157 |
| 158 // Scroll back to the top, in case this was a scrolling test. |
| 159 onscreenParent.scrollIntoView(); |
| 160 |
| 161 // Also make sure that the page is visible again. Hidden pages cause the |
| 162 // test to proceed very slowly. |
| 163 testRunner.setPageVisibility("visible"); |
| 164 |
| 140 queueNextExperiment(); | 165 queueNextExperiment(); |
| 141 } | 166 } |
| 142 | 167 |
| 143 function runOneConfig(spec) | 168 function runOneConfig(spec) |
| 144 { | 169 { |
| 145 internals.settings.setAutoplayExperimentMode(spec.experimentType); | 170 internals.settings.setAutoplayExperimentMode(spec.experimentType); |
| 146 | 171 |
| 147 // Create, configure, and attach a media element. | 172 // Create, configure, and attach a media element. |
| 148 var element = document.createElement(spec.elementType); | 173 var element = document.createElement(spec.elementType); |
| 149 element.controls = true; | 174 element.controls = true; |
| 150 | 175 |
| 151 onscreenParent.appendChild(element); | 176 // Hide or show the page. |
| 177 if (spec.visType == "obscured") |
| 178 testRunner.setPageVisibility("hidden"); |
| 179 |
| 180 // Pick whether the element will be visible when canPlayThrough. |
| 181 if (spec.visType == "offscreen" || spec.visType == "scroll") |
| 182 offscreenParent.appendChild(element); |
| 183 else |
| 184 onscreenParent.appendChild(element); |
| 152 | 185 |
| 153 // Set any attributes before canPlayThrough. | 186 // Set any attributes before canPlayThrough. |
| 154 if (spec.mutedType == "yes") | 187 if (spec.mutedType == "yes") |
| 155 element.muted = true; | 188 element.muted = true; |
| 156 if (spec.autoplayType == "attr") | 189 if (spec.autoplayType == "attr") |
| 157 element.autoplay = true; | 190 element.autoplay = true; |
| 191 |
| 158 becomeOptimizedForMobile(spec.mobileType == "yes"); | 192 becomeOptimizedForMobile(spec.mobileType == "yes"); |
| 159 | 193 |
| 194 spec.playedEarly = "-"; |
| 195 |
| 160 // Record the spec in the element, so that we can display the | 196 // Record the spec in the element, so that we can display the |
| 161 // results later. | 197 // results later. |
| 162 element.spec = spec; | 198 element.spec = spec; |
| 199 window.internals.triggerAutoplayViewportCheck(element); |
| 163 | 200 |
| 164 // Wait for canplaythrough before continuing, so that the media | 201 // Wait for canplaythrough before continuing, so that the media |
| 165 // might actually be playing. | 202 // might actually be playing. |
| 166 element.addEventListener("canplaythrough", function() | 203 element.addEventListener("canplaythrough", function() |
| 167 { | 204 { |
| 168 // Now that we can play, if we're supposed to play / mute via js do so. | 205 // Now that we can play, if we're supposed to play / mute via js do so. |
| 169 configureElementViaScript(element, spec); | 206 configureElementViaScript(element, spec); |
| 170 | 207 |
| 171 // Record the results. | 208 // If we're supposed to scroll the item onscreen after it is ready to |
| 172 checkElementStatus(element); | 209 // play, then do so now. |
| 210 if(spec.visType == "scroll") { |
| 211 // Record the play state here, too, before we scroll. |
| 212 spec.playedEarly = didPlaybackStart(element) ? "yes" : "no"; |
| 213 |
| 214 // We are supposed to scroll the player into view. |
| 215 element.scrollIntoView(true); |
| 216 // TODO(liberato): remove once autoplay gesture override experiment
concludes. |
| 217 window.internals.triggerAutoplayViewportCheck(element); |
| 218 // Once these two methods return, changes to the element state due |
| 219 // to the autoplay experiment should be observable synchronously. |
| 220 checkElementStatus(element, spec); |
| 221 } else { |
| 222 // Record the results immediately. |
| 223 checkElementStatus(element, spec); |
| 224 } |
| 173 }); | 225 }); |
| 174 | 226 |
| 175 // Set the source, which will eventually lead to canPlayThrough. | 227 // Set the source, which will eventually lead to canPlayThrough. |
| 176 element.src = mediaFile; | 228 element.src = mediaFile; |
| 177 } | 229 } |
| 178 | 230 |
| 179 var experimentTypes = [ | 231 var experimentTypes = [ |
| 180 "none", | 232 "none", |
| 181 "enabled-forvideo", | 233 "enabled-forvideo", |
| 182 "enabled-forvideo-ifmuted", | 234 "enabled-forvideo-ifpagevisible", |
| 183 "enabled-forvideo-playmuted", | 235 "enabled-forvideo-ifviewport", |
| 236 "enabled-forvideo-ifviewport-ifmuted", |
| 237 "enabled-forvideo-ifviewport-playmuted", |
| 184 "enabled-foraudio", | 238 "enabled-foraudio", |
| 185 "enabled-forvideo-ifmobile", | 239 "enabled-forvideo-ifmobile", |
| 240 "enabled-foraudio-ifviewport", |
| 186 ]; | 241 ]; |
| 187 var elementTypes = ["video", "audio"]; | 242 var elementTypes = ["video", "audio"]; |
| 188 var autoplayTypes = ["none", "attr", "play()"]; | 243 var autoplayTypes = ["none", "attr", "play()"]; |
| 189 var mutedTypes = ["no", "yes"]; | 244 var mutedTypes = ["no", "yes"]; |
| 245 var visTypes = ["onscreen", "scroll", "offscreen", "obscured"]; |
| 190 // mobileTypes must always start with no, since we cannot un-optimize the page. | 246 // mobileTypes must always start with no, since we cannot un-optimize the page. |
| 191 var mobileTypes = ["no", "yes"]; | 247 var mobileTypes = ["no", "yes"]; |
| 192 | 248 |
| 193 function runNextConfig() | 249 function runNextConfig() |
| 194 { | 250 { |
| 195 // Convert configNumber into a spec, and run it. | 251 // Convert configNumber into a spec, and run it. |
| 196 var exp = configNumber; | 252 var exp = configNumber; |
| 197 | 253 |
| 198 // Convert this experiment number into settings. | 254 // Convert this experiment number into settings. |
| 199 var spec = {}; | 255 var spec = {}; |
| 200 spec.elementType = elementTypes[exp % elementTypes.length]; | 256 spec.elementType = elementTypes[exp % elementTypes.length]; |
| 201 exp = Math.floor(exp / elementTypes.length); | 257 exp = Math.floor(exp / elementTypes.length); |
| 202 spec.experimentType = experimentTypes[exp % experimentTypes.length]; | 258 spec.experimentType = experimentTypes[exp % experimentTypes.length]; |
| 203 exp = Math.floor(exp / experimentTypes.length); | 259 exp = Math.floor(exp / experimentTypes.length); |
| 204 spec.autoplayType = autoplayTypes[exp % autoplayTypes.length]; | 260 spec.autoplayType = autoplayTypes[exp % autoplayTypes.length]; |
| 205 exp = Math.floor(exp / autoplayTypes.length); | 261 exp = Math.floor(exp / autoplayTypes.length); |
| 206 spec.mutedType = mutedTypes[exp % mutedTypes.length]; | 262 spec.mutedType = mutedTypes[exp % mutedTypes.length]; |
| 207 exp = Math.floor(exp / mutedTypes.length); | 263 exp = Math.floor(exp / mutedTypes.length); |
| 264 spec.visType = visTypes[exp % visTypes.length]; |
| 265 exp = Math.floor(exp / visTypes.length); |
| 208 // Mobile must always change last, so that all the "no" cases precede | 266 // Mobile must always change last, so that all the "no" cases precede |
| 209 // all the "yes" cases, since we can't switch the doc back to "not | 267 // all the "yes" cases, since we can't switch the doc back to "not |
| 210 // optimized for mobile". | 268 // optimized for mobile". |
| 211 spec.mobileType = mobileTypes[exp % mobileTypes.length]; | 269 spec.mobileType = mobileTypes[exp % mobileTypes.length]; |
| 212 exp = Math.floor(exp / mobileTypes.length); | 270 exp = Math.floor(exp / mobileTypes.length); |
| 213 spec.experimentNumber = configNumber; | 271 spec.experimentNumber = configNumber; |
| 214 | 272 |
| 215 // Return null if configNumber was larger than the highest experiment. | 273 // Return null if configNumber was larger than the highest experiment. |
| 216 if (exp > 0) | 274 if (exp > 0) |
| 217 endTest(); | 275 endTest(); |
| 218 | 276 |
| 219 configNumber++; | 277 configNumber++; |
| 220 | 278 |
| 221 // To keep the test fast, skip a few combinations. | 279 // To keep the test fast, skip a few combinations. |
| 222 var skip = false; | 280 var skip = false; |
| 223 if (spec.experimentType.indexOf("-ifmuted") == -1 && spec.mutedType != "no") | 281 if (!spec.experimentType.includes("-ifmuted") && spec.mutedType != "no") |
| 224 skip = true; | 282 skip = true; |
| 225 | 283 |
| 226 // Only allow basic combinations for the mobile case. We just want to | 284 // Only allow basic combinations for the mobile case. We just want to |
| 227 // test video with autoplay, no mute options when testing -ifmobile. | 285 // test video with autoplay, no mute options when testing -ifmobile. |
| 228 // Similarly, if we're setting the page to be optimied for mobile, then | 286 // Similarly, if we're setting the page to be optimied for mobile, then |
| 229 // require that we're one of those tests. | 287 // require that we're one of those tests. |
| 230 if ((spec.mobileType == "yes" || spec.experimentType.indexOf("-ifmobile")>0) | 288 if ((spec.mobileType == "yes" || spec.experimentType.includes("-ifmobile")) |
| 231 && (spec.elementType != "video" || spec.autoplayType != "attr" | 289 && (spec.elementType != "video" || spec.autoplayType != "attr" |
| 232 || spec.mutedType != "no" | 290 || spec.mutedType != "no" |
| 291 || spec.visType != "onscreen" |
| 233 || (spec.experimentType != "enabled-forvideo" | 292 || (spec.experimentType != "enabled-forvideo" |
| 234 && spec.experimentType != "enabled-forvideo-ifmobile"))) | 293 && spec.experimentType != "enabled-forvideo-ifmobile"))) |
| 235 skip = true; | 294 skip = true; |
| 236 | 295 |
| 296 var mismatched =(spec.elementType == "video" |
| 297 && spec.experimentType.includes("-foraudio")) |
| 298 || (spec.elementType == "audio" |
| 299 && spec.experimentType.includes("-forvideo")); |
| 300 |
| 301 if (spec.autoplayType == "none" && spec.visType != 'onscreen') |
| 302 skip = true; |
| 303 else if (spec.experimentType.includes("-ifmuted") |
| 304 && spec.visType != "onscreen") |
| 305 skip = true; |
| 306 else if (spec.visType == "offscreen" |
| 307 && spec.autoplayType != "attr") |
| 308 skip = true; |
| 309 else if (!spec.experimentType.includes("-ifmuted") |
| 310 && spec.mutedType == "yes") |
| 311 skip = true; |
| 312 else if (spec.elementType == "audio" && spec.mutedType == "yes") |
| 313 skip = true; |
| 314 else if (spec.elementType == "audio" && spec.visType != "scroll") |
| 315 skip = true; |
| 316 else if (mismatched && spec.visType !="onscreen") |
| 317 skip = true; |
| 318 else if (mismatched && spec.autoplayType != "attr") |
| 319 skip = true; |
| 320 else if (spec.visType == "obscured" |
| 321 && !spec.experimentType.includes("-ifpagevisible")) |
| 322 skip = true; |
| 323 else if ((spec.visType == "offscreen" || spec.visType == "scroll" |
| 324 || spec.autoplayType != "attr" || spec.elementType != "video") |
| 325 && spec.experimentType.includes("-ifpagevisible")) |
| 326 skip = true; |
| 327 |
| 237 if (skip) | 328 if (skip) |
| 238 queueNextExperiment(); | 329 queueNextExperiment(); |
| 239 else | 330 else |
| 240 runOneConfig(spec); | 331 runOneConfig(spec); |
| 241 } | 332 } |
| 242 | 333 |
| 243 window.internals.settings.setMediaPlaybackRequiresUserGesture(true); | 334 window.internals.settings.setMediaPlaybackRequiresUserGesture(true); |
| 244 runNextConfig(); | 335 runNextConfig(); |
| 245 | 336 |
| 246 </script> | 337 </script> |
| 247 </html> | 338 </html> |
| OLD | NEW |