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


Quelle  PlacesObservers.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 "PlacesObservers.h"

#include "PlacesWeakCallbackWrapper.h"
#include "nsIWeakReferenceUtils.h"
#include "nsIXPConnect.h"
#include "mozilla/ClearOnShutdown.h"

namespace mozilla::dom {

template <class T>
struct Flagged {
  Flagged(uint32_t aFlags, T&& aValue)
      : flags(aFlags), value(std::forward<T>(aValue)) {}
  Flagged(Flagged&& aOther)
      : Flagged(std::move(aOther.flags), std::move(aOther.value)) {}
  Flagged(const Flagged& aOther) = default;
  ~Flagged() = default;

  uint32_t flags = 0;
  T value;
};

template <class T>
using FlaggedArray = nsTArray<Flagged<T>>;

template <class T>
struct ListenerCollection {
  static StaticAutoPtr<FlaggedArray<T>> gListeners;
  static StaticAutoPtr<FlaggedArray<T>> gListenersToRemove;

  static FlaggedArray<T>* GetListeners(bool aDoNotInit = false) {
    MOZ_ASSERT(NS_IsMainThread());
    if (!gListeners && !aDoNotInit) {
      gListeners = new FlaggedArray<T>();
      ClearOnShutdown(&gListeners);
    }
    return gListeners;
  }

  static FlaggedArray<T>* GetListenersToRemove(bool aDoNotInit = false) {
    MOZ_ASSERT(NS_IsMainThread());
    if (!gListenersToRemove && !aDoNotInit) {
      gListenersToRemove = new FlaggedArray<T>();
      ClearOnShutdown(&gListenersToRemove);
    }
    return gListenersToRemove;
  }
};

template <class T>
MOZ_GLOBINIT StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListeners;
template <class T>
MOZ_GLOBINIT StaticAutoPtr<FlaggedArray<T>>
    ListenerCollection<T>::gListenersToRemove;

using JSListeners = ListenerCollection<RefPtr<PlacesEventCallback>>;
using WeakJSListeners = ListenerCollection<WeakPtr<PlacesWeakCallbackWrapper>>;
using WeakNativeListeners =
    ListenerCollection<WeakPtr<places::INativePlacesEventCallback>>;

// Even if NotifyListeners is called any timing, we mange the notifications with
// adding to this queue, then sending in sequence. This avoids sending nested
// notifications while previous ones are still being sent.
MOZ_RUNINIT static nsTArray<Sequence<OwningNonNull<PlacesEvent>>>
    gNotificationQueue;

uint32_t GetEventTypeFlag(PlacesEventType aEventType) {
  if (aEventType == PlacesEventType::None) {
    return 0;
  }
  return 1 << ((uint32_t)aEventType - 1);
}

uint32_t GetFlagsForEventTypes(const nsTArray<PlacesEventType>& aEventTypes) {
  uint32_t flags = 0;
  for (PlacesEventType eventType : aEventTypes) {
    flags |= GetEventTypeFlag(eventType);
  }
  return flags;
}

uint32_t GetFlagsForEvents(
    const nsTArray<OwningNonNull<PlacesEvent>>& aEvents) {
  uint32_t flags = 0;
  for (const PlacesEvent& event : aEvents) {
    flags |= GetEventTypeFlag(event.Type());
  }
  return flags;
}

template <class TWrapped, class TUnwrapped, class TListenerCollection>
MOZ_CAN_RUN_SCRIPT void CallListeners(
    uint32_t aEventFlags, const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
    unsigned long aListenersLengthToCall,
    const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
    const std::function<void(TUnwrapped&,
                             const Sequence<OwningNonNull<PlacesEvent>>&)>&
        aCallListener) {
  auto& listeners = *TListenerCollection::GetListeners();
  for (uint32_t i = 0; i < aListenersLengthToCall; i++) {
    Flagged<TWrapped>& listener = listeners[i];
    TUnwrapped unwrapped = aUnwrapListener(listener.value);
    if (!unwrapped) {
      continue;
    }

    if ((listener.flags & aEventFlags) == aEventFlags) {
      aCallListener(unwrapped, aEvents);
    } else if (listener.flags & aEventFlags) {
      Sequence<OwningNonNull<PlacesEvent>> filtered;
      for (const OwningNonNull<PlacesEvent>& event : aEvents) {
        if (listener.flags & GetEventTypeFlag(event->Type())) {
          bool success = !!filtered.AppendElement(event, fallible);
          MOZ_RELEASE_ASSERT(success);
        }
      }
      aCallListener(unwrapped, filtered);
    }
  }
}

StaticRefPtr<PlacesEventCounts> PlacesObservers::sCounts;
static void EnsureCountsInitialized() {
  if (!PlacesObservers::sCounts) {
    PlacesObservers::sCounts = new PlacesEventCounts();
    ClearOnShutdown(&PlacesObservers::sCounts);
  }
}

void PlacesObservers::AddListener(GlobalObject& aGlobal,
                                  const nsTArray<PlacesEventType>& aEventTypes,
                                  PlacesEventCallback& aCallback,
                                  ErrorResult& rv) {
  uint32_t flags = GetFlagsForEventTypes(aEventTypes);

  FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
      JSListeners::GetListeners();
  Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
  listeners->AppendElement(pair);
}

void PlacesObservers::AddListener(GlobalObject& aGlobal,
                                  const nsTArray<PlacesEventType>& aEventTypes,
                                  PlacesWeakCallbackWrapper& aCallback,
                                  ErrorResult& rv) {
  uint32_t flags = GetFlagsForEventTypes(aEventTypes);

  FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
      WeakJSListeners::GetListeners();
  WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
  MOZ_ASSERT(weakCb.get());
  Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb));
  listeners->AppendElement(flagged);
}

void PlacesObservers::AddListener(
    const nsTArray<PlacesEventType>& aEventTypes,
    places::INativePlacesEventCallback* aCallback) {
  uint32_t flags = GetFlagsForEventTypes(aEventTypes);

  FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
      WeakNativeListeners::GetListeners();
  Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
  listeners->AppendElement(pair);
}

void PlacesObservers::RemoveListener(
    GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
    PlacesEventCallback& aCallback, ErrorResult& rv) {
  uint32_t flags = GetFlagsForEventTypes(aEventTypes);
  if (!gNotificationQueue.IsEmpty()) {
    FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
        JSListeners::GetListenersToRemove();
    Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
    listeners->AppendElement(pair);
  } else {
    RemoveListener(flags, aCallback);
  }
}

void PlacesObservers::RemoveListener(
    GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
    PlacesWeakCallbackWrapper& aCallback, ErrorResult& rv) {
  uint32_t flags = GetFlagsForEventTypes(aEventTypes);
  if (!gNotificationQueue.IsEmpty()) {
    FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
        WeakJSListeners::GetListenersToRemove();
    WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
    MOZ_ASSERT(weakCb.get());
    Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags,
                                                        std::move(weakCb));
    listeners->AppendElement(flagged);
  } else {
    RemoveListener(flags, aCallback);
  }
}

void PlacesObservers::RemoveListener(
    const nsTArray<PlacesEventType>& aEventTypes,
    places::INativePlacesEventCallback* aCallback) {
  uint32_t flags = GetFlagsForEventTypes(aEventTypes);
  if (!gNotificationQueue.IsEmpty()) {
    FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
        WeakNativeListeners::GetListenersToRemove();
    Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
    listeners->AppendElement(pair);
  } else {
    RemoveListener(flags, aCallback);
  }
}

void PlacesObservers::RemoveListener(uint32_t aFlags,
                                     PlacesEventCallback& aCallback) {
  FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
      JSListeners::GetListeners(/* aDoNotInit: */ true);
  if (!listeners) {
    return;
  }
  for (uint32_t i = 0; i < listeners->Length(); i++) {
    Flagged<RefPtr<PlacesEventCallback>>& l = listeners->ElementAt(i);
    if (!(*l.value == aCallback)) {
      continue;
    }
    if (l.flags == (aFlags & l.flags)) {
      listeners->RemoveElementAt(i);
      i--;
    } else {
      l.flags &= ~aFlags;
    }
  }
}

void PlacesObservers::RemoveListener(uint32_t aFlags,
                                     PlacesWeakCallbackWrapper& aCallback) {
  FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
      WeakJSListeners::GetListeners(/* aDoNotInit: */ true);
  if (!listeners) {
    return;
  }
  for (uint32_t i = 0; i < listeners->Length(); i++) {
    Flagged<WeakPtr<PlacesWeakCallbackWrapper>>& l = listeners->ElementAt(i);
    RefPtr<PlacesWeakCallbackWrapper> unwrapped = l.value.get();
    if (unwrapped != &aCallback) {
      continue;
    }
    if (l.flags == (aFlags & l.flags)) {
      listeners->RemoveElementAt(i);
      i--;
    } else {
      l.flags &= ~aFlags;
    }
  }
}

void PlacesObservers::RemoveListener(
    uint32_t aFlags, places::INativePlacesEventCallback* aCallback) {
  FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
      WeakNativeListeners::GetListeners(/* aDoNotInit: */ true);
  if (!listeners) {
    return;
  }
  for (uint32_t i = 0; i < listeners->Length(); i++) {
    Flagged<WeakPtr<places::INativePlacesEventCallback>>& l =
        listeners->ElementAt(i);
    RefPtr<places::INativePlacesEventCallback> unwrapped = l.value.get();
    if (unwrapped != aCallback) {
      continue;
    }
    if (l.flags == (aFlags & l.flags)) {
      listeners->RemoveElementAt(i);
      i--;
    } else {
      l.flags &= ~aFlags;
    }
  }
}

template <class TWrapped, class TUnwrapped, class TListenerCollection>
void CleanupListeners(
    const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
    const std::function<void(Flagged<TWrapped>&)>& aRemoveListener) {
  auto& listeners = *TListenerCollection::GetListeners();
  for (uint32_t i = 0; i < listeners.Length(); i++) {
    Flagged<TWrapped>& listener = listeners[i];
    TUnwrapped unwrapped = aUnwrapListener(listener.value);
    if (!unwrapped) {
      listeners.RemoveElementAt(i);
      i--;
    }
  }

  auto& listenersToRemove = *TListenerCollection::GetListenersToRemove();
  for (auto& listener : listenersToRemove) {
    aRemoveListener(listener);
  }
  listenersToRemove.Clear();
}

void PlacesObservers::NotifyListeners(
    GlobalObject& aGlobal, const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
    ErrorResult& rv) {
  NotifyListeners(aEvents);
}

void PlacesObservers::NotifyListeners(
    const Sequence<OwningNonNull<PlacesEvent>>& aEvents) {
  MOZ_ASSERT(aEvents.Length() > 0, "Must pass a populated array of events");
  if (aEvents.Length() == 0) {
    return;
  }
  EnsureCountsInitialized();
  for (const auto& event : aEvents) {
    DebugOnly<nsresult> rv = sCounts->Increment(event->Type());
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }
#ifdef DEBUG
  if (!gNotificationQueue.IsEmpty()) {
    NS_WARNING(
        "Avoid nested Places notifications if possible, the order of events "
        "cannot be guaranteed");
    nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
    Unused << xpc->DebugDumpJSStack(falsefalsefalse);
  }
#endif

  gNotificationQueue.AppendElement(aEvents);

  // If gNotificationQueue has only the events we added now, start to notify.
  // Otherwise, as it already started the notification processing,
  // rely on the processing.
  if (gNotificationQueue.Length() == 1) {
    NotifyNext();
  }
}

void PlacesObservers::NotifyNext() {
  auto events = gNotificationQueue[0];
  uint32_t flags = GetFlagsForEvents(events);

  // Send up to the number of current listeners, to avoid handling listeners
  // added during this notification.
  unsigned long jsListenersLength = JSListeners::GetListeners()->Length();
  unsigned long weakNativeListenersLength =
      WeakNativeListeners::GetListeners()->Length();
  unsigned long weakJSListenersLength =
      WeakJSListeners::GetListeners()->Length();

  CallListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>,
                JSListeners>(
      flags, events, jsListenersLength, [](auto& cb) { return cb; },
      // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
      // some internals of the std::function implementation that we can't
      // annotate.  We handle this by annotating CallListeners and making sure
      // it holds a strong ref to the callback.
      [&](auto& cb, const auto& events)
          MOZ_CAN_RUN_SCRIPT_BOUNDARY { MOZ_KnownLive(cb)->Call(events); });

  CallListeners<WeakPtr<places::INativePlacesEventCallback>,
                RefPtr<places::INativePlacesEventCallback>,
                WeakNativeListeners>(
      flags, events, weakNativeListenersLength,
      [](auto& cb) { return cb.get(); },
      [&](auto& cb, const Sequence<OwningNonNull<PlacesEvent>>& events) {
        cb->HandlePlacesEvent(events);
      });

  CallListeners<WeakPtr<PlacesWeakCallbackWrapper>,
                RefPtr<PlacesWeakCallbackWrapper>, WeakJSListeners>(
      flags, events, weakJSListenersLength, [](auto& cb) { return cb.get(); },
      // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
      // some internals of the std::function implementation that we can't
      // annotate.  We handle this by annotating CallListeners and making sure
      // it holds a strong ref to the callback.
      [&](auto& cb, const auto& events) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
        RefPtr<PlacesEventCallback> callback(cb->mCallback);
        callback->Call(events);
      });

  gNotificationQueue.RemoveElementAt(0);

  CleanupListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>,
                   JSListeners>(
      [](auto& cb) { return cb; },
      [&](auto& cb) { RemoveListener(cb.flags, *cb.value); });
  CleanupListeners<WeakPtr<PlacesWeakCallbackWrapper>,
                   RefPtr<PlacesWeakCallbackWrapper>, WeakJSListeners>(
      [](auto& cb) { return cb.get(); },
      [&](auto& cb) { RemoveListener(cb.flags, *cb.value.get()); });
  CleanupListeners<WeakPtr<places::INativePlacesEventCallback>,
                   RefPtr<places::INativePlacesEventCallback>,
                   WeakNativeListeners>(
      [](auto& cb) { return cb.get(); },
      [&](auto& cb) { RemoveListener(cb.flags, cb.value.get()); });

  if (!gNotificationQueue.IsEmpty()) {
    NotifyNext();
  }
}

already_AddRefed<PlacesEventCounts> PlacesObservers::Counts(
    const GlobalObject& global) {
  EnsureCountsInitialized();
  return do_AddRef(sCounts);
};

}  // namespace mozilla::dom

89%


¤ Dauer der Verarbeitung: 0.16 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge