зеркало из https://github.com/mozilla/gecko-dev.git
237 строки
8.2 KiB
C++
237 строки
8.2 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "GVAutoplayPermissionRequest.h"
|
|
|
|
#include "mozilla/dom/HTMLMediaElement.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/StaticPrefs_media.h"
|
|
|
|
mozilla::LazyLogModule gGVAutoplayRequestLog("GVAutoplay");
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
using RType = GVAutoplayRequestType;
|
|
using RStatus = GVAutoplayRequestStatus;
|
|
|
|
const char* ToGVRequestTypeStr(RType aType) {
|
|
switch (aType) {
|
|
case RType::eINAUDIBLE:
|
|
return "inaudible";
|
|
case RType::eAUDIBLE:
|
|
return "audible";
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Invalid request type.");
|
|
return "invalid";
|
|
}
|
|
}
|
|
|
|
const char* ToGVRequestStatusStr(RStatus aStatus) {
|
|
switch (aStatus) {
|
|
case RStatus::eUNKNOWN:
|
|
return "Unknown";
|
|
case RStatus::eALLOWED:
|
|
return "Allowed";
|
|
case RStatus::eDENIED:
|
|
return "Denied";
|
|
case RStatus::ePENDING:
|
|
return "Pending";
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Invalid status.");
|
|
return "Invalid";
|
|
}
|
|
}
|
|
|
|
// avoid redefined macro in unified build
|
|
#undef REQUEST_LOG
|
|
#define REQUEST_LOG(msg, ...) \
|
|
if (MOZ_LOG_TEST(gGVAutoplayRequestLog, mozilla::LogLevel::Debug)) { \
|
|
MOZ_LOG(gGVAutoplayRequestLog, LogLevel::Debug, \
|
|
("Request=%p, Type=%s, " msg, this, \
|
|
ToGVRequestTypeStr(this->mType), ##__VA_ARGS__)); \
|
|
}
|
|
|
|
#undef LOG
|
|
#define LOG(msg, ...) \
|
|
MOZ_LOG(gGVAutoplayRequestLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
|
|
|
static RStatus GetRequestStatus(BrowsingContext* aContext, RType aType) {
|
|
MOZ_ASSERT(aContext);
|
|
AssertIsOnMainThread();
|
|
return aType == RType::eAUDIBLE
|
|
? aContext->GetGVAudibleAutoplayRequestStatus()
|
|
: aContext->GetGVInaudibleAutoplayRequestStatus();
|
|
}
|
|
|
|
// This is copied from the value of `media.geckoview.autoplay.request.testing`.
|
|
enum class TestRequest : uint32_t {
|
|
ePromptAsNormal = 0,
|
|
eAllowAll = 1,
|
|
eDenyAll = 2,
|
|
eAllowAudible = 3,
|
|
eDenyAudible = 4,
|
|
eAllowInAudible = 5,
|
|
eDenyInAudible = 6,
|
|
eLeaveAllPending = 7,
|
|
};
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(GVAutoplayPermissionRequest,
|
|
ContentPermissionRequestBase)
|
|
|
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(GVAutoplayPermissionRequest,
|
|
ContentPermissionRequestBase)
|
|
|
|
/* static */
|
|
void GVAutoplayPermissionRequest::CreateRequest(nsGlobalWindowInner* aWindow,
|
|
BrowsingContext* aContext,
|
|
GVAutoplayRequestType aType) {
|
|
RefPtr<GVAutoplayPermissionRequest> request =
|
|
new GVAutoplayPermissionRequest(aWindow, aContext, aType);
|
|
request->SetRequestStatus(RStatus::ePENDING);
|
|
const TestRequest testingPref = static_cast<TestRequest>(
|
|
StaticPrefs::media_geckoview_autoplay_request_testing());
|
|
if (testingPref != TestRequest::ePromptAsNormal) {
|
|
LOG("Create testing request, tesing value=%u",
|
|
static_cast<uint32_t>(testingPref));
|
|
if (testingPref == TestRequest::eAllowAll ||
|
|
(testingPref == TestRequest::eAllowAudible &&
|
|
aType == RType::eAUDIBLE) ||
|
|
(testingPref == TestRequest::eAllowInAudible &&
|
|
aType == RType::eINAUDIBLE)) {
|
|
request->Allow(JS::UndefinedHandleValue);
|
|
} else if (testingPref == TestRequest::eDenyAll ||
|
|
(testingPref == TestRequest::eDenyAudible &&
|
|
aType == RType::eAUDIBLE) ||
|
|
(testingPref == TestRequest::eDenyInAudible &&
|
|
aType == RType::eINAUDIBLE)) {
|
|
request->Cancel();
|
|
}
|
|
} else {
|
|
LOG("Dispatch async request");
|
|
request->RequestDelayedTask(
|
|
aWindow->EventTargetFor(TaskCategory::Other),
|
|
GVAutoplayPermissionRequest::DelayedTaskType::Request);
|
|
}
|
|
}
|
|
|
|
GVAutoplayPermissionRequest::GVAutoplayPermissionRequest(
|
|
nsGlobalWindowInner* aWindow, BrowsingContext* aContext, RType aType)
|
|
: ContentPermissionRequestBase(
|
|
aWindow->GetPrincipal(), aWindow,
|
|
NS_LITERAL_CSTRING(""), // No testing pref used in this class
|
|
aType == RType::eAUDIBLE
|
|
? NS_LITERAL_CSTRING("autoplay-media-audible")
|
|
: NS_LITERAL_CSTRING("autoplay-media-inaudible")),
|
|
mType(aType),
|
|
mContext(aContext) {
|
|
MOZ_ASSERT(mContext);
|
|
REQUEST_LOG("Request created");
|
|
}
|
|
|
|
GVAutoplayPermissionRequest::~GVAutoplayPermissionRequest() {
|
|
REQUEST_LOG("Request destroyed");
|
|
// If user doesn't response to the request before it gets destroyed (ex.
|
|
// request dismissed, tab closed, naviagation to a new page), then we should
|
|
// treat it as a denial.
|
|
if (mContext) {
|
|
Cancel();
|
|
}
|
|
}
|
|
|
|
void GVAutoplayPermissionRequest::SetRequestStatus(RStatus aStatus) {
|
|
REQUEST_LOG("SetRequestStatus, new status=%s", ToGVRequestStatusStr(aStatus));
|
|
MOZ_ASSERT(mContext);
|
|
AssertIsOnMainThread();
|
|
if (mType == RType::eAUDIBLE) {
|
|
mContext->SetGVAudibleAutoplayRequestStatus(aStatus);
|
|
} else {
|
|
mContext->SetGVInaudibleAutoplayRequestStatus(aStatus);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GVAutoplayPermissionRequest::Cancel() {
|
|
MOZ_ASSERT(mContext, "Do not call 'Cancel()' twice!");
|
|
// As the process of replying of the request is an async task, the status
|
|
// might have be reset at the time we get the result from parent process.
|
|
// Ex. if the page got closed or naviagated immediately after user replied to
|
|
// the request. Therefore, the status should be either `pending` or `unknown`.
|
|
const RStatus status = GetRequestStatus(mContext, mType);
|
|
REQUEST_LOG("Cancel, current status=%s", ToGVRequestStatusStr(status));
|
|
MOZ_ASSERT(status == RStatus::ePENDING || status == RStatus::eUNKNOWN);
|
|
if ((status == RStatus::ePENDING) && !mContext->IsDiscarded()) {
|
|
SetRequestStatus(RStatus::eDENIED);
|
|
}
|
|
mContext = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GVAutoplayPermissionRequest::Allow(JS::HandleValue aChoices) {
|
|
MOZ_ASSERT(mContext, "Do not call 'Allow()' twice!");
|
|
// As the process of replying of the request is an async task, the status
|
|
// might have be reset at the time we get the result from parent process.
|
|
// Ex. if the page got closed or naviagated immediately after user replied to
|
|
// the request. Therefore, the status should be either `pending` or `unknown`.
|
|
const RStatus status = GetRequestStatus(mContext, mType);
|
|
REQUEST_LOG("Allow, current status=%s", ToGVRequestStatusStr(status));
|
|
MOZ_ASSERT(status == RStatus::ePENDING || status == RStatus::eUNKNOWN);
|
|
if (status == RStatus::ePENDING) {
|
|
SetRequestStatus(RStatus::eALLOWED);
|
|
}
|
|
mContext = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */
|
|
void GVAutoplayPermissionRequestor::AskForPermissionIfNeeded(
|
|
nsPIDOMWindowInner* aWindow) {
|
|
LOG("Requestor, AskForPermissionIfNeeded");
|
|
if (!aWindow) {
|
|
return;
|
|
}
|
|
|
|
// The request is used for content permission, so it's no need to create a
|
|
// content request in parent process if we're in e10s.
|
|
if (XRE_IsE10sParentProcess()) {
|
|
return;
|
|
}
|
|
|
|
if (!StaticPrefs::media_geckoview_autoplay_request()) {
|
|
return;
|
|
}
|
|
|
|
LOG("Requestor, check status to decide if we need to create the new request");
|
|
// The request status is stored in top-level browsing context only.
|
|
RefPtr<BrowsingContext> context = aWindow->GetBrowsingContext()->Top();
|
|
if (!HasEverAskForRequest(context, RType::eAUDIBLE)) {
|
|
CreateAsyncRequest(aWindow, context, RType::eAUDIBLE);
|
|
}
|
|
if (!HasEverAskForRequest(context, RType::eINAUDIBLE)) {
|
|
CreateAsyncRequest(aWindow, context, RType::eINAUDIBLE);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
bool GVAutoplayPermissionRequestor::HasEverAskForRequest(
|
|
BrowsingContext* aContext, RType aType) {
|
|
return GetRequestStatus(aContext, aType) != RStatus::eUNKNOWN;
|
|
}
|
|
|
|
/* static */
|
|
void GVAutoplayPermissionRequestor::CreateAsyncRequest(
|
|
nsPIDOMWindowInner* aWindow, BrowsingContext* aContext,
|
|
GVAutoplayRequestType aType) {
|
|
nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
|
|
if (!innerWindow || !innerWindow->GetPrincipal()) {
|
|
return;
|
|
}
|
|
|
|
GVAutoplayPermissionRequest::CreateRequest(innerWindow, aContext, aType);
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|