tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GVAutoplayPermissionRequest.cpp (9534B)


      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/. */
      4 
      5 #include "GVAutoplayPermissionRequest.h"
      6 
      7 #include "mozilla/Logging.h"
      8 #include "mozilla/StaticPrefs_media.h"
      9 #include "mozilla/dom/HTMLMediaElement.h"
     10 #include "nsGlobalWindowInner.h"
     11 
     12 mozilla::LazyLogModule gGVAutoplayRequestLog("GVAutoplay");
     13 
     14 namespace mozilla::dom {
     15 
     16 using RType = GVAutoplayRequestType;
     17 using RStatus = GVAutoplayRequestStatus;
     18 
     19 // avoid redefined macro in unified build
     20 #undef REQUEST_LOG
     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__));          \
     26  }
     27 
     28 #undef LOG
     29 #define LOG(msg, ...) \
     30  MOZ_LOG(gGVAutoplayRequestLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
     31 
     32 static RStatus GetRequestStatus(BrowsingContext* aContext, RType aType) {
     33  MOZ_ASSERT(aContext);
     34  AssertIsOnMainThread();
     35  return aType == RType::eAUDIBLE
     36             ? aContext->GetGVAudibleAutoplayRequestStatus()
     37             : aContext->GetGVInaudibleAutoplayRequestStatus();
     38 }
     39 
     40 // This is copied from the value of `media.geckoview.autoplay.request.testing`.
     41 enum class TestRequest : uint32_t {
     42  ePromptAsNormal = 0,
     43  eAllowAll = 1,
     44  eDenyAll = 2,
     45  eAllowAudible = 3,
     46  eDenyAudible = 4,
     47  eAllowInAudible = 5,
     48  eDenyInAudible = 6,
     49  eLeaveAllPending = 7,
     50  eAllowAllAsync = 8,
     51 };
     52 
     53 NS_IMPL_CYCLE_COLLECTION_INHERITED(GVAutoplayPermissionRequest,
     54                                   ContentPermissionRequestBase)
     55 
     56 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(GVAutoplayPermissionRequest,
     57                                               ContentPermissionRequestBase)
     58 
     59 /* static */
     60 void GVAutoplayPermissionRequest::CreateRequest(nsGlobalWindowInner* aWindow,
     61                                                BrowsingContext* aContext,
     62                                                GVAutoplayRequestType aType) {
     63  RefPtr<GVAutoplayPermissionRequest> request =
     64      new GVAutoplayPermissionRequest(aWindow, aContext, aType);
     65  request->SetRequestStatus(RStatus::ePENDING);
     66  const TestRequest testingPref = static_cast<TestRequest>(
     67      StaticPrefs::media_geckoview_autoplay_request_testing());
     68  if (testingPref != TestRequest::ePromptAsNormal) {
     69    LOG("Create testing request, tesing value=%u",
     70        static_cast<uint32_t>(testingPref));
     71    if (testingPref == TestRequest::eAllowAllAsync) {
     72      request->RequestDelayedTask(
     73          aWindow->SerialEventTarget(),
     74          GVAutoplayPermissionRequest::DelayedTaskType::Allow);
     75    } else if (testingPref == TestRequest::eAllowAll ||
     76               (testingPref == TestRequest::eAllowAudible &&
     77                aType == RType::eAUDIBLE) ||
     78               (testingPref == TestRequest::eAllowInAudible &&
     79                aType == RType::eINAUDIBLE)) {
     80      request->Allow(JS::UndefinedHandleValue);
     81    } else if (testingPref == TestRequest::eDenyAll ||
     82               (testingPref == TestRequest::eDenyAudible &&
     83                aType == RType::eAUDIBLE) ||
     84               (testingPref == TestRequest::eDenyInAudible &&
     85                aType == RType::eINAUDIBLE)) {
     86      request->Cancel();
     87    }
     88  } else {
     89    LOG("Dispatch async request");
     90    request->RequestDelayedTask(
     91        aWindow->SerialEventTarget(),
     92        GVAutoplayPermissionRequest::DelayedTaskType::Request);
     93  }
     94 }
     95 
     96 GVAutoplayPermissionRequest::GVAutoplayPermissionRequest(
     97    nsGlobalWindowInner* aWindow, BrowsingContext* aContext, RType aType)
     98    : ContentPermissionRequestBase(aWindow->GetPrincipal(), aWindow,
     99                                   ""_ns,  // No testing pref used in this class
    100                                   aType == RType::eAUDIBLE
    101                                       ? "autoplay-media-audible"_ns
    102                                       : "autoplay-media-inaudible"_ns),
    103      mType(aType),
    104      mContext(aContext) {
    105  MOZ_ASSERT(mContext);
    106  REQUEST_LOG("Request created");
    107 }
    108 
    109 GVAutoplayPermissionRequest::~GVAutoplayPermissionRequest() {
    110  REQUEST_LOG("Request destroyed");
    111  // If user doesn't response to the request before it gets destroyed (ex.
    112  // request dismissed, tab closed, naviagation to a new page), then we should
    113  // treat it as a denial.
    114  if (mContext) {
    115    Cancel();
    116  }
    117 }
    118 
    119 void GVAutoplayPermissionRequest::SetRequestStatus(RStatus aStatus) {
    120  REQUEST_LOG("SetRequestStatus, new status=%s", EnumValueToString(aStatus));
    121  MOZ_ASSERT(mContext);
    122  AssertIsOnMainThread();
    123  if (mType == RType::eAUDIBLE) {
    124    // Return value of setting synced field should be checked. See bug 1656492.
    125    (void)mContext->SetGVAudibleAutoplayRequestStatus(aStatus);
    126  } else {
    127    // Return value of setting synced field should be checked. See bug 1656492.
    128    (void)mContext->SetGVInaudibleAutoplayRequestStatus(aStatus);
    129  }
    130 }
    131 
    132 NS_IMETHODIMP
    133 GVAutoplayPermissionRequest::Cancel() {
    134  MOZ_ASSERT(mContext, "Do not call 'Cancel()' twice!");
    135  // As the process of replying to the request is an async task, the status
    136  // might have been reset at the time we get the result from parent process.
    137  // Ex. if the page got closed or navigated immediately after user replied to
    138  // the request. Therefore, the status should be either `pending` or `unknown`.
    139  // Additionally, we tolerate `canceled` if the request was already canceled.
    140  // See Bug 1996123 for details on the multiple cancel.
    141  const RStatus status = GetRequestStatus(mContext, mType);
    142  REQUEST_LOG("Cancel, current status=%s", EnumValueToString(status));
    143  MOZ_ASSERT(status == RStatus::ePENDING || status == RStatus::eDENIED ||
    144             status == RStatus::eUNKNOWN);
    145  if ((status == RStatus::ePENDING) && !mContext->IsDiscarded()) {
    146    SetRequestStatus(RStatus::eDENIED);
    147  }
    148  mContext = nullptr;
    149  return NS_OK;
    150 }
    151 
    152 NS_IMETHODIMP
    153 GVAutoplayPermissionRequest::Allow(JS::Handle<JS::Value> aChoices) {
    154  MOZ_ASSERT(mContext, "Do not call 'Allow()' twice!");
    155  // As the process of replying to the request is an async task, the status
    156  // might have been reset at the time we get the result from parent process.
    157  // Ex. if the page got closed or navigated immediately after user replied to
    158  // the request. Therefore, the status should be either `pending` or `unknown`.
    159  // Additionally, we tolerate `allowed` if the request was already allowed.
    160  // See Bug 1996123 for details on the multiple allow.
    161  const RStatus status = GetRequestStatus(mContext, mType);
    162  REQUEST_LOG("Allow, current status=%s", EnumValueToString(status));
    163  MOZ_ASSERT(status == RStatus::ePENDING || status == RStatus::eALLOWED ||
    164             status == RStatus::eUNKNOWN);
    165  if (status == RStatus::ePENDING) {
    166    SetRequestStatus(RStatus::eALLOWED);
    167    // Permission grant may arrive late and elements may be suspended.
    168    // We message to wake them up and resume downloading data if needed.
    169    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    170    if (obs) {
    171      obs->NotifyObservers(ToSupports(mWindow), kGVAutoplayAllowedTopic,
    172                           /* no extra string data */ nullptr);
    173    }
    174  }
    175  mContext = nullptr;
    176  return NS_OK;
    177 }
    178 
    179 /* static */
    180 void GVAutoplayPermissionRequestor::AskForPermissionIfNeeded(
    181    nsPIDOMWindowInner* aWindow) {
    182  LOG("Requestor, AskForPermissionIfNeeded");
    183  if (!aWindow) {
    184    return;
    185  }
    186 
    187  // The request is used for content permission, so it's no need to create a
    188  // content request in parent process if we're in e10s.
    189  if (XRE_IsE10sParentProcess()) {
    190    return;
    191  }
    192 
    193  if (!StaticPrefs::media_geckoview_autoplay_request()) {
    194    return;
    195  }
    196 
    197  LOG("Requestor, check status to decide if we need to create the new request");
    198  // The request status is stored in top-level browsing context only.
    199  RefPtr<BrowsingContext> context = aWindow->GetBrowsingContext()->Top();
    200  if (!HasEverAskForRequest(context, RType::eAUDIBLE)) {
    201    CreateAsyncRequest(aWindow, context, RType::eAUDIBLE);
    202  }
    203  if (!HasEverAskForRequest(context, RType::eINAUDIBLE)) {
    204    CreateAsyncRequest(aWindow, context, RType::eINAUDIBLE);
    205  }
    206 }
    207 
    208 /* static */
    209 bool GVAutoplayPermissionRequestor::HasEverAskForRequest(
    210    BrowsingContext* aContext, RType aType) {
    211  return GetRequestStatus(aContext, aType) != RStatus::eUNKNOWN;
    212 }
    213 
    214 /* static */
    215 bool GVAutoplayPermissionRequestor::HasUnresolvedRequest(
    216    nsPIDOMWindowInner* aWindow) {
    217  if (!aWindow) {
    218    return false;
    219  }
    220 
    221  RefPtr<BrowsingContext> context = aWindow->GetBrowsingContext()->Top();
    222  auto gvAudible = context->GetGVAudibleAutoplayRequestStatus();
    223  auto gvInaudible = context->GetGVInaudibleAutoplayRequestStatus();
    224  return (gvAudible == GVAutoplayRequestStatus::eUNKNOWN) ||
    225         (gvAudible == GVAutoplayRequestStatus::ePENDING) ||
    226         (gvInaudible == GVAutoplayRequestStatus::eUNKNOWN) ||
    227         (gvInaudible == GVAutoplayRequestStatus::ePENDING);
    228 }
    229 
    230 /* static */
    231 void GVAutoplayPermissionRequestor::CreateAsyncRequest(
    232    nsPIDOMWindowInner* aWindow, BrowsingContext* aContext,
    233    GVAutoplayRequestType aType) {
    234  nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
    235  if (!innerWindow || !innerWindow->GetPrincipal()) {
    236    return;
    237  }
    238 
    239  GVAutoplayPermissionRequest::CreateRequest(innerWindow, aContext, aType);
    240 }
    241 
    242 }  // namespace mozilla::dom