tor-browser

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

PermissionObserver.cpp (4233B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "PermissionObserver.h"
      8 
      9 #include "PermissionStatusSink.h"
     10 #include "PermissionUtils.h"
     11 #include "mozilla/Services.h"
     12 #include "mozilla/dom/WindowGlobalChild.h"
     13 #include "nsIObserverService.h"
     14 #include "nsIPermission.h"
     15 
     16 namespace mozilla::dom {
     17 
     18 namespace {
     19 PermissionObserver* gInstance = nullptr;
     20 }  // namespace
     21 
     22 NS_IMPL_ISUPPORTS(PermissionObserver, nsIObserver, nsISupportsWeakReference)
     23 
     24 PermissionObserver::PermissionObserver() {
     25  MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
     26  MOZ_ASSERT(!gInstance);
     27 }
     28 
     29 PermissionObserver::~PermissionObserver() {
     30  MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
     31  MOZ_ASSERT(mSinks.IsEmpty());
     32  MOZ_ASSERT(gInstance == this);
     33 
     34  gInstance = nullptr;
     35 }
     36 
     37 /* static */
     38 already_AddRefed<PermissionObserver> PermissionObserver::GetInstance() {
     39  MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
     40 
     41  RefPtr<PermissionObserver> instance = gInstance;
     42  if (!instance) {
     43    instance = new PermissionObserver();
     44 
     45    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     46    if (NS_WARN_IF(!obs)) {
     47      return nullptr;
     48    }
     49 
     50    nsresult rv = obs->AddObserver(instance, "perm-changed", true);
     51    if (NS_WARN_IF(NS_FAILED(rv))) {
     52      return nullptr;
     53    }
     54 
     55    rv = obs->AddObserver(instance, "perm-changed-notify-only", true);
     56    if (NS_WARN_IF(NS_FAILED(rv))) {
     57      return nullptr;
     58    }
     59 
     60    gInstance = instance;
     61  }
     62 
     63  return instance.forget();
     64 }
     65 
     66 void PermissionObserver::AddSink(PermissionStatusSink* aSink) {
     67  MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
     68  MOZ_ASSERT(aSink);
     69  MOZ_ASSERT(!mSinks.Contains(aSink));
     70 
     71  mSinks.AppendElement(aSink);
     72 }
     73 
     74 void PermissionObserver::RemoveSink(PermissionStatusSink* aSink) {
     75  MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
     76  MOZ_ASSERT(aSink);
     77  MOZ_ASSERT(mSinks.Contains(aSink));
     78 
     79  mSinks.RemoveElement(aSink);
     80 }
     81 
     82 NS_IMETHODIMP
     83 PermissionObserver::Observe(nsISupports* aSubject, const char* aTopic,
     84                            const char16_t* aData) {
     85  MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
     86  MOZ_ASSERT(!strcmp(aTopic, "perm-changed") ||
     87             !strcmp(aTopic, "perm-changed-notify-only"));
     88 
     89  if (mSinks.IsEmpty()) {
     90    return NS_OK;
     91  }
     92 
     93  nsCOMPtr<nsIPermission> perm = nullptr;
     94  nsCOMPtr<nsPIDOMWindowInner> innerWindow = nullptr;
     95  nsAutoCString type;
     96 
     97  if (!strcmp(aTopic, "perm-changed")) {
     98    perm = do_QueryInterface(aSubject);
     99    if (!perm) {
    100      return NS_OK;
    101    }
    102    perm->GetType(type);
    103  } else if (!strcmp(aTopic, "perm-changed-notify-only")) {
    104    innerWindow = do_QueryInterface(aSubject);
    105    if (!innerWindow) {
    106      return NS_OK;
    107    }
    108    type = NS_ConvertUTF16toUTF8(aData);
    109  }
    110 
    111  Maybe<PermissionName> permission = TypeToPermissionName(type);
    112  if (permission) {
    113    for (PermissionStatusSink* sink : mSinks) {
    114      if (sink->Name() != permission.value()) {
    115        continue;
    116      }
    117      // Check for permissions that are changed for this sink's principal
    118      // via the "perm-changed" notification. These permissions affect
    119      // the window the sink (PermissionStatus) is held in directly.
    120      if (perm && sink->MaybeUpdatedByOnMainThread(perm)) {
    121        sink->PermissionChangedOnMainThread();
    122      }
    123      // Check for permissions that are changed for this sink's principal
    124      // via the "perm-changed-notify-only" notification. These permissions
    125      // affect the window the sink (PermissionStatus) is held in indirectly- if
    126      // the window is same-party with the secondary key of a permission. For
    127      // example, a "3rdPartyFrameStorage^https://example.com" permission would
    128      // return true on these checks where sink is in a window that is same-site
    129      // with https://example.com.
    130      if (innerWindow &&
    131          sink->MaybeUpdatedByNotifyOnlyOnMainThread(innerWindow)) {
    132        sink->PermissionChangedOnMainThread();
    133      }
    134    }
    135  }
    136 
    137  return NS_OK;
    138 }
    139 
    140 }  // namespace mozilla::dom