1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/EventSource.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/LoadInfo.h"
11 #include "mozilla/DOMEventTargetHelper.h"
12 #include "mozilla/dom/EventSourceBinding.h"
13 #include "mozilla/dom/MessageEvent.h"
14 #include "mozilla/dom/ScriptSettings.h"
16 #include "nsNetUtil.h"
17 #include "nsMimeTypes.h"
18 #include "nsIPromptFactory.h"
19 #include "nsIWindowWatcher.h"
20 #include "nsPresContext.h"
21 #include "nsContentPolicyUtils.h"
22 #include "nsIStringBundle.h"
23 #include "nsIConsoleService.h"
24 #include "nsIObserverService.h"
25 #include "nsIScriptObjectPrincipal.h"
26 #include "nsJSUtils.h"
27 #include "nsIAsyncVerifyRedirectCallback.h"
28 #include "nsIScriptError.h"
29 #include "mozilla/dom/EncodingUtils.h"
30 #include "nsIContentSecurityPolicy.h"
31 #include "nsContentUtils.h"
32 #include "mozilla/Preferences.h"
33 #include "xpcpublic.h"
34 #include "nsCORSListenerProxy.h"
35 #include "nsWrapperCacheInlines.h"
36 #include "mozilla/Attributes.h"
42 #define REPLACEMENT_CHAR (char16_t)0xFFFD
43 #define BOM_CHAR (char16_t)0xFEFF
44 #define SPACE_CHAR (char16_t)0x0020
45 #define CR_CHAR (char16_t)0x000D
46 #define LF_CHAR (char16_t)0x000A
47 #define COLON_CHAR (char16_t)0x003A
49 #define DEFAULT_BUFFER_SIZE 4096
51 // Reconnection time related values in milliseconds. The default one is equal
52 // to the default value of the pref dom.server-events.default-reconnection-time
53 #define MIN_RECONNECTION_TIME_VALUE 500
54 #define DEFAULT_RECONNECTION_TIME_VALUE 5000
55 #define MAX_RECONNECTION_TIME_VALUE PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
57 EventSource::EventSource(nsPIDOMWindow
* aOwnerWindow
) :
58 DOMEventTargetHelper(aOwnerWindow
),
59 mStatus(PARSE_STATE_OFF
),
61 mErrorLoadOnRedirect(false),
62 mGoingToDispatchAllMessages(false),
63 mWithCredentials(false),
64 mWaitingForOnStopRequest(false),
65 mLastConvertionResult(NS_OK
),
66 mReadyState(CONNECTING
),
72 EventSource::~EventSource()
77 //-----------------------------------------------------------------------------
78 // EventSource::nsISupports
79 //-----------------------------------------------------------------------------
81 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource
)
83 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(EventSource
)
84 bool isBlack
= tmp
->IsBlack();
85 if (isBlack
|| tmp
->mWaitingForOnStopRequest
) {
86 if (tmp
->mListenerManager
) {
87 tmp
->mListenerManager
->MarkForCC();
89 if (!isBlack
&& tmp
->PreservingWrapper()) {
90 // This marks the wrapper black.
95 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
97 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(EventSource
)
98 return tmp
->IsBlack();
99 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
101 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(EventSource
)
102 return tmp
->IsBlack();
103 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
105 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(EventSource
,
106 DOMEventTargetHelper
)
107 NS_IMPL_CYCLE_COLLECTION_TRACE_END
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource
,
110 DOMEventTargetHelper
)
111 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrc
)
112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks
)
113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadGroup
)
114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink
)
115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHttpChannel
)
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer
)
117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnicodeDecoder
)
118 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
120 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource
,
121 DOMEventTargetHelper
)
123 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
125 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(EventSource
)
126 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
127 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
128 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
129 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
130 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
131 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
132 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
134 NS_IMPL_ADDREF_INHERITED(EventSource
, DOMEventTargetHelper
)
135 NS_IMPL_RELEASE_INHERITED(EventSource
, DOMEventTargetHelper
)
138 EventSource::DisconnectFromOwner()
140 DOMEventTargetHelper::DisconnectFromOwner();
147 if (mReadyState
== CLOSED
) {
151 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
153 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
154 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
155 os
->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC
);
167 while (mMessagesToDispatch
.GetSize() != 0) {
168 delete static_cast<Message
*>(mMessagesToDispatch
.PopFront());
174 mUnicodeDecoder
= nullptr;
176 mReadyState
= CLOSED
;
180 EventSource::Init(nsISupports
* aOwner
,
181 const nsAString
& aURL
,
182 bool aWithCredentials
)
184 if (mReadyState
!= CONNECTING
|| !PrefEnabled()) {
185 return NS_ERROR_DOM_SECURITY_ERR
;
188 nsCOMPtr
<nsIScriptGlobalObject
> sgo
= do_QueryInterface(aOwner
);
189 NS_ENSURE_STATE(sgo
);
190 nsCOMPtr
<nsIScriptContext
> scriptContext
= sgo
->GetContext();
191 NS_ENSURE_STATE(scriptContext
);
193 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
194 do_QueryInterface(aOwner
);
195 NS_ENSURE_STATE(scriptPrincipal
);
196 nsCOMPtr
<nsIPrincipal
> principal
= scriptPrincipal
->GetPrincipal();
197 NS_ENSURE_STATE(principal
);
199 mPrincipal
= principal
;
200 mWithCredentials
= aWithCredentials
;
202 // The conditional here is historical and not necessarily sane.
203 if (JSContext
*cx
= nsContentUtils::GetCurrentJSContext()) {
204 nsJSUtils::GetCallingLocation(cx
, mScriptFile
, &mScriptLine
);
205 mInnerWindowID
= nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx
);
208 // Get the load group for the page. When requesting we'll add ourselves to it.
209 // This way any pending requests will be automatically aborted if the user
212 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
214 nsCOMPtr
<nsIDocument
> doc
=
215 nsContentUtils::GetDocumentFromScriptContext(sc
);
217 mLoadGroup
= doc
->GetDocumentLoadGroup();
222 nsCOMPtr
<nsIURI
> baseURI
;
223 rv
= GetBaseURI(getter_AddRefs(baseURI
));
224 NS_ENSURE_SUCCESS(rv
, rv
);
226 nsCOMPtr
<nsIURI
> srcURI
;
227 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, nullptr, baseURI
);
228 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
230 // we observe when the window freezes and thaws
231 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
234 rv
= os
->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC
, true);
235 NS_ENSURE_SUCCESS(rv
, rv
);
236 rv
= os
->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC
, true);
237 NS_ENSURE_SUCCESS(rv
, rv
);
238 rv
= os
->AddObserver(this, DOM_WINDOW_THAWED_TOPIC
, true);
239 NS_ENSURE_SUCCESS(rv
, rv
);
242 rv
= nsContentUtils::GetUTFOrigin(srcURI
, origin
);
243 NS_ENSURE_SUCCESS(rv
, rv
);
246 rv
= srcURI
->GetSpec(spec
);
247 NS_ENSURE_SUCCESS(rv
, rv
);
249 mOriginalURL
= NS_ConvertUTF8toUTF16(spec
);
254 Preferences::GetInt("dom.server-events.default-reconnection-time",
255 DEFAULT_RECONNECTION_TIME_VALUE
);
257 mUnicodeDecoder
= EncodingUtils::DecoderForEncoding("UTF-8");
259 // the constructor should throw a SYNTAX_ERROR only if it fails resolving the
260 // url parameter, so we don't care about the InitChannelAndRequestEventSource
262 InitChannelAndRequestEventSource();
267 /* virtual */ JSObject
*
268 EventSource::WrapObject(JSContext
* aCx
)
270 return EventSourceBinding::Wrap(aCx
, this);
273 /* static */ already_AddRefed
<EventSource
>
274 EventSource::Constructor(const GlobalObject
& aGlobal
,
275 const nsAString
& aURL
,
276 const EventSourceInit
& aEventSourceInitDict
,
279 nsCOMPtr
<nsPIDOMWindow
> ownerWindow
=
280 do_QueryInterface(aGlobal
.GetAsSupports());
282 aRv
.Throw(NS_ERROR_UNEXPECTED
);
285 MOZ_ASSERT(ownerWindow
->IsInnerWindow());
287 nsRefPtr
<EventSource
> eventSource
= new EventSource(ownerWindow
);
288 aRv
= eventSource
->Init(aGlobal
.GetAsSupports(), aURL
,
289 aEventSourceInitDict
.mWithCredentials
);
290 return eventSource
.forget();
293 //-----------------------------------------------------------------------------
294 // EventSource::nsIObserver
295 //-----------------------------------------------------------------------------
298 EventSource::Observe(nsISupports
* aSubject
,
300 const char16_t
* aData
)
302 if (mReadyState
== CLOSED
) {
306 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aSubject
);
307 if (!GetOwner() || window
!= GetOwner()) {
311 DebugOnly
<nsresult
> rv
;
312 if (strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) {
314 NS_ASSERTION(NS_SUCCEEDED(rv
), "Freeze() failed");
315 } else if (strcmp(aTopic
, DOM_WINDOW_THAWED_TOPIC
) == 0) {
317 NS_ASSERTION(NS_SUCCEEDED(rv
), "Thaw() failed");
318 } else if (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0) {
325 //-----------------------------------------------------------------------------
326 // EventSource::nsIStreamListener
327 //-----------------------------------------------------------------------------
330 EventSource::OnStartRequest(nsIRequest
*aRequest
,
333 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
334 NS_ENSURE_SUCCESS(rv
, rv
);
336 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
, &rv
);
337 NS_ENSURE_SUCCESS(rv
, rv
);
340 rv
= aRequest
->GetStatus(&status
);
341 NS_ENSURE_SUCCESS(rv
, rv
);
343 if (NS_FAILED(status
)) {
344 // EventSource::OnStopRequest will evaluate if it shall either reestablish
345 // or fail the connection
346 return NS_ERROR_ABORT
;
350 rv
= httpChannel
->GetResponseStatus(&httpStatus
);
351 NS_ENSURE_SUCCESS(rv
, rv
);
353 if (httpStatus
!= 200) {
354 DispatchFailConnection();
355 return NS_ERROR_ABORT
;
358 nsAutoCString contentType
;
359 rv
= httpChannel
->GetContentType(contentType
);
360 NS_ENSURE_SUCCESS(rv
, rv
);
362 if (!contentType
.EqualsLiteral(TEXT_EVENT_STREAM
)) {
363 DispatchFailConnection();
364 return NS_ERROR_ABORT
;
367 nsCOMPtr
<nsIRunnable
> event
=
368 NS_NewRunnableMethod(this, &EventSource::AnnounceConnection
);
369 NS_ENSURE_STATE(event
);
371 rv
= NS_DispatchToMainThread(event
);
372 NS_ENSURE_SUCCESS(rv
, rv
);
374 mStatus
= PARSE_STATE_BEGIN_OF_STREAM
;
379 // this method parses the characters as they become available instead of
382 EventSource::StreamReaderFunc(nsIInputStream
*aInputStream
,
384 const char *aFromRawSegment
,
387 uint32_t *aWriteCount
)
389 EventSource
* thisObject
= static_cast<EventSource
*>(aClosure
);
390 if (!thisObject
|| !aWriteCount
) {
391 NS_WARNING("EventSource cannot read from stream: no aClosure or aWriteCount");
392 return NS_ERROR_FAILURE
;
397 int32_t srcCount
, outCount
;
401 const char *p
= aFromRawSegment
,
402 *end
= aFromRawSegment
+ aCount
;
405 srcCount
= aCount
- (p
- aFromRawSegment
);
408 thisObject
->mLastConvertionResult
=
409 thisObject
->mUnicodeDecoder
->Convert(p
, &srcCount
, out
, &outCount
);
410 MOZ_ASSERT(thisObject
->mLastConvertionResult
!= NS_ERROR_ILLEGAL_INPUT
);
412 for (int32_t i
= 0; i
< outCount
; ++i
) {
413 rv
= thisObject
->ParseCharacter(out
[i
]);
414 NS_ENSURE_SUCCESS(rv
, rv
);
418 thisObject
->mLastConvertionResult
!= NS_PARTIAL_MORE_INPUT
&&
419 thisObject
->mLastConvertionResult
!= NS_OK
);
421 *aWriteCount
= aCount
;
426 EventSource::OnDataAvailable(nsIRequest
*aRequest
,
427 nsISupports
*aContext
,
428 nsIInputStream
*aInputStream
,
432 NS_ENSURE_ARG_POINTER(aInputStream
);
434 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
435 NS_ENSURE_SUCCESS(rv
, rv
);
438 return aInputStream
->ReadSegments(EventSource::StreamReaderFunc
, this,
443 EventSource::OnStopRequest(nsIRequest
*aRequest
,
444 nsISupports
*aContext
,
445 nsresult aStatusCode
)
447 mWaitingForOnStopRequest
= false;
449 if (mReadyState
== CLOSED
) {
450 return NS_ERROR_ABORT
;
453 // "Network errors that prevents the connection from being established in the
454 // first place (e.g. DNS errors), must cause the user agent to asynchronously
455 // reestablish the connection.
457 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in
458 // response to window.stop() or the user canceling the network connection
459 // manually) must cause the user agent to fail the connection.
461 if (NS_FAILED(aStatusCode
) &&
462 aStatusCode
!= NS_ERROR_CONNECTION_REFUSED
&&
463 aStatusCode
!= NS_ERROR_NET_TIMEOUT
&&
464 aStatusCode
!= NS_ERROR_NET_RESET
&&
465 aStatusCode
!= NS_ERROR_NET_INTERRUPT
&&
466 aStatusCode
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
467 aStatusCode
!= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
) {
468 DispatchFailConnection();
469 return NS_ERROR_ABORT
;
472 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
473 NS_ENSURE_SUCCESS(rv
, rv
);
477 nsCOMPtr
<nsIRunnable
> event
=
478 NS_NewRunnableMethod(this, &EventSource::ReestablishConnection
);
479 NS_ENSURE_STATE(event
);
481 rv
= NS_DispatchToMainThread(event
);
482 NS_ENSURE_SUCCESS(rv
, rv
);
488 * Simple helper class that just forwards the redirect callback back
489 * to the EventSource.
491 class AsyncVerifyRedirectCallbackFwr MOZ_FINAL
: public nsIAsyncVerifyRedirectCallback
494 explicit AsyncVerifyRedirectCallbackFwr(EventSource
* aEventsource
)
495 : mEventSource(aEventsource
)
499 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
500 NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr
)
502 // nsIAsyncVerifyRedirectCallback implementation
503 NS_IMETHOD
OnRedirectVerifyCallback(nsresult aResult
) MOZ_OVERRIDE
505 nsresult rv
= mEventSource
->OnRedirectVerifyCallback(aResult
);
507 mEventSource
->mErrorLoadOnRedirect
= true;
508 mEventSource
->DispatchFailConnection();
515 ~AsyncVerifyRedirectCallbackFwr() {}
516 nsRefPtr
<EventSource
> mEventSource
;
519 NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr
, mEventSource
)
521 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr
)
522 NS_INTERFACE_MAP_ENTRY(nsISupports
)
523 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback
)
526 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr
)
527 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr
)
529 //-----------------------------------------------------------------------------
530 // EventSource::nsIChannelEventSink
531 //-----------------------------------------------------------------------------
534 EventSource::AsyncOnChannelRedirect(nsIChannel
*aOldChannel
,
535 nsIChannel
*aNewChannel
,
537 nsIAsyncVerifyRedirectCallback
*aCallback
)
539 nsCOMPtr
<nsIRequest
> aOldRequest
= do_QueryInterface(aOldChannel
);
540 NS_PRECONDITION(aOldRequest
, "Redirect from a null request?");
542 nsresult rv
= CheckHealthOfRequestCallback(aOldRequest
);
543 NS_ENSURE_SUCCESS(rv
, rv
);
545 NS_PRECONDITION(aNewChannel
, "Redirect without a channel?");
547 nsCOMPtr
<nsIURI
> newURI
;
548 rv
= NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(newURI
));
549 NS_ENSURE_SUCCESS(rv
, rv
);
551 if (!CheckCanRequestSrc(newURI
)) {
552 DispatchFailConnection();
553 return NS_ERROR_DOM_SECURITY_ERR
;
556 // Prepare to receive callback
557 mRedirectFlags
= aFlags
;
558 mRedirectCallback
= aCallback
;
559 mNewRedirectChannel
= aNewChannel
;
561 if (mChannelEventSink
) {
562 nsRefPtr
<AsyncVerifyRedirectCallbackFwr
> fwd
=
563 new AsyncVerifyRedirectCallbackFwr(this);
565 rv
= mChannelEventSink
->AsyncOnChannelRedirect(aOldChannel
,
569 mRedirectCallback
= nullptr;
570 mNewRedirectChannel
= nullptr;
571 mErrorLoadOnRedirect
= true;
572 DispatchFailConnection();
576 OnRedirectVerifyCallback(NS_OK
);
581 EventSource::OnRedirectVerifyCallback(nsresult aResult
)
583 NS_ABORT_IF_FALSE(mRedirectCallback
, "mRedirectCallback not set in callback");
584 NS_ABORT_IF_FALSE(mNewRedirectChannel
,
585 "mNewRedirectChannel not set in callback");
587 NS_ENSURE_SUCCESS(aResult
, aResult
);
589 // update our channel
591 mHttpChannel
= do_QueryInterface(mNewRedirectChannel
);
592 NS_ENSURE_STATE(mHttpChannel
);
594 nsresult rv
= SetupHttpChannel();
595 NS_ENSURE_SUCCESS(rv
, rv
);
597 if ((mRedirectFlags
& nsIChannelEventSink::REDIRECT_PERMANENT
) != 0) {
598 rv
= NS_GetFinalChannelURI(mHttpChannel
, getter_AddRefs(mSrc
));
599 NS_ENSURE_SUCCESS(rv
, rv
);
602 mNewRedirectChannel
= nullptr;
604 mRedirectCallback
->OnRedirectVerifyCallback(aResult
);
605 mRedirectCallback
= nullptr;
610 //-----------------------------------------------------------------------------
611 // EventSource::nsIInterfaceRequestor
612 //-----------------------------------------------------------------------------
615 EventSource::GetInterface(const nsIID
& aIID
,
618 // Make sure to return ourselves for the channel event sink interface,
619 // no matter what. We can forward these to mNotificationCallbacks
620 // if it wants to get notifications for them. But we
621 // need to see these notifications for proper functioning.
622 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
623 mChannelEventSink
= do_GetInterface(mNotificationCallbacks
);
624 *aResult
= static_cast<nsIChannelEventSink
*>(this);
629 // Now give mNotificationCallbacks (if non-null) a chance to return the
630 // desired interface.
631 if (mNotificationCallbacks
) {
632 nsresult rv
= mNotificationCallbacks
->GetInterface(aIID
, aResult
);
633 if (NS_SUCCEEDED(rv
)) {
634 NS_ASSERTION(*aResult
, "Lying nsIInterfaceRequestor implementation!");
639 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
640 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
641 nsresult rv
= CheckInnerWindowCorrectness();
642 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
644 nsCOMPtr
<nsIPromptFactory
> wwatch
=
645 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
646 NS_ENSURE_SUCCESS(rv
, rv
);
648 // Get the an auth prompter for our window so that the parenting
649 // of the dialogs works as it should when using tabs.
651 nsCOMPtr
<nsIDOMWindow
> window
;
653 window
= GetOwner()->GetOuterWindow();
656 return wwatch
->GetPrompt(window
, aIID
, aResult
);
659 return QueryInterface(aIID
, aResult
);
664 EventSource::PrefEnabled(JSContext
* aCx
, JSObject
* aGlobal
)
666 return Preferences::GetBool("dom.server-events.enabled", false);
670 EventSource::GetBaseURI(nsIURI
**aBaseURI
)
672 NS_ENSURE_ARG_POINTER(aBaseURI
);
676 nsCOMPtr
<nsIURI
> baseURI
;
678 // first we try from document->GetBaseURI()
680 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
681 nsCOMPtr
<nsIDocument
> doc
=
682 nsContentUtils::GetDocumentFromScriptContext(sc
);
684 baseURI
= doc
->GetBaseURI();
687 // otherwise we get from the doc's principal
689 rv
= mPrincipal
->GetURI(getter_AddRefs(baseURI
));
690 NS_ENSURE_SUCCESS(rv
, rv
);
693 NS_ENSURE_STATE(baseURI
);
695 baseURI
.forget(aBaseURI
);
700 EventSource::GetReferrerPolicy()
703 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
704 NS_ENSURE_SUCCESS(rv
, mozilla::net::RP_Default
);
706 nsCOMPtr
<nsIDocument
> doc
= nsContentUtils::GetDocumentFromScriptContext(sc
);
707 return doc
? doc
->GetReferrerPolicy() : mozilla::net::RP_Default
;
711 EventSource::SetupHttpChannel()
713 mHttpChannel
->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
715 /* set the http request headers */
717 mHttpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
718 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM
), false);
720 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
722 if (!mLastEventID
.IsEmpty()) {
723 mHttpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"),
724 NS_ConvertUTF16toUTF8(mLastEventID
), false);
727 nsCOMPtr
<nsIURI
> codebase
;
728 nsresult rv
= GetBaseURI(getter_AddRefs(codebase
));
729 if (NS_SUCCEEDED(rv
)) {
730 rv
= mHttpChannel
->SetReferrerWithPolicy(codebase
, this->GetReferrerPolicy());
731 NS_ENSURE_SUCCESS(rv
, rv
);
738 EventSource::InitChannelAndRequestEventSource()
740 if (mReadyState
== CLOSED
) {
741 return NS_ERROR_ABORT
;
744 // eventsource validation
746 if (!CheckCanRequestSrc()) {
747 DispatchFailConnection();
748 return NS_ERROR_DOM_SECURITY_ERR
;
751 nsLoadFlags loadFlags
;
752 loadFlags
= nsIRequest::LOAD_BACKGROUND
| nsIRequest::LOAD_BYPASS_CACHE
;
755 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
756 nsCOMPtr
<nsIDocument
> doc
=
757 nsContentUtils::GetDocumentFromScriptContext(sc
);
759 nsCOMPtr
<nsIChannel
> channel
;
760 // If we have the document, use it
762 rv
= NS_NewChannel(getter_AddRefs(channel
),
765 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
,
766 nsIContentPolicy::TYPE_DATAREQUEST
,
767 mLoadGroup
, // loadGroup
768 nullptr, // aCallbacks
769 loadFlags
); // aLoadFlags
771 // otherwise use the principal
772 rv
= NS_NewChannel(getter_AddRefs(channel
),
775 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
,
776 nsIContentPolicy::TYPE_DATAREQUEST
,
777 mLoadGroup
, // loadGroup
778 nullptr, // aCallbacks
779 loadFlags
); // aLoadFlags
782 NS_ENSURE_SUCCESS(rv
, rv
);
784 mHttpChannel
= do_QueryInterface(channel
);
785 NS_ENSURE_TRUE(mHttpChannel
, NS_ERROR_NO_INTERFACE
);
787 rv
= SetupHttpChannel();
788 NS_ENSURE_SUCCESS(rv
, rv
);
790 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
791 mHttpChannel
->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks
));
792 if (notificationCallbacks
!= this) {
793 mNotificationCallbacks
= notificationCallbacks
;
794 mHttpChannel
->SetNotificationCallbacks(this);
797 nsRefPtr
<nsCORSListenerProxy
> listener
=
798 new nsCORSListenerProxy(this, mPrincipal
, mWithCredentials
);
799 rv
= listener
->Init(mHttpChannel
);
800 NS_ENSURE_SUCCESS(rv
, rv
);
802 // Start reading from the channel
803 rv
= mHttpChannel
->AsyncOpen(listener
, nullptr);
804 if (NS_SUCCEEDED(rv
)) {
805 mWaitingForOnStopRequest
= true;
811 EventSource::AnnounceConnection()
813 if (mReadyState
== CLOSED
) {
817 if (mReadyState
!= CONNECTING
) {
818 NS_WARNING("Unexpected mReadyState!!!");
822 // When a user agent is to announce the connection, the user agent must set
823 // the readyState attribute to OPEN and queue a task to fire a simple event
824 // named open at the EventSource object.
828 nsresult rv
= CheckInnerWindowCorrectness();
833 nsCOMPtr
<nsIDOMEvent
> event
;
834 rv
= NS_NewDOMEvent(getter_AddRefs(event
), this, nullptr, nullptr);
836 NS_WARNING("Failed to create the open event!!!");
840 // it doesn't bubble, and it isn't cancelable
841 rv
= event
->InitEvent(NS_LITERAL_STRING("open"), false, false);
843 NS_WARNING("Failed to init the open event!!!");
847 event
->SetTrusted(true);
849 rv
= DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
851 NS_WARNING("Failed to dispatch the open event!!!");
857 EventSource::ResetConnection()
860 mHttpChannel
->Cancel(NS_ERROR_ABORT
);
863 if (mUnicodeDecoder
) {
864 mUnicodeDecoder
->Reset();
866 mLastConvertionResult
= NS_OK
;
868 mHttpChannel
= nullptr;
869 mNotificationCallbacks
= nullptr;
870 mChannelEventSink
= nullptr;
871 mStatus
= PARSE_STATE_OFF
;
872 mRedirectCallback
= nullptr;
873 mNewRedirectChannel
= nullptr;
875 mReadyState
= CONNECTING
;
881 EventSource::ReestablishConnection()
883 if (mReadyState
== CLOSED
) {
887 nsresult rv
= ResetConnection();
889 NS_WARNING("Failed to reset the connection!!!");
893 rv
= CheckInnerWindowCorrectness();
898 nsCOMPtr
<nsIDOMEvent
> event
;
899 rv
= NS_NewDOMEvent(getter_AddRefs(event
), this, nullptr, nullptr);
901 NS_WARNING("Failed to create the error event!!!");
905 // it doesn't bubble, and it isn't cancelable
906 rv
= event
->InitEvent(NS_LITERAL_STRING("error"), false, false);
908 NS_WARNING("Failed to init the error event!!!");
912 event
->SetTrusted(true);
914 rv
= DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
916 NS_WARNING("Failed to dispatch the error event!!!");
920 rv
= SetReconnectionTimeout();
922 NS_WARNING("Failed to set the timeout for reestablishing the connection!!!");
928 EventSource::SetReconnectionTimeout()
930 if (mReadyState
== CLOSED
) {
931 return NS_ERROR_ABORT
;
934 // the timer will be used whenever the requests are going finished.
936 mTimer
= do_CreateInstance("@mozilla.org/timer;1");
937 NS_ENSURE_STATE(mTimer
);
940 nsresult rv
= mTimer
->InitWithFuncCallback(TimerCallback
, this,
942 nsITimer::TYPE_ONE_SHOT
);
943 NS_ENSURE_SUCCESS(rv
, rv
);
949 EventSource::PrintErrorOnConsole(const char *aBundleURI
,
950 const char16_t
*aError
,
951 const char16_t
**aFormatStrings
,
952 uint32_t aFormatStringsLen
)
954 nsCOMPtr
<nsIStringBundleService
> bundleService
=
955 mozilla::services::GetStringBundleService();
956 NS_ENSURE_STATE(bundleService
);
958 nsCOMPtr
<nsIStringBundle
> strBundle
;
960 bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
961 NS_ENSURE_SUCCESS(rv
, rv
);
963 nsCOMPtr
<nsIConsoleService
> console(
964 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
965 NS_ENSURE_SUCCESS(rv
, rv
);
967 nsCOMPtr
<nsIScriptError
> errObj(
968 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
969 NS_ENSURE_SUCCESS(rv
, rv
);
971 // Localize the error message
972 nsXPIDLString message
;
973 if (aFormatStrings
) {
974 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
,
976 getter_Copies(message
));
978 rv
= strBundle
->GetStringFromName(aError
, getter_Copies(message
));
980 NS_ENSURE_SUCCESS(rv
, rv
);
982 rv
= errObj
->InitWithWindowID(message
,
986 nsIScriptError::errorFlag
,
987 "Event Source", mInnerWindowID
);
988 NS_ENSURE_SUCCESS(rv
, rv
);
990 // print the error message directly to the JS console
991 rv
= console
->LogMessage(errObj
);
992 NS_ENSURE_SUCCESS(rv
, rv
);
998 EventSource::ConsoleError()
1000 nsAutoCString targetSpec
;
1001 nsresult rv
= mSrc
->GetSpec(targetSpec
);
1002 NS_ENSURE_SUCCESS(rv
, rv
);
1004 NS_ConvertUTF8toUTF16
specUTF16(targetSpec
);
1005 const char16_t
*formatStrings
[] = { specUTF16
.get() };
1007 if (mReadyState
== CONNECTING
) {
1008 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1009 MOZ_UTF16("connectionFailure"),
1010 formatStrings
, ArrayLength(formatStrings
));
1012 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1013 MOZ_UTF16("netInterrupt"),
1014 formatStrings
, ArrayLength(formatStrings
));
1016 NS_ENSURE_SUCCESS(rv
, rv
);
1022 EventSource::DispatchFailConnection()
1024 nsCOMPtr
<nsIRunnable
> event
=
1025 NS_NewRunnableMethod(this, &EventSource::FailConnection
);
1026 NS_ENSURE_STATE(event
);
1028 return NS_DispatchToMainThread(event
);
1032 EventSource::FailConnection()
1034 if (mReadyState
== CLOSED
) {
1038 nsresult rv
= ConsoleError();
1039 if (NS_FAILED(rv
)) {
1040 NS_WARNING("Failed to print to the console error");
1043 // When a user agent is to fail the connection, the user agent must set the
1044 // readyState attribute to CLOSED and queue a task to fire a simple event
1045 // named error at the EventSource object.
1047 Close(); // it sets mReadyState to CLOSED
1049 rv
= CheckInnerWindowCorrectness();
1050 if (NS_FAILED(rv
)) {
1054 nsCOMPtr
<nsIDOMEvent
> event
;
1055 rv
= NS_NewDOMEvent(getter_AddRefs(event
), this, nullptr, nullptr);
1056 if (NS_FAILED(rv
)) {
1057 NS_WARNING("Failed to create the error event!!!");
1061 // it doesn't bubble, and it isn't cancelable
1062 rv
= event
->InitEvent(NS_LITERAL_STRING("error"), false, false);
1063 if (NS_FAILED(rv
)) {
1064 NS_WARNING("Failed to init the error event!!!");
1068 event
->SetTrusted(true);
1070 rv
= DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
1071 if (NS_FAILED(rv
)) {
1072 NS_WARNING("Failed to dispatch the error event!!!");
1078 EventSource::CheckCanRequestSrc(nsIURI
* aSrc
)
1080 if (mReadyState
== CLOSED
) {
1084 bool isValidURI
= false;
1085 bool isValidContentLoadPolicy
= false;
1086 bool isValidProtocol
= false;
1088 nsCOMPtr
<nsIURI
> srcToTest
= aSrc
? aSrc
: mSrc
.get();
1089 NS_ENSURE_TRUE(srcToTest
, false);
1091 uint32_t aCheckURIFlags
=
1092 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL
|
1093 nsIScriptSecurityManager::DISALLOW_SCRIPT
;
1095 nsresult rv
= nsContentUtils::GetSecurityManager()->
1096 CheckLoadURIWithPrincipal(mPrincipal
,
1099 isValidURI
= NS_SUCCEEDED(rv
);
1101 // After the security manager, the content-policy check
1103 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
1104 nsCOMPtr
<nsIDocument
> doc
=
1105 nsContentUtils::GetDocumentFromScriptContext(sc
);
1107 // mScriptContext should be initialized because of GetBaseURI() above.
1108 // Still need to consider the case that doc is nullptr however.
1109 rv
= CheckInnerWindowCorrectness();
1110 NS_ENSURE_SUCCESS(rv
, false);
1111 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
1112 rv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_DATAREQUEST
,
1116 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM
),
1119 nsContentUtils::GetContentPolicy(),
1120 nsContentUtils::GetSecurityManager());
1121 isValidContentLoadPolicy
= NS_SUCCEEDED(rv
) && NS_CP_ACCEPTED(shouldLoad
);
1123 nsAutoCString targetURIScheme
;
1124 rv
= srcToTest
->GetScheme(targetURIScheme
);
1125 if (NS_SUCCEEDED(rv
)) {
1126 // We only have the http support for now
1127 isValidProtocol
= targetURIScheme
.EqualsLiteral("http") ||
1128 targetURIScheme
.EqualsLiteral("https");
1131 return isValidURI
&& isValidContentLoadPolicy
&& isValidProtocol
;
1136 EventSource::TimerCallback(nsITimer
* aTimer
, void* aClosure
)
1138 nsRefPtr
<EventSource
> thisObject
= static_cast<EventSource
*>(aClosure
);
1140 if (thisObject
->mReadyState
== CLOSED
) {
1144 NS_PRECONDITION(!thisObject
->mHttpChannel
,
1145 "the channel hasn't been cancelled!!");
1147 if (!thisObject
->mFrozen
) {
1148 nsresult rv
= thisObject
->InitChannelAndRequestEventSource();
1149 if (NS_FAILED(rv
)) {
1150 NS_WARNING("thisObject->InitChannelAndRequestEventSource() failed");
1159 if (mReadyState
== CLOSED
|| !mFrozen
) {
1163 NS_ASSERTION(!mHttpChannel
, "the connection hasn't been closed!!!");
1167 if (!mGoingToDispatchAllMessages
&& mMessagesToDispatch
.GetSize() > 0) {
1168 nsCOMPtr
<nsIRunnable
> event
=
1169 NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents
);
1170 NS_ENSURE_STATE(event
);
1172 mGoingToDispatchAllMessages
= true;
1174 rv
= NS_DispatchToMainThread(event
);
1175 NS_ENSURE_SUCCESS(rv
, rv
);
1178 rv
= InitChannelAndRequestEventSource();
1179 NS_ENSURE_SUCCESS(rv
, rv
);
1185 EventSource::Freeze()
1187 if (mReadyState
== CLOSED
|| mFrozen
) {
1191 NS_ASSERTION(!mHttpChannel
, "the connection hasn't been closed!!!");
1197 EventSource::DispatchCurrentMessageEvent()
1199 nsAutoPtr
<Message
> message(new Message());
1200 *message
= mCurrentMessage
;
1204 if (message
->mData
.IsEmpty()) {
1208 // removes the trailing LF from mData
1209 NS_ASSERTION(message
->mData
.CharAt(message
->mData
.Length() - 1) == LF_CHAR
,
1210 "Invalid trailing character! LF was expected instead.");
1211 message
->mData
.SetLength(message
->mData
.Length() - 1);
1213 if (message
->mEventName
.IsEmpty()) {
1214 message
->mEventName
.AssignLiteral("message");
1217 if (message
->mLastEventID
.IsEmpty() && !mLastEventID
.IsEmpty()) {
1218 message
->mLastEventID
.Assign(mLastEventID
);
1221 int32_t sizeBefore
= mMessagesToDispatch
.GetSize();
1222 mMessagesToDispatch
.Push(message
.forget());
1223 NS_ENSURE_TRUE(mMessagesToDispatch
.GetSize() == sizeBefore
+ 1,
1224 NS_ERROR_OUT_OF_MEMORY
);
1227 if (!mGoingToDispatchAllMessages
) {
1228 nsCOMPtr
<nsIRunnable
> event
=
1229 NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents
);
1230 NS_ENSURE_STATE(event
);
1232 mGoingToDispatchAllMessages
= true;
1234 return NS_DispatchToMainThread(event
);
1241 EventSource::DispatchAllMessageEvents()
1243 if (mReadyState
== CLOSED
|| mFrozen
) {
1247 mGoingToDispatchAllMessages
= false;
1249 nsresult rv
= CheckInnerWindowCorrectness();
1250 if (NS_FAILED(rv
)) {
1255 if (NS_WARN_IF(!jsapi
.Init(GetOwner()))) {
1258 JSContext
* cx
= jsapi
.cx();
1260 while (mMessagesToDispatch
.GetSize() > 0) {
1262 message(static_cast<Message
*>(mMessagesToDispatch
.PopFront()));
1264 // Now we can turn our string into a jsval
1265 JS::Rooted
<JS::Value
> jsData(cx
);
1268 jsString
= JS_NewUCStringCopyN(cx
,
1269 message
->mData
.get(),
1270 message
->mData
.Length());
1271 NS_ENSURE_TRUE_VOID(jsString
);
1273 jsData
= STRING_TO_JSVAL(jsString
);
1276 // create an event that uses the MessageEvent interface,
1277 // which does not bubble, is not cancelable, and has no default action
1279 nsCOMPtr
<nsIDOMEvent
> event
;
1280 rv
= NS_NewDOMMessageEvent(getter_AddRefs(event
), this, nullptr, nullptr);
1281 if (NS_FAILED(rv
)) {
1282 NS_WARNING("Failed to create the message event!!!");
1286 nsCOMPtr
<nsIDOMMessageEvent
> messageEvent
= do_QueryInterface(event
);
1287 rv
= messageEvent
->InitMessageEvent(message
->mEventName
,
1291 message
->mLastEventID
, nullptr);
1292 if (NS_FAILED(rv
)) {
1293 NS_WARNING("Failed to init the message event!!!");
1297 messageEvent
->SetTrusted(true);
1299 rv
= DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
1300 if (NS_FAILED(rv
)) {
1301 NS_WARNING("Failed to dispatch the message event!!!");
1305 mLastEventID
.Assign(message
->mLastEventID
);
1310 EventSource::ClearFields()
1312 // mLastEventID and mReconnectionTime must be cached
1314 mCurrentMessage
.mEventName
.Truncate();
1315 mCurrentMessage
.mLastEventID
.Truncate();
1316 mCurrentMessage
.mData
.Truncate();
1318 mLastFieldName
.Truncate();
1319 mLastFieldValue
.Truncate();
1325 EventSource::SetFieldAndClear()
1327 if (mLastFieldName
.IsEmpty()) {
1328 mLastFieldValue
.Truncate();
1332 char16_t first_char
;
1333 first_char
= mLastFieldName
.CharAt(0);
1335 switch (first_char
) // with no case folding performed
1338 if (mLastFieldName
.EqualsLiteral("data")) {
1339 // If the field name is "data" append the field value to the data
1340 // buffer, then append a single U+000A LINE FEED (LF) character
1341 // to the data buffer.
1342 mCurrentMessage
.mData
.Append(mLastFieldValue
);
1343 mCurrentMessage
.mData
.Append(LF_CHAR
);
1348 if (mLastFieldName
.EqualsLiteral("event")) {
1349 mCurrentMessage
.mEventName
.Assign(mLastFieldValue
);
1354 if (mLastFieldName
.EqualsLiteral("id")) {
1355 mCurrentMessage
.mLastEventID
.Assign(mLastFieldValue
);
1360 if (mLastFieldName
.EqualsLiteral("retry")) {
1361 uint32_t newValue
=0;
1362 uint32_t i
= 0; // we must ensure that there are only digits
1364 for (i
= 0; i
< mLastFieldValue
.Length(); ++i
) {
1365 if (mLastFieldValue
.CharAt(i
) < (char16_t
)'0' ||
1366 mLastFieldValue
.CharAt(i
) > (char16_t
)'9') {
1370 newValue
= newValue
*10 +
1371 (((uint32_t)mLastFieldValue
.CharAt(i
))-
1372 ((uint32_t)((char16_t
)'0')));
1376 if (newValue
< MIN_RECONNECTION_TIME_VALUE
) {
1377 mReconnectionTime
= MIN_RECONNECTION_TIME_VALUE
;
1378 } else if (newValue
> MAX_RECONNECTION_TIME_VALUE
) {
1379 mReconnectionTime
= MAX_RECONNECTION_TIME_VALUE
;
1381 mReconnectionTime
= newValue
;
1389 mLastFieldName
.Truncate();
1390 mLastFieldValue
.Truncate();
1396 EventSource::CheckHealthOfRequestCallback(nsIRequest
*aRequestCallback
)
1398 // check if we have been closed or if the request has been canceled
1399 // or if we have been frozen
1400 if (mReadyState
== CLOSED
|| !mHttpChannel
||
1401 mFrozen
|| mErrorLoadOnRedirect
) {
1402 return NS_ERROR_ABORT
;
1405 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequestCallback
);
1406 NS_ENSURE_STATE(httpChannel
);
1408 if (httpChannel
!= mHttpChannel
) {
1409 NS_WARNING("wrong channel from request callback");
1410 return NS_ERROR_ABORT
;
1417 EventSource::ParseCharacter(char16_t aChr
)
1421 if (mReadyState
== CLOSED
) {
1422 return NS_ERROR_ABORT
;
1427 case PARSE_STATE_OFF
:
1428 NS_ERROR("Invalid state");
1429 return NS_ERROR_FAILURE
;
1432 case PARSE_STATE_BEGIN_OF_STREAM
:
1433 if (aChr
== BOM_CHAR
) {
1434 mStatus
= PARSE_STATE_BOM_WAS_READ
; // ignore it
1435 } else if (aChr
== CR_CHAR
) {
1436 mStatus
= PARSE_STATE_CR_CHAR
;
1437 } else if (aChr
== LF_CHAR
) {
1438 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1439 } else if (aChr
== COLON_CHAR
) {
1440 mStatus
= PARSE_STATE_COMMENT
;
1442 mLastFieldName
+= aChr
;
1443 mStatus
= PARSE_STATE_FIELD_NAME
;
1448 case PARSE_STATE_BOM_WAS_READ
:
1449 if (aChr
== CR_CHAR
) {
1450 mStatus
= PARSE_STATE_CR_CHAR
;
1451 } else if (aChr
== LF_CHAR
) {
1452 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1453 } else if (aChr
== COLON_CHAR
) {
1454 mStatus
= PARSE_STATE_COMMENT
;
1456 mLastFieldName
+= aChr
;
1457 mStatus
= PARSE_STATE_FIELD_NAME
;
1461 case PARSE_STATE_CR_CHAR
:
1462 if (aChr
== CR_CHAR
) {
1463 rv
= DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1464 NS_ENSURE_SUCCESS(rv
, rv
);
1465 } else if (aChr
== LF_CHAR
) {
1466 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1467 } else if (aChr
== COLON_CHAR
) {
1468 mStatus
= PARSE_STATE_COMMENT
;
1470 mLastFieldName
+= aChr
;
1471 mStatus
= PARSE_STATE_FIELD_NAME
;
1476 case PARSE_STATE_COMMENT
:
1477 if (aChr
== CR_CHAR
) {
1478 mStatus
= PARSE_STATE_CR_CHAR
;
1479 } else if (aChr
== LF_CHAR
) {
1480 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1485 case PARSE_STATE_FIELD_NAME
:
1486 if (aChr
== CR_CHAR
) {
1487 rv
= SetFieldAndClear();
1488 NS_ENSURE_SUCCESS(rv
, rv
);
1490 mStatus
= PARSE_STATE_CR_CHAR
;
1491 } else if (aChr
== LF_CHAR
) {
1492 rv
= SetFieldAndClear();
1493 NS_ENSURE_SUCCESS(rv
, rv
);
1495 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1496 } else if (aChr
== COLON_CHAR
) {
1497 mStatus
= PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
;
1499 mLastFieldName
+= aChr
;
1504 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
:
1505 if (aChr
== CR_CHAR
) {
1506 rv
= SetFieldAndClear();
1507 NS_ENSURE_SUCCESS(rv
, rv
);
1509 mStatus
= PARSE_STATE_CR_CHAR
;
1510 } else if (aChr
== LF_CHAR
) {
1511 rv
= SetFieldAndClear();
1512 NS_ENSURE_SUCCESS(rv
, rv
);
1514 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1515 } else if (aChr
== SPACE_CHAR
) {
1516 mStatus
= PARSE_STATE_FIELD_VALUE
;
1518 mLastFieldValue
+= aChr
;
1519 mStatus
= PARSE_STATE_FIELD_VALUE
;
1524 case PARSE_STATE_FIELD_VALUE
:
1525 if (aChr
== CR_CHAR
) {
1526 rv
= SetFieldAndClear();
1527 NS_ENSURE_SUCCESS(rv
, rv
);
1529 mStatus
= PARSE_STATE_CR_CHAR
;
1530 } else if (aChr
== LF_CHAR
) {
1531 rv
= SetFieldAndClear();
1532 NS_ENSURE_SUCCESS(rv
, rv
);
1534 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1536 mLastFieldValue
+= aChr
;
1541 case PARSE_STATE_BEGIN_OF_LINE
:
1542 if (aChr
== CR_CHAR
) {
1543 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1544 NS_ENSURE_SUCCESS(rv
, rv
);
1546 mStatus
= PARSE_STATE_CR_CHAR
;
1547 } else if (aChr
== LF_CHAR
) {
1548 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1549 NS_ENSURE_SUCCESS(rv
, rv
);
1551 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1552 } else if (aChr
== COLON_CHAR
) {
1553 mStatus
= PARSE_STATE_COMMENT
;
1555 mLastFieldName
+= aChr
;
1556 mStatus
= PARSE_STATE_FIELD_NAME
;
1566 } // namespace mozilla