/* -*- 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/. */
// Reconnection time related values in milliseconds. The default one is equal // to the default value of the pref dom.serverEvents.defaultReconnectionTime #define MIN_RECONNECTION_TIME_VALUE 500 #define DEFAULT_RECONNECTION_TIME_VALUE 5000 #define MAX_RECONNECTION_TIME_VALUE \
PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
class EventSourceImpl final : public nsIChannelEventSink, public nsIInterfaceRequestor, public nsISerialEventTarget, public nsITimerCallback, public nsINamed, public nsIThreadRetargetableStreamListener, public GlobalTeardownObserver, public GlobalFreezeObserver { public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIEVENTTARGET_FULL
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
uint16_t ReadyState() { auto lock = mSharedData.Lock(); if (lock->mEventSource) { return lock->mEventSource->mReadyState;
} // EventSourceImpl keeps EventSource alive. If mEventSource is null, it // means that the EventSource has been closed. return CLOSED;
}
// Connection related data members. Should only be accessed on main thread.
nsCOMPtr<nsIURI> mSrc;
uint32_t mReconnectionTime; // in ms
nsCOMPtr<nsIPrincipal> mPrincipal;
nsString mOrigin;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIHttpChannel> mHttpChannel;
struct Message {
nsString mEventName; // We need to be able to distinguish between different states of id field: // 1) is not given at all // 2) is given but is empty // 3) is given and has a value // We can't check for the 1st state with a simple nsString.
Maybe<nsString> mLastEventID;
nsString mData;
};
// Message related data members. May be set / initialized when initializing // EventSourceImpl on target thread but should only be used on target thread.
nsString mLastEventID;
UniquePtr<Message> mCurrentMessage;
nsDeque<Message> mMessagesToDispatch;
ParserStatus mStatus;
mozilla::UniquePtr<mozilla::Decoder> mUnicodeDecoder;
nsString mLastFieldName;
nsString mLastFieldValue;
// EventSourceImpl internal states. // WorkerRef to keep the worker alive. (accessed on worker thread only)
RefPtr<ThreadSafeWorkerRef> mWorkerRef; // Whether the window is frozen. May be set on main thread and read on target // thread.
Atomic<bool> mFrozen; // There are some messages are going to be dispatched when thaw. bool mGoingToDispatchAllMessages; // Whether the EventSource is run on main thread. constbool mIsMainThread; // Whether the EventSourceImpl is going to be destroyed.
Atomic<bool> mIsShutDown;
~EventSourceServiceNotifier() { // It is safe to call this on any thread because // EventSourceConnectionClosed method is thread safe and // NS_ReleaseOnMainThread explicitly releases the service on the main // thread. if (mConnectionOpened) { // We want to notify about connection being closed only if we told // it was ever opened. The check is needed if OnStartRequest is called // on the main thread while close() is called on a worker thread.
mService->EventSourceConnectionClosed(mHttpChannelId, mInnerWindowID);
}
NS_ReleaseOnMainThread("EventSourceServiceNotifier::mService",
mService.forget());
}
// Event Source owner information: // - the script file name // - source code line number and column number where the Event Source object // was constructed. // - the ID of the inner window where the script lives. Note that this may not // be the same as the Event Source owner window. // These attributes are used for error reporting. Should only be accessed on // target thread
JSCallingLocation mCallingLocation;
uint64_t mInnerWindowID;
// Pointer to the target thread for checking whether we are // on the target thread. This is intentionally a non-owning // pointer in order not to affect the thread destruction // sequence. This pointer must only be compared for equality // and must not be dereferenced.
nsIThread* mTargetThread;
// prevent bad usage
EventSourceImpl(const EventSourceImpl& x) = delete;
EventSourceImpl& operator=(const EventSourceImpl& x) = delete;
~EventSourceImpl() { if (IsClosed()) { return;
} // If we threw during Init we never called Close
SetReadyState(CLOSED);
CloseInternal();
}
};
class CleanupRunnable final : public WorkerMainThreadRunnable { public: explicit CleanupRunnable(RefPtr<EventSourceImpl>&& aEventSourceImpl)
: WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(), "EventSource :: Cleanup"_ns),
mESImpl(std::move(aEventSourceImpl)) {
MOZ_ASSERT(mESImpl);
}
bool MainThreadRun() override {
MOZ_ASSERT(mESImpl);
mESImpl->CleanupOnMainThread(); // We want to ensure the shortest possible remaining lifetime // and not depend on the Runnable's destruction.
mESImpl = nullptr; returntrue;
}
protected:
RefPtr<EventSourceImpl> mESImpl;
};
void EventSourceImpl::Close() { if (IsClosed()) { return;
}
SetReadyState(CLOSED); // CloseInternal potentially kills ourself, ensure // to not access any members afterwards.
CloseInternal();
}
RefPtr<EventSource> myES;
{ auto lock = mSharedData.Lock(); // We want to ensure to release ourself even if we have // the shutdown case, thus we put aside a pointer // to the EventSource and null it out right now.
myES = std::move(lock->mEventSource);
lock->mEventSource = nullptr;
lock->mServiceNotifier = nullptr;
}
MOZ_ASSERT(!mIsShutDown); if (mIsShutDown) { return;
}
// Invoke CleanupOnMainThread before cleaning any members. It will call // ShutDown, which is supposed to be called before cleaning any members. if (NS_IsMainThread()) {
CleanupOnMainThread();
} else {
ErrorResult rv; // run CleanupOnMainThread synchronously on main thread since it touches // observers and members only can be accessed on main thread.
RefPtr<CleanupRunnable> runnable = new CleanupRunnable(this);
runnable->Dispatch(GetCurrentThreadWorkerPrivate(), Killing, rv);
MOZ_ASSERT(!rv.Failed());
ReleaseWorkerRef();
}
while (mMessagesToDispatch.GetSize() != 0) { delete mMessagesToDispatch.PopFront();
}
mFrozen = false;
ResetDecoder();
mUnicodeDecoder = nullptr; // Release the object on its owner. Don't access to any members // after it.
myES->mESImpl = nullptr;
}
class ConnectRunnable final : public WorkerMainThreadRunnable { public: explicit ConnectRunnable(WorkerPrivate* aWorkerPrivate,
RefPtr<EventSourceImpl> aEventSourceImpl)
: WorkerMainThreadRunnable(aWorkerPrivate, "EventSource :: Connect"_ns),
mESImpl(std::move(aEventSourceImpl)) {
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mESImpl);
}
bool MainThreadRun() override {
MOZ_ASSERT(mESImpl); // We are allowed to access the event target since this runnable is // synchronized with the thread the event target lives on.
mESImpl->InitChannelAndRequestEventSource(true); // We want to ensure the shortest possible remaining lifetime // and not depend on the Runnable's destruction.
mESImpl = nullptr; returntrue;
}
private:
RefPtr<EventSourceImpl> mESImpl;
};
nsresult EventSourceImpl::ParseURL(const nsAString& aURL) {
MOZ_ASSERT(!mIsShutDown); // get the src
nsCOMPtr<nsIURI> baseURI;
nsresult rv = GetBaseURI(getter_AddRefs(baseURI));
NS_ENSURE_SUCCESS(rv, rv);
// This assignment doesn't require extra synchronization because this function // is only ever called from EventSourceImpl::Init(), which is either called // directly if mEventSource was created on the main thread, or via a // synchronous runnable if it was created on a worker thread.
{ // We can't use GetEventSource() here because it would modify the refcount, // and that's not allowed off the owning thread. auto lock = mSharedData.Lock();
lock->mEventSource->mOriginalURL = NS_ConvertUTF8toUTF16(spec);
}
mSrc = srcURI;
mOrigin = origin; return NS_OK;
}
void EventSourceImpl::Init(nsIGlobalObject* aWindowGlobal,
nsIPrincipal* aPrincipal, const nsAString& aURL,
ErrorResult& aRv) { // aWindowGlobal should only exist for main-thread EventSource
MOZ_ASSERT_IF(aWindowGlobal, mIsMainThread);
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(ReadyState() == CONNECTING);
mPrincipal = aPrincipal;
aRv = ParseURL(aURL); if (NS_WARN_IF(aRv.Failed())) { return;
} // The conditional here is historical and not necessarily sane. if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
mCallingLocation = JSCallingLocation::Get(); if (mIsMainThread) {
mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
}
}
if (mIsMainThread) { // We observe when the window freezes and thaws. // // This will store raw pointer of us in GTO, which sounds scary as the // object can be accessed cross thread. But it should be safe as GTO and // EventSourceImpl lifetime should be managed in the same main thread. // // XXX(krosylight): But what about workers? See bug 1910585.
aRv = AddGlobalObservers(aWindowGlobal); if (NS_WARN_IF(aRv.Failed())) { return;
}
}
mReconnectionTime = StaticPrefs::dom_serverEvents_defaultReconnectionTime(); if (!mReconnectionTime) {
mReconnectionTime = DEFAULT_RECONNECTION_TIME_VALUE;
}
if (NS_FAILED(status)) { // EventSource::OnStopRequest will evaluate if it shall either reestablish // or fail the connection, based on the status. return status;
}
if (!contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
DispatchFailConnection(); return NS_ERROR_ABORT;
}
if (!mIsMainThread) { // Try to retarget to worker thread, otherwise fall back to main thread.
nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(httpChannel); if (rr) {
rv = rr->RetargetDeliveryTo(this); if (NS_WARN_IF(NS_FAILED(rv))) {
NS_WARNING("Retargeting failed");
}
}
}
// this method parses the characters as they become available instead of // buffering them.
nsresult EventSourceImpl::StreamReaderFunc(nsIInputStream* aInputStream, void* aClosure, constchar* aFromRawSegment,
uint32_t aToOffset, uint32_t aCount,
uint32_t* aWriteCount) { // The EventSourceImpl instance is hold alive on the // synchronously calling stack, so raw pointer is fine here.
EventSourceImpl* thisObject = static_cast<EventSourceImpl*>(aClosure); if (!thisObject || !aWriteCount) {
NS_WARNING( "EventSource cannot read from stream: no aClosure or aWriteCount"); return NS_ERROR_FAILURE;
}
thisObject->AssertIsOnTargetThread();
MOZ_ASSERT(!thisObject->mIsShutDown);
thisObject->ParseSegment((constchar*)aFromRawSegment, aCount);
*aWriteCount = aCount; return NS_OK;
}
void EventSourceImpl::ParseSegment(constchar* aBuffer, uint32_t aLength) {
AssertIsOnTargetThread(); if (IsClosed()) { return;
}
char16_t buffer[1024]; auto dst = Span(buffer); auto src = AsBytes(Span(aBuffer, aLength)); // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018 for (;;) {
uint32_t result;
size_t read;
size_t written;
std::tie(result, read, written, std::ignore) =
mUnicodeDecoder->DecodeToUTF16(src, dst, false); for (auto c : dst.To(written)) {
nsresult rv = ParseCharacter(c);
NS_ENSURE_SUCCESS_VOID(rv);
} if (result == kInputEmpty) { return;
}
src = src.From(read);
}
}
if (IsClosed()) { return NS_ERROR_ABORT;
}
MOZ_ASSERT(mSrc); // "Network errors that prevents the connection from being established in the // first place (e.g. DNS errors), must cause the user agent to asynchronously // reestablish the connection. // // (...) the cancelation of the fetch algorithm by the user agent (e.g. in // response to window.stop() or the user canceling the network connection // manually) must cause the user agent to fail the connection. // There could be additional network errors that are not covered in the above // checks // See Bug 1808511 if (NS_FAILED(aStatusCode) && aStatusCode != NS_ERROR_CONNECTION_REFUSED &&
aStatusCode != NS_ERROR_NET_TIMEOUT &&
aStatusCode != NS_ERROR_NET_RESET &&
aStatusCode != NS_ERROR_NET_INTERRUPT &&
aStatusCode != NS_ERROR_NET_PARTIAL_TRANSFER &&
aStatusCode != NS_ERROR_NET_TIMEOUT_EXTERNAL &&
aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED &&
aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL &&
aStatusCode != NS_ERROR_INVALID_CONTENT_ENCODING) {
DispatchFailConnection(); return NS_ERROR_ABORT;
}
// To avoid a data race we may only access the event target if it lives on // the main thread. if (mIsMainThread) { auto lock = mSharedData.Lock();
rv = lock->mEventSource->CheckCurrentGlobalCorrectness();
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
// first we try from document->GetBaseURI()
nsCOMPtr<Document> doc =
mIsMainThread ? GetEventSource()->GetDocumentIfCurrent() : nullptr; if (doc) {
baseURI = doc->GetBaseURI();
}
// otherwise we get from the doc's principal if (!baseURI) { auto* basePrin = BasePrincipal::Cast(mPrincipal);
nsresult rv = basePrin->GetURI(getter_AddRefs(baseURI));
NS_ENSURE_SUCCESS(rv, rv);
}
nsresult rv = aEventTargetAccessAllowed ? [this]() { // We can't call GetEventSource() because we're not // allowed to touch the refcount off the worker thread // due to an assertion, event if it would have otherwise // been safe. auto lock = mSharedData.Lock(); return lock->mEventSource->CheckCurrentGlobalCorrectness();
}()
: NS_OK; if (NS_FAILED(rv) || !isValidScheme) {
DispatchFailConnection(); return NS_ERROR_DOM_SECURITY_ERR;
}
if (lock->mEventSource->mWithCredentials) {
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
}
}
// The html spec requires we use fetch cache mode of "no-store". This // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
nsLoadFlags loadFlags;
loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE |
nsIRequest::INHIBIT_CACHING;
nsCOMPtr<nsIChannel> channel; // If we have the document, use it if (doc) {
MOZ_ASSERT(mCookieJarSettings == doc->CookieJarSettings());
{ auto lock = mSharedData.Lock(); if (lock->mServiceNotifier) {
lock->mServiceNotifier->ConnectionOpened();
}
}
// When a user agent is to announce the connection, the user agent must set // the readyState attribute to OPEN and queue a task to fire a simple event // named open at the EventSource object.
SetReadyState(OPEN);
nsresult rv = GetEventSource()->CheckCurrentGlobalCorrectness(); if (NS_FAILED(rv)) { return;
} // We can't hold the mutex while dispatching the event because the mutex is // not reentrant, and content might call back into our code.
rv = GetEventSource()->CreateAndDispatchSimpleEvent(u"open"_ns); if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch the error event!!!"); return;
}
}
class CallRestartConnection final : public WorkerMainThreadRunnable { public: explicit CallRestartConnection(RefPtr<EventSourceImpl>&& aEventSourceImpl)
: WorkerMainThreadRunnable(aEventSourceImpl->mWorkerRef->Private(), "EventSource :: RestartConnection"_ns),
mESImpl(std::move(aEventSourceImpl)) {
MOZ_ASSERT(mESImpl);
}
bool MainThreadRun() override {
MOZ_ASSERT(mESImpl);
mESImpl->RestartConnection(); // We want to ensure the shortest possible remaining lifetime // and not depend on the Runnable's destruction.
mESImpl = nullptr; returntrue;
}
protected:
RefPtr<EventSourceImpl> mESImpl;
};
nsresult EventSourceImpl::RestartConnection() {
AssertIsOnMainThread(); if (IsClosed()) { return NS_ERROR_ABORT;
}
rv = GetEventSource()->CheckCurrentGlobalCorrectness(); if (NS_FAILED(rv)) { return;
}
SetReadyState(CONNECTING);
ResetDecoder(); // We can't hold the mutex while dispatching the event because the mutex is // not reentrant, and content might call back into our code.
rv = GetEventSource()->CreateAndDispatchSimpleEvent(u"error"_ns); if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch the error event!!!"); return;
}
}
nsresult EventSourceImpl::SetReconnectionTimeout() {
AssertIsOnMainThread(); if (IsClosed()) { return NS_ERROR_ABORT;
}
// the timer will be used whenever the requests are going finished. if (!mTimer) {
mTimer = NS_NewTimer();
NS_ENSURE_STATE(mTimer);
}
void EventSourceImpl::DispatchFailConnection() {
AssertIsOnMainThread(); if (IsClosed()) { return;
}
nsresult rv = ConsoleError(); if (NS_FAILED(rv)) {
NS_WARNING("Failed to print to the console error");
}
rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this,
&EventSourceImpl::FailConnection),
NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { // if the worker is shutting down, the dispatching of normal WorkerRunnables // fails. return;
}
}
void EventSourceImpl::FailConnection() {
AssertIsOnTargetThread(); if (IsClosed()) { return;
} // Must change state to closed before firing event to content.
SetReadyState(CLOSED); // When a user agent is to fail the connection, the user agent must set the // readyState attribute to CLOSED and queue a task to fire a simple event // named error at the EventSource object.
nsresult rv = GetEventSource()->CheckCurrentGlobalCorrectness(); if (NS_SUCCEEDED(rv)) { // We can't hold the mutex while dispatching the event because the mutex // is not reentrant, and content might call back into our code.
rv = GetEventSource()->CreateAndDispatchSimpleEvent(u"error"_ns); if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch the error event!!!");
}
} // Call CloseInternal in the end of function because it may release // EventSourceImpl.
CloseInternal();
}
nsresult rv;
AutoJSAPI jsapi;
{ auto lock = mSharedData.Lock();
rv = lock->mEventSource->CheckCurrentGlobalCorrectness(); if (NS_FAILED(rv)) { return;
}
if (NS_WARN_IF(!jsapi.Init(lock->mEventSource->GetOwnerGlobal()))) { return;
}
}
JSContext* cx = jsapi.cx();
while (mMessagesToDispatch.GetSize() > 0) {
UniquePtr<Message> message(mMessagesToDispatch.PopFront());
if (message->mLastEventID.isSome()) {
mLastEventID.Assign(message->mLastEventID.value());
}
if (message->mLastEventID.isNothing() && !mLastEventID.IsEmpty()) {
message->mLastEventID = Some(mLastEventID);
}
{ auto lock = mSharedData.Lock(); if (lock->mServiceNotifier) {
lock->mServiceNotifier->EventReceived(message->mEventName, mLastEventID,
message->mData, mReconnectionTime,
PR_Now());
}
}
// Now we can turn our string into a jsval
JS::Rooted<JS::Value> jsData(cx);
{
JSString* jsString;
jsString = JS_NewUCStringCopyN(cx, message->mData.get(),
message->mData.Length());
NS_ENSURE_TRUE_VOID(jsString);
jsData.setString(jsString);
}
// create an event that uses the MessageEvent interface, // which does not bubble, is not cancelable, and has no default action
// We can't hold the mutex while dispatching the event because the mutex is // not reentrant, and content might call back into our code.
IgnoredErrorResult err;
eventSource->DispatchEvent(*event, err); if (err.Failed()) {
NS_WARNING("Failed to dispatch the message event!!!"); return;
}
// with no case folding performed switch (first_char) { case char16_t('d'): if (mLastFieldName.EqualsLiteral("data")) { // If the field name is "data" append the field value to the data // buffer, then append a single U+000A LINE FEED (LF) character // to the data buffer.
mCurrentMessage->mData.Append(mLastFieldValue);
mCurrentMessage->mData.Append(LF_CHAR);
} break;
case char16_t('e'): if (mLastFieldName.EqualsLiteral("event")) {
mCurrentMessage->mEventName.Assign(mLastFieldValue);
} break;
case char16_t('i'): if (mLastFieldName.EqualsLiteral("id")) {
mCurrentMessage->mLastEventID = Some(mLastFieldValue);
} break;
case char16_t('r'): if (mLastFieldName.EqualsLiteral("retry")) {
uint32_t newValue = 0;
uint32_t i = 0; // we must ensure that there are only digits bool assign = true; for (i = 0; i < mLastFieldValue.Length(); ++i) { if (mLastFieldValue.CharAt(i) < (char16_t)'0' ||
mLastFieldValue.CharAt(i) > (char16_t)'9') {
assign = false; break;
}
newValue = newValue * 10 + (((uint32_t)mLastFieldValue.CharAt(i)) -
((uint32_t)((char16_t)'0')));
}
nsresult EventSourceImpl::CheckHealthOfRequestCallback(
nsIRequest* aRequestCallback) { // This function could be run on target thread if http channel support // nsIThreadRetargetableRequest. otherwise, it's run on main thread.
// check if we have been closed or if the request has been canceled // or if we have been frozen if (IsClosed() || mFrozen || !mHttpChannel) { return NS_ERROR_ABORT;
}
mStatus = PARSE_STATE_BEGIN_OF_LINE;
} elseif (aChr != 0) { // Avoid appending the null char to the field value.
mLastFieldValue += aChr;
} elseif (mLastFieldName.EqualsLiteral("id")) { // Ignore the whole id field if aChr is null
mStatus = PARSE_STATE_IGNORE_FIELD_VALUE;
mLastFieldValue.Truncate();
}
void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) override { // Ensure we drop the RefPtr on the worker thread // and to not keep us alive longer than needed.
mEventSourceImpl = nullptr;
}
bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { // We don't call WorkerRunnable::PreDispatch because it would assert the // wrong thing about which thread we're on. We're on whichever thread the // channel implementation is running on (probably the main thread or // transport thread). returntrue;
}
void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override { // We don't call WorkerRunnable::PostDispatch because it would assert the // wrong thing about which thread we're on. We're on whichever thread the // channel implementation is running on (probably the main thread or // transport thread).
}
if (mIsShutDown) { // We want to avoid clutter about errors in our shutdown logs, // so just report NS_OK (we have no explicit return value // for shutdown). return NS_OK;
}
// If the target is a worker, we have to use a custom WorkerRunnableDispatcher // runnable.
RefPtr<WorkerRunnableDispatcher> event = new WorkerRunnableDispatcher( this, mWorkerRef->Private(), event_ref.forget());
if (!event->Dispatch(mWorkerRef->Private())) { return NS_ERROR_FAILURE;
} return NS_OK;
}
//----------------------------------------------------------------------------- // EventSourceImpl::nsIThreadRetargetableStreamListener //-----------------------------------------------------------------------------
NS_IMETHODIMP
EventSourceImpl::CheckListenerChain() {
MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!"); return NS_OK;
}
RefPtr<EventSource> eventSource = new EventSource(
global, cookieJarSettings, aEventSourceInitDict.mWithCredentials);
if (NS_IsMainThread()) { // Get principal from document and init EventSourceImpl
nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
do_QueryInterface(aGlobal.GetAsSupports()); if (!scriptPrincipal) {
aRv.Throw(NS_ERROR_FAILURE); return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal(); if (!principal) {
aRv.Throw(NS_ERROR_FAILURE); return nullptr;
}
eventSource->mESImpl->Init(global, principal, aURL, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
eventSource->mESImpl->Init(nullptr, workerPrivate->GetPrincipal(), aURL,
aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
// In workers we have to keep the worker alive using a WorkerRef in order // to dispatch messages correctly. if (!eventSource->mESImpl->CreateWorkerRef(workerPrivate)) { // The worker is already shutting down. Let's return an already closed // object, but marked as Connecting. if (eventSource->mESImpl) { // mESImpl is nulled by this call such that EventSourceImpl is // released before returning the object, otherwise // it will set EventSource to a CLOSED state in its DTOR..
eventSource->mESImpl->Close();
}
eventSource->mReadyState = EventSourceImpl::CONNECTING;
void EventSource::Close() {
AssertIsOnTargetThread(); if (mESImpl) { // Close potentially kills ourself, ensure // to not access any members afterwards.
mESImpl->Close();
}
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource,
DOMEventTargetHelper) if (tmp->mESImpl) { // IsCertainlyaliveForCC will return true and cause the cycle // collector to skip this instance when mESImpl is non-null and // points back to ourself. // mESImpl is initialized to be non-null in the constructor // and should have been wiped out in our close function.
MOZ_ASSERT_UNREACHABLE("Paranoia cleanup that should never happen.");
tmp->Close();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
bool EventSource::IsCertainlyAliveForCC() const { // Until we are double linked forth and back, we want to stay alive. if (!mESImpl) { returnfalse;
} auto lock = mESImpl->mSharedData.Lock(); return lock->mEventSource == this;
}
¤ 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.0.75Bemerkung:
(vorverarbeitet)
¤
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.