/* -*- 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 "EventSourceEventService.h"
#include "mozilla/StaticPtr.h"
#include "nsISupportsPrimitives.h"
#include "nsIObserverService.h"
#include "nsXULAppAPI.h"
#include "nsSocketTransportService2.h"
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
namespace mozilla::dom {
namespace {
StaticRefPtr<EventSourceEventService> gEventSourceEventService;
} // anonymous namespace
class EventSourceBaseRunnable : public Runnable {
public :
EventSourceBaseRunnable(uint64_t aHttpChannelId, uint64_t aInnerWindowID)
: Runnable("dom::EventSourceBaseRunnable" ),
mHttpChannelId(aHttpChannelId),
mInnerWindowID(aInnerWindowID) {}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<EventSourceEventService> service =
EventSourceEventService::GetOrCreate();
MOZ_ASSERT(service);
EventSourceEventService::EventSourceListeners listeners;
service->GetListeners(mInnerWindowID, listeners);
for (uint32_t i = 0; i < listeners.Length(); ++i) {
DoWork(listeners[i]);
}
return NS_OK;
}
protected :
~EventSourceBaseRunnable() = default ;
virtual void DoWork(nsIEventSourceEventListener* aListener) = 0;
uint64_t mHttpChannelId;
uint64_t mInnerWindowID;
};
class EventSourceConnectionOpenedRunnable final
: public EventSourceBaseRunnable {
public :
EventSourceConnectionOpenedRunnable(uint64_t aHttpChannelId,
uint64_t aInnerWindowID)
: EventSourceBaseRunnable(aHttpChannelId, aInnerWindowID) {}
private :
virtual void DoWork(nsIEventSourceEventListener* aListener) override {
DebugOnly<nsresult> rv =
aListener->EventSourceConnectionOpened(mHttpChannelId);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EventSourceConnectionOpened failed" );
}
};
class EventSourceConnectionClosedRunnable final
: public EventSourceBaseRunnable {
public :
EventSourceConnectionClosedRunnable(uint64_t aHttpChannelId,
uint64_t aInnerWindowID)
: EventSourceBaseRunnable(aHttpChannelId, aInnerWindowID) {}
private :
virtual void DoWork(nsIEventSourceEventListener* aListener) override {
DebugOnly<nsresult> rv =
aListener->EventSourceConnectionClosed(mHttpChannelId);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EventSourceConnectionClosed failed" );
}
};
class EventSourceEventRunnable final : public EventSourceBaseRunnable {
public :
EventSourceEventRunnable(uint64_t aHttpChannelId, uint64_t aInnerWindowID,
const nsAString& aEventName,
const nsAString& aLastEventID,
const nsAString& aData, uint32_t aRetry,
DOMHighResTimeStamp aTimeStamp)
: EventSourceBaseRunnable(aHttpChannelId, aInnerWindowID),
mEventName(aEventName),
mLastEventID(aLastEventID),
mData(aData),
mRetry(aRetry),
mTimeStamp(aTimeStamp) {}
private :
virtual void DoWork(nsIEventSourceEventListener* aListener) override {
DebugOnly<nsresult> rv = aListener->EventReceived(
mHttpChannelId, mEventName, mLastEventID, mData, mRetry, mTimeStamp);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Event op failed" );
}
nsString mEventName;
nsString mLastEventID;
nsString mData;
uint32_t mRetry;
DOMHighResTimeStamp mTimeStamp;
};
/* static */
already_AddRefed<EventSourceEventService>
EventSourceEventService::GetOrCreate() {
MOZ_ASSERT(NS_IsMainThread());
if (!gEventSourceEventService) {
gEventSourceEventService = new EventSourceEventService();
}
RefPtr<EventSourceEventService> service = gEventSourceEventService.get();
return service.forget();
}
NS_INTERFACE_MAP_BEGIN(EventSourceEventService)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEventSourceEventService)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIEventSourceEventService)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(EventSourceEventService)
NS_IMPL_RELEASE(EventSourceEventService)
EventSourceEventService::EventSourceEventService() : mCountListeners(0) {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this , "xpcom-shutdown" , false );
obs->AddObserver(this , "inner-window-destroyed" , false );
}
}
EventSourceEventService::~EventSourceEventService() {
MOZ_ASSERT(NS_IsMainThread());
}
void EventSourceEventService::EventSourceConnectionOpened(
uint64_t aHttpChannelId, uint64_t aInnerWindowID) {
// Let's continue only if we have some listeners.
if (!HasListeners()) {
return ;
}
RefPtr<EventSourceConnectionOpenedRunnable> runnable =
new EventSourceConnectionOpenedRunnable(aHttpChannelId, aInnerWindowID);
DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed" );
}
void EventSourceEventService::EventSourceConnectionClosed(
uint64_t aHttpChannelId, uint64_t aInnerWindowID) {
// Let's continue only if we have some listeners.
if (!HasListeners()) {
return ;
}
RefPtr<EventSourceConnectionClosedRunnable> runnable =
new EventSourceConnectionClosedRunnable(aHttpChannelId, aInnerWindowID);
DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed" );
}
void EventSourceEventService::EventReceived(
uint64_t aHttpChannelId, uint64_t aInnerWindowID,
const nsAString& aEventName, const nsAString& aLastEventID,
const nsAString& aData, uint32_t aRetry, DOMHighResTimeStamp aTimeStamp) {
// Let's continue only if we have some listeners.
if (!HasListeners()) {
return ;
}
RefPtr<EventSourceEventRunnable> runnable =
new EventSourceEventRunnable(aHttpChannelId, aInnerWindowID, aEventName,
aLastEventID, aData, aRetry, aTimeStamp);
DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed" );
}
NS_IMETHODIMP
EventSourceEventService::AddListener(uint64_t aInnerWindowID,
nsIEventSourceEventListener* aListener) {
MOZ_ASSERT(NS_IsMainThread());
if (!aListener) {
return NS_ERROR_FAILURE;
}
++mCountListeners;
WindowListener* listener = mWindows.GetOrInsertNew(aInnerWindowID);
listener->mListeners.AppendElement(aListener);
return NS_OK;
}
NS_IMETHODIMP
EventSourceEventService::RemoveListener(
uint64_t aInnerWindowID, nsIEventSourceEventListener* aListener) {
MOZ_ASSERT(NS_IsMainThread());
if (!aListener) {
return NS_ERROR_FAILURE;
}
WindowListener* listener = mWindows.Get(aInnerWindowID);
if (!listener) {
return NS_ERROR_FAILURE;
}
if (!listener->mListeners.RemoveElement(aListener)) {
return NS_ERROR_FAILURE;
}
// The last listener for this window.
if (listener->mListeners.IsEmpty()) {
mWindows.Remove(aInnerWindowID);
}
MOZ_ASSERT(mCountListeners);
--mCountListeners;
return NS_OK;
}
NS_IMETHODIMP
EventSourceEventService::HasListenerFor(uint64_t aInnerWindowID,
bool * aResult) {
MOZ_ASSERT(NS_IsMainThread());
*aResult = mWindows.Get(aInnerWindowID);
return NS_OK;
}
NS_IMETHODIMP
EventSourceEventService::Observe(nsISupports* aSubject, const char * aTopic,
const char16_t* aData) {
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(aTopic, "xpcom-shutdown" )) {
Shutdown();
return NS_OK;
}
if (!strcmp(aTopic, "inner-window-destroyed" ) && HasListeners()) {
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
uint64_t innerID;
nsresult rv = wrapper->GetData(&innerID);
NS_ENSURE_SUCCESS(rv, rv);
WindowListener* listener = mWindows.Get(innerID);
if (!listener) {
return NS_OK;
}
MOZ_ASSERT(mCountListeners >= listener->mListeners.Length());
mCountListeners -= listener->mListeners.Length();
mWindows.Remove(innerID);
}
// This should not happen.
return NS_ERROR_FAILURE;
}
void EventSourceEventService::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
if (gEventSourceEventService) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(gEventSourceEventService, "xpcom-shutdown" );
obs->RemoveObserver(gEventSourceEventService, "inner-window-destroyed" );
}
mWindows.Clear();
gEventSourceEventService = nullptr;
}
}
bool EventSourceEventService::HasListeners() const { return !!mCountListeners; }
void EventSourceEventService::GetListeners(
uint64_t aInnerWindowID,
EventSourceEventService::EventSourceListeners& aListeners) const {
aListeners.Clear();
WindowListener* listener = mWindows.Get(aInnerWindowID);
if (!listener) {
return ;
}
aListeners.AppendElements(listener->mListeners);
}
} // namespace mozilla::dom
quality 98%
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland