Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/push/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 10 kB image not shown  

Quelle  PushSubscription.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/PushSubscription.h"

#include "nsGlobalWindowInner.h"
#include "nsIPushService.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsServiceManagerUtils.h"

#include "mozilla/Base64.h"
#include "mozilla/Unused.h"

#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/PushSubscriptionOptions.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/dom/WorkerScope.h"

namespace mozilla::dom {

namespace {

class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback {
 public:
  NS_DECL_ISUPPORTS

  explicit UnsubscribeResultCallback(Promise* aPromise) : mPromise(aPromise) {
    AssertIsOnMainThread();
  }

  NS_IMETHOD
  OnUnsubscribe(nsresult aStatus, bool aSuccess) override {
    if (NS_SUCCEEDED(aStatus)) {
      mPromise->MaybeResolve(aSuccess);
    } else {
      mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
    }

    return NS_OK;
  }

 private:
  ~UnsubscribeResultCallback() = default;

  RefPtr<Promise> mPromise;
};

NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback)

class UnsubscribeResultRunnable final : public WorkerThreadRunnable {
 public:
  UnsubscribeResultRunnable(WorkerPrivate* aWorkerPrivate,
                            RefPtr<PromiseWorkerProxy>&& aProxy,
                            nsresult aStatus, bool aSuccess)
      : WorkerThreadRunnable("UnsubscribeResultRunnable"),
        mProxy(std::move(aProxy)),
        mStatus(aStatus),
        mSuccess(aSuccess) {
    AssertIsOnMainThread();
  }

  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    MOZ_ASSERT(aWorkerPrivate);
    aWorkerPrivate->AssertIsOnWorkerThread();

    RefPtr<Promise> promise = mProxy->GetWorkerPromise();
    // Once Worker had already started shutdown, workerPromise would be nullptr
    if (!promise) {
      return true;
    }
    if (NS_SUCCEEDED(mStatus)) {
      promise->MaybeResolve(mSuccess);
    } else {
      promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
    }

    mProxy->CleanUp();

    return true;
  }

 private:
  ~UnsubscribeResultRunnable() = default;

  RefPtr<PromiseWorkerProxy> mProxy;
  nsresult mStatus;
  bool mSuccess;
};

class WorkerUnsubscribeResultCallback final
    : public nsIUnsubscribeResultCallback {
 public:
  NS_DECL_ISUPPORTS

  explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
      : mProxy(aProxy) {
    AssertIsOnMainThread();
  }

  NS_IMETHOD
  OnUnsubscribe(nsresult aStatus, bool aSuccess) override {
    AssertIsOnMainThread();
    MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");

    MutexAutoLock lock(mProxy->Lock());
    if (mProxy->CleanedUp()) {
      return NS_OK;
    }

    WorkerPrivate* worker = mProxy->GetWorkerPrivate();
    RefPtr<UnsubscribeResultRunnable> r = new UnsubscribeResultRunnable(
        worker, std::move(mProxy), aStatus, aSuccess);
    MOZ_ALWAYS_TRUE(r->Dispatch(worker));

    return NS_OK;
  }

 private:
  ~WorkerUnsubscribeResultCallback() = default;

  RefPtr<PromiseWorkerProxy> mProxy;
};

NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)

class UnsubscribeRunnable final : public Runnable {
 public:
  UnsubscribeRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope)
      : Runnable("dom::UnsubscribeRunnable"), mProxy(aProxy), mScope(aScope) {
    MOZ_ASSERT(aProxy);
    MOZ_ASSERT(!aScope.IsEmpty());
  }

  NS_IMETHOD
  Run() override {
    AssertIsOnMainThread();

    nsCOMPtr<nsIPrincipal> principal;

    {
      MutexAutoLock lock(mProxy->Lock());
      if (mProxy->CleanedUp()) {
        return NS_OK;
      }
      principal = mProxy->GetWorkerPrivate()->GetPrincipal();
    }

    MOZ_ASSERT(principal);

    RefPtr<WorkerUnsubscribeResultCallback> callback =
        new WorkerUnsubscribeResultCallback(mProxy);

    nsCOMPtr<nsIPushService> service =
        do_GetService("@mozilla.org/push/Service;1");
    if (NS_WARN_IF(!service)) {
      callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
      return NS_OK;
    }

    if (NS_WARN_IF(
            NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) {
      callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
      return NS_OK;
    }

    return NS_OK;
  }

 private:
  ~UnsubscribeRunnable() = default;

  RefPtr<PromiseWorkerProxy> mProxy;
  nsString mScope;
};

}  // anonymous namespace

PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
                                   const nsAString& aEndpoint,
                                   const nsAString& aScope,
                                   Nullable<EpochTimeStamp>&& aExpirationTime,
                                   nsTArray<uint8_t>&& aRawP256dhKey,
                                   nsTArray<uint8_t>&& aAuthSecret,
                                   nsTArray<uint8_t>&& aAppServerKey)
    : mEndpoint(aEndpoint),
      mScope(aScope),
      mExpirationTime(std::move(aExpirationTime)),
      mRawP256dhKey(std::move(aRawP256dhKey)),
      mAuthSecret(std::move(aAuthSecret)) {
  if (NS_IsMainThread()) {
    mGlobal = aGlobal;
  } else {
#ifdef DEBUG
    // There's only one global on a worker, so we don't need to pass a global
    // object to the constructor.
    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
    MOZ_ASSERT(worker);
    worker->AssertIsOnWorkerThread();
#endif
  }
  mOptions = new PushSubscriptionOptions(mGlobal, std::move(aAppServerKey));
}

PushSubscription::~PushSubscription() = default;

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mOptions)
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

JSObject* PushSubscription::WrapObject(JSContext* aCx,
                                       JS::Handle<JSObject*> aGivenProto) {
  return PushSubscription_Binding::Wrap(aCx, this, aGivenProto);
}

// static
already_AddRefed<PushSubscription> PushSubscription::Constructor(
    GlobalObject& aGlobal, const PushSubscriptionInit& aInitDict,
    ErrorResult& aRv) {
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());

  nsTArray<uint8_t> rawKey;
  if (!aInitDict.mP256dhKey.IsNull() &&
      !aInitDict.mP256dhKey.Value().AppendDataTo(rawKey)) {
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return nullptr;
  }

  nsTArray<uint8_t> authSecret;
  if (!aInitDict.mAuthSecret.IsNull() &&
      !aInitDict.mAuthSecret.Value().AppendDataTo(authSecret)) {
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return nullptr;
  }

  nsTArray<uint8_t> appServerKey;
  if (!aInitDict.mAppServerKey.IsNull() &&
      !AppendTypedArrayDataTo(aInitDict.mAppServerKey.Value(), appServerKey)) {
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return nullptr;
  }

  Nullable<EpochTimeStamp> expirationTime;
  if (aInitDict.mExpirationTime.IsNull()) {
    expirationTime.SetNull();
  } else {
    expirationTime.SetValue(aInitDict.mExpirationTime.Value());
  }

  RefPtr<PushSubscription> sub = new PushSubscription(
      global, aInitDict.mEndpoint, aInitDict.mScope, std::move(expirationTime),
      std::move(rawKey), std::move(authSecret), std::move(appServerKey));

  return sub.forget();
}

already_AddRefed<Promise> PushSubscription::Unsubscribe(ErrorResult& aRv) {
  if (!NS_IsMainThread()) {
    RefPtr<Promise> p = UnsubscribeFromWorker(aRv);
    return p.forget();
  }

  MOZ_ASSERT(mGlobal);

  nsCOMPtr<nsIPushService> service =
      do_GetService("@mozilla.org/push/Service;1");
  if (NS_WARN_IF(!service)) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
  if (!window) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  RefPtr<Promise> p = Promise::Create(mGlobal, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  RefPtr<UnsubscribeResultCallback> callback = new UnsubscribeResultCallback(p);
  Unused << NS_WARN_IF(NS_FAILED(service->Unsubscribe(
      mScope, nsGlobalWindowInner::Cast(window)->GetClientPrincipal(),
      callback)));

  return p.forget();
}

void PushSubscription::GetKey(JSContext* aCx, PushEncryptionKeyName aType,
                              JS::MutableHandle<JSObject*> aKey,
                              ErrorResult& aRv) {
  if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
    aKey.set(ArrayBuffer::Create(aCx, mRawP256dhKey, aRv));
  } else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) {
    aKey.set(ArrayBuffer::Create(aCx, mAuthSecret, aRv));
  } else {
    aKey.set(nullptr);
  }
}

void PushSubscription::ToJSON(PushSubscriptionJSON& aJSON, ErrorResult& aRv) {
  aJSON.mEndpoint.Construct();
  aJSON.mEndpoint.Value() = mEndpoint;

  aJSON.mKeys.mP256dh.Construct();
  nsresult rv = Base64URLEncode(
      mRawP256dhKey.Length(), mRawP256dhKey.Elements(),
      Base64URLEncodePaddingPolicy::Omit, aJSON.mKeys.mP256dh.Value());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
  }

  aJSON.mKeys.mAuth.Construct();
  rv = Base64URLEncode(mAuthSecret.Length(), mAuthSecret.Elements(),
                       Base64URLEncodePaddingPolicy::Omit,
                       aJSON.mKeys.mAuth.Value());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
  }
  aJSON.mExpirationTime.Construct(mExpirationTime);
}

already_AddRefed<PushSubscriptionOptions> PushSubscription::Options() {
  RefPtr<PushSubscriptionOptions> options = mOptions;
  return options.forget();
}

already_AddRefed<Promise> PushSubscription::UnsubscribeFromWorker(
    ErrorResult& aRv) {
  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
  MOZ_ASSERT(worker);
  worker->AssertIsOnWorkerThread();

  nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
  RefPtr<Promise> p = Promise::Create(global, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
  if (!proxy) {
    p->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
    return p.forget();
  }

  RefPtr<UnsubscribeRunnable> r = new UnsubscribeRunnable(proxy, mScope);
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));

  return p.forget();
}

}  // namespace mozilla::dom

100%


¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.