Commit 02610543 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

Reland: Feature Policy: Enable "autoplay" feature

Add an "autoplay" feature that controls autoplay and delegate
activation state through frames.

BUG=788390

TBR=skyostil@chromium.org,iclelland@chromium.org

Change-Id: I67e40ae00b0ca7acad7bbef4b4cb4525a9e65fb3
Reviewed-on: https://chromium-review.googlesource.com/800851
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#520589}
parent 4675fd70
......@@ -4037,3 +4037,13 @@ crbug.com/789533 virtual/gpu/fast/canvas/OffscreenCanvas-2d-imageSmoothing.html
crbug.com/789485 [ Win ] virtual/scroll_customization/fast/events/touch/gesture/gesture-tap-mouse-events-between-frames.html [ Pass Failure ]
crbug.com/789921 [ Win7 ] media/video-controls-overflow-menu-last-button-visible.html [ Pass Failure ]
crbug.com/789111 virtual/pwa-full-code-cache/http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass Failure ]
# These tests are sporadically timing out on Linux.
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-delegation.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-gesture.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-header.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-feature-policy-alternating.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-feature-policy-iframe-no-gesture.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-feature-policy-same-origin.html [ Pass Timeout ]
crbug.com/788390 [ Linux ] http/tests/media/autoplay/document-user-activation-iframe-delegation.html [ Pass Timeout ]
<!DOCTYPE html>
<html>
<title>Test that gestures are delegated to child frames when the attribute is used.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a cross origin iframe with a same origin child with allow="autoplay" -->
<iframe allow="autoplay" src="http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html"></iframe>
<script>
simulateViewportClick();
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-cross-origin-feature-policy-delegation.html': true,
'http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html': true,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': true
});
</script>
</html>
<!DOCTYPE html>
<html>
<title>Test that autoplay is disabled for cross origin by default.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a cross origin iframe with a same origin child with allow="autoplay" -->
<iframe src="http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html"></iframe>
<script>
simulateViewportClick();
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html': true,
'http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html': false,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': false
});
</script>
</html>
<!DOCTYPE html>
<html>
<title>Test that autoplay works on a cross origin iframe if there is a gesture in that frame.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a cross origin iframe with a same origin child with allow="autoplay" -->
<iframe allow="autoplay" src="http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html"></iframe>
<script>
simulateFrameClick();
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-cross-origin-feature-policy-gesture.html': true,
'http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html': true,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': true
});
</script>
</html>
<!DOCTYPE html>
<html>
<title>Test that the header will override allow=autoplay.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a cross origin iframe that disables autoplay with a HTTP header -->
<iframe allow="autoplay" src="http://localhost:8000/media/autoplay/resources/nested-frame-with-header.php"></iframe>
<script>
simulateViewportClick();
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-cross-origin-feature-policy-header.html': true,
'http://localhost:8000/media/autoplay/resources/nested-frame-with-header.php': false
});
</script>
</html>
<!DOCTYPE html>
<html>
<title>Test that autoplay works on child frames, but only if allowed.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a cross origin iframe with a same origin child with no allow attribute -->
<iframe allow="autoplay" src="http://localhost:8000/media/autoplay/resources/nested-iframe-1b.html"></iframe>
<script>
simulateViewportClick();
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-feature-policy-alternating.html': true,
'http://localhost:8000/media/autoplay/resources/nested-iframe-1b.html': true,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': false
});
</script>
</html>
<!DOCTYPE html>
<html>
<title>Test that autoplay is still disabled if there is no gesture, but autoplay is switched on.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a cross origin iframe with a same origin child with allow="autoplay" -->
<iframe allow="autoplay" src="http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html"></iframe>
<script>
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-cross-origin-feature-policy-delegation.html': true,
'http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html': false,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': false
});
</script>
</html>
<!DOCTYPE html>
<html>
<title>Test that gestures are delegated automatically to same origin child frames.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="resources/autoplay-test.js"></script>
<!-- This is a same origin frame with a sub origin child frame with no allow attributes -->
<iframe src="/media/autoplay/resources/nested-iframe-1b.html"></iframe>
<script>
simulateViewportClick();
runTest({
'http://127.0.0.1:8000/media/autoplay/document-user-activation-feature-policy-same-origin.html': true,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-1b.html': true,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': true
});
</script>
</html>
<!DOCTYPE html>
<title>Test user gesture is delegated to iframes after navigation.</title>
<script>
chrome.gpuBenchmarking.pointerActionSequence([
{"source": "mouse",
"actions": [
{ "name": "pointerDown", "x": 0, "y": 0 },
{ "name": "pointerUp" } ]}]);
if (window.testRunner) {
testRunner.waitUntilDone();
testRunner.dumpAsText();
}
document.location.href = 'resources/test-autoplay-post-navigation.html';
</script>
/**
* Tests autoplay on a page and its subframes.
*/
// Tracks the results and how many active tests we have running.
let activeTests;
let testExpectations = {};
let callback;
function tearDown(result) {
// Reset the flag state.
window.internals.settings.setAutoplayPolicy('no-user-gesture-required');
let canAutoplay = true;
// Ensure that play failed because autoplay was blocked. If playback failed
// for another reason then we don't care because autoplay is always checked
// first.
if (result && result.name == 'NotAllowedError')
canAutoplay = false;
receivedResult({
url: window.location.href,
message: canAutoplay
});
}
function receivedResult(data) {
// Forward the result to the top frame.
if (!callback) {
top.postMessage(data, '*');
return;
}
activeTests--;
assert_equals(testExpectations[data.url], data.message);
if (!activeTests)
callback();
}
function runVideoTest() {
const video = document.createElement('video');
video.src = findMediaFile('video', '/media-resources/content/test');
video.play().then(tearDown, tearDown);
}
function simulateViewportClick() {
chrome.gpuBenchmarking.pointerActionSequence([
{"source": "mouse",
"actions": [
{ "name": "pointerDown", "x": 0, "y": 0 },
{ "name": "pointerUp" } ]}]);
}
function simulateFrameClick() {
const frame = document.getElementsByTagName('iframe')[0];
const rect = frame.getBoundingClientRect();
chrome.gpuBenchmarking.pointerActionSequence([
{"source": "mouse",
"actions": [
{
"name": "pointerDown",
"x": rect.left + (rect.width / 2),
"y": rect.top + (rect.height / 2)
},
{ "name": "pointerUp" } ]}]);
}
function runTest(expectations) {
async_test((t) => {
testExpectations = expectations;
activeTests = Object.keys(expectations).length;
callback = t.step_func_done();
});
}
window.addEventListener('load', () => {
if (!window.testRunner)
return;
// Setup the flags before the test is run.
window.internals.settings.setAutoplayPolicy('document-user-activation-required');
// Setup the event listener to forward messages.
window.addEventListener('message', (e) => {
if (callback) {
receivedResult(e.data);
} else {
top.postMessage(e.data, '*');
}
});
runVideoTest();
}, { once: true });
<?php
Header("Feature-Policy: autoplay 'none'");
?>
<!DOCTYPE html>
<html>
<script src="../../../media-resources/media-file.js"></script>
<script src="autoplay-test.js"></script>
</html>
<!DOCTYPE html>
<html>
<script src="../../../media-resources/media-file.js"></script>
<script src="autoplay-test.js"></script>
<iframe allow="autoplay" src="http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html"></iframe>
<!DOCTYPE html>
<html>
<script src="../../../media-resources/media-file.js"></script>
<script src="autoplay-test.js"></script>
<iframe src="http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html"></iframe>
<!DOCTYPE html>
<html>
<script src="../../../media-resources/media-file.js"></script>
<script src="autoplay-test.js"></script>
</html>
<!DOCTYPE html>
<html>
<title>Test user gesture is delegated to iframes after navigation.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/media-resources/media-file.js"></script>
<script src="autoplay-test.js"></script>
<!-- This is a cross origin iframe with a same origin child with allow="autoplay" -->
<iframe allow="autoplay" src="http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html"></iframe>
<script>
runTest({
'http://127.0.0.1:8000/media/autoplay/resources/test-autoplay-post-navigation.html': true,
'http://localhost:8000/media/autoplay/resources/nested-iframe-1a.html': true,
'http://127.0.0.1:8000/media/autoplay/resources/nested-iframe-2.html': true
});
</script>
</html>
......@@ -177,40 +177,4 @@ promise_test(t => {
document.body.appendChild(iframe);
});
}, 'iframe allowed to play if it received a gesture');
promise_test(t => {
t.add_cleanup(() => {
window.internals.settings.setAutoplayPolicy('no-user-gesture-required');
document.body.removeChild(document.querySelector('iframe'));
});
window.internals.settings.setAutoplayPolicy('document-user-activation-required');
var iframe = document.createElement('iframe');
return new Promise((resolve, reject) => {
iframe.addEventListener('load', () => {
var bounds = document.querySelector('#target').getBoundingClientRect();
var x = bounds.left + bounds.width / 2;
var y = bounds.top + bounds.height / 2;
chrome.gpuBenchmarking.pointerActionSequence([{
'source': 'mouse',
'actions': [
{ 'name': 'pointerDown', 'x': x, 'y': y },
{ 'name': 'pointerUp' },
],
}]);
window.addEventListener('message', e => {
e.data.result ? reject() : resolve();
});
iframe.contentWindow.postMessage({
command: 'play'
}, '*');
});
iframe.src = 'resources/document-user-activation-iframe.html';
document.body.appendChild(iframe);
});
}, 'iframe is not allowed to autoplay if the main frame received a gesture');
</script>
......@@ -16,6 +16,7 @@
#include "platform/wtf/Assertions.h"
#include "public/platform/WebMediaPlayer.h"
#include "public/web/WebSettings.h"
#include "third_party/WebKit/common/feature_policy/feature_policy_feature.h"
namespace blink {
......@@ -61,6 +62,27 @@ bool ComputeLockPendingUserGestureRequired(const Document& document) {
return true;
}
// Return true if any frame between |frame| and the root has been activated in
// either the current or previous navigation. If the
// FeaturePolicyAutoplayFeature flag is disabled then it will stop at the
// current frame.
bool HasBeenActivated(const Frame& frame) {
// Check if the current frame has received a user activation.
if (frame.HasBeenActivated() ||
frame.HasReceivedUserGestureBeforeNavigation()) {
return true;
}
// If feature policy is disabled then do not traverse the tree.
if (!RuntimeEnabledFeatures::FeaturePolicyAutoplayFeatureEnabled())
return false;
// If there is a parent check if the parent has received a
// user gesture.
const Frame* parent = frame.Tree().Parent();
return parent && HasBeenActivated(*parent);
}
} // anonymous namespace
// static
......@@ -84,8 +106,14 @@ AutoplayPolicy::Type AutoplayPolicy::GetAutoplayPolicyForDocument(
bool AutoplayPolicy::IsDocumentAllowedToPlay(const Document& document) {
if (!document.GetFrame())
return false;
return document.GetFrame()->HasBeenActivated() ||
document.GetFrame()->HasReceivedUserGestureBeforeNavigation();
// Check feature policy to see if autoplay is enabled.
if (RuntimeEnabledFeatures::FeaturePolicyAutoplayFeatureEnabled() &&
!document.GetFrame()->IsFeatureEnabled(FeaturePolicyFeature::kAutoplay)) {
return false;
}
return HasBeenActivated(*document.GetFrame());
}
AutoplayPolicy::AutoplayPolicy(HTMLMediaElement* element)
......
......@@ -241,6 +241,9 @@ const FeatureNameMap& GetDefaultFeatureNameMap() {
FeaturePolicyFeature::kSyncScript);
default_feature_name_map.Set("sync-xhr", FeaturePolicyFeature::kSyncXHR);
}
if (RuntimeEnabledFeatures::FeaturePolicyAutoplayFeatureEnabled()) {
default_feature_name_map.Set("autoplay", FeaturePolicyFeature::kAutoplay);
}
}
return default_feature_name_map;
}
......
......@@ -383,6 +383,10 @@
name: "FeaturePolicy",
status: "stable",
},
{
name: "FeaturePolicyAutoplayFeature",
status: "experimental"
},
{
name: "FeaturePolicyExperimentalFeatures",
},
......
......@@ -236,7 +236,9 @@ void FeaturePolicy::AddContainerPolicy(
// each feature (in spec, implemented, etc).
const FeaturePolicy::FeatureList& FeaturePolicy::GetDefaultFeatureList() {
CR_DEFINE_STATIC_LOCAL(FeatureList, default_feature_list,
({{FeaturePolicyFeature::kCamera,
({{FeaturePolicyFeature::kAutoplay,
FeaturePolicy::FeatureDefault::EnableForSelf},
{FeaturePolicyFeature::kCamera,
FeaturePolicy::FeatureDefault::EnableForSelf},
{FeaturePolicyFeature::kEncryptedMedia,
FeaturePolicy::FeatureDefault::EnableForSelf},
......
......@@ -15,6 +15,8 @@ namespace blink {
// featurePolicyExperimentalFeatures flag.
enum class FeaturePolicyFeature {
kNotFound = 0,
// Controls access to media autoplay.
kAutoplay,
// Controls access to video input devices.
kCamera,
// Controls whether navigator.requestMediaKeySystemAccess is allowed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment