Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  NotificationUtils.cpp

  Sprache: C
 

/* 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 "NotificationUtils.h"

#include "mozilla/BasePrincipal.h"
#include "mozilla/Components.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/NotificationBinding.h"
#include "mozilla/glean/DomNotificationMetrics.h"
#include "nsContentUtils.h"
#include "nsIAlertsService.h"
#include "nsINotificationStorage.h"
#include "nsIPermissionManager.h"
#include "nsIPushService.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"

static bool gTriedStorageCleanup = false;

namespace mozilla::dom::notification {

using GleanLabel = glean::web_notification::ShowOriginLabel;

static void ReportTelemetry(GleanLabel aLabel,
                            PermissionCheckPurpose aPurpose) {
  switch (aPurpose) {
    case PermissionCheckPurpose::PermissionAttribute:
      glean::web_notification::permission_origin
          .EnumGet(static_cast<glean::web_notification::PermissionOriginLabel>(
              aLabel))
          .Add();
      return;
    case PermissionCheckPurpose::PermissionRequest:
      glean::web_notification::request_permission_origin
          .EnumGet(static_cast<
                   glean::web_notification::RequestPermissionOriginLabel>(
              aLabel))
          .Add();
      return;
    case PermissionCheckPurpose::NotificationShow:
      glean::web_notification::show_origin.EnumGet(aLabel).Add();
      return;
    case PermissionCheckPurpose::LoadImageForShow:
      // This will always be followed by a NotificationShow permissions check
      // anyway.
      return;
    default:
      MOZ_CRASH("Unknown permission checker");
      return;
  }
}

bool IsNotificationAllowedFor(nsIPrincipal* aPrincipal) {
  if (aPrincipal->IsSystemPrincipal()) {
    return true;
  }
  // Allow files to show notifications by default.
  return aPrincipal->SchemeIs("file");
}

bool IsNotificationForbiddenFor(nsIPrincipal* aPrincipal,
                                nsIPrincipal* aEffectiveStoragePrincipal,
                                bool isSecureContext,
                                PermissionCheckPurpose aPurpose,
                                Document* aRequestorDoc) {
  if (aPrincipal->GetIsInPrivateBrowsing() &&
      !StaticPrefs::dom_webnotifications_privateBrowsing_enabled()) {
    return true;
  }

  if (!isSecureContext) {
    if (aRequestorDoc) {
      glean::web_notification::insecure_context_permission_request.Add();
      nsContentUtils::ReportToConsole(
          nsIScriptError::errorFlag, "DOM"_ns, aRequestorDoc,
          PropertiesFile::DOM_PROPERTIES,
          "NotificationsInsecureRequestIsForbidden");
    }
    return true;
  }

  const nsString& partitionKey =
      aEffectiveStoragePrincipal->OriginAttributesRef().mPartitionKey;

  if (aEffectiveStoragePrincipal->OriginAttributesRef()
          .mPartitionKey.IsEmpty()) {
    // first party
    ReportTelemetry(GleanLabel::eFirstParty, aPurpose);
    return false;
  }
  nsString outScheme;
  nsString outBaseDomain;
  int32_t outPort;
  bool outForeignByAncestorContext;
  OriginAttributes::ParsePartitionKey(partitionKey, outScheme, outBaseDomain,
                                      outPort, outForeignByAncestorContext);
  if (outForeignByAncestorContext) {
    // nested first party
    ReportTelemetry(GleanLabel::eNestedFirstParty, aPurpose);
    return StaticPrefs::
        dom_webnotifications_forbid_nested_first_party_enabled();
  }

  // third party
  ReportTelemetry(GleanLabel::eThirdParty, aPurpose);
  if (aRequestorDoc) {
    nsContentUtils::ReportToConsole(
        nsIScriptError::errorFlag, "DOM"_ns, aRequestorDoc,
        PropertiesFile::DOM_PROPERTIES,
        "NotificationsCrossOriginIframeRequestIsForbidden");
  }
  return !StaticPrefs::dom_webnotifications_allowcrossoriginiframe();
}

NotificationPermission GetRawNotificationPermission(nsIPrincipal* aPrincipal) {
  AssertIsOnMainThread();

  uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;

  nsCOMPtr<nsIPermissionManager> permissionManager =
      components::PermissionManager::Service();
  if (!permissionManager) {
    return NotificationPermission::Default;
  }

  permissionManager->TestExactPermissionFromPrincipal(
      aPrincipal, "desktop-notification"_ns, &permission);

  // Convert the result to one of the enum types.
  switch (permission) {
    case nsIPermissionManager::ALLOW_ACTION:
      return NotificationPermission::Granted;
    case nsIPermissionManager::DENY_ACTION:
      return NotificationPermission::Denied;
    default:
      return NotificationPermission::Default;
  }
}

NotificationPermission GetNotificationPermission(
    nsIPrincipal* aPrincipal, nsIPrincipal* aEffectiveStoragePrincipal,
    bool isSecureContext, PermissionCheckPurpose aPurpose) {
  if (IsNotificationAllowedFor(aPrincipal)) {
    return NotificationPermission::Granted;
  }
  if (IsNotificationForbiddenFor(aPrincipal, aEffectiveStoragePrincipal,
                                 isSecureContext, aPurpose)) {
    return NotificationPermission::Denied;
  }

  return GetRawNotificationPermission(aPrincipal);
}

nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) {
  if (!aPrincipal) {
    return NS_ERROR_FAILURE;
  }

  nsAutoCString origin;
  MOZ_TRY(aPrincipal->GetOrigin(origin));

  CopyUTF8toUTF16(origin, aOrigin);

  return NS_OK;
}

nsCOMPtr<nsINotificationStorage> GetNotificationStorage(bool isPrivate) {
  return do_GetService(isPrivate ? NS_MEMORY_NOTIFICATION_STORAGE_CONTRACTID
                                 : NS_NOTIFICATION_STORAGE_CONTRACTID);
}

class NotificationsCallback : public nsINotificationStorageCallback {
 public:
  NS_DECL_ISUPPORTS

  already_AddRefed<NotificationsPromise> Promise() {
    return mPromiseHolder.Ensure(__func__);
  }

  NS_IMETHOD Done(
      const nsTArray<RefPtr<nsINotificationStorageEntry>>& aEntries) final {
    AssertIsOnMainThread();

    nsTArray<IPCNotification> notifications(aEntries.Length());
    for (const auto& entry : aEntries) {
      auto result = NotificationStorageEntry::ToIPC(*entry);
      if (result.isErr()) {
        continue;
      }
      MOZ_ASSERT(!result.inspect().id().IsEmpty());
      notifications.AppendElement(result.unwrap());
    }

    mPromiseHolder.Resolve(std::move(notifications), __func__);
    return NS_OK;
  }

 protected:
  virtual ~NotificationsCallback() {
    // We may be shutting down prematurely without getting the result, so make
    // sure to settle the promise.
    mPromiseHolder.RejectIfExists(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
  };

  MozPromiseHolder<NotificationsPromise> mPromiseHolder;
};

NS_IMPL_ISUPPORTS(NotificationsCallback, nsINotificationStorageCallback)

already_AddRefed<NotificationsPromise> GetStoredNotificationsForScope(
    nsIPrincipal* aPrincipal, const nsACString& aScope, const nsAString& aTag) {
  nsString origin;
  nsresult rv = GetOrigin(aPrincipal, origin);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return NotificationsPromise::CreateAndReject(rv, __func__).forget();
  }

  RefPtr<NotificationsCallback> callback = new NotificationsCallback();
  RefPtr<NotificationsPromise> promise = callback->Promise();

  nsCOMPtr<nsINotificationStorage> notificationStorage =
      GetNotificationStorage(aPrincipal->GetIsInPrivateBrowsing());
  if (!notificationStorage) {
    return NotificationsPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
                                                 __func__)
        .forget();
  }

  rv = notificationStorage->Get(origin, NS_ConvertUTF8toUTF16(aScope), aTag,
                                callback);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return NotificationsPromise::CreateAndReject(rv, __func__).forget();
  }
  return promise.forget();
}

nsresult PersistNotification(nsIPrincipal* aPrincipal,
                             const IPCNotification& aNotification,
                             const nsString& aScope) {
  nsCOMPtr<nsINotificationStorage> notificationStorage =
      GetNotificationStorage(aPrincipal->GetIsInPrivateBrowsing());
  if (NS_WARN_IF(!notificationStorage)) {
    return NS_ERROR_UNEXPECTED;
  }

  nsString origin;
  nsresult rv = GetOrigin(aPrincipal, origin);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  RefPtr<NotificationStorageEntry> entry =
      new NotificationStorageEntry(aNotification);

  rv = notificationStorage->Put(origin, entry, aScope);

  if (NS_FAILED(rv)) {
    return rv;
  }

  return NS_OK;
}

nsresult UnpersistNotification(nsIPrincipal* aPrincipal, const nsString& aId) {
  if (!aPrincipal) {
    return NS_ERROR_FAILURE;
  }
  if (nsCOMPtr<nsINotificationStorage> notificationStorage =
          GetNotificationStorage(aPrincipal->GetIsInPrivateBrowsing())) {
    nsString origin;
    MOZ_TRY(GetOrigin(aPrincipal, origin));
    return notificationStorage->Delete(origin, aId);
  }
  return NS_ERROR_FAILURE;
}

nsresult UnpersistAllNotificationsExcept(const nsTArray<nsString>& aIds) {
  // Cleanup makes only sense for on-disk storage
  if (nsCOMPtr<nsINotificationStorage> notificationStorage =
          GetNotificationStorage(false)) {
    return notificationStorage->DeleteAllExcept(aIds);
  }
  return NS_ERROR_FAILURE;
}

void UnregisterNotification(nsIPrincipal* aPrincipal, const nsString& aId) {
  UnpersistNotification(aPrincipal, aId);
  if (nsCOMPtr<nsIAlertsService> alertService = components::Alerts::Service()) {
    alertService->CloseAlert(aId, /* aContextClosed */ false);
  }
}

nsresult ShowAlertWithCleanup(nsIAlertNotification* aAlert,
                              nsIObserver* aAlertListener) {
  nsCOMPtr<nsIAlertsService> alertService = components::Alerts::Service();
  if (!gTriedStorageCleanup ||
      StaticPrefs::
          dom_webnotifications_testing_force_storage_cleanup_enabled()) {
    // The below may fail, but retry probably won't make it work
    gTriedStorageCleanup = true;

    // Get the list of currently displayed notifications known to the
    // notification backend and unpersist all other notifications from
    // NotificationDB.
    // (This won't affect the following persist call by ShowAlert, as the DB
    // maintains a job queue)
    // Note that we ignore the result of GetHistory - we still go ahead and
    // clears notifications even if it fails, as the failure implies there's no
    // history and thus we should clear everything.
    nsTArray<nsString> history;
    (void)alertService->GetHistory(history);
    UnpersistAllNotificationsExcept(history);
  }

  MOZ_TRY(alertService->ShowAlert(aAlert, aAlertListener));
  return NS_OK;
}

nsresult RemovePermission(nsIPrincipal* aPrincipal) {
  MOZ_ASSERT(XRE_IsParentProcess());
  nsCOMPtr<nsIPermissionManager> permissionManager =
      mozilla::components::PermissionManager::Service();
  if (!permissionManager) {
    return NS_ERROR_FAILURE;
  }
  permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification"_ns);
  return NS_OK;
}

nsresult OpenSettings(nsIPrincipal* aPrincipal) {
  MOZ_ASSERT(XRE_IsParentProcess());
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (!obs) {
    return NS_ERROR_FAILURE;
  }
  // Notify other observers so they can show settings UI.
  obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
  return NS_OK;
}

nsresult AdjustPushQuota(nsIPrincipal* aPrincipal,
                         NotificationStatusChange aChange) {
  MOZ_ASSERT(XRE_IsParentProcess());
  nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
      do_GetService("@mozilla.org/push/Service;1");
  if (!pushQuotaManager) {
    return NS_ERROR_FAILURE;
  }

  nsAutoCString origin;
  MOZ_TRY(aPrincipal->GetOrigin(origin));

  if (aChange == NotificationStatusChange::Shown) {
    return pushQuotaManager->NotificationForOriginShown(origin.get());
  }
  return pushQuotaManager->NotificationForOriginClosed(origin.get());
}

NS_IMPL_ISUPPORTS(NotificationActionStorageEntry,
                  nsINotificationActionStorageEntry)

NS_IMETHODIMP NotificationActionStorageEntry::GetName(nsAString& aName) {
  aName = mIPCAction.name();
  return NS_OK;
}

NS_IMETHODIMP NotificationActionStorageEntry::GetTitle(nsAString& aTitle) {
  aTitle = mIPCAction.title();
  return NS_OK;
}

Result<IPCNotificationAction, nsresult> NotificationActionStorageEntry::ToIPC(
    nsINotificationActionStorageEntry& aEntry) {
  IPCNotificationAction action;
  MOZ_TRY(aEntry.GetName(action.name()));
  MOZ_TRY(aEntry.GetTitle(action.title()));
  return action;
}

NS_IMPL_ISUPPORTS(NotificationStorageEntry, nsINotificationStorageEntry)

NS_IMETHODIMP NotificationStorageEntry::GetId(nsAString& aId) {
  aId = mIPCNotification.id();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetTitle(nsAString& aTitle) {
  aTitle = mIPCNotification.options().title();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetDir(nsACString& aDir) {
  aDir = GetEnumString(mIPCNotification.options().dir());
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetLang(nsAString& aLang) {
  aLang = mIPCNotification.options().lang();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetBody(nsAString& aBody) {
  aBody = mIPCNotification.options().body();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetTag(nsAString& aTag) {
  aTag = mIPCNotification.options().tag();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetIcon(nsACString& aIcon) {
  nsIURI* iconUri = mIPCNotification.options().icon();
  if (!iconUri) {
    aIcon.Truncate();
    return NS_OK;
  }
  iconUri->GetSpec(aIcon);
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetRequireInteraction(
    bool* aRequireInteraction) {
  *aRequireInteraction = mIPCNotification.options().requireInteraction();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetSilent(bool* aSilent) {
  *aSilent = mIPCNotification.options().silent();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetDataSerialized(
    nsAString& aDataSerialized) {
  aDataSerialized = mIPCNotification.options().dataSerialized();
  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetActions(
    nsTArray<RefPtr<nsINotificationActionStorageEntry>>& aRetVal) {
  nsTArray<RefPtr<nsINotificationActionStorageEntry>> actions(
      mIPCNotification.options().actions().Length());

  for (const auto& action : mIPCNotification.options().actions()) {
    actions.AppendElement(new NotificationActionStorageEntry(action));
  }

  aRetVal = std::move(actions);

  return NS_OK;
}

NS_IMETHODIMP NotificationStorageEntry::GetServiceWorkerRegistrationScope(
    nsAString& aScope) {
  // Scope is only provided from JS, for now
  // TODO(krosylight): Change nsINotificationStorage::Put to provide scope via
  // storage entry?
  aScope.SetIsVoid(true);
  return NS_OK;
}

Result<IPCNotification, nsresult> NotificationStorageEntry::ToIPC(
    nsINotificationStorageEntry& aEntry) {
  IPCNotification notification;
  IPCNotificationOptions& options = notification.options();
  MOZ_TRY(aEntry.GetId(notification.id()));
  MOZ_TRY(aEntry.GetTitle(options.title()));

  nsCString dir;
  MOZ_TRY(aEntry.GetDir(dir));
  options.dir() = StringToEnum<NotificationDirection>(dir).valueOr(
      NotificationDirection::Auto);

  MOZ_TRY(aEntry.GetLang(options.lang()));
  MOZ_TRY(aEntry.GetBody(options.body()));
  MOZ_TRY(aEntry.GetTag(options.tag()));

  nsAutoCString iconUrl;
  MOZ_TRY(aEntry.GetIcon(iconUrl));
  if (!iconUrl.IsEmpty()) {
    MOZ_TRY(NS_NewURI(getter_AddRefs(notification.options().icon()), iconUrl));
  }

  MOZ_TRY(aEntry.GetRequireInteraction(&options.requireInteraction()));
  MOZ_TRY(aEntry.GetSilent(&options.silent()));
  MOZ_TRY(aEntry.GetDataSerialized(options.dataSerialized()));

  nsTArray<RefPtr<nsINotificationActionStorageEntry>> actionEntries;
  MOZ_TRY(aEntry.GetActions(actionEntries));
  nsTArray<IPCNotificationAction> actions(actionEntries.Length());
  for (const auto& actionEntry : actionEntries) {
    IPCNotificationAction action =
        MOZ_TRY(NotificationActionStorageEntry::ToIPC(*actionEntry));
    actions.AppendElement(std::move(action));
  }
  options.actions() = std::move(actions);

  return notification;
}

}  // namespace mozilla::dom::notification

Messung V0.5 in Prozent
C=90 H=97 G=93

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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 und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142