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:
Farooq AR 2020-04-25 19:27:07 +00:00
Родитель 4540e975f8
Коммит c4534f1f07
14 изменённых файлов: 784 добавлений и 0 удалений

Просмотреть файл

@ -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'):