1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "GVAutoplayPermissionRequest.h"
7 #include "mozilla/Logging.h"
8 #include "mozilla/StaticPrefs_media.h"
9 #include "mozilla/dom/HTMLMediaElement.h"
10 #include "nsGlobalWindowInner.h"
12 mozilla::LazyLogModule
gGVAutoplayRequestLog("GVAutoplay");
14 namespace mozilla::dom
{
16 using RType
= GVAutoplayRequestType
;
17 using RStatus
= GVAutoplayRequestStatus
;
19 // avoid redefined macro in unified build
21 #define REQUEST_LOG(msg, ...) \
22 if (MOZ_LOG_TEST(gGVAutoplayRequestLog, mozilla::LogLevel::Debug)) { \
23 MOZ_LOG(gGVAutoplayRequestLog, LogLevel::Debug, \
24 ("Request=%p, Type=%s, " msg, this, \
25 EnumValueToString(this->mType), ##__VA_ARGS__)); \
29 #define LOG(msg, ...) \
30 MOZ_LOG(gGVAutoplayRequestLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
32 static RStatus
GetRequestStatus(BrowsingContext
* aContext
, RType aType
) {
34 AssertIsOnMainThread();
35 return aType
== RType::eAUDIBLE
36 ? aContext
->GetGVAudibleAutoplayRequestStatus()
37 : aContext
->GetGVInaudibleAutoplayRequestStatus();
40 // This is copied from the value of `media.geckoview.autoplay.request.testing`.
41 enum class TestRequest
: uint32_t {
52 NS_IMPL_CYCLE_COLLECTION_INHERITED(GVAutoplayPermissionRequest
,
53 ContentPermissionRequestBase
)
55 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(GVAutoplayPermissionRequest
,
56 ContentPermissionRequestBase
)
59 void GVAutoplayPermissionRequest::CreateRequest(nsGlobalWindowInner
* aWindow
,
60 BrowsingContext
* aContext
,
61 GVAutoplayRequestType aType
) {
62 RefPtr
<GVAutoplayPermissionRequest
> request
=
63 new GVAutoplayPermissionRequest(aWindow
, aContext
, aType
);
64 request
->SetRequestStatus(RStatus::ePENDING
);
65 const TestRequest testingPref
= static_cast<TestRequest
>(
66 StaticPrefs::media_geckoview_autoplay_request_testing());
67 if (testingPref
!= TestRequest::ePromptAsNormal
) {
68 LOG("Create testing request, tesing value=%u",
69 static_cast<uint32_t>(testingPref
));
70 if (testingPref
== TestRequest::eAllowAll
||
71 (testingPref
== TestRequest::eAllowAudible
&&
72 aType
== RType::eAUDIBLE
) ||
73 (testingPref
== TestRequest::eAllowInAudible
&&
74 aType
== RType::eINAUDIBLE
)) {
75 request
->Allow(JS::UndefinedHandleValue
);
76 } else if (testingPref
== TestRequest::eDenyAll
||
77 (testingPref
== TestRequest::eDenyAudible
&&
78 aType
== RType::eAUDIBLE
) ||
79 (testingPref
== TestRequest::eDenyInAudible
&&
80 aType
== RType::eINAUDIBLE
)) {
84 LOG("Dispatch async request");
85 request
->RequestDelayedTask(
86 aWindow
->SerialEventTarget(),
87 GVAutoplayPermissionRequest::DelayedTaskType::Request
);
91 GVAutoplayPermissionRequest::GVAutoplayPermissionRequest(
92 nsGlobalWindowInner
* aWindow
, BrowsingContext
* aContext
, RType aType
)
93 : ContentPermissionRequestBase(aWindow
->GetPrincipal(), aWindow
,
94 ""_ns
, // No testing pref used in this class
95 aType
== RType::eAUDIBLE
96 ? "autoplay-media-audible"_ns
97 : "autoplay-media-inaudible"_ns
),
100 MOZ_ASSERT(mContext
);
101 REQUEST_LOG("Request created");
104 GVAutoplayPermissionRequest::~GVAutoplayPermissionRequest() {
105 REQUEST_LOG("Request destroyed");
106 // If user doesn't response to the request before it gets destroyed (ex.
107 // request dismissed, tab closed, naviagation to a new page), then we should
108 // treat it as a denial.
114 void GVAutoplayPermissionRequest::SetRequestStatus(RStatus aStatus
) {
115 REQUEST_LOG("SetRequestStatus, new status=%s", EnumValueToString(aStatus
));
116 MOZ_ASSERT(mContext
);
117 AssertIsOnMainThread();
118 if (mType
== RType::eAUDIBLE
) {
119 // Return value of setting synced field should be checked. See bug 1656492.
120 Unused
<< mContext
->SetGVAudibleAutoplayRequestStatus(aStatus
);
122 // Return value of setting synced field should be checked. See bug 1656492.
123 Unused
<< mContext
->SetGVInaudibleAutoplayRequestStatus(aStatus
);
128 GVAutoplayPermissionRequest::Cancel() {
129 MOZ_ASSERT(mContext
, "Do not call 'Cancel()' twice!");
130 // As the process of replying of the request is an async task, the status
131 // might have be reset at the time we get the result from parent process.
132 // Ex. if the page got closed or naviagated immediately after user replied to
133 // the request. Therefore, the status should be either `pending` or `unknown`.
134 const RStatus status
= GetRequestStatus(mContext
, mType
);
135 REQUEST_LOG("Cancel, current status=%s", EnumValueToString(status
));
136 MOZ_ASSERT(status
== RStatus::ePENDING
|| status
== RStatus::eUNKNOWN
);
137 if ((status
== RStatus::ePENDING
) && !mContext
->IsDiscarded()) {
138 SetRequestStatus(RStatus::eDENIED
);
145 GVAutoplayPermissionRequest::Allow(JS::Handle
<JS::Value
> aChoices
) {
146 MOZ_ASSERT(mContext
, "Do not call 'Allow()' twice!");
147 // As the process of replying of the request is an async task, the status
148 // might have be reset at the time we get the result from parent process.
149 // Ex. if the page got closed or naviagated immediately after user replied to
150 // the request. Therefore, the status should be either `pending` or `unknown`.
151 const RStatus status
= GetRequestStatus(mContext
, mType
);
152 REQUEST_LOG("Allow, current status=%s", EnumValueToString(status
));
153 MOZ_ASSERT(status
== RStatus::ePENDING
|| status
== RStatus::eUNKNOWN
);
154 if (status
== RStatus::ePENDING
) {
155 SetRequestStatus(RStatus::eALLOWED
);
162 void GVAutoplayPermissionRequestor::AskForPermissionIfNeeded(
163 nsPIDOMWindowInner
* aWindow
) {
164 LOG("Requestor, AskForPermissionIfNeeded");
169 // The request is used for content permission, so it's no need to create a
170 // content request in parent process if we're in e10s.
171 if (XRE_IsE10sParentProcess()) {
175 if (!StaticPrefs::media_geckoview_autoplay_request()) {
179 LOG("Requestor, check status to decide if we need to create the new request");
180 // The request status is stored in top-level browsing context only.
181 RefPtr
<BrowsingContext
> context
= aWindow
->GetBrowsingContext()->Top();
182 if (!HasEverAskForRequest(context
, RType::eAUDIBLE
)) {
183 CreateAsyncRequest(aWindow
, context
, RType::eAUDIBLE
);
185 if (!HasEverAskForRequest(context
, RType::eINAUDIBLE
)) {
186 CreateAsyncRequest(aWindow
, context
, RType::eINAUDIBLE
);
191 bool GVAutoplayPermissionRequestor::HasEverAskForRequest(
192 BrowsingContext
* aContext
, RType aType
) {
193 return GetRequestStatus(aContext
, aType
) != RStatus::eUNKNOWN
;
197 void GVAutoplayPermissionRequestor::CreateAsyncRequest(
198 nsPIDOMWindowInner
* aWindow
, BrowsingContext
* aContext
,
199 GVAutoplayRequestType aType
) {
200 nsGlobalWindowInner
* innerWindow
= nsGlobalWindowInner::Cast(aWindow
);
201 if (!innerWindow
|| !innerWindow
->GetPrincipal()) {
205 GVAutoplayPermissionRequest::CreateRequest(innerWindow
, aContext
, aType
);
208 } // namespace mozilla::dom