Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / EventSource.cpp
blob04647b070e5aeaf8927bdc568b2df127aebd295c
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"
37 #include "nsError.h"
39 namespace mozilla {
40 namespace dom {
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),
60 mFrozen(false),
61 mErrorLoadOnRedirect(false),
62 mGoingToDispatchAllMessages(false),
63 mWithCredentials(false),
64 mWaitingForOnStopRequest(false),
65 mLastConvertionResult(NS_OK),
66 mReadyState(CONNECTING),
67 mScriptLine(0),
68 mInnerWindowID(0)
72 EventSource::~EventSource()
74 Close();
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.
91 tmp->GetWrapper();
93 return true;
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)
122 tmp->Close();
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)
137 void
138 EventSource::DisconnectFromOwner()
140 DOMEventTargetHelper::DisconnectFromOwner();
141 Close();
144 void
145 EventSource::Close()
147 if (mReadyState == CLOSED) {
148 return;
151 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
152 if (os) {
153 os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
154 os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
155 os->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC);
158 if (mTimer) {
159 mTimer->Cancel();
160 mTimer = nullptr;
163 ResetConnection();
165 ClearFields();
167 while (mMessagesToDispatch.GetSize() != 0) {
168 delete static_cast<Message*>(mMessagesToDispatch.PopFront());
171 mSrc = nullptr;
172 mFrozen = false;
174 mUnicodeDecoder = nullptr;
176 mReadyState = CLOSED;
179 nsresult
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
210 // leaves the page.
211 nsresult rv;
212 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
213 if (sc) {
214 nsCOMPtr<nsIDocument> doc =
215 nsContentUtils::GetDocumentFromScriptContext(sc);
216 if (doc) {
217 mLoadGroup = doc->GetDocumentLoadGroup();
221 // get the src
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();
232 NS_ENSURE_STATE(os);
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);
241 nsAutoString origin;
242 rv = nsContentUtils::GetUTFOrigin(srcURI, origin);
243 NS_ENSURE_SUCCESS(rv, rv);
245 nsAutoCString spec;
246 rv = srcURI->GetSpec(spec);
247 NS_ENSURE_SUCCESS(rv, rv);
249 mOriginalURL = NS_ConvertUTF8toUTF16(spec);
250 mSrc = srcURI;
251 mOrigin = origin;
253 mReconnectionTime =
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
261 // result.
262 InitChannelAndRequestEventSource();
264 return NS_OK;
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,
277 ErrorResult& aRv)
279 nsCOMPtr<nsPIDOMWindow> ownerWindow =
280 do_QueryInterface(aGlobal.GetAsSupports());
281 if (!ownerWindow) {
282 aRv.Throw(NS_ERROR_UNEXPECTED);
283 return nullptr;
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 //-----------------------------------------------------------------------------
297 NS_IMETHODIMP
298 EventSource::Observe(nsISupports* aSubject,
299 const char* aTopic,
300 const char16_t* aData)
302 if (mReadyState == CLOSED) {
303 return NS_OK;
306 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
307 if (!GetOwner() || window != GetOwner()) {
308 return NS_OK;
311 DebugOnly<nsresult> rv;
312 if (strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) {
313 rv = Freeze();
314 NS_ASSERTION(NS_SUCCEEDED(rv), "Freeze() failed");
315 } else if (strcmp(aTopic, DOM_WINDOW_THAWED_TOPIC) == 0) {
316 rv = Thaw();
317 NS_ASSERTION(NS_SUCCEEDED(rv), "Thaw() failed");
318 } else if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) {
319 Close();
322 return NS_OK;
325 //-----------------------------------------------------------------------------
326 // EventSource::nsIStreamListener
327 //-----------------------------------------------------------------------------
329 NS_IMETHODIMP
330 EventSource::OnStartRequest(nsIRequest *aRequest,
331 nsISupports *ctxt)
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);
339 nsresult status;
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;
349 uint32_t httpStatus;
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;
376 return NS_OK;
379 // this method parses the characters as they become available instead of
380 // buffering them.
381 NS_METHOD
382 EventSource::StreamReaderFunc(nsIInputStream *aInputStream,
383 void *aClosure,
384 const char *aFromRawSegment,
385 uint32_t aToOffset,
386 uint32_t aCount,
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;
395 *aWriteCount = 0;
397 int32_t srcCount, outCount;
398 char16_t out[2];
399 nsresult rv;
401 const char *p = aFromRawSegment,
402 *end = aFromRawSegment + aCount;
404 do {
405 srcCount = aCount - (p - aFromRawSegment);
406 outCount = 2;
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);
416 p = p + srcCount;
417 } while (p < end &&
418 thisObject->mLastConvertionResult != NS_PARTIAL_MORE_INPUT &&
419 thisObject->mLastConvertionResult != NS_OK);
421 *aWriteCount = aCount;
422 return NS_OK;
425 NS_IMETHODIMP
426 EventSource::OnDataAvailable(nsIRequest *aRequest,
427 nsISupports *aContext,
428 nsIInputStream *aInputStream,
429 uint64_t aOffset,
430 uint32_t aCount)
432 NS_ENSURE_ARG_POINTER(aInputStream);
434 nsresult rv = CheckHealthOfRequestCallback(aRequest);
435 NS_ENSURE_SUCCESS(rv, rv);
437 uint32_t totalRead;
438 return aInputStream->ReadSegments(EventSource::StreamReaderFunc, this,
439 aCount, &totalRead);
442 NS_IMETHODIMP
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);
475 ClearFields();
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);
484 return NS_OK;
488 * Simple helper class that just forwards the redirect callback back
489 * to the EventSource.
491 class AsyncVerifyRedirectCallbackFwr MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
493 public:
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);
506 if (NS_FAILED(rv)) {
507 mEventSource->mErrorLoadOnRedirect = true;
508 mEventSource->DispatchFailConnection();
511 return NS_OK;
514 private:
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)
524 NS_INTERFACE_MAP_END
526 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr)
527 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr)
529 //-----------------------------------------------------------------------------
530 // EventSource::nsIChannelEventSink
531 //-----------------------------------------------------------------------------
533 NS_IMETHODIMP
534 EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
535 nsIChannel *aNewChannel,
536 uint32_t aFlags,
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,
566 aNewChannel,
567 aFlags, fwd);
568 if (NS_FAILED(rv)) {
569 mRedirectCallback = nullptr;
570 mNewRedirectChannel = nullptr;
571 mErrorLoadOnRedirect = true;
572 DispatchFailConnection();
574 return rv;
576 OnRedirectVerifyCallback(NS_OK);
577 return NS_OK;
580 nsresult
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;
607 return NS_OK;
610 //-----------------------------------------------------------------------------
611 // EventSource::nsIInterfaceRequestor
612 //-----------------------------------------------------------------------------
614 NS_IMETHODIMP
615 EventSource::GetInterface(const nsIID & aIID,
616 void **aResult)
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);
625 NS_ADDREF_THIS();
626 return NS_OK;
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!");
635 return rv;
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;
652 if (GetOwner()) {
653 window = GetOwner()->GetOuterWindow();
656 return wwatch->GetPrompt(window, aIID, aResult);
659 return QueryInterface(aIID, aResult);
662 // static
663 bool
664 EventSource::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
666 return Preferences::GetBool("dom.server-events.enabled", false);
669 nsresult
670 EventSource::GetBaseURI(nsIURI **aBaseURI)
672 NS_ENSURE_ARG_POINTER(aBaseURI);
674 *aBaseURI = nullptr;
676 nsCOMPtr<nsIURI> baseURI;
678 // first we try from document->GetBaseURI()
679 nsresult rv;
680 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
681 nsCOMPtr<nsIDocument> doc =
682 nsContentUtils::GetDocumentFromScriptContext(sc);
683 if (doc) {
684 baseURI = doc->GetBaseURI();
687 // otherwise we get from the doc's principal
688 if (!baseURI) {
689 rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
690 NS_ENSURE_SUCCESS(rv, rv);
693 NS_ENSURE_STATE(baseURI);
695 baseURI.forget(aBaseURI);
696 return NS_OK;
699 net::ReferrerPolicy
700 EventSource::GetReferrerPolicy()
702 nsresult rv;
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;
710 nsresult
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);
734 return NS_OK;
737 nsresult
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;
754 nsresult rv;
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
761 if (doc) {
762 rv = NS_NewChannel(getter_AddRefs(channel),
763 mSrc,
764 doc,
765 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
766 nsIContentPolicy::TYPE_DATAREQUEST,
767 mLoadGroup, // loadGroup
768 nullptr, // aCallbacks
769 loadFlags); // aLoadFlags
770 } else {
771 // otherwise use the principal
772 rv = NS_NewChannel(getter_AddRefs(channel),
773 mSrc,
774 mPrincipal,
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;
807 return rv;
810 void
811 EventSource::AnnounceConnection()
813 if (mReadyState == CLOSED) {
814 return;
817 if (mReadyState != CONNECTING) {
818 NS_WARNING("Unexpected mReadyState!!!");
819 return;
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.
826 mReadyState = OPEN;
828 nsresult rv = CheckInnerWindowCorrectness();
829 if (NS_FAILED(rv)) {
830 return;
833 nsCOMPtr<nsIDOMEvent> event;
834 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
835 if (NS_FAILED(rv)) {
836 NS_WARNING("Failed to create the open event!!!");
837 return;
840 // it doesn't bubble, and it isn't cancelable
841 rv = event->InitEvent(NS_LITERAL_STRING("open"), false, false);
842 if (NS_FAILED(rv)) {
843 NS_WARNING("Failed to init the open event!!!");
844 return;
847 event->SetTrusted(true);
849 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
850 if (NS_FAILED(rv)) {
851 NS_WARNING("Failed to dispatch the open event!!!");
852 return;
856 nsresult
857 EventSource::ResetConnection()
859 if (mHttpChannel) {
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;
877 return NS_OK;
880 void
881 EventSource::ReestablishConnection()
883 if (mReadyState == CLOSED) {
884 return;
887 nsresult rv = ResetConnection();
888 if (NS_FAILED(rv)) {
889 NS_WARNING("Failed to reset the connection!!!");
890 return;
893 rv = CheckInnerWindowCorrectness();
894 if (NS_FAILED(rv)) {
895 return;
898 nsCOMPtr<nsIDOMEvent> event;
899 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
900 if (NS_FAILED(rv)) {
901 NS_WARNING("Failed to create the error event!!!");
902 return;
905 // it doesn't bubble, and it isn't cancelable
906 rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false);
907 if (NS_FAILED(rv)) {
908 NS_WARNING("Failed to init the error event!!!");
909 return;
912 event->SetTrusted(true);
914 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
915 if (NS_FAILED(rv)) {
916 NS_WARNING("Failed to dispatch the error event!!!");
917 return;
920 rv = SetReconnectionTimeout();
921 if (NS_FAILED(rv)) {
922 NS_WARNING("Failed to set the timeout for reestablishing the connection!!!");
923 return;
927 nsresult
928 EventSource::SetReconnectionTimeout()
930 if (mReadyState == CLOSED) {
931 return NS_ERROR_ABORT;
934 // the timer will be used whenever the requests are going finished.
935 if (!mTimer) {
936 mTimer = do_CreateInstance("@mozilla.org/timer;1");
937 NS_ENSURE_STATE(mTimer);
940 nsresult rv = mTimer->InitWithFuncCallback(TimerCallback, this,
941 mReconnectionTime,
942 nsITimer::TYPE_ONE_SHOT);
943 NS_ENSURE_SUCCESS(rv, rv);
945 return NS_OK;
948 nsresult
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;
959 nsresult rv =
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,
975 aFormatStringsLen,
976 getter_Copies(message));
977 } else {
978 rv = strBundle->GetStringFromName(aError, getter_Copies(message));
980 NS_ENSURE_SUCCESS(rv, rv);
982 rv = errObj->InitWithWindowID(message,
983 mScriptFile,
984 EmptyString(),
985 mScriptLine, 0,
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);
994 return NS_OK;
997 nsresult
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));
1011 } else {
1012 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1013 MOZ_UTF16("netInterrupt"),
1014 formatStrings, ArrayLength(formatStrings));
1016 NS_ENSURE_SUCCESS(rv, rv);
1018 return NS_OK;
1021 nsresult
1022 EventSource::DispatchFailConnection()
1024 nsCOMPtr<nsIRunnable> event =
1025 NS_NewRunnableMethod(this, &EventSource::FailConnection);
1026 NS_ENSURE_STATE(event);
1028 return NS_DispatchToMainThread(event);
1031 void
1032 EventSource::FailConnection()
1034 if (mReadyState == CLOSED) {
1035 return;
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)) {
1051 return;
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!!!");
1058 return;
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!!!");
1065 return;
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!!!");
1073 return;
1077 bool
1078 EventSource::CheckCanRequestSrc(nsIURI* aSrc)
1080 if (mReadyState == CLOSED) {
1081 return false;
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,
1097 srcToTest,
1098 aCheckURIFlags);
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,
1113 srcToTest,
1114 mPrincipal,
1115 doc,
1116 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM),
1117 nullptr, // extra
1118 &shouldLoad,
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;
1134 // static
1135 void
1136 EventSource::TimerCallback(nsITimer* aTimer, void* aClosure)
1138 nsRefPtr<EventSource> thisObject = static_cast<EventSource*>(aClosure);
1140 if (thisObject->mReadyState == CLOSED) {
1141 return;
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");
1151 return;
1156 nsresult
1157 EventSource::Thaw()
1159 if (mReadyState == CLOSED || !mFrozen) {
1160 return NS_OK;
1163 NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
1165 mFrozen = false;
1166 nsresult rv;
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);
1181 return NS_OK;
1184 nsresult
1185 EventSource::Freeze()
1187 if (mReadyState == CLOSED || mFrozen) {
1188 return NS_OK;
1191 NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
1192 mFrozen = true;
1193 return NS_OK;
1196 nsresult
1197 EventSource::DispatchCurrentMessageEvent()
1199 nsAutoPtr<Message> message(new Message());
1200 *message = mCurrentMessage;
1202 ClearFields();
1204 if (message->mData.IsEmpty()) {
1205 return NS_OK;
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);
1237 return NS_OK;
1240 void
1241 EventSource::DispatchAllMessageEvents()
1243 if (mReadyState == CLOSED || mFrozen) {
1244 return;
1247 mGoingToDispatchAllMessages = false;
1249 nsresult rv = CheckInnerWindowCorrectness();
1250 if (NS_FAILED(rv)) {
1251 return;
1254 AutoJSAPI jsapi;
1255 if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
1256 return;
1258 JSContext* cx = jsapi.cx();
1260 while (mMessagesToDispatch.GetSize() > 0) {
1261 nsAutoPtr<Message>
1262 message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
1264 // Now we can turn our string into a jsval
1265 JS::Rooted<JS::Value> jsData(cx);
1267 JSString* jsString;
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!!!");
1283 return;
1286 nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
1287 rv = messageEvent->InitMessageEvent(message->mEventName,
1288 false, false,
1289 jsData,
1290 mOrigin,
1291 message->mLastEventID, nullptr);
1292 if (NS_FAILED(rv)) {
1293 NS_WARNING("Failed to init the message event!!!");
1294 return;
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!!!");
1302 return;
1305 mLastEventID.Assign(message->mLastEventID);
1309 nsresult
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();
1321 return NS_OK;
1324 nsresult
1325 EventSource::SetFieldAndClear()
1327 if (mLastFieldName.IsEmpty()) {
1328 mLastFieldValue.Truncate();
1329 return NS_OK;
1332 char16_t first_char;
1333 first_char = mLastFieldName.CharAt(0);
1335 switch (first_char) // with no case folding performed
1337 case char16_t('d'):
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);
1345 break;
1347 case char16_t('e'):
1348 if (mLastFieldName.EqualsLiteral("event")) {
1349 mCurrentMessage.mEventName.Assign(mLastFieldValue);
1351 break;
1353 case char16_t('i'):
1354 if (mLastFieldName.EqualsLiteral("id")) {
1355 mCurrentMessage.mLastEventID.Assign(mLastFieldValue);
1357 break;
1359 case char16_t('r'):
1360 if (mLastFieldName.EqualsLiteral("retry")) {
1361 uint32_t newValue=0;
1362 uint32_t i = 0; // we must ensure that there are only digits
1363 bool assign = true;
1364 for (i = 0; i < mLastFieldValue.Length(); ++i) {
1365 if (mLastFieldValue.CharAt(i) < (char16_t)'0' ||
1366 mLastFieldValue.CharAt(i) > (char16_t)'9') {
1367 assign = false;
1368 break;
1370 newValue = newValue*10 +
1371 (((uint32_t)mLastFieldValue.CharAt(i))-
1372 ((uint32_t)((char16_t)'0')));
1375 if (assign) {
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;
1380 } else {
1381 mReconnectionTime = newValue;
1384 break;
1386 break;
1389 mLastFieldName.Truncate();
1390 mLastFieldValue.Truncate();
1392 return NS_OK;
1395 nsresult
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;
1413 return NS_OK;
1416 nsresult
1417 EventSource::ParseCharacter(char16_t aChr)
1419 nsresult rv;
1421 if (mReadyState == CLOSED) {
1422 return NS_ERROR_ABORT;
1425 switch (mStatus)
1427 case PARSE_STATE_OFF:
1428 NS_ERROR("Invalid state");
1429 return NS_ERROR_FAILURE;
1430 break;
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;
1441 } else {
1442 mLastFieldName += aChr;
1443 mStatus = PARSE_STATE_FIELD_NAME;
1446 break;
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;
1455 } else {
1456 mLastFieldName += aChr;
1457 mStatus = PARSE_STATE_FIELD_NAME;
1459 break;
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;
1469 } else {
1470 mLastFieldName += aChr;
1471 mStatus = PARSE_STATE_FIELD_NAME;
1474 break;
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;
1483 break;
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;
1498 } else {
1499 mLastFieldName += aChr;
1502 break;
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;
1517 } else {
1518 mLastFieldValue += aChr;
1519 mStatus = PARSE_STATE_FIELD_VALUE;
1522 break;
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;
1535 } else {
1536 mLastFieldValue += aChr;
1539 break;
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;
1554 } else {
1555 mLastFieldName += aChr;
1556 mStatus = PARSE_STATE_FIELD_NAME;
1559 break;
1562 return NS_OK;
1565 } // namespace dom
1566 } // namespace mozilla