tor-browser

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

PermissionStatusSink.cpp (8891B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "PermissionStatusSink.h"
      8 
      9 #include "PermissionObserver.h"
     10 #include "PermissionStatus.h"
     11 #include "mozilla/Permission.h"
     12 #include "mozilla/PermissionDelegateHandler.h"
     13 #include "mozilla/PermissionManager.h"
     14 #include "mozilla/dom/WorkerPrivate.h"
     15 #include "mozilla/dom/WorkerRef.h"
     16 #include "nsGlobalWindowInner.h"
     17 
     18 namespace mozilla::dom {
     19 
     20 PermissionStatusSink::PermissionStatusSink(PermissionStatus* aPermissionStatus,
     21                                           PermissionName aPermissionName,
     22                                           const nsACString& aPermissionType)
     23    : mSerialEventTarget(NS_GetCurrentThread()),
     24      mPermissionStatus(aPermissionStatus),
     25      mMutex("PermissionStatusSink::mMutex"),
     26      mPermissionName(aPermissionName),
     27      mPermissionType(aPermissionType) {
     28  MOZ_ASSERT(aPermissionStatus);
     29  MOZ_ASSERT(mSerialEventTarget);
     30 
     31  nsCOMPtr<nsIGlobalObject> global = aPermissionStatus->GetOwnerGlobal();
     32  if (NS_WARN_IF(!global)) {
     33    return;
     34  }
     35 
     36  nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
     37  if (NS_WARN_IF(!principal)) {
     38    return;
     39  }
     40 
     41  mPrincipalForPermission = Permission::ClonePrincipalForPermission(principal);
     42 }
     43 
     44 PermissionStatusSink::~PermissionStatusSink() = default;
     45 
     46 RefPtr<PermissionStatusSink::PermissionStatePromise>
     47 PermissionStatusSink::Init() {
     48  if (!NS_IsMainThread()) {
     49    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     50    MOZ_ASSERT(workerPrivate);
     51 
     52    MutexAutoLock lock(mMutex);
     53 
     54    RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
     55        workerPrivate, "PermissionStatusSink",
     56        [self = RefPtr(this)]() { self->Disentangle(); });
     57    if (NS_WARN_IF(!workerRef)) {
     58      // If WorkerRef creation fails, the Worker has started shutting down. But
     59      // we are on the Worker thread, promise handlers in
     60      // PermissionStatus::Init()/Permissions::Query() can still be dispatched
     61      // to the Worker thread for outer promise rejection.
     62      return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
     63                                                     __func__);
     64      ;
     65    }
     66 
     67    mWorkerRef = new ThreadSafeWorkerRef(workerRef);
     68  }
     69 
     70  // On the Worker thread, so the below async function must be executed before
     71  // WorkerRef callback which should also be on the Worker thread. So the above
     72  // created WorkerRef should protect the outer promise handling can be
     73  // dispatched on the Worker thread.
     74  return InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
     75                     [self = RefPtr(this)] {
     76                       MOZ_ASSERT(!self->mObserver);
     77 
     78                       // Covers the onchange part
     79                       // Whenever the user agent is aware that the state of a
     80                       // PermissionStatus instance status has changed: ... (The
     81                       // observer calls PermissionChanged() to do the steps)
     82                       self->mObserver = PermissionObserver::GetInstance();
     83                       if (NS_WARN_IF(!self->mObserver)) {
     84                         return PermissionStatePromise::CreateAndReject(
     85                             NS_ERROR_FAILURE, __func__);
     86                       }
     87 
     88                       self->mObserver->AddSink(self);
     89 
     90                       // Covers the query part (Step 8.2 - 8.4)
     91                       return self->ComputeStateOnMainThread();
     92                     });
     93 }
     94 
     95 bool PermissionStatusSink::MaybeUpdatedByOnMainThread(
     96    nsIPermission* aPermission) {
     97  MOZ_ASSERT(NS_IsMainThread());
     98 
     99  if (!mPrincipalForPermission) {
    100    return false;
    101  }
    102 
    103  nsCOMPtr<nsIPrincipal> permissionPrincipal;
    104  aPermission->GetPrincipal(getter_AddRefs(permissionPrincipal));
    105  if (!permissionPrincipal) {
    106    return false;
    107  }
    108 
    109  return mPrincipalForPermission->Equals(permissionPrincipal);
    110 }
    111 
    112 bool PermissionStatusSink::MaybeUpdatedByNotifyOnlyOnMainThread(
    113    nsPIDOMWindowInner* aInnerWindow) {
    114  MOZ_ASSERT(NS_IsMainThread());
    115  return false;
    116 }
    117 
    118 void PermissionStatusSink::PermissionChangedOnMainThread() {
    119  MOZ_ASSERT(NS_IsMainThread());
    120 
    121  // Nothing to do if Worker had shutted down.
    122  if (!mSerialEventTarget->IsOnCurrentThread()) {
    123    MutexAutoLock lock(mMutex);
    124    if (!mWorkerRef) {
    125      return;
    126    }
    127  }
    128 
    129  // mWorkerRef is not nullptr, it will protect the promise handling can be
    130  // dispatched to the Worker thread, even though the Worker starts shutdown,
    131  // because mWorkerRef is nullify on the main thread.
    132  ComputeStateOnMainThread()->Then(
    133      mSerialEventTarget, __func__,
    134      [self = RefPtr(this)](
    135          const PermissionStatePromise::ResolveOrRejectValue& aResult) {
    136        if (aResult.IsResolve() && self->mPermissionStatus) {
    137          self->mPermissionStatus->PermissionChanged(aResult.ResolveValue());
    138        }
    139      });
    140 }
    141 
    142 void PermissionStatusSink::Disentangle() {
    143  MOZ_ASSERT(mSerialEventTarget->IsOnCurrentThread());
    144 
    145  mPermissionStatus = nullptr;
    146 
    147  NS_DispatchToMainThread(
    148      NS_NewRunnableFunction(__func__, [self = RefPtr(this)] {
    149        if (self->mObserver) {
    150          self->mObserver->RemoveSink(self);
    151          self->mObserver = nullptr;
    152        }
    153        {
    154          MutexAutoLock lock(self->mMutex);
    155          self->mWorkerRef = nullptr;
    156        }
    157      }));
    158 }
    159 
    160 RefPtr<PermissionStatusSink::PermissionStatePromise>
    161 PermissionStatusSink::ComputeStateOnMainThread() {
    162  MOZ_ASSERT(NS_IsMainThread());
    163 
    164  // Step 1: If settings wasn't passed, set it to the current settings object.
    165  // Step 2: If settings is a non-secure context, return "denied".
    166  // XXX(krosylight): No such steps here, and no WPT coverage?
    167 
    168  // The permission handler covers the rest of the steps, although the model
    169  // does not exactly match what the spec has. (Not passing "permission key" for
    170  // example)
    171 
    172  if (mSerialEventTarget->IsOnCurrentThread()) {
    173    if (!mPermissionStatus) {
    174      return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
    175                                                     __func__);
    176    }
    177 
    178    RefPtr<nsGlobalWindowInner> window = mPermissionStatus->GetOwnerWindow();
    179    return ComputeStateOnMainThreadInternal(window);
    180  }
    181 
    182  nsCOMPtr<nsPIDOMWindowInner> ancestorWindow;
    183  nsCOMPtr<nsIPrincipal> workerPrincipal;
    184 
    185  {
    186    MutexAutoLock lock(mMutex);
    187 
    188    if (!mWorkerRef) {
    189      // We have been disentangled.
    190      return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
    191                                                     __func__);
    192    }
    193 
    194    // If we have mWorkerRef, we haven't received the WorkerRef notification
    195    // yet.
    196    WorkerPrivate* workerPrivate = mWorkerRef->Private();
    197    MOZ_ASSERT(workerPrivate);
    198 
    199    ancestorWindow = workerPrivate->GetAncestorWindow();
    200    workerPrincipal = workerPrivate->GetPrincipal();
    201  }
    202 
    203  if (ancestorWindow) {
    204    return ComputeStateOnMainThreadInternal(ancestorWindow);
    205  }
    206 
    207  if (NS_WARN_IF(!workerPrincipal)) {
    208    return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    209  }
    210 
    211  RefPtr<nsIPermissionManager> permissionManager =
    212      PermissionManager::GetInstance();
    213  if (!permissionManager) {
    214    return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    215  }
    216 
    217  uint32_t action = nsIPermissionManager::DENY_ACTION;
    218  nsresult rv = permissionManager->TestPermissionFromPrincipal(
    219      workerPrincipal, mPermissionType, &action);
    220  if (NS_WARN_IF(NS_FAILED(rv))) {
    221    return PermissionStatePromise::CreateAndReject(rv, __func__);
    222  }
    223 
    224  return PermissionStatePromise::CreateAndResolve(action, __func__);
    225 }
    226 
    227 RefPtr<PermissionStatusSink::PermissionStatePromise>
    228 PermissionStatusSink::ComputeStateOnMainThreadInternal(
    229    nsPIDOMWindowInner* aWindow) {
    230  MOZ_ASSERT(NS_IsMainThread());
    231 
    232  if (NS_WARN_IF(!aWindow)) {
    233    return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    234  }
    235 
    236  RefPtr<Document> document = aWindow->GetExtantDoc();
    237  if (NS_WARN_IF(!document)) {
    238    return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    239  }
    240 
    241  uint32_t action = nsIPermissionManager::DENY_ACTION;
    242 
    243  PermissionDelegateHandler* permissionHandler =
    244      document->GetPermissionDelegateHandler();
    245  if (NS_WARN_IF(!permissionHandler)) {
    246    return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    247  }
    248 
    249  nsresult rv = permissionHandler->GetPermissionForPermissionsAPI(
    250      mPermissionType, &action);
    251  if (NS_WARN_IF(NS_FAILED(rv))) {
    252    return PermissionStatePromise::CreateAndReject(rv, __func__);
    253  }
    254 
    255  return PermissionStatePromise::CreateAndResolve(action, __func__);
    256 }
    257 
    258 }  // namespace mozilla::dom