1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/EventSource.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1555 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/dom/EventSource.h" 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 +#include "mozilla/DebugOnly.h" 1.13 +#include "mozilla/DOMEventTargetHelper.h" 1.14 +#include "mozilla/dom/EventSourceBinding.h" 1.15 +#include "mozilla/dom/MessageEvent.h" 1.16 + 1.17 +#include "js/OldDebugAPI.h" 1.18 +#include "nsNetUtil.h" 1.19 +#include "nsMimeTypes.h" 1.20 +#include "nsIPromptFactory.h" 1.21 +#include "nsIWindowWatcher.h" 1.22 +#include "nsPresContext.h" 1.23 +#include "nsContentPolicyUtils.h" 1.24 +#include "nsIStringBundle.h" 1.25 +#include "nsIConsoleService.h" 1.26 +#include "nsIObserverService.h" 1.27 +#include "nsIScriptObjectPrincipal.h" 1.28 +#include "nsJSUtils.h" 1.29 +#include "nsIAsyncVerifyRedirectCallback.h" 1.30 +#include "nsIScriptError.h" 1.31 +#include "mozilla/dom/EncodingUtils.h" 1.32 +#include "nsIChannelPolicy.h" 1.33 +#include "nsIContentSecurityPolicy.h" 1.34 +#include "nsContentUtils.h" 1.35 +#include "nsCxPusher.h" 1.36 +#include "mozilla/Preferences.h" 1.37 +#include "xpcpublic.h" 1.38 +#include "nsCrossSiteListenerProxy.h" 1.39 +#include "nsWrapperCacheInlines.h" 1.40 +#include "mozilla/Attributes.h" 1.41 +#include "nsError.h" 1.42 + 1.43 +namespace mozilla { 1.44 +namespace dom { 1.45 + 1.46 +#define REPLACEMENT_CHAR (char16_t)0xFFFD 1.47 +#define BOM_CHAR (char16_t)0xFEFF 1.48 +#define SPACE_CHAR (char16_t)0x0020 1.49 +#define CR_CHAR (char16_t)0x000D 1.50 +#define LF_CHAR (char16_t)0x000A 1.51 +#define COLON_CHAR (char16_t)0x003A 1.52 + 1.53 +#define DEFAULT_BUFFER_SIZE 4096 1.54 + 1.55 +// Reconnection time related values in milliseconds. The default one is equal 1.56 +// to the default value of the pref dom.server-events.default-reconnection-time 1.57 +#define MIN_RECONNECTION_TIME_VALUE 500 1.58 +#define DEFAULT_RECONNECTION_TIME_VALUE 5000 1.59 +#define MAX_RECONNECTION_TIME_VALUE PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT) 1.60 + 1.61 +EventSource::EventSource(nsPIDOMWindow* aOwnerWindow) : 1.62 + DOMEventTargetHelper(aOwnerWindow), 1.63 + mStatus(PARSE_STATE_OFF), 1.64 + mFrozen(false), 1.65 + mErrorLoadOnRedirect(false), 1.66 + mGoingToDispatchAllMessages(false), 1.67 + mWithCredentials(false), 1.68 + mWaitingForOnStopRequest(false), 1.69 + mInterrupted(false), 1.70 + mLastConvertionResult(NS_OK), 1.71 + mReadyState(CONNECTING), 1.72 + mScriptLine(0), 1.73 + mInnerWindowID(0) 1.74 +{ 1.75 +} 1.76 + 1.77 +EventSource::~EventSource() 1.78 +{ 1.79 + Close(); 1.80 +} 1.81 + 1.82 +//----------------------------------------------------------------------------- 1.83 +// EventSource::nsISupports 1.84 +//----------------------------------------------------------------------------- 1.85 + 1.86 +NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource) 1.87 + 1.88 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(EventSource) 1.89 + bool isBlack = tmp->IsBlack(); 1.90 + if (isBlack || tmp->mWaitingForOnStopRequest) { 1.91 + if (tmp->mListenerManager) { 1.92 + tmp->mListenerManager->MarkForCC(); 1.93 + } 1.94 + if (!isBlack && tmp->PreservingWrapper()) { 1.95 + // This marks the wrapper black. 1.96 + tmp->GetWrapper(); 1.97 + } 1.98 + return true; 1.99 + } 1.100 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 1.101 + 1.102 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(EventSource) 1.103 + return tmp->IsBlack(); 1.104 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 1.105 + 1.106 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(EventSource) 1.107 + return tmp->IsBlack(); 1.108 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 1.109 + 1.110 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(EventSource, 1.111 + DOMEventTargetHelper) 1.112 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.113 + 1.114 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource, 1.115 + DOMEventTargetHelper) 1.116 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrc) 1.117 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks) 1.118 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadGroup) 1.119 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink) 1.120 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHttpChannel) 1.121 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer) 1.122 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnicodeDecoder) 1.123 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.124 + 1.125 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource, 1.126 + DOMEventTargetHelper) 1.127 + tmp->Close(); 1.128 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.129 + 1.130 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(EventSource) 1.131 + NS_INTERFACE_MAP_ENTRY(nsIObserver) 1.132 + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 1.133 + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 1.134 + NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) 1.135 + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 1.136 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.137 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.138 + 1.139 +NS_IMPL_ADDREF_INHERITED(EventSource, DOMEventTargetHelper) 1.140 +NS_IMPL_RELEASE_INHERITED(EventSource, DOMEventTargetHelper) 1.141 + 1.142 +void 1.143 +EventSource::DisconnectFromOwner() 1.144 +{ 1.145 + DOMEventTargetHelper::DisconnectFromOwner(); 1.146 + Close(); 1.147 +} 1.148 + 1.149 +void 1.150 +EventSource::Close() 1.151 +{ 1.152 + if (mReadyState == CLOSED) { 1.153 + return; 1.154 + } 1.155 + 1.156 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.157 + if (os) { 1.158 + os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC); 1.159 + os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC); 1.160 + os->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC); 1.161 + } 1.162 + 1.163 + if (mTimer) { 1.164 + mTimer->Cancel(); 1.165 + mTimer = nullptr; 1.166 + } 1.167 + 1.168 + ResetConnection(); 1.169 + 1.170 + ClearFields(); 1.171 + 1.172 + while (mMessagesToDispatch.GetSize() != 0) { 1.173 + delete static_cast<Message*>(mMessagesToDispatch.PopFront()); 1.174 + } 1.175 + 1.176 + mSrc = nullptr; 1.177 + mFrozen = false; 1.178 + 1.179 + mUnicodeDecoder = nullptr; 1.180 + 1.181 + mReadyState = CLOSED; 1.182 +} 1.183 + 1.184 +nsresult 1.185 +EventSource::Init(nsISupports* aOwner, 1.186 + const nsAString& aURL, 1.187 + bool aWithCredentials) 1.188 +{ 1.189 + if (mReadyState != CONNECTING || !PrefEnabled()) { 1.190 + return NS_ERROR_DOM_SECURITY_ERR; 1.191 + } 1.192 + 1.193 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner); 1.194 + NS_ENSURE_STATE(sgo); 1.195 + nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext(); 1.196 + NS_ENSURE_STATE(scriptContext); 1.197 + 1.198 + nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = 1.199 + do_QueryInterface(aOwner); 1.200 + NS_ENSURE_STATE(scriptPrincipal); 1.201 + nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal(); 1.202 + NS_ENSURE_STATE(principal); 1.203 + 1.204 + mPrincipal = principal; 1.205 + mWithCredentials = aWithCredentials; 1.206 + 1.207 + // The conditional here is historical and not necessarily sane. 1.208 + if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) { 1.209 + const char *filename; 1.210 + if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) { 1.211 + mScriptFile.AssignASCII(filename); 1.212 + } 1.213 + 1.214 + mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx); 1.215 + } 1.216 + 1.217 + // Get the load group for the page. When requesting we'll add ourselves to it. 1.218 + // This way any pending requests will be automatically aborted if the user 1.219 + // leaves the page. 1.220 + nsresult rv; 1.221 + nsIScriptContext* sc = GetContextForEventHandlers(&rv); 1.222 + if (sc) { 1.223 + nsCOMPtr<nsIDocument> doc = 1.224 + nsContentUtils::GetDocumentFromScriptContext(sc); 1.225 + if (doc) { 1.226 + mLoadGroup = doc->GetDocumentLoadGroup(); 1.227 + } 1.228 + } 1.229 + 1.230 + // get the src 1.231 + nsCOMPtr<nsIURI> baseURI; 1.232 + rv = GetBaseURI(getter_AddRefs(baseURI)); 1.233 + NS_ENSURE_SUCCESS(rv, rv); 1.234 + 1.235 + nsCOMPtr<nsIURI> srcURI; 1.236 + rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI); 1.237 + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); 1.238 + 1.239 + // we observe when the window freezes and thaws 1.240 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.241 + NS_ENSURE_STATE(os); 1.242 + 1.243 + rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true); 1.244 + NS_ENSURE_SUCCESS(rv, rv); 1.245 + rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true); 1.246 + NS_ENSURE_SUCCESS(rv, rv); 1.247 + rv = os->AddObserver(this, DOM_WINDOW_THAWED_TOPIC, true); 1.248 + NS_ENSURE_SUCCESS(rv, rv); 1.249 + 1.250 + nsAutoString origin; 1.251 + rv = nsContentUtils::GetUTFOrigin(srcURI, origin); 1.252 + NS_ENSURE_SUCCESS(rv, rv); 1.253 + 1.254 + nsAutoCString spec; 1.255 + rv = srcURI->GetSpec(spec); 1.256 + NS_ENSURE_SUCCESS(rv, rv); 1.257 + 1.258 + mOriginalURL = NS_ConvertUTF8toUTF16(spec); 1.259 + mSrc = srcURI; 1.260 + mOrigin = origin; 1.261 + 1.262 + mReconnectionTime = 1.263 + Preferences::GetInt("dom.server-events.default-reconnection-time", 1.264 + DEFAULT_RECONNECTION_TIME_VALUE); 1.265 + 1.266 + mUnicodeDecoder = EncodingUtils::DecoderForEncoding("UTF-8"); 1.267 + 1.268 + // the constructor should throw a SYNTAX_ERROR only if it fails resolving the 1.269 + // url parameter, so we don't care about the InitChannelAndRequestEventSource 1.270 + // result. 1.271 + InitChannelAndRequestEventSource(); 1.272 + 1.273 + return NS_OK; 1.274 +} 1.275 + 1.276 +/* virtual */ JSObject* 1.277 +EventSource::WrapObject(JSContext* aCx) 1.278 +{ 1.279 + return EventSourceBinding::Wrap(aCx, this); 1.280 +} 1.281 + 1.282 +/* static */ already_AddRefed<EventSource> 1.283 +EventSource::Constructor(const GlobalObject& aGlobal, 1.284 + const nsAString& aURL, 1.285 + const EventSourceInit& aEventSourceInitDict, 1.286 + ErrorResult& aRv) 1.287 +{ 1.288 + nsCOMPtr<nsPIDOMWindow> ownerWindow = 1.289 + do_QueryInterface(aGlobal.GetAsSupports()); 1.290 + if (!ownerWindow) { 1.291 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.292 + return nullptr; 1.293 + } 1.294 + MOZ_ASSERT(ownerWindow->IsInnerWindow()); 1.295 + 1.296 + nsRefPtr<EventSource> eventSource = new EventSource(ownerWindow); 1.297 + aRv = eventSource->Init(aGlobal.GetAsSupports(), aURL, 1.298 + aEventSourceInitDict.mWithCredentials); 1.299 + return eventSource.forget(); 1.300 +} 1.301 + 1.302 +//----------------------------------------------------------------------------- 1.303 +// EventSource::nsIObserver 1.304 +//----------------------------------------------------------------------------- 1.305 + 1.306 +NS_IMETHODIMP 1.307 +EventSource::Observe(nsISupports* aSubject, 1.308 + const char* aTopic, 1.309 + const char16_t* aData) 1.310 +{ 1.311 + if (mReadyState == CLOSED) { 1.312 + return NS_OK; 1.313 + } 1.314 + 1.315 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject); 1.316 + if (!GetOwner() || window != GetOwner()) { 1.317 + return NS_OK; 1.318 + } 1.319 + 1.320 + DebugOnly<nsresult> rv; 1.321 + if (strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) { 1.322 + rv = Freeze(); 1.323 + NS_ASSERTION(NS_SUCCEEDED(rv), "Freeze() failed"); 1.324 + } else if (strcmp(aTopic, DOM_WINDOW_THAWED_TOPIC) == 0) { 1.325 + rv = Thaw(); 1.326 + NS_ASSERTION(NS_SUCCEEDED(rv), "Thaw() failed"); 1.327 + } else if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) { 1.328 + Close(); 1.329 + } 1.330 + 1.331 + return NS_OK; 1.332 +} 1.333 + 1.334 +//----------------------------------------------------------------------------- 1.335 +// EventSource::nsIStreamListener 1.336 +//----------------------------------------------------------------------------- 1.337 + 1.338 +NS_IMETHODIMP 1.339 +EventSource::OnStartRequest(nsIRequest *aRequest, 1.340 + nsISupports *ctxt) 1.341 +{ 1.342 + nsresult rv = CheckHealthOfRequestCallback(aRequest); 1.343 + NS_ENSURE_SUCCESS(rv, rv); 1.344 + 1.345 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv); 1.346 + NS_ENSURE_SUCCESS(rv, rv); 1.347 + 1.348 + bool requestSucceeded; 1.349 + rv = httpChannel->GetRequestSucceeded(&requestSucceeded); 1.350 + NS_ENSURE_SUCCESS(rv, rv); 1.351 + 1.352 + nsAutoCString contentType; 1.353 + rv = httpChannel->GetContentType(contentType); 1.354 + NS_ENSURE_SUCCESS(rv, rv); 1.355 + 1.356 + nsresult status; 1.357 + aRequest->GetStatus(&status); 1.358 + 1.359 + if (NS_FAILED(status) || !requestSucceeded || 1.360 + !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) { 1.361 + DispatchFailConnection(); 1.362 + return NS_ERROR_NOT_AVAILABLE; 1.363 + } 1.364 + 1.365 + uint32_t httpStatus; 1.366 + rv = httpChannel->GetResponseStatus(&httpStatus); 1.367 + NS_ENSURE_SUCCESS(rv, rv); 1.368 + 1.369 + if (httpStatus != 200) { 1.370 + mInterrupted = true; 1.371 + DispatchFailConnection(); 1.372 + return NS_ERROR_ABORT; 1.373 + } 1.374 + 1.375 + nsCOMPtr<nsIPrincipal> principal = mPrincipal; 1.376 + if (nsContentUtils::IsSystemPrincipal(principal)) { 1.377 + // Don't give this channel the system principal. 1.378 + principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); 1.379 + NS_ENSURE_SUCCESS(rv, rv); 1.380 + } 1.381 + rv = httpChannel->SetOwner(principal); 1.382 + NS_ENSURE_SUCCESS(rv, rv); 1.383 + 1.384 + nsCOMPtr<nsIRunnable> event = 1.385 + NS_NewRunnableMethod(this, &EventSource::AnnounceConnection); 1.386 + NS_ENSURE_STATE(event); 1.387 + 1.388 + rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.389 + NS_ENSURE_SUCCESS(rv, rv); 1.390 + 1.391 + mStatus = PARSE_STATE_BEGIN_OF_STREAM; 1.392 + 1.393 + return NS_OK; 1.394 +} 1.395 + 1.396 +// this method parses the characters as they become available instead of 1.397 +// buffering them. 1.398 +NS_METHOD 1.399 +EventSource::StreamReaderFunc(nsIInputStream *aInputStream, 1.400 + void *aClosure, 1.401 + const char *aFromRawSegment, 1.402 + uint32_t aToOffset, 1.403 + uint32_t aCount, 1.404 + uint32_t *aWriteCount) 1.405 +{ 1.406 + EventSource* thisObject = static_cast<EventSource*>(aClosure); 1.407 + if (!thisObject || !aWriteCount) { 1.408 + NS_WARNING("EventSource cannot read from stream: no aClosure or aWriteCount"); 1.409 + return NS_ERROR_FAILURE; 1.410 + } 1.411 + 1.412 + *aWriteCount = 0; 1.413 + 1.414 + int32_t srcCount, outCount; 1.415 + char16_t out[2]; 1.416 + nsresult rv; 1.417 + 1.418 + const char *p = aFromRawSegment, 1.419 + *end = aFromRawSegment + aCount; 1.420 + 1.421 + do { 1.422 + srcCount = aCount - (p - aFromRawSegment); 1.423 + outCount = 2; 1.424 + 1.425 + thisObject->mLastConvertionResult = 1.426 + thisObject->mUnicodeDecoder->Convert(p, &srcCount, out, &outCount); 1.427 + MOZ_ASSERT(thisObject->mLastConvertionResult != NS_ERROR_ILLEGAL_INPUT); 1.428 + 1.429 + for (int32_t i = 0; i < outCount; ++i) { 1.430 + rv = thisObject->ParseCharacter(out[i]); 1.431 + NS_ENSURE_SUCCESS(rv, rv); 1.432 + } 1.433 + p = p + srcCount; 1.434 + } while (p < end && 1.435 + thisObject->mLastConvertionResult != NS_PARTIAL_MORE_INPUT && 1.436 + thisObject->mLastConvertionResult != NS_OK); 1.437 + 1.438 + *aWriteCount = aCount; 1.439 + return NS_OK; 1.440 +} 1.441 + 1.442 +NS_IMETHODIMP 1.443 +EventSource::OnDataAvailable(nsIRequest *aRequest, 1.444 + nsISupports *aContext, 1.445 + nsIInputStream *aInputStream, 1.446 + uint64_t aOffset, 1.447 + uint32_t aCount) 1.448 +{ 1.449 + NS_ENSURE_ARG_POINTER(aInputStream); 1.450 + 1.451 + nsresult rv = CheckHealthOfRequestCallback(aRequest); 1.452 + NS_ENSURE_SUCCESS(rv, rv); 1.453 + 1.454 + uint32_t totalRead; 1.455 + return aInputStream->ReadSegments(EventSource::StreamReaderFunc, this, 1.456 + aCount, &totalRead); 1.457 +} 1.458 + 1.459 +NS_IMETHODIMP 1.460 +EventSource::OnStopRequest(nsIRequest *aRequest, 1.461 + nsISupports *aContext, 1.462 + nsresult aStatusCode) 1.463 +{ 1.464 + mWaitingForOnStopRequest = false; 1.465 + 1.466 + if (mReadyState == CLOSED) { 1.467 + return NS_ERROR_ABORT; 1.468 + } 1.469 + 1.470 + if (NS_FAILED(aStatusCode)) { 1.471 + DispatchFailConnection(); 1.472 + return aStatusCode; 1.473 + } 1.474 + 1.475 + nsresult rv; 1.476 + nsresult healthOfRequestResult = CheckHealthOfRequestCallback(aRequest); 1.477 + if (NS_SUCCEEDED(healthOfRequestResult) && 1.478 + mLastConvertionResult == NS_PARTIAL_MORE_INPUT) { 1.479 + // we had an incomplete UTF8 char at the end of the stream 1.480 + rv = ParseCharacter(REPLACEMENT_CHAR); 1.481 + NS_ENSURE_SUCCESS(rv, rv); 1.482 + } 1.483 + 1.484 + ClearFields(); 1.485 + 1.486 + nsCOMPtr<nsIRunnable> event = 1.487 + NS_NewRunnableMethod(this, &EventSource::ReestablishConnection); 1.488 + NS_ENSURE_STATE(event); 1.489 + 1.490 + rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.491 + NS_ENSURE_SUCCESS(rv, rv); 1.492 + 1.493 + return healthOfRequestResult; 1.494 +} 1.495 + 1.496 +/** 1.497 + * Simple helper class that just forwards the redirect callback back 1.498 + * to the EventSource. 1.499 + */ 1.500 +class AsyncVerifyRedirectCallbackFwr MOZ_FINAL : public nsIAsyncVerifyRedirectCallback 1.501 +{ 1.502 +public: 1.503 + AsyncVerifyRedirectCallbackFwr(EventSource* aEventsource) 1.504 + : mEventSource(aEventsource) 1.505 + { 1.506 + } 1.507 + 1.508 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.509 + NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr) 1.510 + 1.511 + // nsIAsyncVerifyRedirectCallback implementation 1.512 + NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult) 1.513 + { 1.514 + nsresult rv = mEventSource->OnRedirectVerifyCallback(aResult); 1.515 + if (NS_FAILED(rv)) { 1.516 + mEventSource->mErrorLoadOnRedirect = true; 1.517 + mEventSource->DispatchFailConnection(); 1.518 + } 1.519 + 1.520 + return NS_OK; 1.521 + } 1.522 + 1.523 +private: 1.524 + nsRefPtr<EventSource> mEventSource; 1.525 +}; 1.526 + 1.527 +NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr, mEventSource) 1.528 + 1.529 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr) 1.530 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.531 + NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) 1.532 +NS_INTERFACE_MAP_END 1.533 + 1.534 +NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr) 1.535 +NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr) 1.536 + 1.537 +//----------------------------------------------------------------------------- 1.538 +// EventSource::nsIChannelEventSink 1.539 +//----------------------------------------------------------------------------- 1.540 + 1.541 +NS_IMETHODIMP 1.542 +EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel, 1.543 + nsIChannel *aNewChannel, 1.544 + uint32_t aFlags, 1.545 + nsIAsyncVerifyRedirectCallback *aCallback) 1.546 +{ 1.547 + nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel); 1.548 + NS_PRECONDITION(aOldRequest, "Redirect from a null request?"); 1.549 + 1.550 + nsresult rv = CheckHealthOfRequestCallback(aOldRequest); 1.551 + NS_ENSURE_SUCCESS(rv, rv); 1.552 + 1.553 + NS_PRECONDITION(aNewChannel, "Redirect without a channel?"); 1.554 + 1.555 + nsCOMPtr<nsIURI> newURI; 1.556 + rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); 1.557 + NS_ENSURE_SUCCESS(rv, rv); 1.558 + 1.559 + if (!CheckCanRequestSrc(newURI)) { 1.560 + DispatchFailConnection(); 1.561 + return NS_ERROR_DOM_SECURITY_ERR; 1.562 + } 1.563 + 1.564 + // Prepare to receive callback 1.565 + mRedirectFlags = aFlags; 1.566 + mRedirectCallback = aCallback; 1.567 + mNewRedirectChannel = aNewChannel; 1.568 + 1.569 + if (mChannelEventSink) { 1.570 + nsRefPtr<AsyncVerifyRedirectCallbackFwr> fwd = 1.571 + new AsyncVerifyRedirectCallbackFwr(this); 1.572 + 1.573 + rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel, 1.574 + aNewChannel, 1.575 + aFlags, fwd); 1.576 + if (NS_FAILED(rv)) { 1.577 + mRedirectCallback = nullptr; 1.578 + mNewRedirectChannel = nullptr; 1.579 + mErrorLoadOnRedirect = true; 1.580 + DispatchFailConnection(); 1.581 + } 1.582 + return rv; 1.583 + } 1.584 + OnRedirectVerifyCallback(NS_OK); 1.585 + return NS_OK; 1.586 +} 1.587 + 1.588 +nsresult 1.589 +EventSource::OnRedirectVerifyCallback(nsresult aResult) 1.590 +{ 1.591 + NS_ABORT_IF_FALSE(mRedirectCallback, "mRedirectCallback not set in callback"); 1.592 + NS_ABORT_IF_FALSE(mNewRedirectChannel, 1.593 + "mNewRedirectChannel not set in callback"); 1.594 + 1.595 + NS_ENSURE_SUCCESS(aResult, aResult); 1.596 + 1.597 + // update our channel 1.598 + 1.599 + mHttpChannel = do_QueryInterface(mNewRedirectChannel); 1.600 + NS_ENSURE_STATE(mHttpChannel); 1.601 + 1.602 + nsresult rv = SetupHttpChannel(); 1.603 + NS_ENSURE_SUCCESS(rv, rv); 1.604 + 1.605 + if ((mRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) { 1.606 + rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc)); 1.607 + NS_ENSURE_SUCCESS(rv, rv); 1.608 + } 1.609 + 1.610 + mNewRedirectChannel = nullptr; 1.611 + 1.612 + mRedirectCallback->OnRedirectVerifyCallback(aResult); 1.613 + mRedirectCallback = nullptr; 1.614 + 1.615 + return NS_OK; 1.616 +} 1.617 + 1.618 +//----------------------------------------------------------------------------- 1.619 +// EventSource::nsIInterfaceRequestor 1.620 +//----------------------------------------------------------------------------- 1.621 + 1.622 +NS_IMETHODIMP 1.623 +EventSource::GetInterface(const nsIID & aIID, 1.624 + void **aResult) 1.625 +{ 1.626 + // Make sure to return ourselves for the channel event sink interface, 1.627 + // no matter what. We can forward these to mNotificationCallbacks 1.628 + // if it wants to get notifications for them. But we 1.629 + // need to see these notifications for proper functioning. 1.630 + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 1.631 + mChannelEventSink = do_GetInterface(mNotificationCallbacks); 1.632 + *aResult = static_cast<nsIChannelEventSink*>(this); 1.633 + NS_ADDREF_THIS(); 1.634 + return NS_OK; 1.635 + } 1.636 + 1.637 + // Now give mNotificationCallbacks (if non-null) a chance to return the 1.638 + // desired interface. 1.639 + if (mNotificationCallbacks) { 1.640 + nsresult rv = mNotificationCallbacks->GetInterface(aIID, aResult); 1.641 + if (NS_SUCCEEDED(rv)) { 1.642 + NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!"); 1.643 + return rv; 1.644 + } 1.645 + } 1.646 + 1.647 + if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || 1.648 + aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { 1.649 + nsresult rv = CheckInnerWindowCorrectness(); 1.650 + NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); 1.651 + 1.652 + nsCOMPtr<nsIPromptFactory> wwatch = 1.653 + do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); 1.654 + NS_ENSURE_SUCCESS(rv, rv); 1.655 + 1.656 + // Get the an auth prompter for our window so that the parenting 1.657 + // of the dialogs works as it should when using tabs. 1.658 + 1.659 + nsCOMPtr<nsIDOMWindow> window; 1.660 + if (GetOwner()) { 1.661 + window = GetOwner()->GetOuterWindow(); 1.662 + } 1.663 + 1.664 + return wwatch->GetPrompt(window, aIID, aResult); 1.665 + } 1.666 + 1.667 + return QueryInterface(aIID, aResult); 1.668 +} 1.669 + 1.670 +// static 1.671 +bool 1.672 +EventSource::PrefEnabled(JSContext* aCx, JSObject* aGlobal) 1.673 +{ 1.674 + return Preferences::GetBool("dom.server-events.enabled", false); 1.675 +} 1.676 + 1.677 +nsresult 1.678 +EventSource::GetBaseURI(nsIURI **aBaseURI) 1.679 +{ 1.680 + NS_ENSURE_ARG_POINTER(aBaseURI); 1.681 + 1.682 + *aBaseURI = nullptr; 1.683 + 1.684 + nsCOMPtr<nsIURI> baseURI; 1.685 + 1.686 + // first we try from document->GetBaseURI() 1.687 + nsresult rv; 1.688 + nsIScriptContext* sc = GetContextForEventHandlers(&rv); 1.689 + nsCOMPtr<nsIDocument> doc = 1.690 + nsContentUtils::GetDocumentFromScriptContext(sc); 1.691 + if (doc) { 1.692 + baseURI = doc->GetBaseURI(); 1.693 + } 1.694 + 1.695 + // otherwise we get from the doc's principal 1.696 + if (!baseURI) { 1.697 + rv = mPrincipal->GetURI(getter_AddRefs(baseURI)); 1.698 + NS_ENSURE_SUCCESS(rv, rv); 1.699 + } 1.700 + 1.701 + NS_ENSURE_STATE(baseURI); 1.702 + 1.703 + baseURI.forget(aBaseURI); 1.704 + return NS_OK; 1.705 +} 1.706 + 1.707 +nsresult 1.708 +EventSource::SetupHttpChannel() 1.709 +{ 1.710 + mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET")); 1.711 + 1.712 + /* set the http request headers */ 1.713 + 1.714 + mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), 1.715 + NS_LITERAL_CSTRING(TEXT_EVENT_STREAM), false); 1.716 + 1.717 + // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header 1.718 + 1.719 + if (!mLastEventID.IsEmpty()) { 1.720 + mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"), 1.721 + NS_ConvertUTF16toUTF8(mLastEventID), false); 1.722 + } 1.723 + 1.724 + nsCOMPtr<nsIURI> codebase; 1.725 + nsresult rv = GetBaseURI(getter_AddRefs(codebase)); 1.726 + if (NS_SUCCEEDED(rv)) { 1.727 + rv = mHttpChannel->SetReferrer(codebase); 1.728 + NS_ENSURE_SUCCESS(rv, rv); 1.729 + } 1.730 + 1.731 + return NS_OK; 1.732 +} 1.733 + 1.734 +nsresult 1.735 +EventSource::InitChannelAndRequestEventSource() 1.736 +{ 1.737 + if (mReadyState == CLOSED) { 1.738 + return NS_ERROR_ABORT; 1.739 + } 1.740 + 1.741 + // eventsource validation 1.742 + 1.743 + if (!CheckCanRequestSrc()) { 1.744 + DispatchFailConnection(); 1.745 + return NS_ERROR_DOM_SECURITY_ERR; 1.746 + } 1.747 + 1.748 + nsLoadFlags loadFlags; 1.749 + loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE; 1.750 + 1.751 + // get Content Security Policy from principal to pass into channel 1.752 + nsCOMPtr<nsIChannelPolicy> channelPolicy; 1.753 + nsCOMPtr<nsIContentSecurityPolicy> csp; 1.754 + nsresult rv = mPrincipal->GetCsp(getter_AddRefs(csp)); 1.755 + NS_ENSURE_SUCCESS(rv, rv); 1.756 + if (csp) { 1.757 + channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1"); 1.758 + channelPolicy->SetContentSecurityPolicy(csp); 1.759 + channelPolicy->SetLoadType(nsIContentPolicy::TYPE_DATAREQUEST); 1.760 + } 1.761 + 1.762 + nsCOMPtr<nsIChannel> channel; 1.763 + rv = NS_NewChannel(getter_AddRefs(channel), mSrc, nullptr, mLoadGroup, 1.764 + nullptr, loadFlags, channelPolicy); 1.765 + NS_ENSURE_SUCCESS(rv, rv); 1.766 + 1.767 + mHttpChannel = do_QueryInterface(channel); 1.768 + NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE); 1.769 + 1.770 + rv = SetupHttpChannel(); 1.771 + NS_ENSURE_SUCCESS(rv, rv); 1.772 + 1.773 + nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks; 1.774 + mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks)); 1.775 + if (notificationCallbacks != this) { 1.776 + mNotificationCallbacks = notificationCallbacks; 1.777 + mHttpChannel->SetNotificationCallbacks(this); 1.778 + } 1.779 + 1.780 + nsRefPtr<nsCORSListenerProxy> listener = 1.781 + new nsCORSListenerProxy(this, mPrincipal, mWithCredentials); 1.782 + rv = listener->Init(mHttpChannel); 1.783 + NS_ENSURE_SUCCESS(rv, rv); 1.784 + 1.785 + // Start reading from the channel 1.786 + rv = mHttpChannel->AsyncOpen(listener, nullptr); 1.787 + if (NS_SUCCEEDED(rv)) { 1.788 + mWaitingForOnStopRequest = true; 1.789 + } 1.790 + return rv; 1.791 +} 1.792 + 1.793 +void 1.794 +EventSource::AnnounceConnection() 1.795 +{ 1.796 + if (mReadyState == CLOSED) { 1.797 + return; 1.798 + } 1.799 + 1.800 + if (mReadyState != CONNECTING) { 1.801 + NS_WARNING("Unexpected mReadyState!!!"); 1.802 + return; 1.803 + } 1.804 + 1.805 + // When a user agent is to announce the connection, the user agent must set 1.806 + // the readyState attribute to OPEN and queue a task to fire a simple event 1.807 + // named open at the EventSource object. 1.808 + 1.809 + mReadyState = OPEN; 1.810 + 1.811 + nsresult rv = CheckInnerWindowCorrectness(); 1.812 + if (NS_FAILED(rv)) { 1.813 + return; 1.814 + } 1.815 + 1.816 + nsCOMPtr<nsIDOMEvent> event; 1.817 + rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); 1.818 + if (NS_FAILED(rv)) { 1.819 + NS_WARNING("Failed to create the open event!!!"); 1.820 + return; 1.821 + } 1.822 + 1.823 + // it doesn't bubble, and it isn't cancelable 1.824 + rv = event->InitEvent(NS_LITERAL_STRING("open"), false, false); 1.825 + if (NS_FAILED(rv)) { 1.826 + NS_WARNING("Failed to init the open event!!!"); 1.827 + return; 1.828 + } 1.829 + 1.830 + event->SetTrusted(true); 1.831 + 1.832 + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.833 + if (NS_FAILED(rv)) { 1.834 + NS_WARNING("Failed to dispatch the open event!!!"); 1.835 + return; 1.836 + } 1.837 +} 1.838 + 1.839 +nsresult 1.840 +EventSource::ResetConnection() 1.841 +{ 1.842 + if (mHttpChannel) { 1.843 + mHttpChannel->Cancel(NS_ERROR_ABORT); 1.844 + } 1.845 + 1.846 + if (mUnicodeDecoder) { 1.847 + mUnicodeDecoder->Reset(); 1.848 + } 1.849 + mLastConvertionResult = NS_OK; 1.850 + 1.851 + mHttpChannel = nullptr; 1.852 + mNotificationCallbacks = nullptr; 1.853 + mChannelEventSink = nullptr; 1.854 + mStatus = PARSE_STATE_OFF; 1.855 + mRedirectCallback = nullptr; 1.856 + mNewRedirectChannel = nullptr; 1.857 + 1.858 + mReadyState = CONNECTING; 1.859 + 1.860 + return NS_OK; 1.861 +} 1.862 + 1.863 +void 1.864 +EventSource::ReestablishConnection() 1.865 +{ 1.866 + if (mReadyState == CLOSED) { 1.867 + return; 1.868 + } 1.869 + 1.870 + if (mReadyState != OPEN) { 1.871 + NS_WARNING("Unexpected mReadyState!!!"); 1.872 + return; 1.873 + } 1.874 + 1.875 + nsresult rv = ResetConnection(); 1.876 + if (NS_FAILED(rv)) { 1.877 + NS_WARNING("Failed to reset the connection!!!"); 1.878 + return; 1.879 + } 1.880 + 1.881 + rv = CheckInnerWindowCorrectness(); 1.882 + if (NS_FAILED(rv)) { 1.883 + return; 1.884 + } 1.885 + 1.886 + nsCOMPtr<nsIDOMEvent> event; 1.887 + rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); 1.888 + if (NS_FAILED(rv)) { 1.889 + NS_WARNING("Failed to create the error event!!!"); 1.890 + return; 1.891 + } 1.892 + 1.893 + // it doesn't bubble, and it isn't cancelable 1.894 + rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false); 1.895 + if (NS_FAILED(rv)) { 1.896 + NS_WARNING("Failed to init the error event!!!"); 1.897 + return; 1.898 + } 1.899 + 1.900 + event->SetTrusted(true); 1.901 + 1.902 + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.903 + if (NS_FAILED(rv)) { 1.904 + NS_WARNING("Failed to dispatch the error event!!!"); 1.905 + return; 1.906 + } 1.907 + 1.908 + rv = SetReconnectionTimeout(); 1.909 + if (NS_FAILED(rv)) { 1.910 + NS_WARNING("Failed to set the timeout for reestablishing the connection!!!"); 1.911 + return; 1.912 + } 1.913 +} 1.914 + 1.915 +nsresult 1.916 +EventSource::SetReconnectionTimeout() 1.917 +{ 1.918 + if (mReadyState == CLOSED) { 1.919 + return NS_ERROR_ABORT; 1.920 + } 1.921 + 1.922 + // the timer will be used whenever the requests are going finished. 1.923 + if (!mTimer) { 1.924 + mTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.925 + NS_ENSURE_STATE(mTimer); 1.926 + } 1.927 + 1.928 + nsresult rv = mTimer->InitWithFuncCallback(TimerCallback, this, 1.929 + mReconnectionTime, 1.930 + nsITimer::TYPE_ONE_SHOT); 1.931 + NS_ENSURE_SUCCESS(rv, rv); 1.932 + 1.933 + return NS_OK; 1.934 +} 1.935 + 1.936 +nsresult 1.937 +EventSource::PrintErrorOnConsole(const char *aBundleURI, 1.938 + const char16_t *aError, 1.939 + const char16_t **aFormatStrings, 1.940 + uint32_t aFormatStringsLen) 1.941 +{ 1.942 + nsCOMPtr<nsIStringBundleService> bundleService = 1.943 + mozilla::services::GetStringBundleService(); 1.944 + NS_ENSURE_STATE(bundleService); 1.945 + 1.946 + nsCOMPtr<nsIStringBundle> strBundle; 1.947 + nsresult rv = 1.948 + bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle)); 1.949 + NS_ENSURE_SUCCESS(rv, rv); 1.950 + 1.951 + nsCOMPtr<nsIConsoleService> console( 1.952 + do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv)); 1.953 + NS_ENSURE_SUCCESS(rv, rv); 1.954 + 1.955 + nsCOMPtr<nsIScriptError> errObj( 1.956 + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv)); 1.957 + NS_ENSURE_SUCCESS(rv, rv); 1.958 + 1.959 + // Localize the error message 1.960 + nsXPIDLString message; 1.961 + if (aFormatStrings) { 1.962 + rv = strBundle->FormatStringFromName(aError, aFormatStrings, 1.963 + aFormatStringsLen, 1.964 + getter_Copies(message)); 1.965 + } else { 1.966 + rv = strBundle->GetStringFromName(aError, getter_Copies(message)); 1.967 + } 1.968 + NS_ENSURE_SUCCESS(rv, rv); 1.969 + 1.970 + rv = errObj->InitWithWindowID(message, 1.971 + mScriptFile, 1.972 + EmptyString(), 1.973 + mScriptLine, 0, 1.974 + nsIScriptError::errorFlag, 1.975 + "Event Source", mInnerWindowID); 1.976 + NS_ENSURE_SUCCESS(rv, rv); 1.977 + 1.978 + // print the error message directly to the JS console 1.979 + rv = console->LogMessage(errObj); 1.980 + NS_ENSURE_SUCCESS(rv, rv); 1.981 + 1.982 + return NS_OK; 1.983 +} 1.984 + 1.985 +nsresult 1.986 +EventSource::ConsoleError() 1.987 +{ 1.988 + nsAutoCString targetSpec; 1.989 + nsresult rv = mSrc->GetSpec(targetSpec); 1.990 + NS_ENSURE_SUCCESS(rv, rv); 1.991 + 1.992 + NS_ConvertUTF8toUTF16 specUTF16(targetSpec); 1.993 + const char16_t *formatStrings[] = { specUTF16.get() }; 1.994 + 1.995 + if (mReadyState == CONNECTING && !mInterrupted) { 1.996 + rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties", 1.997 + MOZ_UTF16("connectionFailure"), 1.998 + formatStrings, ArrayLength(formatStrings)); 1.999 + } else { 1.1000 + rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties", 1.1001 + MOZ_UTF16("netInterrupt"), 1.1002 + formatStrings, ArrayLength(formatStrings)); 1.1003 + } 1.1004 + NS_ENSURE_SUCCESS(rv, rv); 1.1005 + 1.1006 + return NS_OK; 1.1007 +} 1.1008 + 1.1009 +nsresult 1.1010 +EventSource::DispatchFailConnection() 1.1011 +{ 1.1012 + nsCOMPtr<nsIRunnable> event = 1.1013 + NS_NewRunnableMethod(this, &EventSource::FailConnection); 1.1014 + NS_ENSURE_STATE(event); 1.1015 + 1.1016 + return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.1017 +} 1.1018 + 1.1019 +void 1.1020 +EventSource::FailConnection() 1.1021 +{ 1.1022 + if (mReadyState == CLOSED) { 1.1023 + return; 1.1024 + } 1.1025 + 1.1026 + nsresult rv = ConsoleError(); 1.1027 + if (NS_FAILED(rv)) { 1.1028 + NS_WARNING("Failed to print to the console error"); 1.1029 + } 1.1030 + 1.1031 + // When a user agent is to fail the connection, the user agent must set the 1.1032 + // readyState attribute to CLOSED and queue a task to fire a simple event 1.1033 + // named error at the EventSource object. 1.1034 + 1.1035 + Close(); // it sets mReadyState to CLOSED 1.1036 + 1.1037 + rv = CheckInnerWindowCorrectness(); 1.1038 + if (NS_FAILED(rv)) { 1.1039 + return; 1.1040 + } 1.1041 + 1.1042 + nsCOMPtr<nsIDOMEvent> event; 1.1043 + rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); 1.1044 + if (NS_FAILED(rv)) { 1.1045 + NS_WARNING("Failed to create the error event!!!"); 1.1046 + return; 1.1047 + } 1.1048 + 1.1049 + // it doesn't bubble, and it isn't cancelable 1.1050 + rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false); 1.1051 + if (NS_FAILED(rv)) { 1.1052 + NS_WARNING("Failed to init the error event!!!"); 1.1053 + return; 1.1054 + } 1.1055 + 1.1056 + event->SetTrusted(true); 1.1057 + 1.1058 + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.1059 + if (NS_FAILED(rv)) { 1.1060 + NS_WARNING("Failed to dispatch the error event!!!"); 1.1061 + return; 1.1062 + } 1.1063 +} 1.1064 + 1.1065 +bool 1.1066 +EventSource::CheckCanRequestSrc(nsIURI* aSrc) 1.1067 +{ 1.1068 + if (mReadyState == CLOSED) { 1.1069 + return false; 1.1070 + } 1.1071 + 1.1072 + bool isValidURI = false; 1.1073 + bool isValidContentLoadPolicy = false; 1.1074 + bool isValidProtocol = false; 1.1075 + 1.1076 + nsCOMPtr<nsIURI> srcToTest = aSrc ? aSrc : mSrc.get(); 1.1077 + NS_ENSURE_TRUE(srcToTest, false); 1.1078 + 1.1079 + uint32_t aCheckURIFlags = 1.1080 + nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL | 1.1081 + nsIScriptSecurityManager::DISALLOW_SCRIPT; 1.1082 + 1.1083 + nsresult rv = nsContentUtils::GetSecurityManager()-> 1.1084 + CheckLoadURIWithPrincipal(mPrincipal, 1.1085 + srcToTest, 1.1086 + aCheckURIFlags); 1.1087 + isValidURI = NS_SUCCEEDED(rv); 1.1088 + 1.1089 + // After the security manager, the content-policy check 1.1090 + 1.1091 + nsIScriptContext* sc = GetContextForEventHandlers(&rv); 1.1092 + nsCOMPtr<nsIDocument> doc = 1.1093 + nsContentUtils::GetDocumentFromScriptContext(sc); 1.1094 + 1.1095 + // mScriptContext should be initialized because of GetBaseURI() above. 1.1096 + // Still need to consider the case that doc is nullptr however. 1.1097 + rv = CheckInnerWindowCorrectness(); 1.1098 + NS_ENSURE_SUCCESS(rv, false); 1.1099 + int16_t shouldLoad = nsIContentPolicy::ACCEPT; 1.1100 + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_DATAREQUEST, 1.1101 + srcToTest, 1.1102 + mPrincipal, 1.1103 + doc, 1.1104 + NS_LITERAL_CSTRING(TEXT_EVENT_STREAM), 1.1105 + nullptr, // extra 1.1106 + &shouldLoad, 1.1107 + nsContentUtils::GetContentPolicy(), 1.1108 + nsContentUtils::GetSecurityManager()); 1.1109 + isValidContentLoadPolicy = NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); 1.1110 + 1.1111 + nsAutoCString targetURIScheme; 1.1112 + rv = srcToTest->GetScheme(targetURIScheme); 1.1113 + if (NS_SUCCEEDED(rv)) { 1.1114 + // We only have the http support for now 1.1115 + isValidProtocol = targetURIScheme.EqualsLiteral("http") || 1.1116 + targetURIScheme.EqualsLiteral("https"); 1.1117 + } 1.1118 + 1.1119 + return isValidURI && isValidContentLoadPolicy && isValidProtocol; 1.1120 +} 1.1121 + 1.1122 +// static 1.1123 +void 1.1124 +EventSource::TimerCallback(nsITimer* aTimer, void* aClosure) 1.1125 +{ 1.1126 + nsRefPtr<EventSource> thisObject = static_cast<EventSource*>(aClosure); 1.1127 + 1.1128 + if (thisObject->mReadyState == CLOSED) { 1.1129 + return; 1.1130 + } 1.1131 + 1.1132 + NS_PRECONDITION(!thisObject->mHttpChannel, 1.1133 + "the channel hasn't been cancelled!!"); 1.1134 + 1.1135 + if (!thisObject->mFrozen) { 1.1136 + nsresult rv = thisObject->InitChannelAndRequestEventSource(); 1.1137 + if (NS_FAILED(rv)) { 1.1138 + NS_WARNING("thisObject->InitChannelAndRequestEventSource() failed"); 1.1139 + return; 1.1140 + } 1.1141 + } 1.1142 +} 1.1143 + 1.1144 +nsresult 1.1145 +EventSource::Thaw() 1.1146 +{ 1.1147 + if (mReadyState == CLOSED || !mFrozen) { 1.1148 + return NS_OK; 1.1149 + } 1.1150 + 1.1151 + NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!"); 1.1152 + 1.1153 + mFrozen = false; 1.1154 + nsresult rv; 1.1155 + if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) { 1.1156 + nsCOMPtr<nsIRunnable> event = 1.1157 + NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents); 1.1158 + NS_ENSURE_STATE(event); 1.1159 + 1.1160 + mGoingToDispatchAllMessages = true; 1.1161 + 1.1162 + rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.1163 + NS_ENSURE_SUCCESS(rv, rv); 1.1164 + } 1.1165 + 1.1166 + rv = InitChannelAndRequestEventSource(); 1.1167 + NS_ENSURE_SUCCESS(rv, rv); 1.1168 + 1.1169 + return NS_OK; 1.1170 +} 1.1171 + 1.1172 +nsresult 1.1173 +EventSource::Freeze() 1.1174 +{ 1.1175 + if (mReadyState == CLOSED || mFrozen) { 1.1176 + return NS_OK; 1.1177 + } 1.1178 + 1.1179 + NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!"); 1.1180 + mFrozen = true; 1.1181 + return NS_OK; 1.1182 +} 1.1183 + 1.1184 +nsresult 1.1185 +EventSource::DispatchCurrentMessageEvent() 1.1186 +{ 1.1187 + nsAutoPtr<Message> message(new Message()); 1.1188 + *message = mCurrentMessage; 1.1189 + 1.1190 + ClearFields(); 1.1191 + 1.1192 + if (message->mData.IsEmpty()) { 1.1193 + return NS_OK; 1.1194 + } 1.1195 + 1.1196 + // removes the trailing LF from mData 1.1197 + NS_ASSERTION(message->mData.CharAt(message->mData.Length() - 1) == LF_CHAR, 1.1198 + "Invalid trailing character! LF was expected instead."); 1.1199 + message->mData.SetLength(message->mData.Length() - 1); 1.1200 + 1.1201 + if (message->mEventName.IsEmpty()) { 1.1202 + message->mEventName.AssignLiteral("message"); 1.1203 + } 1.1204 + 1.1205 + if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) { 1.1206 + message->mLastEventID.Assign(mLastEventID); 1.1207 + } 1.1208 + 1.1209 + int32_t sizeBefore = mMessagesToDispatch.GetSize(); 1.1210 + mMessagesToDispatch.Push(message.forget()); 1.1211 + NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1, 1.1212 + NS_ERROR_OUT_OF_MEMORY); 1.1213 + 1.1214 + 1.1215 + if (!mGoingToDispatchAllMessages) { 1.1216 + nsCOMPtr<nsIRunnable> event = 1.1217 + NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents); 1.1218 + NS_ENSURE_STATE(event); 1.1219 + 1.1220 + mGoingToDispatchAllMessages = true; 1.1221 + 1.1222 + return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.1223 + } 1.1224 + 1.1225 + return NS_OK; 1.1226 +} 1.1227 + 1.1228 +void 1.1229 +EventSource::DispatchAllMessageEvents() 1.1230 +{ 1.1231 + if (mReadyState == CLOSED || mFrozen) { 1.1232 + return; 1.1233 + } 1.1234 + 1.1235 + mGoingToDispatchAllMessages = false; 1.1236 + 1.1237 + nsresult rv = CheckInnerWindowCorrectness(); 1.1238 + if (NS_FAILED(rv)) { 1.1239 + return; 1.1240 + } 1.1241 + 1.1242 + // Let's play get the JSContext 1.1243 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner()); 1.1244 + NS_ENSURE_TRUE_VOID(sgo); 1.1245 + 1.1246 + nsIScriptContext* scriptContext = sgo->GetContext(); 1.1247 + NS_ENSURE_TRUE_VOID(scriptContext); 1.1248 + 1.1249 + AutoPushJSContext cx(scriptContext->GetNativeContext()); 1.1250 + NS_ENSURE_TRUE_VOID(cx); 1.1251 + 1.1252 + while (mMessagesToDispatch.GetSize() > 0) { 1.1253 + nsAutoPtr<Message> 1.1254 + message(static_cast<Message*>(mMessagesToDispatch.PopFront())); 1.1255 + 1.1256 + // Now we can turn our string into a jsval 1.1257 + JS::Rooted<JS::Value> jsData(cx); 1.1258 + { 1.1259 + JSString* jsString; 1.1260 + jsString = JS_NewUCStringCopyN(cx, 1.1261 + message->mData.get(), 1.1262 + message->mData.Length()); 1.1263 + NS_ENSURE_TRUE_VOID(jsString); 1.1264 + 1.1265 + jsData = STRING_TO_JSVAL(jsString); 1.1266 + } 1.1267 + 1.1268 + // create an event that uses the MessageEvent interface, 1.1269 + // which does not bubble, is not cancelable, and has no default action 1.1270 + 1.1271 + nsCOMPtr<nsIDOMEvent> event; 1.1272 + rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr); 1.1273 + if (NS_FAILED(rv)) { 1.1274 + NS_WARNING("Failed to create the message event!!!"); 1.1275 + return; 1.1276 + } 1.1277 + 1.1278 + nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event); 1.1279 + rv = messageEvent->InitMessageEvent(message->mEventName, 1.1280 + false, false, 1.1281 + jsData, 1.1282 + mOrigin, 1.1283 + message->mLastEventID, nullptr); 1.1284 + if (NS_FAILED(rv)) { 1.1285 + NS_WARNING("Failed to init the message event!!!"); 1.1286 + return; 1.1287 + } 1.1288 + 1.1289 + messageEvent->SetTrusted(true); 1.1290 + 1.1291 + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.1292 + if (NS_FAILED(rv)) { 1.1293 + NS_WARNING("Failed to dispatch the message event!!!"); 1.1294 + return; 1.1295 + } 1.1296 + 1.1297 + mLastEventID.Assign(message->mLastEventID); 1.1298 + } 1.1299 +} 1.1300 + 1.1301 +nsresult 1.1302 +EventSource::ClearFields() 1.1303 +{ 1.1304 + // mLastEventID and mReconnectionTime must be cached 1.1305 + 1.1306 + mCurrentMessage.mEventName.Truncate(); 1.1307 + mCurrentMessage.mLastEventID.Truncate(); 1.1308 + mCurrentMessage.mData.Truncate(); 1.1309 + 1.1310 + mLastFieldName.Truncate(); 1.1311 + mLastFieldValue.Truncate(); 1.1312 + 1.1313 + return NS_OK; 1.1314 +} 1.1315 + 1.1316 +nsresult 1.1317 +EventSource::SetFieldAndClear() 1.1318 +{ 1.1319 + if (mLastFieldName.IsEmpty()) { 1.1320 + mLastFieldValue.Truncate(); 1.1321 + return NS_OK; 1.1322 + } 1.1323 + 1.1324 + char16_t first_char; 1.1325 + first_char = mLastFieldName.CharAt(0); 1.1326 + 1.1327 + switch (first_char) // with no case folding performed 1.1328 + { 1.1329 + case char16_t('d'): 1.1330 + if (mLastFieldName.EqualsLiteral("data")) { 1.1331 + // If the field name is "data" append the field value to the data 1.1332 + // buffer, then append a single U+000A LINE FEED (LF) character 1.1333 + // to the data buffer. 1.1334 + mCurrentMessage.mData.Append(mLastFieldValue); 1.1335 + mCurrentMessage.mData.Append(LF_CHAR); 1.1336 + } 1.1337 + break; 1.1338 + 1.1339 + case char16_t('e'): 1.1340 + if (mLastFieldName.EqualsLiteral("event")) { 1.1341 + mCurrentMessage.mEventName.Assign(mLastFieldValue); 1.1342 + } 1.1343 + break; 1.1344 + 1.1345 + case char16_t('i'): 1.1346 + if (mLastFieldName.EqualsLiteral("id")) { 1.1347 + mCurrentMessage.mLastEventID.Assign(mLastFieldValue); 1.1348 + } 1.1349 + break; 1.1350 + 1.1351 + case char16_t('r'): 1.1352 + if (mLastFieldName.EqualsLiteral("retry")) { 1.1353 + uint32_t newValue=0; 1.1354 + uint32_t i = 0; // we must ensure that there are only digits 1.1355 + bool assign = true; 1.1356 + for (i = 0; i < mLastFieldValue.Length(); ++i) { 1.1357 + if (mLastFieldValue.CharAt(i) < (char16_t)'0' || 1.1358 + mLastFieldValue.CharAt(i) > (char16_t)'9') { 1.1359 + assign = false; 1.1360 + break; 1.1361 + } 1.1362 + newValue = newValue*10 + 1.1363 + (((uint32_t)mLastFieldValue.CharAt(i))- 1.1364 + ((uint32_t)((char16_t)'0'))); 1.1365 + } 1.1366 + 1.1367 + if (assign) { 1.1368 + if (newValue < MIN_RECONNECTION_TIME_VALUE) { 1.1369 + mReconnectionTime = MIN_RECONNECTION_TIME_VALUE; 1.1370 + } else if (newValue > MAX_RECONNECTION_TIME_VALUE) { 1.1371 + mReconnectionTime = MAX_RECONNECTION_TIME_VALUE; 1.1372 + } else { 1.1373 + mReconnectionTime = newValue; 1.1374 + } 1.1375 + } 1.1376 + break; 1.1377 + } 1.1378 + break; 1.1379 + } 1.1380 + 1.1381 + mLastFieldName.Truncate(); 1.1382 + mLastFieldValue.Truncate(); 1.1383 + 1.1384 + return NS_OK; 1.1385 +} 1.1386 + 1.1387 +nsresult 1.1388 +EventSource::CheckHealthOfRequestCallback(nsIRequest *aRequestCallback) 1.1389 +{ 1.1390 + // check if we have been closed or if the request has been canceled 1.1391 + // or if we have been frozen 1.1392 + if (mReadyState == CLOSED || !mHttpChannel || 1.1393 + mFrozen || mErrorLoadOnRedirect) { 1.1394 + return NS_ERROR_ABORT; 1.1395 + } 1.1396 + 1.1397 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequestCallback); 1.1398 + NS_ENSURE_STATE(httpChannel); 1.1399 + 1.1400 + if (httpChannel != mHttpChannel) { 1.1401 + NS_WARNING("wrong channel from request callback"); 1.1402 + return NS_ERROR_ABORT; 1.1403 + } 1.1404 + 1.1405 + return NS_OK; 1.1406 +} 1.1407 + 1.1408 +nsresult 1.1409 +EventSource::ParseCharacter(char16_t aChr) 1.1410 +{ 1.1411 + nsresult rv; 1.1412 + 1.1413 + if (mReadyState == CLOSED) { 1.1414 + return NS_ERROR_ABORT; 1.1415 + } 1.1416 + 1.1417 + switch (mStatus) 1.1418 + { 1.1419 + case PARSE_STATE_OFF: 1.1420 + NS_ERROR("Invalid state"); 1.1421 + return NS_ERROR_FAILURE; 1.1422 + break; 1.1423 + 1.1424 + case PARSE_STATE_BEGIN_OF_STREAM: 1.1425 + if (aChr == BOM_CHAR) { 1.1426 + mStatus = PARSE_STATE_BOM_WAS_READ; // ignore it 1.1427 + } else if (aChr == CR_CHAR) { 1.1428 + mStatus = PARSE_STATE_CR_CHAR; 1.1429 + } else if (aChr == LF_CHAR) { 1.1430 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1431 + } else if (aChr == COLON_CHAR) { 1.1432 + mStatus = PARSE_STATE_COMMENT; 1.1433 + } else { 1.1434 + mLastFieldName += aChr; 1.1435 + mStatus = PARSE_STATE_FIELD_NAME; 1.1436 + } 1.1437 + 1.1438 + break; 1.1439 + 1.1440 + case PARSE_STATE_BOM_WAS_READ: 1.1441 + if (aChr == CR_CHAR) { 1.1442 + mStatus = PARSE_STATE_CR_CHAR; 1.1443 + } else if (aChr == LF_CHAR) { 1.1444 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1445 + } else if (aChr == COLON_CHAR) { 1.1446 + mStatus = PARSE_STATE_COMMENT; 1.1447 + } else { 1.1448 + mLastFieldName += aChr; 1.1449 + mStatus = PARSE_STATE_FIELD_NAME; 1.1450 + } 1.1451 + break; 1.1452 + 1.1453 + case PARSE_STATE_CR_CHAR: 1.1454 + if (aChr == CR_CHAR) { 1.1455 + rv = DispatchCurrentMessageEvent(); // there is an empty line (CRCR) 1.1456 + NS_ENSURE_SUCCESS(rv, rv); 1.1457 + } else if (aChr == LF_CHAR) { 1.1458 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1459 + } else if (aChr == COLON_CHAR) { 1.1460 + mStatus = PARSE_STATE_COMMENT; 1.1461 + } else { 1.1462 + mLastFieldName += aChr; 1.1463 + mStatus = PARSE_STATE_FIELD_NAME; 1.1464 + } 1.1465 + 1.1466 + break; 1.1467 + 1.1468 + case PARSE_STATE_COMMENT: 1.1469 + if (aChr == CR_CHAR) { 1.1470 + mStatus = PARSE_STATE_CR_CHAR; 1.1471 + } else if (aChr == LF_CHAR) { 1.1472 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1473 + } 1.1474 + 1.1475 + break; 1.1476 + 1.1477 + case PARSE_STATE_FIELD_NAME: 1.1478 + if (aChr == CR_CHAR) { 1.1479 + rv = SetFieldAndClear(); 1.1480 + NS_ENSURE_SUCCESS(rv, rv); 1.1481 + 1.1482 + mStatus = PARSE_STATE_CR_CHAR; 1.1483 + } else if (aChr == LF_CHAR) { 1.1484 + rv = SetFieldAndClear(); 1.1485 + NS_ENSURE_SUCCESS(rv, rv); 1.1486 + 1.1487 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1488 + } else if (aChr == COLON_CHAR) { 1.1489 + mStatus = PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE; 1.1490 + } else { 1.1491 + mLastFieldName += aChr; 1.1492 + } 1.1493 + 1.1494 + break; 1.1495 + 1.1496 + case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE: 1.1497 + if (aChr == CR_CHAR) { 1.1498 + rv = SetFieldAndClear(); 1.1499 + NS_ENSURE_SUCCESS(rv, rv); 1.1500 + 1.1501 + mStatus = PARSE_STATE_CR_CHAR; 1.1502 + } else if (aChr == LF_CHAR) { 1.1503 + rv = SetFieldAndClear(); 1.1504 + NS_ENSURE_SUCCESS(rv, rv); 1.1505 + 1.1506 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1507 + } else if (aChr == SPACE_CHAR) { 1.1508 + mStatus = PARSE_STATE_FIELD_VALUE; 1.1509 + } else { 1.1510 + mLastFieldValue += aChr; 1.1511 + mStatus = PARSE_STATE_FIELD_VALUE; 1.1512 + } 1.1513 + 1.1514 + break; 1.1515 + 1.1516 + case PARSE_STATE_FIELD_VALUE: 1.1517 + if (aChr == CR_CHAR) { 1.1518 + rv = SetFieldAndClear(); 1.1519 + NS_ENSURE_SUCCESS(rv, rv); 1.1520 + 1.1521 + mStatus = PARSE_STATE_CR_CHAR; 1.1522 + } else if (aChr == LF_CHAR) { 1.1523 + rv = SetFieldAndClear(); 1.1524 + NS_ENSURE_SUCCESS(rv, rv); 1.1525 + 1.1526 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1527 + } else { 1.1528 + mLastFieldValue += aChr; 1.1529 + } 1.1530 + 1.1531 + break; 1.1532 + 1.1533 + case PARSE_STATE_BEGIN_OF_LINE: 1.1534 + if (aChr == CR_CHAR) { 1.1535 + rv = DispatchCurrentMessageEvent(); // there is an empty line 1.1536 + NS_ENSURE_SUCCESS(rv, rv); 1.1537 + 1.1538 + mStatus = PARSE_STATE_CR_CHAR; 1.1539 + } else if (aChr == LF_CHAR) { 1.1540 + rv = DispatchCurrentMessageEvent(); // there is an empty line 1.1541 + NS_ENSURE_SUCCESS(rv, rv); 1.1542 + 1.1543 + mStatus = PARSE_STATE_BEGIN_OF_LINE; 1.1544 + } else if (aChr == COLON_CHAR) { 1.1545 + mStatus = PARSE_STATE_COMMENT; 1.1546 + } else { 1.1547 + mLastFieldName += aChr; 1.1548 + mStatus = PARSE_STATE_FIELD_NAME; 1.1549 + } 1.1550 + 1.1551 + break; 1.1552 + } 1.1553 + 1.1554 + return NS_OK; 1.1555 +} 1.1556 + 1.1557 +} // namespace dom 1.1558 +} // namespace mozilla