tor-browser

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

MediaUtils.cpp (9355B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=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 "MediaUtils.h"
      8 
      9 #include "mozilla/AppShutdown.h"
     10 #include "mozilla/Preferences.h"
     11 #include "mozilla/Services.h"
     12 #include "mozilla/dom/WorkerCommon.h"
     13 #include "mozilla/dom/WorkerRef.h"
     14 #include "nsIObserver.h"
     15 #include "nsIObserverService.h"
     16 #include "nsNetUtil.h"
     17 
     18 namespace mozilla::media {
     19 
     20 bool HostnameInPref(const char* aPref, const nsCString& aHostName) {
     21  auto HostInDomain = [](const nsCString& aHost, const nsCString& aPattern) {
     22    int32_t patternOffset = 0;
     23    int32_t hostOffset = 0;
     24 
     25    // Act on '*.' wildcard in the left-most position in a domain pattern.
     26    if (StringBeginsWith(aPattern, nsCString("*."))) {
     27      patternOffset = 2;
     28 
     29      // Ignore the lowest level sub-domain for the hostname.
     30      hostOffset = aHost.FindChar('.') + 1;
     31 
     32      if (hostOffset <= 1) {
     33        // Reject a match between a wildcard and a TLD or '.foo' form.
     34        return false;
     35      }
     36    }
     37 
     38    nsDependentCString hostRoot(aHost, hostOffset);
     39    return hostRoot.EqualsIgnoreCase(aPattern.BeginReading() + patternOffset);
     40  };
     41 
     42  nsCString domainList;
     43  nsresult rv = Preferences::GetCString(aPref, domainList);
     44 
     45  if (NS_FAILED(rv)) {
     46    return false;
     47  }
     48 
     49  domainList.StripWhitespace();
     50 
     51  if (domainList.IsEmpty() || aHostName.IsEmpty()) {
     52    return false;
     53  }
     54 
     55  // Test each domain name in the comma separated list
     56  // after converting from UTF8 to ASCII. Each domain
     57  // must match exactly or have a single leading '*.' wildcard.
     58  for (const nsACString& each : domainList.Split(',')) {
     59    nsCString domainPattern;
     60    rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(each, domainPattern);
     61    if (NS_SUCCEEDED(rv)) {
     62      if (HostInDomain(aHostName, domainPattern)) {
     63        return true;
     64      }
     65    } else {
     66      NS_WARNING("Failed to convert UTF-8 host to ASCII");
     67    }
     68  }
     69  return false;
     70 }
     71 
     72 nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier() {
     73  nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
     74  if (!svc) {
     75    // We can fail to get the shutdown service if we're already shutting down.
     76    return nullptr;
     77  }
     78 
     79  nsCOMPtr<nsIAsyncShutdownClient> barrier;
     80  nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
     81  if (!barrier) {
     82    // We are probably in a content process. We need to do cleanup at
     83    // XPCOM shutdown in leakchecking builds.
     84    rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
     85  }
     86  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     87  MOZ_RELEASE_ASSERT(barrier);
     88  return barrier;
     89 }
     90 
     91 nsCOMPtr<nsIAsyncShutdownClient> MustGetShutdownBarrier() {
     92  nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
     93  MOZ_RELEASE_ASSERT(barrier);
     94  return barrier;
     95 }
     96 
     97 NS_IMPL_ISUPPORTS(ShutdownBlocker, nsIAsyncShutdownBlocker)
     98 
     99 namespace {
    100 class TicketBlocker : public ShutdownBlocker {
    101  using ShutdownMozPromise = ShutdownBlockingTicket::ShutdownMozPromise;
    102 
    103 public:
    104  explicit TicketBlocker(const nsAString& aName)
    105      : ShutdownBlocker(aName), mPromise(mHolder.Ensure(__func__)) {}
    106 
    107  NS_IMETHOD
    108  BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override {
    109    mHolder.Resolve(true, __func__);
    110    return NS_OK;
    111  }
    112 
    113  void RejectIfExists() { mHolder.RejectIfExists(false, __func__); }
    114 
    115  ShutdownMozPromise* ShutdownPromise() { return mPromise; }
    116 
    117 private:
    118  ~TicketBlocker() = default;
    119 
    120  MozPromiseHolder<ShutdownMozPromise> mHolder;
    121  const RefPtr<ShutdownMozPromise> mPromise;
    122 };
    123 
    124 class ShutdownBlockingTicketImpl : public ShutdownBlockingTicket {
    125 private:
    126  RefPtr<TicketBlocker> mBlocker;
    127 
    128 public:
    129  explicit ShutdownBlockingTicketImpl(RefPtr<TicketBlocker> aBlocker)
    130      : mBlocker(std::move(aBlocker)) {}
    131 
    132  static UniquePtr<ShutdownBlockingTicket> Create(const nsAString& aName,
    133                                                  const nsAString& aFileName,
    134                                                  int32_t aLineNr) {
    135    auto blocker = MakeRefPtr<TicketBlocker>(aName);
    136    NS_DispatchToMainThread(NS_NewRunnableFunction(
    137        "ShutdownBlockingTicketImpl::AddBlocker",
    138        [blocker, file = nsString(aFileName), aLineNr] {
    139          MustGetShutdownBarrier()->AddBlocker(blocker, file, aLineNr, u""_ns);
    140        }));
    141    if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
    142      // Adding a blocker is not guaranteed to succeed. Remove the blocker in
    143      // case it succeeded anyway, and bail.
    144      NS_DispatchToMainThread(NS_NewRunnableFunction(
    145          "ShutdownBlockingTicketImpl::RemoveBlocker", [blocker] {
    146            MustGetShutdownBarrier()->RemoveBlocker(blocker);
    147            blocker->RejectIfExists();
    148          }));
    149      return nullptr;
    150    }
    151 
    152    // Adding a blocker is now guaranteed to succeed:
    153    // - If AppShutdown::IsInOrBeyond(AppShutdown) returned false,
    154    // - then the AddBlocker main thread task was queued before AppShutdown's
    155    //   sCurrentShutdownPhase is set to ShutdownPhase::AppShutdown,
    156    // - which is before AppShutdown will drain the (main thread) event queue to
    157    //   run the AddBlocker task, if not already run,
    158    // - which is before profile-before-change (the earliest barrier we'd add a
    159    //   blocker to, see GetShutdownBarrier()) is notified,
    160    // - which is when AsyncShutdown prevents further conditions (blockers)
    161    //   being added to the profile-before-change barrier.
    162    return MakeUnique<ShutdownBlockingTicketImpl>(std::move(blocker));
    163  }
    164 
    165  ~ShutdownBlockingTicketImpl() {
    166    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
    167        NS_NewRunnableFunction(__func__, [blocker = std::move(mBlocker)] {
    168          GetShutdownBarrier()->RemoveBlocker(blocker);
    169          blocker->RejectIfExists();
    170        })));
    171  }
    172 
    173  ShutdownMozPromise* ShutdownPromise() override {
    174    return mBlocker->ShutdownPromise();
    175  }
    176 };
    177 }  // namespace
    178 
    179 UniquePtr<ShutdownBlockingTicket> ShutdownBlockingTicket::Create(
    180    const nsAString& aName, const nsAString& aFileName, int32_t aLineNr) {
    181  return ShutdownBlockingTicketImpl::Create(aName, aFileName, aLineNr);
    182 }
    183 
    184 class MainShutdownWatcher final : public ShutdownWatcher, public nsIObserver {
    185 public:
    186  NS_DECL_ISUPPORTS
    187 
    188  explicit MainShutdownWatcher(ShutdownConsumer* aConsumer)
    189      : ShutdownWatcher(aConsumer) {}
    190 
    191  bool Initialize() {
    192    if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    193      mConsumer = nullptr;
    194      return false;
    195    }
    196 
    197    nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    198    if (NS_WARN_IF(!obsService)) {
    199      mConsumer = nullptr;
    200      return false;
    201    }
    202 
    203    if (NS_WARN_IF(NS_FAILED(obsService->AddObserver(
    204            this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false)))) {
    205      mConsumer = nullptr;
    206      return false;
    207    }
    208 
    209    mRegistered = true;
    210    return true;
    211  }
    212 
    213  void Destroy() override {
    214    if (!mRegistered) {
    215      return;
    216    }
    217 
    218    mRegistered = false;
    219    mConsumer = nullptr;
    220 
    221    if (nsCOMPtr<nsIObserverService> obsService =
    222            services::GetObserverService()) {
    223      obsService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
    224    }
    225  }
    226 
    227  NS_IMETHODIMP Observe(nsISupports* aSubject, const char* aTopic,
    228                        const char16_t* aData) override {
    229    MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) == 0);
    230    if (mConsumer) {
    231      mConsumer->OnShutdown();
    232    }
    233    Destroy();
    234    return NS_OK;
    235  }
    236 
    237 private:
    238  ~MainShutdownWatcher() override { Destroy(); }
    239 
    240  bool mRegistered = false;
    241 };
    242 
    243 NS_IMPL_ISUPPORTS(MainShutdownWatcher, nsIObserver);
    244 
    245 class WorkerShutdownWatcher final : public ShutdownWatcher {
    246 public:
    247  NS_DECL_ISUPPORTS
    248 
    249  explicit WorkerShutdownWatcher(ShutdownConsumer* aConsumer)
    250      : ShutdownWatcher(aConsumer) {}
    251 
    252  bool Initialize(dom::WorkerPrivate* aWorkerPrivate) {
    253    if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    254      mConsumer = nullptr;
    255      return false;
    256    }
    257 
    258    mWorkerRef = dom::WeakWorkerRef::Create(
    259        aWorkerPrivate, [self = RefPtr{this}] { self->OnShutdown(); });
    260    if (NS_WARN_IF(!mWorkerRef)) {
    261      mConsumer = nullptr;
    262      return false;
    263    }
    264 
    265    return true;
    266  }
    267 
    268  void OnShutdown() {
    269    if (mConsumer) {
    270      mConsumer->OnShutdown();
    271    }
    272    Destroy();
    273  }
    274 
    275  void Destroy() override {
    276    mWorkerRef = nullptr;
    277    mConsumer = nullptr;
    278  }
    279 
    280 private:
    281  ~WorkerShutdownWatcher() override { Destroy(); }
    282 
    283  RefPtr<dom::WeakWorkerRef> mWorkerRef;
    284 };
    285 
    286 NS_IMPL_ISUPPORTS0(WorkerShutdownWatcher);
    287 
    288 /* static */
    289 already_AddRefed<ShutdownWatcher> ShutdownWatcher::Create(
    290    ShutdownConsumer* aConsumer) {
    291  if (NS_IsMainThread()) {
    292    auto watcher = MakeRefPtr<MainShutdownWatcher>(aConsumer);
    293    if (watcher->Initialize()) {
    294      return watcher.forget().downcast<ShutdownWatcher>();
    295    }
    296  } else if (dom::WorkerPrivate* workerPrivate =
    297                 dom::GetCurrentThreadWorkerPrivate()) {
    298    auto watcher = MakeRefPtr<WorkerShutdownWatcher>(aConsumer);
    299    if (watcher->Initialize(workerPrivate)) {
    300      return watcher.forget().downcast<ShutdownWatcher>();
    301    }
    302  }
    303 
    304  return nullptr;
    305 }
    306 
    307 }  // namespace mozilla::media