зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1603585 - Notify DevTools when an EventSource object is created. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D67918
This commit is contained in:
Родитель
4540e975f8
Коммит
c4534f1f07
|
@ -18,6 +18,7 @@
|
||||||
#include "mozilla/dom/WorkerRef.h"
|
#include "mozilla/dom/WorkerRef.h"
|
||||||
#include "mozilla/dom/WorkerRunnable.h"
|
#include "mozilla/dom/WorkerRunnable.h"
|
||||||
#include "mozilla/dom/WorkerScope.h"
|
#include "mozilla/dom/WorkerScope.h"
|
||||||
|
#include "mozilla/dom/EventSourceEventService.h"
|
||||||
#include "mozilla/UniquePtrExtensions.h"
|
#include "mozilla/UniquePtrExtensions.h"
|
||||||
#include "nsIThreadRetargetableStreamListener.h"
|
#include "nsIThreadRetargetableStreamListener.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
|
@ -276,6 +277,34 @@ class EventSourceImpl final : public nsIObserver,
|
||||||
// Whether the EventSourceImpl is going to be destroyed.
|
// Whether the EventSourceImpl is going to be destroyed.
|
||||||
bool mIsShutDown;
|
bool mIsShutDown;
|
||||||
|
|
||||||
|
class EventSourceServiceNotifier final {
|
||||||
|
public:
|
||||||
|
EventSourceServiceNotifier(uint64_t aHttpChannelId, uint64_t aInnerWindowID)
|
||||||
|
: mHttpChannelId(aHttpChannelId), mInnerWindowID(aInnerWindowID) {
|
||||||
|
mService = EventSourceEventService::GetOrCreate();
|
||||||
|
mService->EventSourceConnectionOpened(aHttpChannelId, aInnerWindowID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventReceived(const nsAString& aEventName,
|
||||||
|
const nsAString& aLastEventID, const nsAString& aData,
|
||||||
|
uint32_t aRetry, DOMHighResTimeStamp aTimeStamp) {
|
||||||
|
mService->EventReceived(mHttpChannelId, mInnerWindowID, aEventName,
|
||||||
|
aLastEventID, aData, aRetry, aTimeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
~EventSourceServiceNotifier() {
|
||||||
|
mService->EventSourceConnectionClosed(mHttpChannelId, mInnerWindowID);
|
||||||
|
NS_ReleaseOnMainThread("EventSourceServiceNotifier::mService",
|
||||||
|
mService.forget());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<EventSourceEventService> mService;
|
||||||
|
uint64_t mHttpChannelId;
|
||||||
|
uint64_t mInnerWindowID;
|
||||||
|
};
|
||||||
|
|
||||||
|
UniquePtr<EventSourceServiceNotifier> mServiceNotifier;
|
||||||
// Event Source owner information:
|
// Event Source owner information:
|
||||||
// - the script file name
|
// - the script file name
|
||||||
// - source code line number and column number where the Event Source object
|
// - source code line number and column number where the Event Source object
|
||||||
|
@ -363,6 +392,8 @@ void EventSourceImpl::Close() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mServiceNotifier = nullptr;
|
||||||
|
|
||||||
SetReadyState(CLOSED);
|
SetReadyState(CLOSED);
|
||||||
CloseInternal();
|
CloseInternal();
|
||||||
}
|
}
|
||||||
|
@ -657,6 +688,9 @@ EventSourceImpl::OnStartRequest(nsIRequest* aRequest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mServiceNotifier = MakeUnique<EventSourceServiceNotifier>(
|
||||||
|
mHttpChannel->ChannelId(), mInnerWindowID);
|
||||||
rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
|
rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
|
||||||
this, &EventSourceImpl::AnnounceConnection),
|
this, &EventSourceImpl::AnnounceConnection),
|
||||||
NS_DISPATCH_NORMAL);
|
NS_DISPATCH_NORMAL);
|
||||||
|
@ -754,6 +788,7 @@ EventSourceImpl::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
|
||||||
aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED &&
|
aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED &&
|
||||||
aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL) {
|
aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL) {
|
||||||
DispatchFailConnection();
|
DispatchFailConnection();
|
||||||
|
mServiceNotifier = nullptr;
|
||||||
return NS_ERROR_ABORT;
|
return NS_ERROR_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,6 +1085,7 @@ void EventSourceImpl::AnnounceConnection() {
|
||||||
|
|
||||||
nsresult EventSourceImpl::ResetConnection() {
|
nsresult EventSourceImpl::ResetConnection() {
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
mServiceNotifier = nullptr;
|
||||||
if (mHttpChannel) {
|
if (mHttpChannel) {
|
||||||
mHttpChannel->Cancel(NS_ERROR_ABORT);
|
mHttpChannel->Cancel(NS_ERROR_ABORT);
|
||||||
mHttpChannel = nullptr;
|
mHttpChannel = nullptr;
|
||||||
|
@ -1091,6 +1127,7 @@ nsresult EventSourceImpl::RestartConnection() {
|
||||||
if (IsClosed()) {
|
if (IsClosed()) {
|
||||||
return NS_ERROR_ABORT;
|
return NS_ERROR_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = ResetConnection();
|
nsresult rv = ResetConnection();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = SetReconnectionTimeout();
|
rv = SetReconnectionTimeout();
|
||||||
|
@ -1340,6 +1377,8 @@ nsresult EventSourceImpl::DispatchCurrentMessageEvent() {
|
||||||
message->mLastEventID.Assign(mLastEventID);
|
message->mLastEventID.Assign(mLastEventID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mServiceNotifier->EventReceived(message->mEventName, message->mLastEventID,
|
||||||
|
message->mData, mReconnectionTime, PR_Now());
|
||||||
mMessagesToDispatch.Push(message.release());
|
mMessagesToDispatch.Push(message.release());
|
||||||
|
|
||||||
if (!mGoingToDispatchAllMessages) {
|
if (!mGoingToDispatchAllMessages) {
|
||||||
|
@ -1852,6 +1891,8 @@ already_AddRefed<EventSource> EventSource::Constructor(
|
||||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||||
MOZ_ASSERT(workerPrivate);
|
MOZ_ASSERT(workerPrivate);
|
||||||
|
|
||||||
|
eventSourceImp->mInnerWindowID = workerPrivate->WindowID();
|
||||||
|
|
||||||
RefPtr<InitRunnable> initRunnable =
|
RefPtr<InitRunnable> initRunnable =
|
||||||
new InitRunnable(workerPrivate, eventSourceImp, aURL);
|
new InitRunnable(workerPrivate, eventSourceImp, aURL);
|
||||||
initRunnable->Dispatch(Canceling, aRv);
|
initRunnable->Dispatch(Canceling, aRv);
|
||||||
|
|
|
@ -0,0 +1,318 @@
|
||||||
|
/* -*- 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 {
|
||||||
|
namespace 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.Get(aInnerWindowID);
|
||||||
|
if (!listener) {
|
||||||
|
listener = new WindowListener();
|
||||||
|
mWindows.Put(aInnerWindowID, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* -*- 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_EventSourceEventService_h
|
||||||
|
#define mozilla_dom_EventSourceEventService_h
|
||||||
|
|
||||||
|
#include "mozilla/AlreadyAddRefed.h"
|
||||||
|
#include "mozilla/Atomics.h"
|
||||||
|
#include "nsIEventSourceEventService.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
#include "nsClassHashtable.h"
|
||||||
|
#include "nsHashKeys.h"
|
||||||
|
#include "nsIObserver.h"
|
||||||
|
#include "nsISupportsImpl.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class EventSourceEventService final : public nsIEventSourceEventService,
|
||||||
|
public nsIObserver {
|
||||||
|
friend class EventSourceBaseRunnable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
NS_DECL_NSIOBSERVER
|
||||||
|
NS_DECL_NSIEVENTSOURCEEVENTSERVICE
|
||||||
|
|
||||||
|
static already_AddRefed<EventSourceEventService> GetOrCreate();
|
||||||
|
|
||||||
|
void EventSourceConnectionOpened(uint64_t aHttpChannelId,
|
||||||
|
uint64_t aInnerWindowID);
|
||||||
|
|
||||||
|
void EventSourceConnectionClosed(uint64_t aHttpChannelId,
|
||||||
|
uint64_t aInnerWindowID);
|
||||||
|
|
||||||
|
void EventReceived(uint64_t aHttpChannelId, uint64_t aInnerWindowID,
|
||||||
|
const nsAString& aEventName, const nsAString& aLastEventID,
|
||||||
|
const nsAString& aData, uint32_t aRetry,
|
||||||
|
DOMHighResTimeStamp aTimeStamp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventSourceEventService();
|
||||||
|
~EventSourceEventService();
|
||||||
|
|
||||||
|
bool HasListeners() const;
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
using EventSourceListeners = nsTArray<nsCOMPtr<nsIEventSourceEventListener>>;
|
||||||
|
|
||||||
|
struct WindowListener {
|
||||||
|
EventSourceListeners mListeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
void GetListeners(uint64_t aInnerWindowID,
|
||||||
|
EventSourceListeners& aListeners) const;
|
||||||
|
|
||||||
|
// Used only on the main-thread.
|
||||||
|
nsClassHashtable<nsUint64HashKey, WindowListener> mWindows;
|
||||||
|
|
||||||
|
Atomic<uint64_t> mCountListeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Casting EventSourceEventService to nsISupports is ambiguous.
|
||||||
|
* This method handles that.
|
||||||
|
*/
|
||||||
|
inline nsISupports* ToSupports(mozilla::dom::EventSourceEventService* p) {
|
||||||
|
return NS_ISUPPORTS_CAST(nsIEventSourceEventService*, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // mozilla_dom_EventSourceEventService_h
|
|
@ -18,6 +18,7 @@ XPIDL_SOURCES += [
|
||||||
'nsIDocumentEncoder.idl',
|
'nsIDocumentEncoder.idl',
|
||||||
'nsIDOMRequestService.idl',
|
'nsIDOMRequestService.idl',
|
||||||
'nsIDroppedLinkHandler.idl',
|
'nsIDroppedLinkHandler.idl',
|
||||||
|
'nsIEventSourceEventService.idl',
|
||||||
'nsIImageLoadingContent.idl',
|
'nsIImageLoadingContent.idl',
|
||||||
'nsIMessageManager.idl',
|
'nsIMessageManager.idl',
|
||||||
'nsIObjectLoadingContent.idl',
|
'nsIObjectLoadingContent.idl',
|
||||||
|
@ -183,6 +184,7 @@ EXPORTS.mozilla.dom += [
|
||||||
'Element.h',
|
'Element.h',
|
||||||
'ElementInlines.h',
|
'ElementInlines.h',
|
||||||
'EventSource.h',
|
'EventSource.h',
|
||||||
|
'EventSourceEventService.h',
|
||||||
'FilteredNodeIterator.h',
|
'FilteredNodeIterator.h',
|
||||||
'FormData.h',
|
'FormData.h',
|
||||||
'FragmentOrElement.h',
|
'FragmentOrElement.h',
|
||||||
|
@ -303,6 +305,7 @@ UNIFIED_SOURCES += [
|
||||||
'DOMStringList.cpp',
|
'DOMStringList.cpp',
|
||||||
'Element.cpp',
|
'Element.cpp',
|
||||||
'EventSource.cpp',
|
'EventSource.cpp',
|
||||||
|
'EventSourceEventService.cpp',
|
||||||
'FormData.cpp',
|
'FormData.cpp',
|
||||||
'FragmentOrElement.cpp',
|
'FragmentOrElement.cpp',
|
||||||
'GeneratedImageContent.cpp',
|
'GeneratedImageContent.cpp',
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/* vim: set sw=4 ts=4 et 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 "domstubs.idl"
|
||||||
|
#include "nsISupports.idl"
|
||||||
|
|
||||||
|
[scriptable, uuid(d2cc6222-b7f2-490d-adc2-497d89878fa2)]
|
||||||
|
interface nsIEventSourceEventListener : nsISupports
|
||||||
|
{
|
||||||
|
[must_use] void eventSourceConnectionOpened(in uint64_t aHttpChannelId);
|
||||||
|
|
||||||
|
[must_use] void eventSourceConnectionClosed(in uint64_t aHttpChannelId);
|
||||||
|
|
||||||
|
[must_use] void eventReceived(in uint64_t aHttpChannelId,
|
||||||
|
in AString aEventName,
|
||||||
|
in AString aLastEventID,
|
||||||
|
in AString aData,
|
||||||
|
in uint32_t aRetry,
|
||||||
|
in DOMHighResTimeStamp aTimeStamp);
|
||||||
|
};
|
||||||
|
|
||||||
|
[scriptable, builtinclass, uuid(c0378840-8a74-4b0a-9225-c3a0ac1fac41)]
|
||||||
|
interface nsIEventSourceEventService : nsISupports
|
||||||
|
{
|
||||||
|
[must_use] void addListener(in unsigned long long aInnerWindowID,
|
||||||
|
in nsIEventSourceEventListener aListener);
|
||||||
|
|
||||||
|
[must_use] void removeListener(in unsigned long long aInnerWindowID,
|
||||||
|
in nsIEventSourceEventListener aListener);
|
||||||
|
|
||||||
|
[must_use] bool hasListenerFor(in unsigned long long aInnerWindowID);
|
||||||
|
};
|
|
@ -0,0 +1,12 @@
|
||||||
|
function handleRequest(request, response) {
|
||||||
|
var match = request.queryString.match(/^status=(.*)$/);
|
||||||
|
var status = match ? +match[1] : 200;
|
||||||
|
|
||||||
|
if (status != 200) {
|
||||||
|
response.setStatusLine(request.httpVersion, status, "Err");
|
||||||
|
}
|
||||||
|
response.setHeader("Content-Type", "text/event-stream");
|
||||||
|
response.setHeader("Cache-Control", "no-cache");
|
||||||
|
response.write("data: msg 1\n");
|
||||||
|
response.write("id: 1\n\n");
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
function handleRequest(request, response) {
|
||||||
|
var reconnecting = getState("eventsource_reconnecting");
|
||||||
|
var data = reconnecting ? +reconnecting : 0;
|
||||||
|
var body = "";
|
||||||
|
if (!reconnecting) {
|
||||||
|
body = "retry: 2\n";
|
||||||
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||||
|
setState("eventsource_reconnecting", "0");
|
||||||
|
} else if (reconnecting === "0") {
|
||||||
|
response.setStatusLine(request.httpVersion, 204, "No Content");
|
||||||
|
setState("eventsource_reconnecting", "");
|
||||||
|
} else {
|
||||||
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||||
|
setState("eventsource_reconnecting", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setHeader("Content-Type", "text/event-stream");
|
||||||
|
response.setHeader("Cache-Control", "no-cache");
|
||||||
|
|
||||||
|
body += "data: " + data + "\n\n";
|
||||||
|
response.write(body);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
const es = new EventSource(
|
||||||
|
"http://mochi.test:8888/tests/dom/base/test/eventsource_message.sjs"
|
||||||
|
);
|
||||||
|
es.onmessage = function() {
|
||||||
|
es.close();
|
||||||
|
};
|
|
@ -58,12 +58,15 @@ support-files =
|
||||||
chrome/bug418986-1.js
|
chrome/bug418986-1.js
|
||||||
copypaste.js
|
copypaste.js
|
||||||
delayedServerEvents.sjs
|
delayedServerEvents.sjs
|
||||||
|
eventsource_message.sjs
|
||||||
|
eventsource_reconnect.sjs
|
||||||
eventsource.resource
|
eventsource.resource
|
||||||
eventsource.resource^headers^
|
eventsource.resource^headers^
|
||||||
eventsource_redirect.resource
|
eventsource_redirect.resource
|
||||||
eventsource_redirect.resource^headers^
|
eventsource_redirect.resource^headers^
|
||||||
eventsource_redirect_to.resource
|
eventsource_redirect_to.resource
|
||||||
eventsource_redirect_to.resource^headers^
|
eventsource_redirect_to.resource^headers^
|
||||||
|
eventsource_worker.js
|
||||||
file_base_xbl.xml
|
file_base_xbl.xml
|
||||||
file_bug1091883_frame.html
|
file_bug1091883_frame.html
|
||||||
file_bug1091883_subframe.html
|
file_bug1091883_subframe.html
|
||||||
|
@ -661,6 +664,10 @@ skip-if = toolkit == 'android' && !is_fennec # Bug 1525959
|
||||||
[test_encodeToStringWithRequiresReinitAfterOutput.html]
|
[test_encodeToStringWithRequiresReinitAfterOutput.html]
|
||||||
[test_EventSource_redirects.html]
|
[test_EventSource_redirects.html]
|
||||||
[test_eventsource_event_listener_leaks.html]
|
[test_eventsource_event_listener_leaks.html]
|
||||||
|
[test_eventsourceservice_basic.html]
|
||||||
|
[test_eventsourceservice_reconnect_error.html]
|
||||||
|
[test_eventsourceservice_status_error.html]
|
||||||
|
[test_eventsourceservice_worker.html]
|
||||||
[test_explicit_user_agent.html]
|
[test_explicit_user_agent.html]
|
||||||
[test_find.html]
|
[test_find.html]
|
||||||
skip-if = (toolkit == 'android') # Android: Bug 1465387
|
skip-if = (toolkit == 'android') # Android: Bug 1465387
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>EventSource event service basic test</title>
|
||||||
|
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
var service = SpecialPowers.Cc["@mozilla.org/eventsourceevent/service;1"]
|
||||||
|
.getService(SpecialPowers.Ci.nsIEventSourceEventService);
|
||||||
|
ok(!!service, "We have the nsIEventSourceEventService");
|
||||||
|
|
||||||
|
var innerId = SpecialPowers.getDOMWindowUtils(window).currentInnerWindowID;
|
||||||
|
ok(innerId, "We have a valid innerWindowID: " + innerId);
|
||||||
|
|
||||||
|
var channelId = null;
|
||||||
|
var listener = {
|
||||||
|
QueryInterface(aIID) {
|
||||||
|
if (
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIEventSourceEventListener)
|
||||||
|
) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
eventSourceConnectionOpened(httpChannelId) {
|
||||||
|
info("eventSourceConnectionOpened");
|
||||||
|
ok(httpChannelId > 0, "Channel ID received");
|
||||||
|
channelId = httpChannelId;
|
||||||
|
},
|
||||||
|
eventSourceConnectionClosed(httpChannelId) {
|
||||||
|
info("eventSourceConnectionClosed");
|
||||||
|
ok(httpChannelId > 0, "Channel ID received");
|
||||||
|
ok(httpChannelId === channelId, "Channel ID matched");
|
||||||
|
service.removeListener(innerId, listener);
|
||||||
|
ok(true, "Listener removed");
|
||||||
|
ok(!service.hasListenerFor(innerId), "hasListenerFor(innerId) should be false");
|
||||||
|
SimpleTest.finish();
|
||||||
|
},
|
||||||
|
eventReceived(httpChannelId, eventName, lastEventId, data, retry, timeStamp) {
|
||||||
|
info("eventReceived");
|
||||||
|
is(eventName, "message", "Event name is 'message'");
|
||||||
|
is(lastEventId, "1", "Last event id is '1'");
|
||||||
|
is(data, "msg 1", "Data is 'msg 1'");
|
||||||
|
ok(retry > 0, "Reconnection time received");
|
||||||
|
ok(httpChannelId === channelId, "Channel ID matched");
|
||||||
|
ok(timeStamp > 0, "TimeStamp received");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service.addListener(innerId, listener);
|
||||||
|
ok(true, "Listener added");
|
||||||
|
ok(service.hasListenerFor(innerId), "hasListenerFor(innerId) should be true");
|
||||||
|
addLoadEvent(function () {
|
||||||
|
const es = new EventSource("http://mochi.test:8888/tests/dom/base/test/eventsource_message.sjs");
|
||||||
|
es.onmessage = function (e) {
|
||||||
|
es.close();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>EventSource event service reconnect error test</title>
|
||||||
|
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
var service = SpecialPowers.Cc["@mozilla.org/eventsourceevent/service;1"]
|
||||||
|
.getService(SpecialPowers.Ci.nsIEventSourceEventService);
|
||||||
|
ok(!!service, "We have the nsIEventSourceEventService");
|
||||||
|
|
||||||
|
var innerId = SpecialPowers.getDOMWindowUtils(window).currentInnerWindowID;
|
||||||
|
ok(innerId, "We have a valid innerWindowID: " + innerId);
|
||||||
|
|
||||||
|
var channelId;
|
||||||
|
|
||||||
|
var listener = {
|
||||||
|
QueryInterface(aIID) {
|
||||||
|
if (
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIEventSourceEventListener)
|
||||||
|
) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
eventSourceConnectionOpened(httpChannelId) {
|
||||||
|
info("eventSourceConnectionOpened");
|
||||||
|
ok(httpChannelId > 0, "Channel ID received");
|
||||||
|
channelId = httpChannelId;
|
||||||
|
},
|
||||||
|
eventSourceConnectionClosed(httpChannelId) {
|
||||||
|
info("eventSourceConnectionClosed");
|
||||||
|
ok (httpChannelId === channelId, "Channel was closed on failure");
|
||||||
|
SimpleTest.finish();
|
||||||
|
},
|
||||||
|
eventReceived(httpChannelId, eventName, lastEventId, data, retry, timeStamp) {
|
||||||
|
info("eventReceived");
|
||||||
|
is(eventName, "message", "Event name is 'message'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service.addListener(innerId, listener);
|
||||||
|
ok(true, "Listener added");
|
||||||
|
addLoadEvent(function () {
|
||||||
|
const es = new EventSource("http://mochi.test:8888/tests/dom/base/test/eventsource_reconnect.sjs");
|
||||||
|
});
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>EventSource event service status error test</title>
|
||||||
|
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
var service = SpecialPowers.Cc["@mozilla.org/eventsourceevent/service;1"]
|
||||||
|
.getService(SpecialPowers.Ci.nsIEventSourceEventService);
|
||||||
|
ok(!!service, "We have the nsIEventSourceEventService");
|
||||||
|
|
||||||
|
var innerId = SpecialPowers.getDOMWindowUtils(window).currentInnerWindowID;
|
||||||
|
ok(innerId, "We have a valid innerWindowID: " + innerId);
|
||||||
|
|
||||||
|
var listener = {
|
||||||
|
QueryInterface(aIID) {
|
||||||
|
if (
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIEventSourceEventListener)
|
||||||
|
) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
eventSourceConnectionOpened(httpChannelId) {
|
||||||
|
ok(false, "This should not happen");
|
||||||
|
},
|
||||||
|
eventSourceConnectionClosed(httpChannelId) {
|
||||||
|
ok(false, "This should not happen");
|
||||||
|
},
|
||||||
|
eventReceived(httpChannelId, eventName, lastEventId, data, retry, timeStamp) {
|
||||||
|
ok(false, "This should not happen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service.addListener(innerId, listener);
|
||||||
|
ok(true, "Listener added");
|
||||||
|
|
||||||
|
var NUM_TESTS = 2;
|
||||||
|
addLoadEvent(function () {
|
||||||
|
doTest(404);
|
||||||
|
doTest(502);
|
||||||
|
});
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
function doTest(status) {
|
||||||
|
const es = new EventSource(
|
||||||
|
"http://mochi.test:8888/tests/dom/base/test/eventsource_message.sjs?status=" + status
|
||||||
|
);
|
||||||
|
es.onerror = function (e) {
|
||||||
|
count++;
|
||||||
|
if (count >= NUM_TESTS) {
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>EventSource event service worker test</title>
|
||||||
|
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
var service = SpecialPowers.Cc["@mozilla.org/eventsourceevent/service;1"]
|
||||||
|
.getService(SpecialPowers.Ci.nsIEventSourceEventService);
|
||||||
|
ok(!!service, "We have the nsIEventSourceEventService");
|
||||||
|
|
||||||
|
var innerId = SpecialPowers.getDOMWindowUtils(window).currentInnerWindowID;
|
||||||
|
ok(innerId, "We have a valid innerWindowID: " + innerId);
|
||||||
|
|
||||||
|
var channelId = null;
|
||||||
|
var listener = {
|
||||||
|
QueryInterface(aIID) {
|
||||||
|
if (
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
|
||||||
|
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIEventSourceEventListener)
|
||||||
|
) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
eventSourceConnectionOpened(httpChannelId) {
|
||||||
|
info("eventSourceConnectionOpened");
|
||||||
|
ok(httpChannelId > 0, "Channel ID received");
|
||||||
|
channelId = httpChannelId;
|
||||||
|
},
|
||||||
|
eventSourceConnectionClosed(httpChannelId) {
|
||||||
|
info("eventSourceConnectionClosed");
|
||||||
|
ok(httpChannelId === channelId, "Channel ID matched");
|
||||||
|
SimpleTest.finish();
|
||||||
|
},
|
||||||
|
eventReceived(httpChannelId, eventName, lastEventId, data, retry, timeStamp) {
|
||||||
|
info("eventReceived");
|
||||||
|
is(eventName, "message", "Event name is 'message'");
|
||||||
|
is(lastEventId, "1", "Last event id is '1'");
|
||||||
|
is(data, "msg 1", "Data is 'msg 1'");
|
||||||
|
ok(retry > 0, "Reconnection time received");
|
||||||
|
ok(httpChannelId === channelId, "Channel ID matched");
|
||||||
|
ok(timeStamp > 0, "TimeStamp received");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service.addListener(innerId, listener);
|
||||||
|
ok(true, "Listener added");
|
||||||
|
addLoadEvent(function () {
|
||||||
|
const worker = new Worker("eventsource_worker.js");
|
||||||
|
});
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -423,6 +423,14 @@ Classes = [
|
||||||
'headers': ['mozilla/net/WebSocketEventService.h'],
|
'headers': ['mozilla/net/WebSocketEventService.h'],
|
||||||
'constructor': 'mozilla::net::WebSocketEventService::GetOrCreate',
|
'constructor': 'mozilla::net::WebSocketEventService::GetOrCreate',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'cid': '{abfbb785-5a44-49cf-88db-2f300bf727c9}',
|
||||||
|
'contract_ids': ['@mozilla.org/eventsourceevent/service;1'],
|
||||||
|
'singleton': True,
|
||||||
|
'type': 'mozilla::dom::EventSourceEventService',
|
||||||
|
'headers': ['mozilla/dom/EventSourceEventService.h'],
|
||||||
|
'constructor': 'mozilla::dom::EventSourceEventService::GetOrCreate',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if defined('MOZ_WEBSPEECH'):
|
if defined('MOZ_WEBSPEECH'):
|
||||||
|
|
Загрузка…
Ссылка в новой задаче