Commit e40637e2 authored by François Beaufort's avatar François Beaufort Committed by Commit Bot

[Picture-In-Picture] Expose some APIs behind a runtime feature.

https://wicg.github.io/picture-in-picture/

The API is currently not working. It is mostly about implementing the shell: it
exposes document.pictureInPictureEnabled, document.exitPictureInPicture(), and
video.requestPictureInPicture(). It also adds the "picture-in-picture" feature
policy. Everything is gated by the "PictureInPictureAPI" runtime feature.

Intent to implement: https://groups.google.com/a/chromium.org/d/msg/blink-dev/U8Apo-WLBm4/03sO4ITYAQAJ

Change-Id: I64d8e17c6975017565c850afeeca19d94e9f9ceb
Bug: 806249
Reviewed-on: https://chromium-review.googlesource.com/867915
Commit-Queue: Mounir Lamouri (slow) <mlamouri@chromium.org>
Reviewed-by: default avatarMounir Lamouri (slow) <mlamouri@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarIan Clelland <iclelland@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533310}
parent cad9c3d1
<!DOCTYPE html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src=/feature-policy/resources/featurepolicy.js></script>
<script src=/feature-policy/resources/picture-in-picture.js></script>
<script>
'use strict';
const relative_path = '/feature-policy/resources/feature-policy-picture-in-picture.html';
const base_src = '/feature-policy/resources/redirect-on-load.html#';
const same_origin_src = base_src + relative_path;
const cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' +
relative_path;
const header = 'Feature-Policy allow="picture-in-picture"';
async_test(t => {
test_feature_availability(
'picture-in-picture', t, same_origin_src,
expect_feature_available_default, 'picture-in-picture');
}, header + ' allows same-origin navigation in an iframe.');
async_test(t => {
test_feature_availability(
'picture-in-picture', t, cross_origin_src,
expect_feature_unavailable_default, 'picture-in-picture');
}, header + ' disallows cross-origin navigation in an iframe.');
</script>
</body>
<!DOCTYPE html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src=/feature-policy/resources/featurepolicy.js></script>
<script src=/feature-policy/resources/picture-in-picture.js></script>
<script>
'use strict';
const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html';
const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
same_origin_src;
const feature_name = 'Feature policy "picture-in-picture"';
const header = 'allow="picture-in-picture" attribute';
async_test(t => {
test_feature_availability(
'picture-in-picture', t, same_origin_src,
expect_feature_available_default, 'picture-in-picture');
}, feature_name + ' can be enabled in same-origin iframe using ' + header);
async_test(t => {
test_feature_availability(
'picture-in-picture', t, cross_origin_src,
expect_feature_available_default, 'picture-in-picture');
}, feature_name + ' can be enabled in cross-origin iframe using ' + header);
</script>
</body>
<!DOCTYPE html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src=/feature-policy/resources/featurepolicy.js></script>
<script src=/feature-policy/resources/picture-in-picture.js></script>
<script>
'use strict';
const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html';
const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
same_origin_src;
const header = 'Feature-Policy header: picture-in-picture *';
async_test(t => {
isPictureInPictureAllowed().then(t.step_func_done((result) => {
assert_true(result);
}));
}, header + ' allows the top-level document.');
async_test(t => {
test_feature_availability('picture-in-picture', t, same_origin_src,
expect_feature_available_default);
}, header + ' allows same-origin iframes.');
async_test(t => {
test_feature_availability('picture-in-picture', t, cross_origin_src,
expect_feature_available_default);
}, header + ' allows cross-origin iframes.');
</script>
</body>
<!DOCTYPE html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src=/feature-policy/resources/featurepolicy.js></script>
<script src=/feature-policy/resources/picture-in-picture.js></script>
<script>
'use strict';
const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html';
const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
same_origin_src;
const header = 'Default "picture-in-picture" feature policy ["self"]';
async_test(t => {
isPictureInPictureAllowed().then(t.step_func_done((result) => {
assert_true(result);
}));
}, header + ' allows the top-level document.');
async_test(t => {
test_feature_availability('picture-in-picture', t, same_origin_src,
expect_feature_available_default);
}, header + ' allows same-origin iframes.');
async_test(t => {
test_feature_availability('picture-in-picture', t, cross_origin_src,
expect_feature_unavailable_default,);
}, header + ' disallows cross-origin iframes.');
</script>
</body>
<!DOCTYPE html>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src=/feature-policy/resources/featurepolicy.js></script>
<script src=/feature-policy/resources/picture-in-picture.js></script>
<script>
'use strict';
const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html';
const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
same_origin_src;
const header = 'Feature-Policy header: picture-in-picture "none"';
async_test(t => {
isPictureInPictureAllowed().then(t.step_func_done((result) => {
assert_false(result);
}));
}, header + ' disallows the top-level document.');
async_test(t => {
test_feature_availability('picture-in-picture', t, same_origin_src,
expect_feature_unavailable_default);
}, header + ' disallows same-origin iframes.');
async_test(t => {
test_feature_availability('picture-in-picture', t, cross_origin_src,
expect_feature_unavailable_default,);
}, header + ' disallows cross-origin iframes.');
</script>
</body>
<script src=/feature-policy/resources/picture-in-picture.js></script>
<script>
'use strict';
window.addEventListener('load', () => {
isPictureInPictureAllowed().then(result => {
window.parent.postMessage({ enabled: result }, '*');
});
}, { once: true });
</script>
function isPictureInPictureAllowed() {
return new Promise((resolve, reject) => {
const video = document.createElement('video');
video.requestPictureInPicture()
.then(() => resolve(document.pictureInPictureEnabled))
.catch(e => {
if (e.name == 'NotAllowedError')
resolve(document.pictureInPictureEnabled);
else
resolve(false);
});
});
}
\ No newline at end of file
<!DOCTYPE html>
<title>Test request picture in picture requires a user gesture</title>
<script src='../../resources/testharness.js'></script>
<script src='../../resources/testharnessreport.js'></script>
<body></body>
<script>
promise_test(t => {
return promise_rejects(t, 'NotAllowedError',
document.createElement('video').requestPictureInPicture());
}, );
</script>
<!DOCTYPE html>
<title>Test request picture in picture</title>
<script src='../../resources/testharness.js'></script>
<script src='../../resources/testharnessreport.js'></script>
<script src="../../resources/testdriver.js"></script>
<script src="../../resources/testdriver-vendor.js"></script>
<script src="resources/picture-in-picture-helpers.js"></script>
<body></body>
<script>
promise_test(t => {
return promise_rejects(t, 'NotSupportedError',
requestPictureInPictureWithTrustedClick(document.createElement('video')));
});
</script>
function callWithTrustedClick(callback) {
return new Promise(resolve => {
let button = document.createElement('button');
button.textContent = 'click to continue test';
button.style.display = 'block';
button.style.fontSize = '20px';
button.style.padding = '10px';
button.onclick = () => {
document.body.removeChild(button);
resolve(callback());
};
document.body.appendChild(button);
test_driver.click(button);
});
}
// Calls requestPictureInPicture() in a context that's 'allowed to request PiP'.
function requestPictureInPictureWithTrustedClick(videoElement) {
return callWithTrustedClick(
() => videoElement.requestPictureInPicture());
}
\ No newline at end of file
......@@ -1093,6 +1093,7 @@ html element video
property preload
property readyState
property remote
property requestPictureInPicture
property seekable
property seeking
property setMediaKeys
......
......@@ -1576,6 +1576,7 @@ interface Document : Node
getter onwebkitfullscreenerror
getter onwheel
getter origin
getter pictureInPictureEnabled
getter plugins
getter pointerLockElement
getter policy
......@@ -1631,6 +1632,7 @@ interface Document : Node
method evaluate
method execCommand
method exitFullscreen
method exitPictureInPicture
method exitPointerLock
method getAnimations
method getElementById
......@@ -3643,6 +3645,7 @@ interface HTMLVideoElement : HTMLMediaElement
getter width
method constructor
method getVideoPlaybackQuality
method requestPictureInPicture
method webkitEnterFullScreen
method webkitEnterFullscreen
method webkitExitFullScreen
......
......@@ -112,7 +112,6 @@ core_idl_files =
"dom/DOMStringList.idl",
"dom/DOMStringMap.idl",
"dom/DOMTokenList.idl",
"dom/Document.idl",
"dom/DocumentFragment.idl",
"dom/DocumentType.idl",
"dom/Element.idl",
......@@ -458,6 +457,7 @@ core_idl_with_modules_dependency_files =
get_path_info([
"clipboard/DataTransferItem.idl",
"css/CSS.idl",
"dom/Document.idl",
"frame/Navigator.idl",
"frame/Screen.idl",
"frame/Window.idl",
......
......@@ -135,6 +135,7 @@ target("jumbo_" + modules_target_type, "modules") {
"//third_party/WebKit/Source/modules/payments",
"//third_party/WebKit/Source/modules/peerconnection",
"//third_party/WebKit/Source/modules/permissions",
"//third_party/WebKit/Source/modules/picture_in_picture",
"//third_party/WebKit/Source/modules/plugins",
"//third_party/WebKit/Source/modules/presentation",
"//third_party/WebKit/Source/modules/push_messaging",
......@@ -287,6 +288,7 @@ jumbo_source_set("unit_tests") {
"payments/PaymentsValidatorsTest.cpp",
"peerconnection/RTCDataChannelTest.cpp",
"peerconnection/RTCPeerConnectionTest.cpp",
"picture_in_picture/PictureInPictureTest.cpp",
"presentation/MockPresentationService.h",
"presentation/MockWebPresentationClient.h",
"presentation/PresentationAvailabilityStateTest.cpp",
......
......@@ -700,6 +700,8 @@ modules_dependency_idl_files =
"payments/PaymentAppServiceWorkerRegistration.idl",
"permissions/NavigatorPermissions.idl",
"permissions/WorkerNavigatorPermissions.idl",
"picture_in_picture/DocumentPictureInPicture.idl",
"picture_in_picture/HTMLVideoElementPictureInPicture.idl",
"plugins/NavigatorPlugins.idl",
"presentation/NavigatorPresentation.idl",
"push_messaging/ServiceWorkerGlobalScopePush.idl",
......
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/WebKit/Source/modules/modules.gni")
blink_modules_sources("picture_in_picture") {
sources = [
"DocumentPictureInPicture.cpp",
"DocumentPictureInPicture.h",
"HTMLVideoElementPictureInPicture.cpp",
"HTMLVideoElementPictureInPicture.h",
"PictureInPictureController.cpp",
"PictureInPictureController.h",
]
}
include_rules = [
"-modules",
"+modules/ModulesExport.h",
"+modules/picture_in_picture",
]
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/picture_in_picture/DocumentPictureInPicture.h"
#include "modules/picture_in_picture/PictureInPictureController.h"
namespace blink {
// static
bool DocumentPictureInPicture::pictureInPictureEnabled(Document& document) {
return PictureInPictureController::Ensure(document).PictureInPictureEnabled();
}
// static
ScriptPromise DocumentPictureInPicture::exitPictureInPicture(
ScriptState* script_state,
const Document&) {
// TODO(crbug.com/806249): Call element.exitPictureInPicture().
return ScriptPromise::CastUndefined(script_state);
}
} // namespace blink
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DocumentPictureInPicture_h
#define DocumentPictureInPicture_h
#include "platform/heap/Handle.h"
namespace blink {
class Document;
class ScriptPromise;
class ScriptState;
class DocumentPictureInPicture {
STATIC_ONLY(DocumentPictureInPicture);
public:
static bool pictureInPictureEnabled(Document&);
static ScriptPromise exitPictureInPicture(ScriptState*, const Document&);
};
} // namespace blink
#endif // DocumentPictureInPicture_h
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://wicg.github.io/picture-in-picture/#document-extensions
[
ImplementedAs=DocumentPictureInPicture,
RuntimeEnabled=PictureInPictureAPI
]
partial interface Document {
readonly attribute boolean pictureInPictureEnabled;
[CallWith=ScriptState] Promise<void> exitPictureInPicture();
};
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/picture_in_picture/HTMLVideoElementPictureInPicture.h"
#include "core/dom/DOMException.h"
#include "core/html/media/HTMLVideoElement.h"
#include "modules/picture_in_picture/PictureInPictureController.h"
#include "platform/feature_policy/FeaturePolicy.h"
namespace blink {
using Status = PictureInPictureController::Status;
namespace {
const char kDetachedError[] =
"The element is no longer associated with a document.";
const char kFeaturePolicyBlocked[] =
"Access to the feature \"picture-in-picture\" is disallowed by feature "
"policy.";
const char kNotAvailable[] = "Picture-in-Picture is not available.";
const char kUserGestureRequired[] =
"Must be handling a user gesture to request picture in picture.";
} // namespace
// static
ScriptPromise HTMLVideoElementPictureInPicture::requestPictureInPicture(
ScriptState* script_state,
HTMLVideoElement& element) {
Document& document = element.GetDocument();
if (!document.GetFrame()) {
return ScriptPromise::RejectWithDOMException(
script_state, DOMException::Create(kInvalidStateError, kDetachedError));
}
switch (PictureInPictureController::Ensure(document).GetStatus()) {
case Status::kDisabledByFeaturePolicy:
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(kSecurityError, kFeaturePolicyBlocked));
case Status::kDisabledBySystem:
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(kNotSupportedError, kNotAvailable));
case Status::kEnabled:
break;
}
LocalFrame* frame = element.GetFrame();
if (!Frame::ConsumeTransientUserActivation(frame)) {
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(kNotAllowedError, kUserGestureRequired));
}
// TODO(crbug.com/806249): Call element.enterPictureInPicture().
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(kNotSupportedError, "Not implemented yet"));
}
} // namespace blink
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef HTMLVideoElementPictureInPicture_h
#define HTMLVideoElementPictureInPicture_h
#include "modules/ModulesExport.h"
#include "platform/heap/Handle.h"
namespace blink {
class HTMLVideoElement;
class ScriptPromise;
class ScriptState;
class MODULES_EXPORT HTMLVideoElementPictureInPicture {
STATIC_ONLY(HTMLVideoElementPictureInPicture);
public:
static ScriptPromise requestPictureInPicture(ScriptState*, HTMLVideoElement&);
};
} // namespace blink
#endif // HTMLVideoElementPictureInPicture_h
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://wicg.github.io/picture-in-picture/#htmlvideoelement-extensions
[
ImplementedAs=HTMLVideoElementPictureInPicture,
RuntimeEnabled=PictureInPictureAPI
]
partial interface HTMLVideoElement {
// TODO(crbug.com/806249): Promise should return a PiPWindow.
[CallWith=ScriptState] Promise<void> requestPictureInPicture();
// TODO(crbug.com/806249): Implement PiP video events.
//attribute EventHandler onenterpictureinpicture;
//attribute EventHandler onleavepictureinpicture;
};
\ No newline at end of file
# TEAM: media-dev@chromium.org
# COMPONENT: Blink>Media>PictureInPicture
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/picture_in_picture/PictureInPictureController.h"
#include "core/dom/Document.h"
#include "platform/feature_policy/FeaturePolicy.h"
namespace blink {
PictureInPictureController::PictureInPictureController(Document& document)
: Supplement<Document>(document) {}
PictureInPictureController::~PictureInPictureController() = default;
// static
PictureInPictureController& PictureInPictureController::Ensure(
Document& document) {
PictureInPictureController* controller =
static_cast<PictureInPictureController*>(
Supplement<Document>::From(document, SupplementName()));
if (!controller) {
controller = new PictureInPictureController(document);
ProvideTo(document, SupplementName(), controller);
}
return *controller;
}
// static
const char* PictureInPictureController::SupplementName() {
return "PictureInPictureController";
}
bool PictureInPictureController::PictureInPictureEnabled() const {
return GetStatus() == Status::kEnabled;
}
PictureInPictureController::Status PictureInPictureController::GetStatus()
const {
DCHECK(GetSupplementable());
// If document is not allowed to use the policy-controlled feature named
// "picture-in-picture", return kDisabledByFeaturePolicy status.
LocalFrame* frame = GetSupplementable()->GetFrame();
if (IsSupportedInFeaturePolicy(
blink::FeaturePolicyFeature::kPictureInPicture) &&
!frame->IsFeatureEnabled(
blink::FeaturePolicyFeature::kPictureInPicture)) {
return Status::kDisabledByFeaturePolicy;
}
// TODO(crbug.com/806249): Handle status when disabled by attribute
// `picture_in_picture_enabled_` is set to false by the embedder when it
// or the system forbids the page from using Picture-in-Picture.
if (!picture_in_picture_enabled_)
return Status::kDisabledBySystem;
return Status::kEnabled;
}
void PictureInPictureController::SetPictureInPictureEnabledForTesting(
bool value) {
picture_in_picture_enabled_ = value;
}
} // namespace blink
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PictureInPictureController_h
#define PictureInPictureController_h
#include "core/frame/LocalFrame.h"
#include "modules/ModulesExport.h"
namespace blink {
class MODULES_EXPORT PictureInPictureController
: public GarbageCollectedFinalized<PictureInPictureController>,
public Supplement<Document> {
USING_GARBAGE_COLLECTED_MIXIN(PictureInPictureController);
WTF_MAKE_NONCOPYABLE(PictureInPictureController);
public:
virtual ~PictureInPictureController();
static PictureInPictureController& Ensure(Document&);
static const char* SupplementName();
bool PictureInPictureEnabled() const;
void SetPictureInPictureEnabledForTesting(bool);
enum class Status {
kEnabled,
kDisabledByFeaturePolicy,
kDisabledBySystem,
};
Status GetStatus() const;
private:
explicit PictureInPictureController(Document&);
bool picture_in_picture_enabled_ = true;
};
} // namespace blink
#endif // PictureInPictureController_h
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "bindings/core/v8/V8BindingForCore.h"
#include "core/html/media/HTMLVideoElement.h"
#include "core/testing/PageTestBase.h"
#include "modules/picture_in_picture/HTMLVideoElementPictureInPicture.h"
#include "modules/picture_in_picture/PictureInPictureController.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
const char kNotSupportedString[] =
"NotSupportedError: Picture-in-Picture is not available.";
class PictureInPictureTest : public PageTestBase {
protected:
void SetUp() final { PageTestBase::SetUp(); }
ScriptState* GetScriptState() {
return ToScriptStateForMainWorld(GetDocument().GetFrame());
}
v8::Isolate* GetIsolate() { return GetScriptState()->GetIsolate(); }
v8::Local<v8::Context> GetContext() { return GetScriptState()->GetContext(); }
// Convenience methods for testing the returned promises.
ScriptValue GetRejectValue(ScriptPromise& promise) {
ScriptValue on_reject;
promise.Then(UnreachableFunction::Create(GetScriptState()),
TestFunction::Create(GetScriptState(), &on_reject));
v8::MicrotasksScope::PerformCheckpoint(GetIsolate());
return on_reject;
}
std::string GetRejectString(ScriptPromise& promise) {
ScriptValue on_reject = GetRejectValue(promise);
return ToCoreString(
on_reject.V8Value()->ToString(GetContext()).ToLocalChecked())
.Ascii()
.data();
}
private:
// A ScriptFunction that creates a test failure if it is ever called.
class UnreachableFunction : public ScriptFunction {
public:
static v8::Local<v8::Function> Create(ScriptState* script_state) {
UnreachableFunction* self = new UnreachableFunction(script_state);
return self->BindToV8Function();
}
ScriptValue Call(ScriptValue value) override {
ADD_FAILURE() << "Unexpected call to a null ScriptFunction.";
return value;
}
private:
UnreachableFunction(ScriptState* script_state)
: ScriptFunction(script_state) {}
};
// A ScriptFunction that saves its parameter; used by tests to assert on
// correct values being passed.
class TestFunction : public ScriptFunction {
public:
static v8::Local<v8::Function> Create(ScriptState* script_state,
ScriptValue* out_value) {
TestFunction* self = new TestFunction(script_state, out_value);
return self->BindToV8Function();
}
ScriptValue Call(ScriptValue value) override {
DCHECK(!value.IsEmpty());
*value_ = value;
return value;
}
private:
TestFunction(ScriptState* script_state, ScriptValue* out_value)
: ScriptFunction(script_state), value_(out_value) {}
ScriptValue* value_;
};
};
TEST_F(PictureInPictureTest,
RequestPictureInPictureRejectsWhenPictureInPictureEnabledIsFalse) {
Persistent<PictureInPictureController> controller =
PictureInPictureController::Ensure(GetDocument());
ScriptState::Scope scope(GetScriptState());
HTMLVideoElement& video =
static_cast<HTMLVideoElement&>(*HTMLVideoElement::Create(GetDocument()));
controller->SetPictureInPictureEnabledForTesting(false);
ScriptPromise promise =
HTMLVideoElementPictureInPicture::requestPictureInPicture(
GetScriptState(), video);
EXPECT_EQ(kNotSupportedString, GetRejectString(promise));
}
TEST_F(PictureInPictureTest,
PictureInPictureEnabledReturnsFalseWhenPictureInPictureEnabledIsFalse) {
Persistent<PictureInPictureController> controller =
PictureInPictureController::Ensure(GetDocument());
controller->SetPictureInPictureEnabledForTesting(false);
EXPECT_FALSE(controller->PictureInPictureEnabled());
}
} // namespace blink
......@@ -211,6 +211,9 @@ bool IsSupportedInFeaturePolicy(FeaturePolicyFeature feature) {
case FeaturePolicyFeature::kAmbientLightSensor:
case FeaturePolicyFeature::kGyroscope:
case FeaturePolicyFeature::kMagnetometer:
return true;
case FeaturePolicyFeature::kPictureInPicture:
return RuntimeEnabledFeatures::PictureInPictureAPIEnabled();
case FeaturePolicyFeature::kSyncXHR:
return true;
case FeaturePolicyFeature::kVibrate:
......@@ -246,6 +249,10 @@ const FeatureNameMap& GetDefaultFeatureNameMap() {
default_feature_name_map.Set("gyroscope", FeaturePolicyFeature::kGyroscope);
default_feature_name_map.Set("magnetometer",
FeaturePolicyFeature::kMagnetometer);
if (RuntimeEnabledFeatures::PictureInPictureAPIEnabled()) {
default_feature_name_map.Set("picture-in-picture",
FeaturePolicyFeature::kPictureInPicture);
}
if (RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled()) {
default_feature_name_map.Set("vibrate", FeaturePolicyFeature::kVibrate);
default_feature_name_map.Set("cookie",
......
......@@ -827,6 +827,10 @@
name: "PictureInPicture",
settable_from_internals: true,
},
{
name: "PictureInPictureAPI",
status: "test",
},
{
name: "PreciseMemoryInfo",
},
......
......@@ -44,7 +44,7 @@ A step-to-step guide with examples.
##### Shipping features behind a flag
There are currently two runtime-enabled flags: `FeaturePolicy` (status:
stable) and `FeaturePolicyExperiementalFeatures` (staus: experimental).
stable) and `FeaturePolicyExperimentalFeatures` (status: experimental).
If the additional feature is unshipped, or if the correct behaviour with feature
policy is undetermined, consider shipping the feature behind a flag (i.e.,
`FeaturePolicyExperimentalFeatures`).
......@@ -55,7 +55,13 @@ policy is undetermined, consider shipping the feature behind a flag (i.e.,
enum with a brief decription about what the feature does in the comment, right
above `LAST_FEATURE`
2. Update `third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp`:
2. Append the new feature enum with a brief description as well in
`third_party/WebKit/common/feature_policy/feature_policy.mojom`
3. Update `third_party/WebKit/common/feature_policy/feature_policy_struct_traits.h`
to include the new feature
4. Update `third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp`:
Add your `("feature-name", FeatureEnumValue)` mapping to
`GetDefaultFeatureNameMap()` (note: "feature-name" is the string web
developers will be using to define the policy in the HTTP header and iframe
......
......@@ -279,7 +279,9 @@ const FeaturePolicy::FeatureList& FeaturePolicy::GetDefaultFeatureList() {
{FeaturePolicyFeature::kMagnetometer,
FeaturePolicy::FeatureDefault::EnableForSelf},
{FeaturePolicyFeature::kUnsizedMedia,
FeaturePolicy::FeatureDefault::EnableForAll}}));
FeaturePolicy::FeatureDefault::EnableForAll},
{FeaturePolicyFeature::kPictureInPicture,
FeaturePolicy::FeatureDefault::EnableForSelf}}));
return default_feature_list;
}
......
......@@ -84,6 +84,8 @@ enum FeaturePolicyFeature {
// Controls the layout size of intrinsically sized images and videos. When
// disabled, default size (300 x 150) is used to prevent relayout.
kUnsizedMedia,
// Controls access to Picture-in-Picture.
kPictureInPicture,
};
// This struct holds feature policy whitelist data that needs to be replicated
......
......@@ -62,7 +62,9 @@ enum class FeaturePolicyFeature {
// Controls the layout size of intrinsically sized images and videos. When
// disabled, default size (300 x 150) is used to prevent relayout.
kUnsizedMedia,
LAST_FEATURE = kUnsizedMedia
// Controls access to Picture-in-Picture.
kPictureInPicture,
LAST_FEATURE = kPictureInPicture
};
} // namespace blink
......
......@@ -65,6 +65,8 @@ STATIC_ASSERT_ENUM(::blink::FeaturePolicyFeature::kGyroscope,
::blink::mojom::FeaturePolicyFeature::kGyroscope);
STATIC_ASSERT_ENUM(::blink::FeaturePolicyFeature::kMagnetometer,
::blink::mojom::FeaturePolicyFeature::kMagnetometer);
STATIC_ASSERT_ENUM(::blink::FeaturePolicyFeature::kPictureInPicture,
::blink::mojom::FeaturePolicyFeature::kPictureInPicture);
// TODO(crbug.com/789818) - Merge these 2 WebSandboxFlags enums.
STATIC_ASSERT_ENUM(::blink::WebSandboxFlags::kNone,
......
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