content/base/src/EventSource.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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));
  1001   NS_ENSURE_SUCCESS(rv, rv);
  1003   return NS_OK;
  1006 nsresult
  1007 EventSource::DispatchFailConnection()
  1009   nsCOMPtr<nsIRunnable> event =
  1010     NS_NewRunnableMethod(this, &EventSource::FailConnection);
  1011   NS_ENSURE_STATE(event);
  1013   return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
  1016 void
  1017 EventSource::FailConnection()
  1019   if (mReadyState == CLOSED) {
  1020     return;
  1023   nsresult rv = ConsoleError();
  1024   if (NS_FAILED(rv)) {
  1025     NS_WARNING("Failed to print to the console error");
  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;
  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;
  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;
  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;
  1062 bool
  1063 EventSource::CheckCanRequestSrc(nsIURI* aSrc)
  1065   if (mReadyState == CLOSED) {
  1066     return false;
  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");
  1116   return isValidURI && isValidContentLoadPolicy && isValidProtocol;
  1119 // static
  1120 void
  1121 EventSource::TimerCallback(nsITimer* aTimer, void* aClosure)
  1123   nsRefPtr<EventSource> thisObject = static_cast<EventSource*>(aClosure);
  1125   if (thisObject->mReadyState == CLOSED) {
  1126     return;
  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;
  1141 nsresult
  1142 EventSource::Thaw()
  1144   if (mReadyState == CLOSED || !mFrozen) {
  1145     return NS_OK;
  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);
  1163   rv = InitChannelAndRequestEventSource();
  1164   NS_ENSURE_SUCCESS(rv, rv);
  1166   return NS_OK;
  1169 nsresult
  1170 EventSource::Freeze()
  1172   if (mReadyState == CLOSED || mFrozen) {
  1173     return NS_OK;
  1176   NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
  1177   mFrozen = true;
  1178   return NS_OK;
  1181 nsresult
  1182 EventSource::DispatchCurrentMessageEvent()
  1184   nsAutoPtr<Message> message(new Message());
  1185   *message = mCurrentMessage;
  1187   ClearFields();
  1189   if (message->mData.IsEmpty()) {
  1190     return NS_OK;
  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");
  1202   if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) {
  1203     message->mLastEventID.Assign(mLastEventID);
  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);
  1222   return NS_OK;
  1225 void
  1226 EventSource::DispatchAllMessageEvents()
  1228   if (mReadyState == CLOSED || mFrozen) {
  1229     return;
  1232   mGoingToDispatchAllMessages = false;
  1234   nsresult rv = CheckInnerWindowCorrectness();
  1235   if (NS_FAILED(rv)) {
  1236     return;
  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);
  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);
  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;
  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;
  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;
  1294     mLastEventID.Assign(message->mLastEventID);
  1298 nsresult
  1299 EventSource::ClearFields()
  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;
  1313 nsresult
  1314 EventSource::SetFieldAndClear()
  1316   if (mLastFieldName.IsEmpty()) {
  1317     mLastFieldValue.Truncate();
  1318     return NS_OK;
  1321   char16_t first_char;
  1322   first_char = mLastFieldName.CharAt(0);
  1324   switch (first_char)  // with no case folding performed
  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);
  1334       break;
  1336     case char16_t('e'):
  1337       if (mLastFieldName.EqualsLiteral("event")) {
  1338         mCurrentMessage.mEventName.Assign(mLastFieldValue);
  1340       break;
  1342     case char16_t('i'):
  1343       if (mLastFieldName.EqualsLiteral("id")) {
  1344         mCurrentMessage.mLastEventID.Assign(mLastFieldValue);
  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;
  1359           newValue = newValue*10 +
  1360                      (((uint32_t)mLastFieldValue.CharAt(i))-
  1361                        ((uint32_t)((char16_t)'0')));
  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;
  1373         break;
  1375       break;
  1378   mLastFieldName.Truncate();
  1379   mLastFieldValue.Truncate();
  1381   return NS_OK;
  1384 nsresult
  1385 EventSource::CheckHealthOfRequestCallback(nsIRequest *aRequestCallback)
  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;
  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;
  1402   return NS_OK;
  1405 nsresult
  1406 EventSource::ParseCharacter(char16_t aChr)
  1408   nsresult rv;
  1410   if (mReadyState == CLOSED) {
  1411     return NS_ERROR_ABORT;
  1414   switch (mStatus)
  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;
  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;
  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;
  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;
  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;
  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;
  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;
  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;
  1548       break;
  1551   return NS_OK;
  1554 } // namespace dom
  1555 } // namespace mozilla

mercurial