Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/DOMEventTargetHelper.h"
11 #include "mozilla/dom/EventSourceBinding.h"
12 #include "mozilla/dom/MessageEvent.h"
14 #include "js/OldDebugAPI.h"
15 #include "nsNetUtil.h"
16 #include "nsMimeTypes.h"
17 #include "nsIPromptFactory.h"
18 #include "nsIWindowWatcher.h"
19 #include "nsPresContext.h"
20 #include "nsContentPolicyUtils.h"
21 #include "nsIStringBundle.h"
22 #include "nsIConsoleService.h"
23 #include "nsIObserverService.h"
24 #include "nsIScriptObjectPrincipal.h"
25 #include "nsJSUtils.h"
26 #include "nsIAsyncVerifyRedirectCallback.h"
27 #include "nsIScriptError.h"
28 #include "mozilla/dom/EncodingUtils.h"
29 #include "nsIChannelPolicy.h"
30 #include "nsIContentSecurityPolicy.h"
31 #include "nsContentUtils.h"
32 #include "nsCxPusher.h"
33 #include "mozilla/Preferences.h"
34 #include "xpcpublic.h"
35 #include "nsCrossSiteListenerProxy.h"
36 #include "nsWrapperCacheInlines.h"
37 #include "mozilla/Attributes.h"
38 #include "nsError.h"
40 namespace mozilla {
41 namespace dom {
43 #define REPLACEMENT_CHAR (char16_t)0xFFFD
44 #define BOM_CHAR (char16_t)0xFEFF
45 #define SPACE_CHAR (char16_t)0x0020
46 #define CR_CHAR (char16_t)0x000D
47 #define LF_CHAR (char16_t)0x000A
48 #define COLON_CHAR (char16_t)0x003A
50 #define DEFAULT_BUFFER_SIZE 4096
52 // Reconnection time related values in milliseconds. The default one is equal
53 // to the default value of the pref dom.server-events.default-reconnection-time
54 #define MIN_RECONNECTION_TIME_VALUE 500
55 #define DEFAULT_RECONNECTION_TIME_VALUE 5000
56 #define MAX_RECONNECTION_TIME_VALUE PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
58 EventSource::EventSource(nsPIDOMWindow* aOwnerWindow) :
59 DOMEventTargetHelper(aOwnerWindow),
60 mStatus(PARSE_STATE_OFF),
61 mFrozen(false),
62 mErrorLoadOnRedirect(false),
63 mGoingToDispatchAllMessages(false),
64 mWithCredentials(false),
65 mWaitingForOnStopRequest(false),
66 mInterrupted(false),
67 mLastConvertionResult(NS_OK),
68 mReadyState(CONNECTING),
69 mScriptLine(0),
70 mInnerWindowID(0)
71 {
72 }
74 EventSource::~EventSource()
75 {
76 Close();
77 }
79 //-----------------------------------------------------------------------------
80 // EventSource::nsISupports
81 //-----------------------------------------------------------------------------
83 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource)
85 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(EventSource)
86 bool isBlack = tmp->IsBlack();
87 if (isBlack || tmp->mWaitingForOnStopRequest) {
88 if (tmp->mListenerManager) {
89 tmp->mListenerManager->MarkForCC();
90 }
91 if (!isBlack && tmp->PreservingWrapper()) {
92 // This marks the wrapper black.
93 tmp->GetWrapper();
94 }
95 return true;
96 }
97 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
99 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(EventSource)
100 return tmp->IsBlack();
101 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
103 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(EventSource)
104 return tmp->IsBlack();
105 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
107 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(EventSource,
108 DOMEventTargetHelper)
109 NS_IMPL_CYCLE_COLLECTION_TRACE_END
111 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource,
112 DOMEventTargetHelper)
113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrc)
114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadGroup)
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHttpChannel)
118 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
119 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnicodeDecoder)
120 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
122 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource,
123 DOMEventTargetHelper)
124 tmp->Close();
125 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
127 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(EventSource)
128 NS_INTERFACE_MAP_ENTRY(nsIObserver)
129 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
130 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
131 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
132 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
133 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
134 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
136 NS_IMPL_ADDREF_INHERITED(EventSource, DOMEventTargetHelper)
137 NS_IMPL_RELEASE_INHERITED(EventSource, DOMEventTargetHelper)
139 void
140 EventSource::DisconnectFromOwner()
141 {
142 DOMEventTargetHelper::DisconnectFromOwner();
143 Close();
144 }
146 void
147 EventSource::Close()
148 {
149 if (mReadyState == CLOSED) {
150 return;
151 }
153 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
154 if (os) {
155 os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
156 os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
157 os->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC);
158 }
160 if (mTimer) {
161 mTimer->Cancel();
162 mTimer = nullptr;
163 }
165 ResetConnection();
167 ClearFields();
169 while (mMessagesToDispatch.GetSize() != 0) {
170 delete static_cast<Message*>(mMessagesToDispatch.PopFront());
171 }
173 mSrc = nullptr;
174 mFrozen = false;
176 mUnicodeDecoder = nullptr;
178 mReadyState = CLOSED;
179 }
181 nsresult
182 EventSource::Init(nsISupports* aOwner,
183 const nsAString& aURL,
184 bool aWithCredentials)
185 {
186 if (mReadyState != CONNECTING || !PrefEnabled()) {
187 return NS_ERROR_DOM_SECURITY_ERR;
188 }
190 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
191 NS_ENSURE_STATE(sgo);
192 nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
193 NS_ENSURE_STATE(scriptContext);
195 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
196 do_QueryInterface(aOwner);
197 NS_ENSURE_STATE(scriptPrincipal);
198 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
199 NS_ENSURE_STATE(principal);
201 mPrincipal = principal;
202 mWithCredentials = aWithCredentials;
204 // The conditional here is historical and not necessarily sane.
205 if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
206 const char *filename;
207 if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) {
208 mScriptFile.AssignASCII(filename);
209 }
211 mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
212 }
214 // Get the load group for the page. When requesting we'll add ourselves to it.
215 // This way any pending requests will be automatically aborted if the user
216 // leaves the page.
217 nsresult rv;
218 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
219 if (sc) {
220 nsCOMPtr<nsIDocument> doc =
221 nsContentUtils::GetDocumentFromScriptContext(sc);
222 if (doc) {
223 mLoadGroup = doc->GetDocumentLoadGroup();
224 }
225 }
227 // get the src
228 nsCOMPtr<nsIURI> baseURI;
229 rv = GetBaseURI(getter_AddRefs(baseURI));
230 NS_ENSURE_SUCCESS(rv, rv);
232 nsCOMPtr<nsIURI> srcURI;
233 rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI);
234 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
236 // we observe when the window freezes and thaws
237 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
238 NS_ENSURE_STATE(os);
240 rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
241 NS_ENSURE_SUCCESS(rv, rv);
242 rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
243 NS_ENSURE_SUCCESS(rv, rv);
244 rv = os->AddObserver(this, DOM_WINDOW_THAWED_TOPIC, true);
245 NS_ENSURE_SUCCESS(rv, rv);
247 nsAutoString origin;
248 rv = nsContentUtils::GetUTFOrigin(srcURI, origin);
249 NS_ENSURE_SUCCESS(rv, rv);
251 nsAutoCString spec;
252 rv = srcURI->GetSpec(spec);
253 NS_ENSURE_SUCCESS(rv, rv);
255 mOriginalURL = NS_ConvertUTF8toUTF16(spec);
256 mSrc = srcURI;
257 mOrigin = origin;
259 mReconnectionTime =
260 Preferences::GetInt("dom.server-events.default-reconnection-time",
261 DEFAULT_RECONNECTION_TIME_VALUE);
263 mUnicodeDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
265 // the constructor should throw a SYNTAX_ERROR only if it fails resolving the
266 // url parameter, so we don't care about the InitChannelAndRequestEventSource
267 // result.
268 InitChannelAndRequestEventSource();
270 return NS_OK;
271 }
273 /* virtual */ JSObject*
274 EventSource::WrapObject(JSContext* aCx)
275 {
276 return EventSourceBinding::Wrap(aCx, this);
277 }
279 /* static */ already_AddRefed<EventSource>
280 EventSource::Constructor(const GlobalObject& aGlobal,
281 const nsAString& aURL,
282 const EventSourceInit& aEventSourceInitDict,
283 ErrorResult& aRv)
284 {
285 nsCOMPtr<nsPIDOMWindow> ownerWindow =
286 do_QueryInterface(aGlobal.GetAsSupports());
287 if (!ownerWindow) {
288 aRv.Throw(NS_ERROR_UNEXPECTED);
289 return nullptr;
290 }
291 MOZ_ASSERT(ownerWindow->IsInnerWindow());
293 nsRefPtr<EventSource> eventSource = new EventSource(ownerWindow);
294 aRv = eventSource->Init(aGlobal.GetAsSupports(), aURL,
295 aEventSourceInitDict.mWithCredentials);
296 return eventSource.forget();
297 }
299 //-----------------------------------------------------------------------------
300 // EventSource::nsIObserver
301 //-----------------------------------------------------------------------------
303 NS_IMETHODIMP
304 EventSource::Observe(nsISupports* aSubject,
305 const char* aTopic,
306 const char16_t* aData)
307 {
308 if (mReadyState == CLOSED) {
309 return NS_OK;
310 }
312 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
313 if (!GetOwner() || window != GetOwner()) {
314 return NS_OK;
315 }
317 DebugOnly<nsresult> rv;
318 if (strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) {
319 rv = Freeze();
320 NS_ASSERTION(NS_SUCCEEDED(rv), "Freeze() failed");
321 } else if (strcmp(aTopic, DOM_WINDOW_THAWED_TOPIC) == 0) {
322 rv = Thaw();
323 NS_ASSERTION(NS_SUCCEEDED(rv), "Thaw() failed");
324 } else if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) {
325 Close();
326 }
328 return NS_OK;
329 }
331 //-----------------------------------------------------------------------------
332 // EventSource::nsIStreamListener
333 //-----------------------------------------------------------------------------
335 NS_IMETHODIMP
336 EventSource::OnStartRequest(nsIRequest *aRequest,
337 nsISupports *ctxt)
338 {
339 nsresult rv = CheckHealthOfRequestCallback(aRequest);
340 NS_ENSURE_SUCCESS(rv, rv);
342 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
343 NS_ENSURE_SUCCESS(rv, rv);
345 bool requestSucceeded;
346 rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
347 NS_ENSURE_SUCCESS(rv, rv);
349 nsAutoCString contentType;
350 rv = httpChannel->GetContentType(contentType);
351 NS_ENSURE_SUCCESS(rv, rv);
353 nsresult status;
354 aRequest->GetStatus(&status);
356 if (NS_FAILED(status) || !requestSucceeded ||
357 !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
358 DispatchFailConnection();
359 return NS_ERROR_NOT_AVAILABLE;
360 }
362 uint32_t httpStatus;
363 rv = httpChannel->GetResponseStatus(&httpStatus);
364 NS_ENSURE_SUCCESS(rv, rv);
366 if (httpStatus != 200) {
367 mInterrupted = true;
368 DispatchFailConnection();
369 return NS_ERROR_ABORT;
370 }
372 nsCOMPtr<nsIPrincipal> principal = mPrincipal;
373 if (nsContentUtils::IsSystemPrincipal(principal)) {
374 // Don't give this channel the system principal.
375 principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
376 NS_ENSURE_SUCCESS(rv, rv);
377 }
378 rv = httpChannel->SetOwner(principal);
379 NS_ENSURE_SUCCESS(rv, rv);
381 nsCOMPtr<nsIRunnable> event =
382 NS_NewRunnableMethod(this, &EventSource::AnnounceConnection);
383 NS_ENSURE_STATE(event);
385 rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
386 NS_ENSURE_SUCCESS(rv, rv);
388 mStatus = PARSE_STATE_BEGIN_OF_STREAM;
390 return NS_OK;
391 }
393 // this method parses the characters as they become available instead of
394 // buffering them.
395 NS_METHOD
396 EventSource::StreamReaderFunc(nsIInputStream *aInputStream,
397 void *aClosure,
398 const char *aFromRawSegment,
399 uint32_t aToOffset,
400 uint32_t aCount,
401 uint32_t *aWriteCount)
402 {
403 EventSource* thisObject = static_cast<EventSource*>(aClosure);
404 if (!thisObject || !aWriteCount) {
405 NS_WARNING("EventSource cannot read from stream: no aClosure or aWriteCount");
406 return NS_ERROR_FAILURE;
407 }
409 *aWriteCount = 0;
411 int32_t srcCount, outCount;
412 char16_t out[2];
413 nsresult rv;
415 const char *p = aFromRawSegment,
416 *end = aFromRawSegment + aCount;
418 do {
419 srcCount = aCount - (p - aFromRawSegment);
420 outCount = 2;
422 thisObject->mLastConvertionResult =
423 thisObject->mUnicodeDecoder->Convert(p, &srcCount, out, &outCount);
424 MOZ_ASSERT(thisObject->mLastConvertionResult != NS_ERROR_ILLEGAL_INPUT);
426 for (int32_t i = 0; i < outCount; ++i) {
427 rv = thisObject->ParseCharacter(out[i]);
428 NS_ENSURE_SUCCESS(rv, rv);
429 }
430 p = p + srcCount;
431 } while (p < end &&
432 thisObject->mLastConvertionResult != NS_PARTIAL_MORE_INPUT &&
433 thisObject->mLastConvertionResult != NS_OK);
435 *aWriteCount = aCount;
436 return NS_OK;
437 }
439 NS_IMETHODIMP
440 EventSource::OnDataAvailable(nsIRequest *aRequest,
441 nsISupports *aContext,
442 nsIInputStream *aInputStream,
443 uint64_t aOffset,
444 uint32_t aCount)
445 {
446 NS_ENSURE_ARG_POINTER(aInputStream);
448 nsresult rv = CheckHealthOfRequestCallback(aRequest);
449 NS_ENSURE_SUCCESS(rv, rv);
451 uint32_t totalRead;
452 return aInputStream->ReadSegments(EventSource::StreamReaderFunc, this,
453 aCount, &totalRead);
454 }
456 NS_IMETHODIMP
457 EventSource::OnStopRequest(nsIRequest *aRequest,
458 nsISupports *aContext,
459 nsresult aStatusCode)
460 {
461 mWaitingForOnStopRequest = false;
463 if (mReadyState == CLOSED) {
464 return NS_ERROR_ABORT;
465 }
467 if (NS_FAILED(aStatusCode)) {
468 DispatchFailConnection();
469 return aStatusCode;
470 }
472 nsresult rv;
473 nsresult healthOfRequestResult = CheckHealthOfRequestCallback(aRequest);
474 if (NS_SUCCEEDED(healthOfRequestResult) &&
475 mLastConvertionResult == NS_PARTIAL_MORE_INPUT) {
476 // we had an incomplete UTF8 char at the end of the stream
477 rv = ParseCharacter(REPLACEMENT_CHAR);
478 NS_ENSURE_SUCCESS(rv, rv);
479 }
481 ClearFields();
483 nsCOMPtr<nsIRunnable> event =
484 NS_NewRunnableMethod(this, &EventSource::ReestablishConnection);
485 NS_ENSURE_STATE(event);
487 rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
488 NS_ENSURE_SUCCESS(rv, rv);
490 return healthOfRequestResult;
491 }
493 /**
494 * Simple helper class that just forwards the redirect callback back
495 * to the EventSource.
496 */
497 class AsyncVerifyRedirectCallbackFwr MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
498 {
499 public:
500 AsyncVerifyRedirectCallbackFwr(EventSource* aEventsource)
501 : mEventSource(aEventsource)
502 {
503 }
505 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
506 NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
508 // nsIAsyncVerifyRedirectCallback implementation
509 NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult)
510 {
511 nsresult rv = mEventSource->OnRedirectVerifyCallback(aResult);
512 if (NS_FAILED(rv)) {
513 mEventSource->mErrorLoadOnRedirect = true;
514 mEventSource->DispatchFailConnection();
515 }
517 return NS_OK;
518 }
520 private:
521 nsRefPtr<EventSource> mEventSource;
522 };
524 NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr, mEventSource)
526 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr)
527 NS_INTERFACE_MAP_ENTRY(nsISupports)
528 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
529 NS_INTERFACE_MAP_END
531 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr)
532 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr)
534 //-----------------------------------------------------------------------------
535 // EventSource::nsIChannelEventSink
536 //-----------------------------------------------------------------------------
538 NS_IMETHODIMP
539 EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
540 nsIChannel *aNewChannel,
541 uint32_t aFlags,
542 nsIAsyncVerifyRedirectCallback *aCallback)
543 {
544 nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel);
545 NS_PRECONDITION(aOldRequest, "Redirect from a null request?");
547 nsresult rv = CheckHealthOfRequestCallback(aOldRequest);
548 NS_ENSURE_SUCCESS(rv, rv);
550 NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
552 nsCOMPtr<nsIURI> newURI;
553 rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
554 NS_ENSURE_SUCCESS(rv, rv);
556 if (!CheckCanRequestSrc(newURI)) {
557 DispatchFailConnection();
558 return NS_ERROR_DOM_SECURITY_ERR;
559 }
561 // Prepare to receive callback
562 mRedirectFlags = aFlags;
563 mRedirectCallback = aCallback;
564 mNewRedirectChannel = aNewChannel;
566 if (mChannelEventSink) {
567 nsRefPtr<AsyncVerifyRedirectCallbackFwr> fwd =
568 new AsyncVerifyRedirectCallbackFwr(this);
570 rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
571 aNewChannel,
572 aFlags, fwd);
573 if (NS_FAILED(rv)) {
574 mRedirectCallback = nullptr;
575 mNewRedirectChannel = nullptr;
576 mErrorLoadOnRedirect = true;
577 DispatchFailConnection();
578 }
579 return rv;
580 }
581 OnRedirectVerifyCallback(NS_OK);
582 return NS_OK;
583 }
585 nsresult
586 EventSource::OnRedirectVerifyCallback(nsresult aResult)
587 {
588 NS_ABORT_IF_FALSE(mRedirectCallback, "mRedirectCallback not set in callback");
589 NS_ABORT_IF_FALSE(mNewRedirectChannel,
590 "mNewRedirectChannel not set in callback");
592 NS_ENSURE_SUCCESS(aResult, aResult);
594 // update our channel
596 mHttpChannel = do_QueryInterface(mNewRedirectChannel);
597 NS_ENSURE_STATE(mHttpChannel);
599 nsresult rv = SetupHttpChannel();
600 NS_ENSURE_SUCCESS(rv, rv);
602 if ((mRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
603 rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
604 NS_ENSURE_SUCCESS(rv, rv);
605 }
607 mNewRedirectChannel = nullptr;
609 mRedirectCallback->OnRedirectVerifyCallback(aResult);
610 mRedirectCallback = nullptr;
612 return NS_OK;
613 }
615 //-----------------------------------------------------------------------------
616 // EventSource::nsIInterfaceRequestor
617 //-----------------------------------------------------------------------------
619 NS_IMETHODIMP
620 EventSource::GetInterface(const nsIID & aIID,
621 void **aResult)
622 {
623 // Make sure to return ourselves for the channel event sink interface,
624 // no matter what. We can forward these to mNotificationCallbacks
625 // if it wants to get notifications for them. But we
626 // need to see these notifications for proper functioning.
627 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
628 mChannelEventSink = do_GetInterface(mNotificationCallbacks);
629 *aResult = static_cast<nsIChannelEventSink*>(this);
630 NS_ADDREF_THIS();
631 return NS_OK;
632 }
634 // Now give mNotificationCallbacks (if non-null) a chance to return the
635 // desired interface.
636 if (mNotificationCallbacks) {
637 nsresult rv = mNotificationCallbacks->GetInterface(aIID, aResult);
638 if (NS_SUCCEEDED(rv)) {
639 NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
640 return rv;
641 }
642 }
644 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
645 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
646 nsresult rv = CheckInnerWindowCorrectness();
647 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
649 nsCOMPtr<nsIPromptFactory> wwatch =
650 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
651 NS_ENSURE_SUCCESS(rv, rv);
653 // Get the an auth prompter for our window so that the parenting
654 // of the dialogs works as it should when using tabs.
656 nsCOMPtr<nsIDOMWindow> window;
657 if (GetOwner()) {
658 window = GetOwner()->GetOuterWindow();
659 }
661 return wwatch->GetPrompt(window, aIID, aResult);
662 }
664 return QueryInterface(aIID, aResult);
665 }
667 // static
668 bool
669 EventSource::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
670 {
671 return Preferences::GetBool("dom.server-events.enabled", false);
672 }
674 nsresult
675 EventSource::GetBaseURI(nsIURI **aBaseURI)
676 {
677 NS_ENSURE_ARG_POINTER(aBaseURI);
679 *aBaseURI = nullptr;
681 nsCOMPtr<nsIURI> baseURI;
683 // first we try from document->GetBaseURI()
684 nsresult rv;
685 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
686 nsCOMPtr<nsIDocument> doc =
687 nsContentUtils::GetDocumentFromScriptContext(sc);
688 if (doc) {
689 baseURI = doc->GetBaseURI();
690 }
692 // otherwise we get from the doc's principal
693 if (!baseURI) {
694 rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
695 NS_ENSURE_SUCCESS(rv, rv);
696 }
698 NS_ENSURE_STATE(baseURI);
700 baseURI.forget(aBaseURI);
701 return NS_OK;
702 }
704 nsresult
705 EventSource::SetupHttpChannel()
706 {
707 mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
709 /* set the http request headers */
711 mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
712 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM), false);
714 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
716 if (!mLastEventID.IsEmpty()) {
717 mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"),
718 NS_ConvertUTF16toUTF8(mLastEventID), false);
719 }
721 nsCOMPtr<nsIURI> codebase;
722 nsresult rv = GetBaseURI(getter_AddRefs(codebase));
723 if (NS_SUCCEEDED(rv)) {
724 rv = mHttpChannel->SetReferrer(codebase);
725 NS_ENSURE_SUCCESS(rv, rv);
726 }
728 return NS_OK;
729 }
731 nsresult
732 EventSource::InitChannelAndRequestEventSource()
733 {
734 if (mReadyState == CLOSED) {
735 return NS_ERROR_ABORT;
736 }
738 // eventsource validation
740 if (!CheckCanRequestSrc()) {
741 DispatchFailConnection();
742 return NS_ERROR_DOM_SECURITY_ERR;
743 }
745 nsLoadFlags loadFlags;
746 loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE;
748 // get Content Security Policy from principal to pass into channel
749 nsCOMPtr<nsIChannelPolicy> channelPolicy;
750 nsCOMPtr<nsIContentSecurityPolicy> csp;
751 nsresult rv = mPrincipal->GetCsp(getter_AddRefs(csp));
752 NS_ENSURE_SUCCESS(rv, rv);
753 if (csp) {
754 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
755 channelPolicy->SetContentSecurityPolicy(csp);
756 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_DATAREQUEST);
757 }
759 nsCOMPtr<nsIChannel> channel;
760 rv = NS_NewChannel(getter_AddRefs(channel), mSrc, nullptr, mLoadGroup,
761 nullptr, loadFlags, channelPolicy);
762 NS_ENSURE_SUCCESS(rv, rv);
764 mHttpChannel = do_QueryInterface(channel);
765 NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
767 rv = SetupHttpChannel();
768 NS_ENSURE_SUCCESS(rv, rv);
770 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
771 mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
772 if (notificationCallbacks != this) {
773 mNotificationCallbacks = notificationCallbacks;
774 mHttpChannel->SetNotificationCallbacks(this);
775 }
777 nsRefPtr<nsCORSListenerProxy> listener =
778 new nsCORSListenerProxy(this, mPrincipal, mWithCredentials);
779 rv = listener->Init(mHttpChannel);
780 NS_ENSURE_SUCCESS(rv, rv);
782 // Start reading from the channel
783 rv = mHttpChannel->AsyncOpen(listener, nullptr);
784 if (NS_SUCCEEDED(rv)) {
785 mWaitingForOnStopRequest = true;
786 }
787 return rv;
788 }
790 void
791 EventSource::AnnounceConnection()
792 {
793 if (mReadyState == CLOSED) {
794 return;
795 }
797 if (mReadyState != CONNECTING) {
798 NS_WARNING("Unexpected mReadyState!!!");
799 return;
800 }
802 // When a user agent is to announce the connection, the user agent must set
803 // the readyState attribute to OPEN and queue a task to fire a simple event
804 // named open at the EventSource object.
806 mReadyState = OPEN;
808 nsresult rv = CheckInnerWindowCorrectness();
809 if (NS_FAILED(rv)) {
810 return;
811 }
813 nsCOMPtr<nsIDOMEvent> event;
814 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
815 if (NS_FAILED(rv)) {
816 NS_WARNING("Failed to create the open event!!!");
817 return;
818 }
820 // it doesn't bubble, and it isn't cancelable
821 rv = event->InitEvent(NS_LITERAL_STRING("open"), false, false);
822 if (NS_FAILED(rv)) {
823 NS_WARNING("Failed to init the open event!!!");
824 return;
825 }
827 event->SetTrusted(true);
829 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
830 if (NS_FAILED(rv)) {
831 NS_WARNING("Failed to dispatch the open event!!!");
832 return;
833 }
834 }
836 nsresult
837 EventSource::ResetConnection()
838 {
839 if (mHttpChannel) {
840 mHttpChannel->Cancel(NS_ERROR_ABORT);
841 }
843 if (mUnicodeDecoder) {
844 mUnicodeDecoder->Reset();
845 }
846 mLastConvertionResult = NS_OK;
848 mHttpChannel = nullptr;
849 mNotificationCallbacks = nullptr;
850 mChannelEventSink = nullptr;
851 mStatus = PARSE_STATE_OFF;
852 mRedirectCallback = nullptr;
853 mNewRedirectChannel = nullptr;
855 mReadyState = CONNECTING;
857 return NS_OK;
858 }
860 void
861 EventSource::ReestablishConnection()
862 {
863 if (mReadyState == CLOSED) {
864 return;
865 }
867 if (mReadyState != OPEN) {
868 NS_WARNING("Unexpected mReadyState!!!");
869 return;
870 }
872 nsresult rv = ResetConnection();
873 if (NS_FAILED(rv)) {
874 NS_WARNING("Failed to reset the connection!!!");
875 return;
876 }
878 rv = CheckInnerWindowCorrectness();
879 if (NS_FAILED(rv)) {
880 return;
881 }
883 nsCOMPtr<nsIDOMEvent> event;
884 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
885 if (NS_FAILED(rv)) {
886 NS_WARNING("Failed to create the error event!!!");
887 return;
888 }
890 // it doesn't bubble, and it isn't cancelable
891 rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false);
892 if (NS_FAILED(rv)) {
893 NS_WARNING("Failed to init the error event!!!");
894 return;
895 }
897 event->SetTrusted(true);
899 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
900 if (NS_FAILED(rv)) {
901 NS_WARNING("Failed to dispatch the error event!!!");
902 return;
903 }
905 rv = SetReconnectionTimeout();
906 if (NS_FAILED(rv)) {
907 NS_WARNING("Failed to set the timeout for reestablishing the connection!!!");
908 return;
909 }
910 }
912 nsresult
913 EventSource::SetReconnectionTimeout()
914 {
915 if (mReadyState == CLOSED) {
916 return NS_ERROR_ABORT;
917 }
919 // the timer will be used whenever the requests are going finished.
920 if (!mTimer) {
921 mTimer = do_CreateInstance("@mozilla.org/timer;1");
922 NS_ENSURE_STATE(mTimer);
923 }
925 nsresult rv = mTimer->InitWithFuncCallback(TimerCallback, this,
926 mReconnectionTime,
927 nsITimer::TYPE_ONE_SHOT);
928 NS_ENSURE_SUCCESS(rv, rv);
930 return NS_OK;
931 }
933 nsresult
934 EventSource::PrintErrorOnConsole(const char *aBundleURI,
935 const char16_t *aError,
936 const char16_t **aFormatStrings,
937 uint32_t aFormatStringsLen)
938 {
939 nsCOMPtr<nsIStringBundleService> bundleService =
940 mozilla::services::GetStringBundleService();
941 NS_ENSURE_STATE(bundleService);
943 nsCOMPtr<nsIStringBundle> strBundle;
944 nsresult rv =
945 bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
946 NS_ENSURE_SUCCESS(rv, rv);
948 nsCOMPtr<nsIConsoleService> console(
949 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
950 NS_ENSURE_SUCCESS(rv, rv);
952 nsCOMPtr<nsIScriptError> errObj(
953 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
954 NS_ENSURE_SUCCESS(rv, rv);
956 // Localize the error message
957 nsXPIDLString message;
958 if (aFormatStrings) {
959 rv = strBundle->FormatStringFromName(aError, aFormatStrings,
960 aFormatStringsLen,
961 getter_Copies(message));
962 } else {
963 rv = strBundle->GetStringFromName(aError, getter_Copies(message));
964 }
965 NS_ENSURE_SUCCESS(rv, rv);
967 rv = errObj->InitWithWindowID(message,
968 mScriptFile,
969 EmptyString(),
970 mScriptLine, 0,
971 nsIScriptError::errorFlag,
972 "Event Source", mInnerWindowID);
973 NS_ENSURE_SUCCESS(rv, rv);
975 // print the error message directly to the JS console
976 rv = console->LogMessage(errObj);
977 NS_ENSURE_SUCCESS(rv, rv);
979 return NS_OK;
980 }
982 nsresult
983 EventSource::ConsoleError()
984 {
985 nsAutoCString targetSpec;
986 nsresult rv = mSrc->GetSpec(targetSpec);
987 NS_ENSURE_SUCCESS(rv, rv);
989 NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
990 const char16_t *formatStrings[] = { specUTF16.get() };
992 if (mReadyState == CONNECTING && !mInterrupted) {
993 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
994 MOZ_UTF16("connectionFailure"),
995 formatStrings, ArrayLength(formatStrings));
996 } else {
997 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
998 MOZ_UTF16("netInterrupt"),
999 formatStrings, ArrayLength(formatStrings));
1000 }
1001 NS_ENSURE_SUCCESS(rv, rv);
1003 return NS_OK;
1004 }
1006 nsresult
1007 EventSource::DispatchFailConnection()
1008 {
1009 nsCOMPtr<nsIRunnable> event =
1010 NS_NewRunnableMethod(this, &EventSource::FailConnection);
1011 NS_ENSURE_STATE(event);
1013 return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1014 }
1016 void
1017 EventSource::FailConnection()
1018 {
1019 if (mReadyState == CLOSED) {
1020 return;
1021 }
1023 nsresult rv = ConsoleError();
1024 if (NS_FAILED(rv)) {
1025 NS_WARNING("Failed to print to the console error");
1026 }
1028 // When a user agent is to fail the connection, the user agent must set the
1029 // readyState attribute to CLOSED and queue a task to fire a simple event
1030 // named error at the EventSource object.
1032 Close(); // it sets mReadyState to CLOSED
1034 rv = CheckInnerWindowCorrectness();
1035 if (NS_FAILED(rv)) {
1036 return;
1037 }
1039 nsCOMPtr<nsIDOMEvent> event;
1040 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
1041 if (NS_FAILED(rv)) {
1042 NS_WARNING("Failed to create the error event!!!");
1043 return;
1044 }
1046 // it doesn't bubble, and it isn't cancelable
1047 rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false);
1048 if (NS_FAILED(rv)) {
1049 NS_WARNING("Failed to init the error event!!!");
1050 return;
1051 }
1053 event->SetTrusted(true);
1055 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
1056 if (NS_FAILED(rv)) {
1057 NS_WARNING("Failed to dispatch the error event!!!");
1058 return;
1059 }
1060 }
1062 bool
1063 EventSource::CheckCanRequestSrc(nsIURI* aSrc)
1064 {
1065 if (mReadyState == CLOSED) {
1066 return false;
1067 }
1069 bool isValidURI = false;
1070 bool isValidContentLoadPolicy = false;
1071 bool isValidProtocol = false;
1073 nsCOMPtr<nsIURI> srcToTest = aSrc ? aSrc : mSrc.get();
1074 NS_ENSURE_TRUE(srcToTest, false);
1076 uint32_t aCheckURIFlags =
1077 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
1078 nsIScriptSecurityManager::DISALLOW_SCRIPT;
1080 nsresult rv = nsContentUtils::GetSecurityManager()->
1081 CheckLoadURIWithPrincipal(mPrincipal,
1082 srcToTest,
1083 aCheckURIFlags);
1084 isValidURI = NS_SUCCEEDED(rv);
1086 // After the security manager, the content-policy check
1088 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1089 nsCOMPtr<nsIDocument> doc =
1090 nsContentUtils::GetDocumentFromScriptContext(sc);
1092 // mScriptContext should be initialized because of GetBaseURI() above.
1093 // Still need to consider the case that doc is nullptr however.
1094 rv = CheckInnerWindowCorrectness();
1095 NS_ENSURE_SUCCESS(rv, false);
1096 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1097 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_DATAREQUEST,
1098 srcToTest,
1099 mPrincipal,
1100 doc,
1101 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM),
1102 nullptr, // extra
1103 &shouldLoad,
1104 nsContentUtils::GetContentPolicy(),
1105 nsContentUtils::GetSecurityManager());
1106 isValidContentLoadPolicy = NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
1108 nsAutoCString targetURIScheme;
1109 rv = srcToTest->GetScheme(targetURIScheme);
1110 if (NS_SUCCEEDED(rv)) {
1111 // We only have the http support for now
1112 isValidProtocol = targetURIScheme.EqualsLiteral("http") ||
1113 targetURIScheme.EqualsLiteral("https");
1114 }
1116 return isValidURI && isValidContentLoadPolicy && isValidProtocol;
1117 }
1119 // static
1120 void
1121 EventSource::TimerCallback(nsITimer* aTimer, void* aClosure)
1122 {
1123 nsRefPtr<EventSource> thisObject = static_cast<EventSource*>(aClosure);
1125 if (thisObject->mReadyState == CLOSED) {
1126 return;
1127 }
1129 NS_PRECONDITION(!thisObject->mHttpChannel,
1130 "the channel hasn't been cancelled!!");
1132 if (!thisObject->mFrozen) {
1133 nsresult rv = thisObject->InitChannelAndRequestEventSource();
1134 if (NS_FAILED(rv)) {
1135 NS_WARNING("thisObject->InitChannelAndRequestEventSource() failed");
1136 return;
1137 }
1138 }
1139 }
1141 nsresult
1142 EventSource::Thaw()
1143 {
1144 if (mReadyState == CLOSED || !mFrozen) {
1145 return NS_OK;
1146 }
1148 NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
1150 mFrozen = false;
1151 nsresult rv;
1152 if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) {
1153 nsCOMPtr<nsIRunnable> event =
1154 NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents);
1155 NS_ENSURE_STATE(event);
1157 mGoingToDispatchAllMessages = true;
1159 rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1160 NS_ENSURE_SUCCESS(rv, rv);
1161 }
1163 rv = InitChannelAndRequestEventSource();
1164 NS_ENSURE_SUCCESS(rv, rv);
1166 return NS_OK;
1167 }
1169 nsresult
1170 EventSource::Freeze()
1171 {
1172 if (mReadyState == CLOSED || mFrozen) {
1173 return NS_OK;
1174 }
1176 NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
1177 mFrozen = true;
1178 return NS_OK;
1179 }
1181 nsresult
1182 EventSource::DispatchCurrentMessageEvent()
1183 {
1184 nsAutoPtr<Message> message(new Message());
1185 *message = mCurrentMessage;
1187 ClearFields();
1189 if (message->mData.IsEmpty()) {
1190 return NS_OK;
1191 }
1193 // removes the trailing LF from mData
1194 NS_ASSERTION(message->mData.CharAt(message->mData.Length() - 1) == LF_CHAR,
1195 "Invalid trailing character! LF was expected instead.");
1196 message->mData.SetLength(message->mData.Length() - 1);
1198 if (message->mEventName.IsEmpty()) {
1199 message->mEventName.AssignLiteral("message");
1200 }
1202 if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) {
1203 message->mLastEventID.Assign(mLastEventID);
1204 }
1206 int32_t sizeBefore = mMessagesToDispatch.GetSize();
1207 mMessagesToDispatch.Push(message.forget());
1208 NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1,
1209 NS_ERROR_OUT_OF_MEMORY);
1212 if (!mGoingToDispatchAllMessages) {
1213 nsCOMPtr<nsIRunnable> event =
1214 NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents);
1215 NS_ENSURE_STATE(event);
1217 mGoingToDispatchAllMessages = true;
1219 return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1220 }
1222 return NS_OK;
1223 }
1225 void
1226 EventSource::DispatchAllMessageEvents()
1227 {
1228 if (mReadyState == CLOSED || mFrozen) {
1229 return;
1230 }
1232 mGoingToDispatchAllMessages = false;
1234 nsresult rv = CheckInnerWindowCorrectness();
1235 if (NS_FAILED(rv)) {
1236 return;
1237 }
1239 // Let's play get the JSContext
1240 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
1241 NS_ENSURE_TRUE_VOID(sgo);
1243 nsIScriptContext* scriptContext = sgo->GetContext();
1244 NS_ENSURE_TRUE_VOID(scriptContext);
1246 AutoPushJSContext cx(scriptContext->GetNativeContext());
1247 NS_ENSURE_TRUE_VOID(cx);
1249 while (mMessagesToDispatch.GetSize() > 0) {
1250 nsAutoPtr<Message>
1251 message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
1253 // Now we can turn our string into a jsval
1254 JS::Rooted<JS::Value> jsData(cx);
1255 {
1256 JSString* jsString;
1257 jsString = JS_NewUCStringCopyN(cx,
1258 message->mData.get(),
1259 message->mData.Length());
1260 NS_ENSURE_TRUE_VOID(jsString);
1262 jsData = STRING_TO_JSVAL(jsString);
1263 }
1265 // create an event that uses the MessageEvent interface,
1266 // which does not bubble, is not cancelable, and has no default action
1268 nsCOMPtr<nsIDOMEvent> event;
1269 rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr);
1270 if (NS_FAILED(rv)) {
1271 NS_WARNING("Failed to create the message event!!!");
1272 return;
1273 }
1275 nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
1276 rv = messageEvent->InitMessageEvent(message->mEventName,
1277 false, false,
1278 jsData,
1279 mOrigin,
1280 message->mLastEventID, nullptr);
1281 if (NS_FAILED(rv)) {
1282 NS_WARNING("Failed to init the message event!!!");
1283 return;
1284 }
1286 messageEvent->SetTrusted(true);
1288 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
1289 if (NS_FAILED(rv)) {
1290 NS_WARNING("Failed to dispatch the message event!!!");
1291 return;
1292 }
1294 mLastEventID.Assign(message->mLastEventID);
1295 }
1296 }
1298 nsresult
1299 EventSource::ClearFields()
1300 {
1301 // mLastEventID and mReconnectionTime must be cached
1303 mCurrentMessage.mEventName.Truncate();
1304 mCurrentMessage.mLastEventID.Truncate();
1305 mCurrentMessage.mData.Truncate();
1307 mLastFieldName.Truncate();
1308 mLastFieldValue.Truncate();
1310 return NS_OK;
1311 }
1313 nsresult
1314 EventSource::SetFieldAndClear()
1315 {
1316 if (mLastFieldName.IsEmpty()) {
1317 mLastFieldValue.Truncate();
1318 return NS_OK;
1319 }
1321 char16_t first_char;
1322 first_char = mLastFieldName.CharAt(0);
1324 switch (first_char) // with no case folding performed
1325 {
1326 case char16_t('d'):
1327 if (mLastFieldName.EqualsLiteral("data")) {
1328 // If the field name is "data" append the field value to the data
1329 // buffer, then append a single U+000A LINE FEED (LF) character
1330 // to the data buffer.
1331 mCurrentMessage.mData.Append(mLastFieldValue);
1332 mCurrentMessage.mData.Append(LF_CHAR);
1333 }
1334 break;
1336 case char16_t('e'):
1337 if (mLastFieldName.EqualsLiteral("event")) {
1338 mCurrentMessage.mEventName.Assign(mLastFieldValue);
1339 }
1340 break;
1342 case char16_t('i'):
1343 if (mLastFieldName.EqualsLiteral("id")) {
1344 mCurrentMessage.mLastEventID.Assign(mLastFieldValue);
1345 }
1346 break;
1348 case char16_t('r'):
1349 if (mLastFieldName.EqualsLiteral("retry")) {
1350 uint32_t newValue=0;
1351 uint32_t i = 0; // we must ensure that there are only digits
1352 bool assign = true;
1353 for (i = 0; i < mLastFieldValue.Length(); ++i) {
1354 if (mLastFieldValue.CharAt(i) < (char16_t)'0' ||
1355 mLastFieldValue.CharAt(i) > (char16_t)'9') {
1356 assign = false;
1357 break;
1358 }
1359 newValue = newValue*10 +
1360 (((uint32_t)mLastFieldValue.CharAt(i))-
1361 ((uint32_t)((char16_t)'0')));
1362 }
1364 if (assign) {
1365 if (newValue < MIN_RECONNECTION_TIME_VALUE) {
1366 mReconnectionTime = MIN_RECONNECTION_TIME_VALUE;
1367 } else if (newValue > MAX_RECONNECTION_TIME_VALUE) {
1368 mReconnectionTime = MAX_RECONNECTION_TIME_VALUE;
1369 } else {
1370 mReconnectionTime = newValue;
1371 }
1372 }
1373 break;
1374 }
1375 break;
1376 }
1378 mLastFieldName.Truncate();
1379 mLastFieldValue.Truncate();
1381 return NS_OK;
1382 }
1384 nsresult
1385 EventSource::CheckHealthOfRequestCallback(nsIRequest *aRequestCallback)
1386 {
1387 // check if we have been closed or if the request has been canceled
1388 // or if we have been frozen
1389 if (mReadyState == CLOSED || !mHttpChannel ||
1390 mFrozen || mErrorLoadOnRedirect) {
1391 return NS_ERROR_ABORT;
1392 }
1394 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequestCallback);
1395 NS_ENSURE_STATE(httpChannel);
1397 if (httpChannel != mHttpChannel) {
1398 NS_WARNING("wrong channel from request callback");
1399 return NS_ERROR_ABORT;
1400 }
1402 return NS_OK;
1403 }
1405 nsresult
1406 EventSource::ParseCharacter(char16_t aChr)
1407 {
1408 nsresult rv;
1410 if (mReadyState == CLOSED) {
1411 return NS_ERROR_ABORT;
1412 }
1414 switch (mStatus)
1415 {
1416 case PARSE_STATE_OFF:
1417 NS_ERROR("Invalid state");
1418 return NS_ERROR_FAILURE;
1419 break;
1421 case PARSE_STATE_BEGIN_OF_STREAM:
1422 if (aChr == BOM_CHAR) {
1423 mStatus = PARSE_STATE_BOM_WAS_READ; // ignore it
1424 } else if (aChr == CR_CHAR) {
1425 mStatus = PARSE_STATE_CR_CHAR;
1426 } else if (aChr == LF_CHAR) {
1427 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1428 } else if (aChr == COLON_CHAR) {
1429 mStatus = PARSE_STATE_COMMENT;
1430 } else {
1431 mLastFieldName += aChr;
1432 mStatus = PARSE_STATE_FIELD_NAME;
1433 }
1435 break;
1437 case PARSE_STATE_BOM_WAS_READ:
1438 if (aChr == CR_CHAR) {
1439 mStatus = PARSE_STATE_CR_CHAR;
1440 } else if (aChr == LF_CHAR) {
1441 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1442 } else if (aChr == COLON_CHAR) {
1443 mStatus = PARSE_STATE_COMMENT;
1444 } else {
1445 mLastFieldName += aChr;
1446 mStatus = PARSE_STATE_FIELD_NAME;
1447 }
1448 break;
1450 case PARSE_STATE_CR_CHAR:
1451 if (aChr == CR_CHAR) {
1452 rv = DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1453 NS_ENSURE_SUCCESS(rv, rv);
1454 } else if (aChr == LF_CHAR) {
1455 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1456 } else if (aChr == COLON_CHAR) {
1457 mStatus = PARSE_STATE_COMMENT;
1458 } else {
1459 mLastFieldName += aChr;
1460 mStatus = PARSE_STATE_FIELD_NAME;
1461 }
1463 break;
1465 case PARSE_STATE_COMMENT:
1466 if (aChr == CR_CHAR) {
1467 mStatus = PARSE_STATE_CR_CHAR;
1468 } else if (aChr == LF_CHAR) {
1469 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1470 }
1472 break;
1474 case PARSE_STATE_FIELD_NAME:
1475 if (aChr == CR_CHAR) {
1476 rv = SetFieldAndClear();
1477 NS_ENSURE_SUCCESS(rv, rv);
1479 mStatus = PARSE_STATE_CR_CHAR;
1480 } else if (aChr == LF_CHAR) {
1481 rv = SetFieldAndClear();
1482 NS_ENSURE_SUCCESS(rv, rv);
1484 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1485 } else if (aChr == COLON_CHAR) {
1486 mStatus = PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE;
1487 } else {
1488 mLastFieldName += aChr;
1489 }
1491 break;
1493 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE:
1494 if (aChr == CR_CHAR) {
1495 rv = SetFieldAndClear();
1496 NS_ENSURE_SUCCESS(rv, rv);
1498 mStatus = PARSE_STATE_CR_CHAR;
1499 } else if (aChr == LF_CHAR) {
1500 rv = SetFieldAndClear();
1501 NS_ENSURE_SUCCESS(rv, rv);
1503 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1504 } else if (aChr == SPACE_CHAR) {
1505 mStatus = PARSE_STATE_FIELD_VALUE;
1506 } else {
1507 mLastFieldValue += aChr;
1508 mStatus = PARSE_STATE_FIELD_VALUE;
1509 }
1511 break;
1513 case PARSE_STATE_FIELD_VALUE:
1514 if (aChr == CR_CHAR) {
1515 rv = SetFieldAndClear();
1516 NS_ENSURE_SUCCESS(rv, rv);
1518 mStatus = PARSE_STATE_CR_CHAR;
1519 } else if (aChr == LF_CHAR) {
1520 rv = SetFieldAndClear();
1521 NS_ENSURE_SUCCESS(rv, rv);
1523 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1524 } else {
1525 mLastFieldValue += aChr;
1526 }
1528 break;
1530 case PARSE_STATE_BEGIN_OF_LINE:
1531 if (aChr == CR_CHAR) {
1532 rv = DispatchCurrentMessageEvent(); // there is an empty line
1533 NS_ENSURE_SUCCESS(rv, rv);
1535 mStatus = PARSE_STATE_CR_CHAR;
1536 } else if (aChr == LF_CHAR) {
1537 rv = DispatchCurrentMessageEvent(); // there is an empty line
1538 NS_ENSURE_SUCCESS(rv, rv);
1540 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1541 } else if (aChr == COLON_CHAR) {
1542 mStatus = PARSE_STATE_COMMENT;
1543 } else {
1544 mLastFieldName += aChr;
1545 mStatus = PARSE_STATE_FIELD_NAME;
1546 }
1548 break;
1549 }
1551 return NS_OK;
1552 }
1554 } // namespace dom
1555 } // namespace mozilla