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.)

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

mercurial