1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/dom/EventSource.h"
8
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/LoadInfo.h"
12 #include "mozilla/DOMEventTargetHelper.h"
13 #include "mozilla/dom/EventSourceBinding.h"
14 #include "mozilla/dom/MessageEvent.h"
15 #include "mozilla/dom/MessageEventBinding.h"
16 #include "mozilla/dom/ScriptSettings.h"
17 #include "mozilla/dom/WorkerPrivate.h"
18 #include "mozilla/dom/WorkerRunnable.h"
19 #include "mozilla/dom/WorkerScope.h"
20 #include "mozilla/UniquePtrExtensions.h"
21 #include "nsAutoPtr.h"
22 #include "nsIThreadRetargetableStreamListener.h"
23 #include "nsNetUtil.h"
24 #include "nsIAuthPrompt.h"
25 #include "nsIAuthPrompt2.h"
26 #include "nsIInputStream.h"
27 #include "nsIInterfaceRequestorUtils.h"
28 #include "nsMimeTypes.h"
29 #include "nsIPromptFactory.h"
30 #include "nsIWindowWatcher.h"
31 #include "nsPresContext.h"
32 #include "nsContentPolicyUtils.h"
33 #include "nsIStringBundle.h"
34 #include "nsIConsoleService.h"
35 #include "nsIObserverService.h"
36 #include "nsIScriptObjectPrincipal.h"
37 #include "nsJSUtils.h"
38 #include "nsIThreadRetargetableRequest.h"
39 #include "nsIAsyncVerifyRedirectCallback.h"
40 #include "nsIScriptError.h"
41 #include "nsIContentSecurityPolicy.h"
42 #include "nsContentUtils.h"
43 #include "mozilla/Preferences.h"
44 #include "xpcpublic.h"
45 #include "nsWrapperCacheInlines.h"
46 #include "mozilla/Attributes.h"
47 #include "nsError.h"
48 #include "mozilla/Encoding.h"
49
50 namespace mozilla {
51 namespace dom {
52
53 static LazyLogModule gEventSourceLog("EventSource");
54
55 #define SPACE_CHAR (char16_t)0x0020
56 #define CR_CHAR (char16_t)0x000D
57 #define LF_CHAR (char16_t)0x000A
58 #define COLON_CHAR (char16_t)0x003A
59
60 // Reconnection time related values in milliseconds. The default one is equal
61 // to the default value of the pref dom.server-events.default-reconnection-time
62 #define MIN_RECONNECTION_TIME_VALUE 500
63 #define DEFAULT_RECONNECTION_TIME_VALUE 5000
64 #define MAX_RECONNECTION_TIME_VALUE \
65 PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
66
67 class EventSourceImpl final : public nsIObserver,
68 public nsIStreamListener,
69 public nsIChannelEventSink,
70 public nsIInterfaceRequestor,
71 public nsSupportsWeakReference,
72 public nsIEventTarget,
73 public nsIThreadRetargetableStreamListener {
74 public:
75 NS_DECL_THREADSAFE_ISUPPORTS
76 NS_DECL_NSIOBSERVER
77 NS_DECL_NSIREQUESTOBSERVER
78 NS_DECL_NSISTREAMLISTENER
79 NS_DECL_NSICHANNELEVENTSINK
80 NS_DECL_NSIINTERFACEREQUESTOR
81 NS_DECL_NSIEVENTTARGET_FULL
82 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
83
84 explicit EventSourceImpl(EventSource* aEventSource);
85
86 enum { CONNECTING = 0U, OPEN = 1U, CLOSED = 2U };
87
88 void Close();
89
90 void Init(nsIPrincipal* aPrincipal, const nsAString& aURL, ErrorResult& aRv);
91
92 nsresult GetBaseURI(nsIURI** aBaseURI);
93
94 void SetupHttpChannel();
95 nsresult SetupReferrerPolicy();
96 nsresult InitChannelAndRequestEventSource();
97 nsresult ResetConnection();
98 void ResetDecoder();
99 nsresult SetReconnectionTimeout();
100
101 void AnnounceConnection();
102 void DispatchAllMessageEvents();
103 nsresult RestartConnection();
104 void ReestablishConnection();
105 void DispatchFailConnection();
106 void FailConnection();
107
108 nsresult Thaw();
109 nsresult Freeze();
110
111 static void TimerCallback(nsITimer* aTimer, void* aClosure);
112
113 nsresult PrintErrorOnConsole(const char* aBundleURI, const char* aError,
114 const char16_t** aFormatStrings,
115 uint32_t aFormatStringsLen);
116 nsresult ConsoleError();
117
118 static nsresult StreamReaderFunc(nsIInputStream* aInputStream, void* aClosure,
119 const char* aFromRawSegment,
120 uint32_t aToOffset, uint32_t aCount,
121 uint32_t* aWriteCount);
122 void ParseSegment(const char* aBuffer, uint32_t aLength);
123 nsresult SetFieldAndClear();
124 void ClearFields();
125 nsresult ResetEvent();
126 nsresult DispatchCurrentMessageEvent();
127 nsresult ParseCharacter(char16_t aChr);
128 nsresult CheckHealthOfRequestCallback(nsIRequest* aRequestCallback);
129 nsresult OnRedirectVerifyCallback(nsresult result);
130 nsresult ParseURL(const nsAString& aURL);
131 nsresult AddWindowObservers();
132 void RemoveWindowObservers();
133
134 void CloseInternal();
135 void CleanupOnMainThread();
136 void AddRefObject();
137 void ReleaseObject();
138
139 bool RegisterWorkerHolder();
140 void UnregisterWorkerHolder();
141
AssertIsOnTargetThread() const142 void AssertIsOnTargetThread() const { MOZ_ASSERT(IsTargetThread()); }
143
IsTargetThread() const144 bool IsTargetThread() const { return NS_IsMainThread() == mIsMainThread; }
145
ReadyState()146 uint16_t ReadyState() {
147 MutexAutoLock lock(mMutex);
148 if (mEventSource) {
149 return mEventSource->mReadyState;
150 }
151 // EventSourceImpl keeps EventSource alive. If mEventSource is null, it
152 // means that the EventSource has been closed.
153 return CLOSED;
154 }
155
SetReadyState(uint16_t aReadyState)156 void SetReadyState(uint16_t aReadyState) {
157 MutexAutoLock lock(mMutex);
158 MOZ_ASSERT(mEventSource);
159 MOZ_ASSERT(!mIsShutDown);
160 mEventSource->mReadyState = aReadyState;
161 }
162
IsFrozen()163 bool IsFrozen() {
164 MutexAutoLock lock(mMutex);
165 return mFrozen;
166 }
167
SetFrozen(bool aFrozen)168 void SetFrozen(bool aFrozen) {
169 MutexAutoLock lock(mMutex);
170 mFrozen = aFrozen;
171 }
172
IsClosed()173 bool IsClosed() { return ReadyState() == CLOSED; }
174
ShutDown()175 void ShutDown() {
176 MutexAutoLock lock(mMutex);
177 MOZ_ASSERT(!mIsShutDown);
178 mIsShutDown = true;
179 }
180
IsShutDown()181 bool IsShutDown() {
182 MutexAutoLock lock(mMutex);
183 return mIsShutDown;
184 }
185
186 RefPtr<EventSource> mEventSource;
187
188 /**
189 * A simple state machine used to manage the event-source's line buffer
190 *
191 * PARSE_STATE_OFF -> PARSE_STATE_BEGIN_OF_STREAM
192 *
193 * PARSE_STATE_BEGIN_OF_STREAM -> PARSE_STATE_CR_CHAR |
194 * PARSE_STATE_BEGIN_OF_LINE |
195 * PARSE_STATE_COMMENT |
196 * PARSE_STATE_FIELD_NAME
197 *
198 * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR |
199 * PARSE_STATE_COMMENT |
200 * PARSE_STATE_FIELD_NAME |
201 * PARSE_STATE_BEGIN_OF_LINE
202 *
203 * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR |
204 * PARSE_STATE_BEGIN_OF_LINE
205 *
206 * PARSE_STATE_FIELD_NAME -> PARSE_STATE_CR_CHAR |
207 * PARSE_STATE_BEGIN_OF_LINE |
208 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
209 *
210 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE -> PARSE_STATE_FIELD_VALUE |
211 * PARSE_STATE_CR_CHAR |
212 * PARSE_STATE_BEGIN_OF_LINE
213 *
214 * PARSE_STATE_FIELD_VALUE -> PARSE_STATE_CR_CHAR |
215 * PARSE_STATE_BEGIN_OF_LINE
216 *
217 * PARSE_STATE_BEGIN_OF_LINE -> PARSE_STATE_CR_CHAR |
218 * PARSE_STATE_COMMENT |
219 * PARSE_STATE_FIELD_NAME |
220 * PARSE_STATE_BEGIN_OF_LINE
221 *
222 * Whenever the parser find an empty line or the end-of-file
223 * it dispatches the stacked event.
224 *
225 */
226 enum ParserStatus {
227 PARSE_STATE_OFF = 0,
228 PARSE_STATE_BEGIN_OF_STREAM,
229 PARSE_STATE_CR_CHAR,
230 PARSE_STATE_COMMENT,
231 PARSE_STATE_FIELD_NAME,
232 PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE,
233 PARSE_STATE_FIELD_VALUE,
234 PARSE_STATE_BEGIN_OF_LINE
235 };
236
237 // Connection related data members. Should only be accessed on main thread.
238 nsCOMPtr<nsIURI> mSrc;
239 uint32_t mReconnectionTime; // in ms
240 nsCOMPtr<nsIPrincipal> mPrincipal;
241 nsString mOrigin;
242 nsCOMPtr<nsITimer> mTimer;
243 nsCOMPtr<nsIHttpChannel> mHttpChannel;
244
245 struct Message {
246 nsString mEventName;
247 nsString mLastEventID;
248 nsString mData;
249 };
250
251 // Message related data members. May be set / initialized when initializing
252 // EventSourceImpl on target thread but should only be used on target thread.
253 nsString mLastEventID;
254 UniquePtr<Message> mCurrentMessage;
255 nsDeque mMessagesToDispatch;
256 ParserStatus mStatus;
257 mozilla::UniquePtr<mozilla::Decoder> mUnicodeDecoder;
258 nsString mLastFieldName;
259 nsString mLastFieldValue;
260
261 // EventSourceImpl internal states.
262 // The worker private where the EventSource is created. nullptr if created on
263 // main thread. (accessed on worker thread only)
264 WorkerPrivate* mWorkerPrivate;
265 // Holder to worker to keep worker alive. (accessed on worker thread only)
266 nsAutoPtr<WorkerHolder> mWorkerHolder;
267 // This mutex protects mFrozen and mEventSource->mReadyState that are used in
268 // different threads.
269 mozilla::Mutex mMutex;
270 // Whether the window is frozen. May be set on main thread and read on target
271 // thread. Use mMutex to protect it before accessing it.
272 bool mFrozen;
273 // There are some messages are going to be dispatched when thaw.
274 bool mGoingToDispatchAllMessages;
275 // Whether the EventSource is run on main thread.
276 bool mIsMainThread;
277 // Whether the EventSourceImpl is going to be destroyed.
278 bool mIsShutDown;
279
280 // Event Source owner information:
281 // - the script file name
282 // - source code line number and column number where the Event Source object
283 // was constructed.
284 // - the ID of the inner window where the script lives. Note that this may not
285 // be the same as the Event Source owner window.
286 // These attributes are used for error reporting. Should only be accessed on
287 // target thread
288 nsString mScriptFile;
289 uint32_t mScriptLine;
290 uint32_t mScriptColumn;
291 uint64_t mInnerWindowID;
292
293 private:
294 // prevent bad usage
295 EventSourceImpl(const EventSourceImpl& x) = delete;
296 EventSourceImpl& operator=(const EventSourceImpl& x) = delete;
~EventSourceImpl()297 ~EventSourceImpl() {
298 if (IsClosed()) {
299 return;
300 }
301 // If we threw during Init we never called Close
302 SetReadyState(CLOSED);
303 CloseInternal();
304 }
305 };
306
NS_IMPL_ISUPPORTS(EventSourceImpl,nsIObserver,nsIStreamListener,nsIRequestObserver,nsIChannelEventSink,nsIInterfaceRequestor,nsISupportsWeakReference,nsIEventTarget,nsIThreadRetargetableStreamListener)307 NS_IMPL_ISUPPORTS(EventSourceImpl, nsIObserver, nsIStreamListener,
308 nsIRequestObserver, nsIChannelEventSink,
309 nsIInterfaceRequestor, nsISupportsWeakReference,
310 nsIEventTarget, nsIThreadRetargetableStreamListener)
311
312 EventSourceImpl::EventSourceImpl(EventSource* aEventSource)
313 : mEventSource(aEventSource),
314 mReconnectionTime(0),
315 mStatus(PARSE_STATE_OFF),
316 mMutex("EventSourceImpl::mMutex"),
317 mFrozen(false),
318 mGoingToDispatchAllMessages(false),
319 mIsMainThread(NS_IsMainThread()),
320 mIsShutDown(false),
321 mScriptLine(0),
322 mScriptColumn(0),
323 mInnerWindowID(0) {
324 MOZ_ASSERT(mEventSource);
325 if (!mIsMainThread) {
326 mWorkerPrivate = GetCurrentThreadWorkerPrivate();
327 MOZ_ASSERT(mWorkerPrivate);
328 mEventSource->mIsMainThread = false;
329 }
330 SetReadyState(CONNECTING);
331 }
332
333 class CleanupRunnable final : public WorkerMainThreadRunnable {
334 public:
CleanupRunnable(EventSourceImpl * aEventSourceImpl)335 explicit CleanupRunnable(EventSourceImpl* aEventSourceImpl)
336 : WorkerMainThreadRunnable(aEventSourceImpl->mWorkerPrivate,
337 NS_LITERAL_CSTRING("EventSource :: Cleanup")),
338 mImpl(aEventSourceImpl) {
339 mImpl->mWorkerPrivate->AssertIsOnWorkerThread();
340 }
341
MainThreadRun()342 bool MainThreadRun() override {
343 mImpl->CleanupOnMainThread();
344 return true;
345 }
346
347 protected:
348 // Raw pointer because this runnable is sync.
349 EventSourceImpl* mImpl;
350 };
351
Close()352 void EventSourceImpl::Close() {
353 if (IsClosed()) {
354 return;
355 }
356 SetReadyState(CLOSED);
357 // Asynchronously call CloseInternal to prevent EventSourceImpl from being
358 // synchronously destoryed while dispatching DOM event.
359 DebugOnly<nsresult> rv =
360 Dispatch(NewRunnableMethod("dom::EventSourceImpl::CloseInternal", this,
361 &EventSourceImpl::CloseInternal),
362 NS_DISPATCH_NORMAL);
363 MOZ_ASSERT(NS_SUCCEEDED(rv));
364 }
365
CloseInternal()366 void EventSourceImpl::CloseInternal() {
367 AssertIsOnTargetThread();
368 MOZ_ASSERT(IsClosed());
369 if (IsShutDown()) {
370 return;
371 }
372
373 // Invoke CleanupOnMainThread before cleaning any members. It will call
374 // ShutDown, which is supposed to be called before cleaning any members.
375 if (NS_IsMainThread()) {
376 CleanupOnMainThread();
377 } else {
378 ErrorResult rv;
379 // run CleanupOnMainThread synchronously on main thread since it touches
380 // observers and members only can be accessed on main thread.
381 RefPtr<CleanupRunnable> runnable = new CleanupRunnable(this);
382 runnable->Dispatch(Killing, rv);
383 MOZ_ASSERT(!rv.Failed());
384 UnregisterWorkerHolder();
385 }
386
387 while (mMessagesToDispatch.GetSize() != 0) {
388 delete static_cast<Message*>(mMessagesToDispatch.PopFront());
389 }
390 SetFrozen(false);
391 ResetDecoder();
392 mUnicodeDecoder = nullptr;
393 // UpdateDontKeepAlive() can release the object. Don't access to any members
394 // after it.
395 mEventSource->UpdateDontKeepAlive();
396 }
397
CleanupOnMainThread()398 void EventSourceImpl::CleanupOnMainThread() {
399 AssertIsOnMainThread();
400 MOZ_ASSERT(IsClosed());
401
402 // Call ShutDown before cleaning any members.
403 ShutDown();
404
405 if (mIsMainThread) {
406 RemoveWindowObservers();
407 }
408
409 if (mTimer) {
410 mTimer->Cancel();
411 mTimer = nullptr;
412 }
413
414 ResetConnection();
415 mPrincipal = nullptr;
416 mSrc = nullptr;
417 }
418
419 class InitRunnable final : public WorkerMainThreadRunnable {
420 public:
InitRunnable(EventSourceImpl * aEventSourceImpl,const nsAString & aURL)421 explicit InitRunnable(EventSourceImpl* aEventSourceImpl,
422 const nsAString& aURL)
423 : WorkerMainThreadRunnable(aEventSourceImpl->mWorkerPrivate,
424 NS_LITERAL_CSTRING("EventSource :: Init")),
425 mImpl(aEventSourceImpl),
426 mURL(aURL) {
427 mImpl->mWorkerPrivate->AssertIsOnWorkerThread();
428 }
429
MainThreadRun()430 bool MainThreadRun() override {
431 // Get principal from worker's owner document or from worker.
432 WorkerPrivate* wp = mImpl->mWorkerPrivate;
433 while (wp->GetParent()) {
434 wp = wp->GetParent();
435 }
436 nsPIDOMWindowInner* window = wp->GetWindow();
437 nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
438 nsCOMPtr<nsIPrincipal> principal =
439 doc ? doc->NodePrincipal() : wp->GetPrincipal();
440 if (!principal) {
441 mRv = NS_ERROR_FAILURE;
442 return true;
443 }
444 ErrorResult rv;
445 mImpl->Init(principal, mURL, rv);
446 mRv = rv.StealNSResult();
447 return true;
448 }
449
ErrorCode() const450 nsresult ErrorCode() const { return mRv; }
451
452 protected:
453 // Raw pointer because this runnable is sync.
454 EventSourceImpl* mImpl;
455 const nsAString& mURL;
456 nsresult mRv;
457 };
458
ParseURL(const nsAString & aURL)459 nsresult EventSourceImpl::ParseURL(const nsAString& aURL) {
460 AssertIsOnMainThread();
461 MOZ_ASSERT(!IsShutDown());
462 // get the src
463 nsCOMPtr<nsIURI> baseURI;
464 nsresult rv = GetBaseURI(getter_AddRefs(baseURI));
465 NS_ENSURE_SUCCESS(rv, rv);
466
467 nsCOMPtr<nsIURI> srcURI;
468 rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI);
469 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
470
471 nsAutoString origin;
472 rv = nsContentUtils::GetUTFOrigin(srcURI, origin);
473 NS_ENSURE_SUCCESS(rv, rv);
474
475 nsAutoCString spec;
476 rv = srcURI->GetSpec(spec);
477 NS_ENSURE_SUCCESS(rv, rv);
478
479 mEventSource->mOriginalURL = NS_ConvertUTF8toUTF16(spec);
480 mSrc = srcURI;
481 mOrigin = origin;
482 return NS_OK;
483 }
484
AddWindowObservers()485 nsresult EventSourceImpl::AddWindowObservers() {
486 AssertIsOnMainThread();
487 MOZ_ASSERT(mIsMainThread);
488 MOZ_ASSERT(!IsShutDown());
489 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
490 NS_ENSURE_STATE(os);
491
492 nsresult rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
493 NS_ENSURE_SUCCESS(rv, rv);
494 rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
495 NS_ENSURE_SUCCESS(rv, rv);
496 rv = os->AddObserver(this, DOM_WINDOW_THAWED_TOPIC, true);
497 NS_ENSURE_SUCCESS(rv, rv);
498 return NS_OK;
499 }
500
RemoveWindowObservers()501 void EventSourceImpl::RemoveWindowObservers() {
502 AssertIsOnMainThread();
503 MOZ_ASSERT(mIsMainThread);
504 MOZ_ASSERT(IsClosed());
505 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
506 if (os) {
507 os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
508 os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
509 os->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC);
510 }
511 }
512
Init(nsIPrincipal * aPrincipal,const nsAString & aURL,ErrorResult & aRv)513 void EventSourceImpl::Init(nsIPrincipal* aPrincipal, const nsAString& aURL,
514 ErrorResult& aRv) {
515 AssertIsOnMainThread();
516 MOZ_ASSERT(aPrincipal);
517 MOZ_ASSERT(ReadyState() == CONNECTING);
518 mPrincipal = aPrincipal;
519 aRv = ParseURL(aURL);
520 if (NS_WARN_IF(aRv.Failed())) {
521 return;
522 }
523 // The conditional here is historical and not necessarily sane.
524 if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
525 nsJSUtils::GetCallingLocation(cx, mScriptFile, &mScriptLine,
526 &mScriptColumn);
527 mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
528 }
529
530 if (mIsMainThread) {
531 // we observe when the window freezes and thaws
532 aRv = AddWindowObservers();
533 if (NS_WARN_IF(aRv.Failed())) {
534 return;
535 }
536 }
537
538 mReconnectionTime =
539 Preferences::GetInt("dom.server-events.default-reconnection-time",
540 DEFAULT_RECONNECTION_TIME_VALUE);
541
542 mUnicodeDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval();
543
544 // the constructor should throw a SYNTAX_ERROR only if it fails resolving the
545 // url parameter, so we don't care about the InitChannelAndRequestEventSource
546 // result.
547 InitChannelAndRequestEventSource();
548 }
549
550 //-----------------------------------------------------------------------------
551 // EventSourceImpl::nsIObserver
552 //-----------------------------------------------------------------------------
553
554 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)555 EventSourceImpl::Observe(nsISupports* aSubject, const char* aTopic,
556 const char16_t* aData) {
557 AssertIsOnMainThread();
558 if (IsClosed()) {
559 return NS_OK;
560 }
561
562 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aSubject);
563 if (!mEventSource->GetOwner() || window != mEventSource->GetOwner()) {
564 return NS_OK;
565 }
566
567 DebugOnly<nsresult> rv;
568 if (strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) {
569 rv = Freeze();
570 MOZ_ASSERT(NS_SUCCEEDED(rv), "Freeze() failed");
571 } else if (strcmp(aTopic, DOM_WINDOW_THAWED_TOPIC) == 0) {
572 rv = Thaw();
573 MOZ_ASSERT(NS_SUCCEEDED(rv), "Thaw() failed");
574 } else if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) {
575 Close();
576 }
577
578 return NS_OK;
579 }
580
581 //-----------------------------------------------------------------------------
582 // EventSourceImpl::nsIStreamListener
583 //-----------------------------------------------------------------------------
584
585 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest,nsISupports * aCtxt)586 EventSourceImpl::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt) {
587 AssertIsOnMainThread();
588 if (IsClosed()) {
589 return NS_ERROR_ABORT;
590 }
591 nsresult rv = CheckHealthOfRequestCallback(aRequest);
592 NS_ENSURE_SUCCESS(rv, rv);
593
594 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
595 NS_ENSURE_SUCCESS(rv, rv);
596
597 nsresult status;
598 rv = aRequest->GetStatus(&status);
599 NS_ENSURE_SUCCESS(rv, rv);
600
601 if (NS_FAILED(status)) {
602 // EventSource::OnStopRequest will evaluate if it shall either reestablish
603 // or fail the connection
604 return NS_ERROR_ABORT;
605 }
606
607 uint32_t httpStatus;
608 rv = httpChannel->GetResponseStatus(&httpStatus);
609 NS_ENSURE_SUCCESS(rv, rv);
610
611 if (httpStatus != 200) {
612 DispatchFailConnection();
613 return NS_ERROR_ABORT;
614 }
615
616 nsAutoCString contentType;
617 rv = httpChannel->GetContentType(contentType);
618 NS_ENSURE_SUCCESS(rv, rv);
619
620 if (!contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
621 DispatchFailConnection();
622 return NS_ERROR_ABORT;
623 }
624
625 if (!mIsMainThread) {
626 // Try to retarget to worker thread, otherwise fall back to main thread.
627 nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(httpChannel);
628 if (rr) {
629 rv = rr->RetargetDeliveryTo(this);
630 if (NS_WARN_IF(NS_FAILED(rv))) {
631 NS_WARNING("Retargeting failed");
632 }
633 }
634 }
635 rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
636 this, &EventSourceImpl::AnnounceConnection),
637 NS_DISPATCH_NORMAL);
638 NS_ENSURE_SUCCESS(rv, rv);
639 mStatus = PARSE_STATE_BEGIN_OF_STREAM;
640 return NS_OK;
641 }
642
643 // this method parses the characters as they become available instead of
644 // buffering them.
StreamReaderFunc(nsIInputStream * aInputStream,void * aClosure,const char * aFromRawSegment,uint32_t aToOffset,uint32_t aCount,uint32_t * aWriteCount)645 nsresult EventSourceImpl::StreamReaderFunc(nsIInputStream* aInputStream,
646 void* aClosure,
647 const char* aFromRawSegment,
648 uint32_t aToOffset, uint32_t aCount,
649 uint32_t* aWriteCount) {
650 EventSourceImpl* thisObject = static_cast<EventSourceImpl*>(aClosure);
651 if (!thisObject || !aWriteCount) {
652 NS_WARNING(
653 "EventSource cannot read from stream: no aClosure or aWriteCount");
654 return NS_ERROR_FAILURE;
655 }
656 thisObject->AssertIsOnTargetThread();
657 MOZ_ASSERT(!thisObject->IsShutDown());
658 thisObject->ParseSegment((const char*)aFromRawSegment, aCount);
659 *aWriteCount = aCount;
660 return NS_OK;
661 }
662
ParseSegment(const char * aBuffer,uint32_t aLength)663 void EventSourceImpl::ParseSegment(const char* aBuffer, uint32_t aLength) {
664 AssertIsOnTargetThread();
665 if (IsClosed()) {
666 return;
667 }
668 char16_t buffer[1024];
669 auto dst = MakeSpan(buffer);
670 auto src = AsBytes(MakeSpan(aBuffer, aLength));
671 // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018
672 for (;;) {
673 uint32_t result;
674 size_t read;
675 size_t written;
676 bool hadErrors;
677 Tie(result, read, written, hadErrors) =
678 mUnicodeDecoder->DecodeToUTF16(src, dst, false);
679 Unused << hadErrors;
680 for (auto c : dst.To(written)) {
681 nsresult rv = ParseCharacter(c);
682 NS_ENSURE_SUCCESS_VOID(rv);
683 }
684 if (result == kInputEmpty) {
685 return;
686 }
687 src = src.From(read);
688 }
689 }
690
691 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsISupports * aContext,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)692 EventSourceImpl::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
693 nsIInputStream* aInputStream, uint64_t aOffset,
694 uint32_t aCount) {
695 AssertIsOnTargetThread();
696 NS_ENSURE_ARG_POINTER(aInputStream);
697 if (IsClosed()) {
698 return NS_ERROR_ABORT;
699 }
700
701 nsresult rv = CheckHealthOfRequestCallback(aRequest);
702 NS_ENSURE_SUCCESS(rv, rv);
703
704 uint32_t totalRead;
705 return aInputStream->ReadSegments(EventSourceImpl::StreamReaderFunc, this,
706 aCount, &totalRead);
707 }
708
709 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsISupports * aContext,nsresult aStatusCode)710 EventSourceImpl::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
711 nsresult aStatusCode) {
712 AssertIsOnMainThread();
713
714 if (IsClosed()) {
715 return NS_ERROR_ABORT;
716 }
717 MOZ_ASSERT(mSrc);
718 // "Network errors that prevents the connection from being established in the
719 // first place (e.g. DNS errors), must cause the user agent to asynchronously
720 // reestablish the connection.
721 //
722 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in
723 // response to window.stop() or the user canceling the network connection
724 // manually) must cause the user agent to fail the connection.
725
726 if (NS_FAILED(aStatusCode) && aStatusCode != NS_ERROR_CONNECTION_REFUSED &&
727 aStatusCode != NS_ERROR_NET_TIMEOUT &&
728 aStatusCode != NS_ERROR_NET_RESET &&
729 aStatusCode != NS_ERROR_NET_INTERRUPT &&
730 aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED &&
731 aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL) {
732 DispatchFailConnection();
733 return NS_ERROR_ABORT;
734 }
735
736 nsresult rv = CheckHealthOfRequestCallback(aRequest);
737 NS_ENSURE_SUCCESS(rv, rv);
738
739 rv =
740 Dispatch(NewRunnableMethod("dom::EventSourceImpl::ReestablishConnection",
741 this, &EventSourceImpl::ReestablishConnection),
742 NS_DISPATCH_NORMAL);
743 NS_ENSURE_SUCCESS(rv, rv);
744
745 return NS_OK;
746 }
747
748 //-----------------------------------------------------------------------------
749 // EventSourceImpl::nsIChannelEventSink
750 //-----------------------------------------------------------------------------
751
752 NS_IMETHODIMP
AsyncOnChannelRedirect(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aFlags,nsIAsyncVerifyRedirectCallback * aCallback)753 EventSourceImpl::AsyncOnChannelRedirect(
754 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
755 nsIAsyncVerifyRedirectCallback* aCallback) {
756 AssertIsOnMainThread();
757 if (IsClosed()) {
758 return NS_ERROR_ABORT;
759 }
760 nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel);
761 NS_PRECONDITION(aOldRequest, "Redirect from a null request?");
762
763 nsresult rv = CheckHealthOfRequestCallback(aOldRequest);
764 NS_ENSURE_SUCCESS(rv, rv);
765
766 NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
767
768 nsCOMPtr<nsIURI> newURI;
769 rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
770 NS_ENSURE_SUCCESS(rv, rv);
771
772 bool isValidScheme =
773 (NS_SUCCEEDED(newURI->SchemeIs("http", &isValidScheme)) &&
774 isValidScheme) ||
775 (NS_SUCCEEDED(newURI->SchemeIs("https", &isValidScheme)) &&
776 isValidScheme);
777
778 rv = mEventSource->CheckInnerWindowCorrectness();
779 if (NS_FAILED(rv) || !isValidScheme) {
780 DispatchFailConnection();
781 return NS_ERROR_DOM_SECURITY_ERR;
782 }
783
784 // update our channel
785
786 mHttpChannel = do_QueryInterface(aNewChannel);
787 NS_ENSURE_STATE(mHttpChannel);
788
789 SetupHttpChannel();
790 // The HTTP impl already copies over the referrer and referrer policy on
791 // redirects, so we don't need to SetupReferrerPolicy().
792
793 if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
794 rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
795 NS_ENSURE_SUCCESS(rv, rv);
796 }
797
798 aCallback->OnRedirectVerifyCallback(NS_OK);
799
800 return NS_OK;
801 }
802
803 //-----------------------------------------------------------------------------
804 // EventSourceImpl::nsIInterfaceRequestor
805 //-----------------------------------------------------------------------------
806
807 NS_IMETHODIMP
GetInterface(const nsIID & aIID,void ** aResult)808 EventSourceImpl::GetInterface(const nsIID& aIID, void** aResult) {
809 AssertIsOnMainThread();
810
811 if (IsClosed()) {
812 return NS_ERROR_FAILURE;
813 }
814
815 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
816 *aResult = static_cast<nsIChannelEventSink*>(this);
817 NS_ADDREF_THIS();
818 return NS_OK;
819 }
820
821 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
822 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
823 nsresult rv = mEventSource->CheckInnerWindowCorrectness();
824 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
825
826 nsCOMPtr<nsIPromptFactory> wwatch =
827 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
828 NS_ENSURE_SUCCESS(rv, rv);
829
830 // Get the an auth prompter for our window so that the parenting
831 // of the dialogs works as it should when using tabs.
832
833 nsCOMPtr<nsPIDOMWindowOuter> window;
834 if (mEventSource->GetOwner()) {
835 window = mEventSource->GetOwner()->GetOuterWindow();
836 }
837
838 return wwatch->GetPrompt(window, aIID, aResult);
839 }
840
841 return QueryInterface(aIID, aResult);
842 }
843
844 NS_IMETHODIMP
IsOnCurrentThread(bool * aResult)845 EventSourceImpl::IsOnCurrentThread(bool* aResult) {
846 *aResult = IsTargetThread();
847 return NS_OK;
848 }
849
NS_IMETHODIMP_(bool)850 NS_IMETHODIMP_(bool)
851 EventSourceImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
852
GetBaseURI(nsIURI ** aBaseURI)853 nsresult EventSourceImpl::GetBaseURI(nsIURI** aBaseURI) {
854 AssertIsOnMainThread();
855 MOZ_ASSERT(!IsShutDown());
856 NS_ENSURE_ARG_POINTER(aBaseURI);
857
858 *aBaseURI = nullptr;
859
860 nsCOMPtr<nsIURI> baseURI;
861
862 // first we try from document->GetBaseURI()
863 nsCOMPtr<nsIDocument> doc = mEventSource->GetDocumentIfCurrent();
864 if (doc) {
865 baseURI = doc->GetBaseURI();
866 }
867
868 // otherwise we get from the doc's principal
869 if (!baseURI) {
870 nsresult rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
871 NS_ENSURE_SUCCESS(rv, rv);
872 }
873
874 NS_ENSURE_STATE(baseURI);
875
876 baseURI.forget(aBaseURI);
877 return NS_OK;
878 }
879
SetupHttpChannel()880 void EventSourceImpl::SetupHttpChannel() {
881 AssertIsOnMainThread();
882 MOZ_ASSERT(!IsShutDown());
883 nsresult rv = mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
884 MOZ_ASSERT(NS_SUCCEEDED(rv));
885
886 /* set the http request headers */
887
888 rv = mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
889 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM),
890 false);
891 MOZ_ASSERT(NS_SUCCEEDED(rv));
892
893 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
894
895 if (mLastEventID.IsEmpty()) {
896 return;
897 }
898 NS_ConvertUTF16toUTF8 eventId(mLastEventID);
899 rv = mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"),
900 eventId, false);
901 #ifdef DEBUG
902 if (NS_FAILED(rv)) {
903 MOZ_LOG(gEventSourceLog, LogLevel::Warning,
904 ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv), eventId.get()));
905 }
906 #endif
907 Unused << rv;
908 }
909
SetupReferrerPolicy()910 nsresult EventSourceImpl::SetupReferrerPolicy() {
911 AssertIsOnMainThread();
912 MOZ_ASSERT(!IsShutDown());
913 nsCOMPtr<nsIDocument> doc = mEventSource->GetDocumentIfCurrent();
914 if (doc) {
915 nsresult rv = mHttpChannel->SetReferrerWithPolicy(doc->GetDocumentURI(),
916 doc->GetReferrerPolicy());
917 NS_ENSURE_SUCCESS(rv, rv);
918 }
919
920 return NS_OK;
921 }
922
InitChannelAndRequestEventSource()923 nsresult EventSourceImpl::InitChannelAndRequestEventSource() {
924 AssertIsOnMainThread();
925 if (IsClosed()) {
926 return NS_ERROR_ABORT;
927 }
928
929 bool isValidScheme =
930 (NS_SUCCEEDED(mSrc->SchemeIs("http", &isValidScheme)) && isValidScheme) ||
931 (NS_SUCCEEDED(mSrc->SchemeIs("https", &isValidScheme)) && isValidScheme);
932
933 nsresult rv = mEventSource->CheckInnerWindowCorrectness();
934 if (NS_FAILED(rv) || !isValidScheme) {
935 DispatchFailConnection();
936 return NS_ERROR_DOM_SECURITY_ERR;
937 }
938
939 // The html spec requires we use fetch cache mode of "no-store". This
940 // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
941 nsLoadFlags loadFlags;
942 loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE |
943 nsIRequest::INHIBIT_CACHING;
944
945 nsCOMPtr<nsIDocument> doc = mEventSource->GetDocumentIfCurrent();
946
947 nsSecurityFlags securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
948
949 if (mEventSource->mWithCredentials) {
950 securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
951 }
952
953 nsCOMPtr<nsIChannel> channel;
954 // If we have the document, use it
955 if (doc) {
956 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
957 rv = NS_NewChannel(getter_AddRefs(channel), mSrc, doc, securityFlags,
958 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
959 nullptr, // aPerformanceStorage
960 loadGroup,
961 nullptr, // aCallbacks
962 loadFlags); // aLoadFlags
963 } else {
964 // otherwise use the principal
965 rv = NS_NewChannel(getter_AddRefs(channel), mSrc, mPrincipal, securityFlags,
966 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
967 nullptr, // aPerformanceStorage
968 nullptr, // loadGroup
969 nullptr, // aCallbacks
970 loadFlags); // aLoadFlags
971 }
972
973 NS_ENSURE_SUCCESS(rv, rv);
974
975 mHttpChannel = do_QueryInterface(channel);
976 NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
977
978 SetupHttpChannel();
979 rv = SetupReferrerPolicy();
980 NS_ENSURE_SUCCESS(rv, rv);
981
982 #ifdef DEBUG
983 {
984 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
985 mHttpChannel->GetNotificationCallbacks(
986 getter_AddRefs(notificationCallbacks));
987 MOZ_ASSERT(!notificationCallbacks);
988 }
989 #endif
990 mHttpChannel->SetNotificationCallbacks(this);
991
992 // Start reading from the channel
993 rv = mHttpChannel->AsyncOpen2(this);
994 if (NS_FAILED(rv)) {
995 DispatchFailConnection();
996 return rv;
997 }
998 // Create the connection. Ask EventSource to hold reference until Close is
999 // called or network error is received.
1000 mEventSource->UpdateMustKeepAlive();
1001 return rv;
1002 }
1003
AnnounceConnection()1004 void EventSourceImpl::AnnounceConnection() {
1005 AssertIsOnTargetThread();
1006 if (ReadyState() != CONNECTING) {
1007 NS_WARNING("Unexpected mReadyState!!!");
1008 return;
1009 }
1010
1011 // When a user agent is to announce the connection, the user agent must set
1012 // the readyState attribute to OPEN and queue a task to fire a simple event
1013 // named open at the EventSource object.
1014
1015 SetReadyState(OPEN);
1016
1017 nsresult rv = mEventSource->CheckInnerWindowCorrectness();
1018 if (NS_FAILED(rv)) {
1019 return;
1020 }
1021 rv = mEventSource->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
1022 if (NS_FAILED(rv)) {
1023 NS_WARNING("Failed to dispatch the error event!!!");
1024 return;
1025 }
1026 }
1027
ResetConnection()1028 nsresult EventSourceImpl::ResetConnection() {
1029 AssertIsOnMainThread();
1030 if (mHttpChannel) {
1031 mHttpChannel->Cancel(NS_ERROR_ABORT);
1032 mHttpChannel = nullptr;
1033 }
1034 return NS_OK;
1035 }
1036
ResetDecoder()1037 void EventSourceImpl::ResetDecoder() {
1038 AssertIsOnTargetThread();
1039 if (mUnicodeDecoder) {
1040 UTF_8_ENCODING->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder);
1041 }
1042 mStatus = PARSE_STATE_OFF;
1043 ClearFields();
1044 }
1045
1046 class CallRestartConnection final : public WorkerMainThreadRunnable {
1047 public:
CallRestartConnection(EventSourceImpl * aEventSourceImpl)1048 explicit CallRestartConnection(EventSourceImpl* aEventSourceImpl)
1049 : WorkerMainThreadRunnable(
1050 aEventSourceImpl->mWorkerPrivate,
1051 NS_LITERAL_CSTRING("EventSource :: RestartConnection")),
1052 mImpl(aEventSourceImpl) {
1053 mImpl->mWorkerPrivate->AssertIsOnWorkerThread();
1054 }
1055
MainThreadRun()1056 bool MainThreadRun() override {
1057 mImpl->RestartConnection();
1058 return true;
1059 }
1060
1061 protected:
1062 // Raw pointer because this runnable is sync.
1063 EventSourceImpl* mImpl;
1064 };
1065
RestartConnection()1066 nsresult EventSourceImpl::RestartConnection() {
1067 AssertIsOnMainThread();
1068 if (IsClosed()) {
1069 return NS_ERROR_ABORT;
1070 }
1071 nsresult rv = ResetConnection();
1072 NS_ENSURE_SUCCESS(rv, rv);
1073 rv = SetReconnectionTimeout();
1074 NS_ENSURE_SUCCESS(rv, rv);
1075 return NS_OK;
1076 }
1077
ReestablishConnection()1078 void EventSourceImpl::ReestablishConnection() {
1079 AssertIsOnTargetThread();
1080 if (IsClosed()) {
1081 return;
1082 }
1083
1084 nsresult rv;
1085 if (mIsMainThread) {
1086 rv = RestartConnection();
1087 } else {
1088 RefPtr<CallRestartConnection> runnable = new CallRestartConnection(this);
1089 ErrorResult result;
1090 runnable->Dispatch(Terminating, result);
1091 MOZ_ASSERT(!result.Failed());
1092 rv = result.StealNSResult();
1093 }
1094 if (NS_FAILED(rv)) {
1095 return;
1096 }
1097
1098 rv = mEventSource->CheckInnerWindowCorrectness();
1099 if (NS_FAILED(rv)) {
1100 return;
1101 }
1102
1103 SetReadyState(CONNECTING);
1104 ResetDecoder();
1105 rv = mEventSource->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
1106 if (NS_FAILED(rv)) {
1107 NS_WARNING("Failed to dispatch the error event!!!");
1108 return;
1109 }
1110 }
1111
SetReconnectionTimeout()1112 nsresult EventSourceImpl::SetReconnectionTimeout() {
1113 AssertIsOnMainThread();
1114 if (IsClosed()) {
1115 return NS_ERROR_ABORT;
1116 }
1117
1118 // the timer will be used whenever the requests are going finished.
1119 if (!mTimer) {
1120 mTimer = NS_NewTimer();
1121 NS_ENSURE_STATE(mTimer);
1122 }
1123
1124 nsresult rv = mTimer->InitWithNamedFuncCallback(
1125 TimerCallback, this, mReconnectionTime, nsITimer::TYPE_ONE_SHOT,
1126 "dom::EventSourceImpl::SetReconnectionTimeout");
1127 NS_ENSURE_SUCCESS(rv, rv);
1128
1129 return NS_OK;
1130 }
1131
PrintErrorOnConsole(const char * aBundleURI,const char * aError,const char16_t ** aFormatStrings,uint32_t aFormatStringsLen)1132 nsresult EventSourceImpl::PrintErrorOnConsole(const char* aBundleURI,
1133 const char* aError,
1134 const char16_t** aFormatStrings,
1135 uint32_t aFormatStringsLen) {
1136 AssertIsOnMainThread();
1137 MOZ_ASSERT(!IsShutDown());
1138 nsCOMPtr<nsIStringBundleService> bundleService =
1139 mozilla::services::GetStringBundleService();
1140 NS_ENSURE_STATE(bundleService);
1141
1142 nsCOMPtr<nsIStringBundle> strBundle;
1143 nsresult rv =
1144 bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
1145 NS_ENSURE_SUCCESS(rv, rv);
1146
1147 nsCOMPtr<nsIConsoleService> console(
1148 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
1149 NS_ENSURE_SUCCESS(rv, rv);
1150
1151 nsCOMPtr<nsIScriptError> errObj(
1152 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
1153 NS_ENSURE_SUCCESS(rv, rv);
1154
1155 // Localize the error message
1156 nsAutoString message;
1157 if (aFormatStrings) {
1158 rv = strBundle->FormatStringFromName(aError, aFormatStrings,
1159 aFormatStringsLen, message);
1160 } else {
1161 rv = strBundle->GetStringFromName(aError, message);
1162 }
1163 NS_ENSURE_SUCCESS(rv, rv);
1164
1165 rv = errObj->InitWithWindowID(
1166 message, mScriptFile, EmptyString(), mScriptLine, mScriptColumn,
1167 nsIScriptError::errorFlag, "Event Source", mInnerWindowID);
1168 NS_ENSURE_SUCCESS(rv, rv);
1169
1170 // print the error message directly to the JS console
1171 rv = console->LogMessage(errObj);
1172 NS_ENSURE_SUCCESS(rv, rv);
1173
1174 return NS_OK;
1175 }
1176
ConsoleError()1177 nsresult EventSourceImpl::ConsoleError() {
1178 AssertIsOnMainThread();
1179 MOZ_ASSERT(!IsShutDown());
1180 nsAutoCString targetSpec;
1181 nsresult rv = mSrc->GetSpec(targetSpec);
1182 NS_ENSURE_SUCCESS(rv, rv);
1183
1184 NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
1185 const char16_t* formatStrings[] = {specUTF16.get()};
1186
1187 if (ReadyState() == CONNECTING) {
1188 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1189 "connectionFailure", formatStrings,
1190 ArrayLength(formatStrings));
1191 } else {
1192 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1193 "netInterrupt", formatStrings,
1194 ArrayLength(formatStrings));
1195 }
1196 NS_ENSURE_SUCCESS(rv, rv);
1197
1198 return NS_OK;
1199 }
1200
DispatchFailConnection()1201 void EventSourceImpl::DispatchFailConnection() {
1202 AssertIsOnMainThread();
1203 if (IsClosed()) {
1204 return;
1205 }
1206 nsresult rv = ConsoleError();
1207 if (NS_FAILED(rv)) {
1208 NS_WARNING("Failed to print to the console error");
1209 }
1210 rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this,
1211 &EventSourceImpl::FailConnection),
1212 NS_DISPATCH_NORMAL);
1213 MOZ_ASSERT(NS_SUCCEEDED(rv));
1214 }
1215
FailConnection()1216 void EventSourceImpl::FailConnection() {
1217 AssertIsOnTargetThread();
1218 if (IsClosed()) {
1219 return;
1220 }
1221 // Must change state to closed before firing event to content.
1222 SetReadyState(CLOSED);
1223 // When a user agent is to fail the connection, the user agent must set the
1224 // readyState attribute to CLOSED and queue a task to fire a simple event
1225 // named error at the EventSource object.
1226 nsresult rv = mEventSource->CheckInnerWindowCorrectness();
1227 if (NS_SUCCEEDED(rv)) {
1228 rv = mEventSource->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
1229 if (NS_FAILED(rv)) {
1230 NS_WARNING("Failed to dispatch the error event!!!");
1231 }
1232 }
1233 // Call CloseInternal in the end of function because it may release
1234 // EventSourceImpl.
1235 CloseInternal();
1236 }
1237
1238 // static
TimerCallback(nsITimer * aTimer,void * aClosure)1239 void EventSourceImpl::TimerCallback(nsITimer* aTimer, void* aClosure) {
1240 AssertIsOnMainThread();
1241 RefPtr<EventSourceImpl> thisObject = static_cast<EventSourceImpl*>(aClosure);
1242
1243 if (thisObject->IsClosed()) {
1244 return;
1245 }
1246
1247 NS_PRECONDITION(!thisObject->mHttpChannel,
1248 "the channel hasn't been cancelled!!");
1249
1250 if (!thisObject->IsFrozen()) {
1251 nsresult rv = thisObject->InitChannelAndRequestEventSource();
1252 if (NS_FAILED(rv)) {
1253 NS_WARNING("thisObject->InitChannelAndRequestEventSource() failed");
1254 return;
1255 }
1256 }
1257 }
1258
Thaw()1259 nsresult EventSourceImpl::Thaw() {
1260 AssertIsOnMainThread();
1261 if (IsClosed() || !IsFrozen()) {
1262 return NS_OK;
1263 }
1264
1265 MOZ_ASSERT(!mHttpChannel, "the connection hasn't been closed!!!");
1266
1267 SetFrozen(false);
1268 nsresult rv;
1269 if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) {
1270 nsCOMPtr<nsIRunnable> event =
1271 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1272 this, &EventSourceImpl::DispatchAllMessageEvents);
1273 NS_ENSURE_STATE(event);
1274
1275 mGoingToDispatchAllMessages = true;
1276
1277 rv = Dispatch(event.forget(), NS_DISPATCH_NORMAL);
1278 NS_ENSURE_SUCCESS(rv, rv);
1279 }
1280
1281 rv = InitChannelAndRequestEventSource();
1282 NS_ENSURE_SUCCESS(rv, rv);
1283
1284 return NS_OK;
1285 }
1286
Freeze()1287 nsresult EventSourceImpl::Freeze() {
1288 AssertIsOnMainThread();
1289 if (IsClosed() || IsFrozen()) {
1290 return NS_OK;
1291 }
1292
1293 MOZ_ASSERT(!mHttpChannel, "the connection hasn't been closed!!!");
1294 SetFrozen(true);
1295 return NS_OK;
1296 }
1297
DispatchCurrentMessageEvent()1298 nsresult EventSourceImpl::DispatchCurrentMessageEvent() {
1299 AssertIsOnTargetThread();
1300 MOZ_ASSERT(!IsShutDown());
1301 UniquePtr<Message> message(Move(mCurrentMessage));
1302 ClearFields();
1303
1304 if (!message || message->mData.IsEmpty()) {
1305 return NS_OK;
1306 }
1307
1308 // removes the trailing LF from mData
1309 MOZ_ASSERT(message->mData.CharAt(message->mData.Length() - 1) == LF_CHAR,
1310 "Invalid trailing character! LF was expected instead.");
1311 message->mData.SetLength(message->mData.Length() - 1);
1312
1313 if (message->mEventName.IsEmpty()) {
1314 message->mEventName.AssignLiteral("message");
1315 }
1316
1317 if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) {
1318 message->mLastEventID.Assign(mLastEventID);
1319 }
1320
1321 size_t sizeBefore = mMessagesToDispatch.GetSize();
1322 mMessagesToDispatch.Push(message.release());
1323 NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1,
1324 NS_ERROR_OUT_OF_MEMORY);
1325
1326 if (!mGoingToDispatchAllMessages) {
1327 nsCOMPtr<nsIRunnable> event =
1328 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1329 this, &EventSourceImpl::DispatchAllMessageEvents);
1330 NS_ENSURE_STATE(event);
1331
1332 mGoingToDispatchAllMessages = true;
1333
1334 return Dispatch(event.forget(), NS_DISPATCH_NORMAL);
1335 }
1336
1337 return NS_OK;
1338 }
1339
DispatchAllMessageEvents()1340 void EventSourceImpl::DispatchAllMessageEvents() {
1341 AssertIsOnTargetThread();
1342 mGoingToDispatchAllMessages = false;
1343
1344 if (IsClosed() || IsFrozen()) {
1345 return;
1346 }
1347
1348 nsresult rv = mEventSource->CheckInnerWindowCorrectness();
1349 if (NS_FAILED(rv)) {
1350 return;
1351 }
1352
1353 AutoJSAPI jsapi;
1354 if (mIsMainThread) {
1355 if (NS_WARN_IF(!jsapi.Init(mEventSource->GetOwner()))) {
1356 return;
1357 }
1358 } else {
1359 MOZ_ASSERT(mWorkerPrivate);
1360 if (NS_WARN_IF(!jsapi.Init(mWorkerPrivate->GlobalScope()))) {
1361 return;
1362 }
1363 }
1364 JSContext* cx = jsapi.cx();
1365
1366 while (mMessagesToDispatch.GetSize() > 0) {
1367 UniquePtr<Message> message(
1368 static_cast<Message*>(mMessagesToDispatch.PopFront()));
1369 // Now we can turn our string into a jsval
1370 JS::Rooted<JS::Value> jsData(cx);
1371 {
1372 JSString* jsString;
1373 jsString = JS_NewUCStringCopyN(cx, message->mData.get(),
1374 message->mData.Length());
1375 NS_ENSURE_TRUE_VOID(jsString);
1376
1377 jsData.setString(jsString);
1378 }
1379
1380 // create an event that uses the MessageEvent interface,
1381 // which does not bubble, is not cancelable, and has no default action
1382
1383 RefPtr<MessageEvent> event =
1384 new MessageEvent(mEventSource, nullptr, nullptr);
1385
1386 event->InitMessageEvent(nullptr, message->mEventName, false, false, jsData,
1387 mOrigin, message->mLastEventID, nullptr,
1388 Sequence<OwningNonNull<MessagePort>>());
1389 event->SetTrusted(true);
1390
1391 bool dummy;
1392 rv = mEventSource->DispatchEvent(static_cast<Event*>(event), &dummy);
1393 if (NS_FAILED(rv)) {
1394 NS_WARNING("Failed to dispatch the message event!!!");
1395 return;
1396 }
1397
1398 mLastEventID.Assign(message->mLastEventID);
1399 if (IsClosed() || IsFrozen()) {
1400 return;
1401 }
1402 }
1403 }
1404
ClearFields()1405 void EventSourceImpl::ClearFields() {
1406 AssertIsOnTargetThread();
1407 mCurrentMessage = nullptr;
1408 mLastFieldName.Truncate();
1409 mLastFieldValue.Truncate();
1410 }
1411
SetFieldAndClear()1412 nsresult EventSourceImpl::SetFieldAndClear() {
1413 MOZ_ASSERT(!IsShutDown());
1414 AssertIsOnTargetThread();
1415 if (mLastFieldName.IsEmpty()) {
1416 mLastFieldValue.Truncate();
1417 return NS_OK;
1418 }
1419 if (!mCurrentMessage) {
1420 mCurrentMessage = MakeUnique<Message>();
1421 }
1422 char16_t first_char;
1423 first_char = mLastFieldName.CharAt(0);
1424
1425 // with no case folding performed
1426 switch (first_char) {
1427 case char16_t('d'):
1428 if (mLastFieldName.EqualsLiteral("data")) {
1429 // If the field name is "data" append the field value to the data
1430 // buffer, then append a single U+000A LINE FEED (LF) character
1431 // to the data buffer.
1432 mCurrentMessage->mData.Append(mLastFieldValue);
1433 mCurrentMessage->mData.Append(LF_CHAR);
1434 }
1435 break;
1436
1437 case char16_t('e'):
1438 if (mLastFieldName.EqualsLiteral("event")) {
1439 mCurrentMessage->mEventName.Assign(mLastFieldValue);
1440 }
1441 break;
1442
1443 case char16_t('i'):
1444 if (mLastFieldName.EqualsLiteral("id")) {
1445 mCurrentMessage->mLastEventID.Assign(mLastFieldValue);
1446 }
1447 break;
1448
1449 case char16_t('r'):
1450 if (mLastFieldName.EqualsLiteral("retry")) {
1451 uint32_t newValue = 0;
1452 uint32_t i = 0; // we must ensure that there are only digits
1453 bool assign = true;
1454 for (i = 0; i < mLastFieldValue.Length(); ++i) {
1455 if (mLastFieldValue.CharAt(i) < (char16_t)'0' ||
1456 mLastFieldValue.CharAt(i) > (char16_t)'9') {
1457 assign = false;
1458 break;
1459 }
1460 newValue = newValue * 10 + (((uint32_t)mLastFieldValue.CharAt(i)) -
1461 ((uint32_t)((char16_t)'0')));
1462 }
1463
1464 if (assign) {
1465 if (newValue < MIN_RECONNECTION_TIME_VALUE) {
1466 mReconnectionTime = MIN_RECONNECTION_TIME_VALUE;
1467 } else if (newValue > MAX_RECONNECTION_TIME_VALUE) {
1468 mReconnectionTime = MAX_RECONNECTION_TIME_VALUE;
1469 } else {
1470 mReconnectionTime = newValue;
1471 }
1472 }
1473 break;
1474 }
1475 break;
1476 }
1477
1478 mLastFieldName.Truncate();
1479 mLastFieldValue.Truncate();
1480
1481 return NS_OK;
1482 }
1483
CheckHealthOfRequestCallback(nsIRequest * aRequestCallback)1484 nsresult EventSourceImpl::CheckHealthOfRequestCallback(
1485 nsIRequest* aRequestCallback) {
1486 // This function could be run on target thread if http channel support
1487 // nsIThreadRetargetableRequest. otherwise, it's run on main thread.
1488
1489 // check if we have been closed or if the request has been canceled
1490 // or if we have been frozen
1491 if (IsClosed() || IsFrozen() || !mHttpChannel) {
1492 return NS_ERROR_ABORT;
1493 }
1494
1495 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequestCallback);
1496 NS_ENSURE_STATE(httpChannel);
1497
1498 if (httpChannel != mHttpChannel) {
1499 NS_WARNING("wrong channel from request callback");
1500 return NS_ERROR_ABORT;
1501 }
1502
1503 return NS_OK;
1504 }
1505
ParseCharacter(char16_t aChr)1506 nsresult EventSourceImpl::ParseCharacter(char16_t aChr) {
1507 AssertIsOnTargetThread();
1508 nsresult rv;
1509
1510 if (IsClosed()) {
1511 return NS_ERROR_ABORT;
1512 }
1513
1514 switch (mStatus) {
1515 case PARSE_STATE_OFF:
1516 NS_ERROR("Invalid state");
1517 return NS_ERROR_FAILURE;
1518 break;
1519
1520 case PARSE_STATE_BEGIN_OF_STREAM:
1521 if (aChr == CR_CHAR) {
1522 mStatus = PARSE_STATE_CR_CHAR;
1523 } else if (aChr == LF_CHAR) {
1524 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1525 } else if (aChr == COLON_CHAR) {
1526 mStatus = PARSE_STATE_COMMENT;
1527 } else {
1528 mLastFieldName += aChr;
1529 mStatus = PARSE_STATE_FIELD_NAME;
1530 }
1531 break;
1532
1533 case PARSE_STATE_CR_CHAR:
1534 if (aChr == CR_CHAR) {
1535 rv = DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1536 NS_ENSURE_SUCCESS(rv, rv);
1537 } else if (aChr == LF_CHAR) {
1538 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1539 } else if (aChr == COLON_CHAR) {
1540 mStatus = PARSE_STATE_COMMENT;
1541 } else {
1542 mLastFieldName += aChr;
1543 mStatus = PARSE_STATE_FIELD_NAME;
1544 }
1545
1546 break;
1547
1548 case PARSE_STATE_COMMENT:
1549 if (aChr == CR_CHAR) {
1550 mStatus = PARSE_STATE_CR_CHAR;
1551 } else if (aChr == LF_CHAR) {
1552 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1553 }
1554
1555 break;
1556
1557 case PARSE_STATE_FIELD_NAME:
1558 if (aChr == CR_CHAR) {
1559 rv = SetFieldAndClear();
1560 NS_ENSURE_SUCCESS(rv, rv);
1561
1562 mStatus = PARSE_STATE_CR_CHAR;
1563 } else if (aChr == LF_CHAR) {
1564 rv = SetFieldAndClear();
1565 NS_ENSURE_SUCCESS(rv, rv);
1566
1567 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1568 } else if (aChr == COLON_CHAR) {
1569 mStatus = PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE;
1570 } else {
1571 mLastFieldName += aChr;
1572 }
1573
1574 break;
1575
1576 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE:
1577 if (aChr == CR_CHAR) {
1578 rv = SetFieldAndClear();
1579 NS_ENSURE_SUCCESS(rv, rv);
1580
1581 mStatus = PARSE_STATE_CR_CHAR;
1582 } else if (aChr == LF_CHAR) {
1583 rv = SetFieldAndClear();
1584 NS_ENSURE_SUCCESS(rv, rv);
1585
1586 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1587 } else if (aChr == SPACE_CHAR) {
1588 mStatus = PARSE_STATE_FIELD_VALUE;
1589 } else {
1590 mLastFieldValue += aChr;
1591 mStatus = PARSE_STATE_FIELD_VALUE;
1592 }
1593
1594 break;
1595
1596 case PARSE_STATE_FIELD_VALUE:
1597 if (aChr == CR_CHAR) {
1598 rv = SetFieldAndClear();
1599 NS_ENSURE_SUCCESS(rv, rv);
1600
1601 mStatus = PARSE_STATE_CR_CHAR;
1602 } else if (aChr == LF_CHAR) {
1603 rv = SetFieldAndClear();
1604 NS_ENSURE_SUCCESS(rv, rv);
1605
1606 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1607 } else if (aChr != 0) {
1608 // Avoid appending the null char to the field value.
1609 mLastFieldValue += aChr;
1610 }
1611
1612 break;
1613
1614 case PARSE_STATE_BEGIN_OF_LINE:
1615 if (aChr == CR_CHAR) {
1616 rv = DispatchCurrentMessageEvent(); // there is an empty line
1617 NS_ENSURE_SUCCESS(rv, rv);
1618
1619 mStatus = PARSE_STATE_CR_CHAR;
1620 } else if (aChr == LF_CHAR) {
1621 rv = DispatchCurrentMessageEvent(); // there is an empty line
1622 NS_ENSURE_SUCCESS(rv, rv);
1623
1624 mStatus = PARSE_STATE_BEGIN_OF_LINE;
1625 } else if (aChr == COLON_CHAR) {
1626 mStatus = PARSE_STATE_COMMENT;
1627 } else if (aChr != 0) {
1628 // Avoid appending the null char to the field name.
1629 mLastFieldName += aChr;
1630 mStatus = PARSE_STATE_FIELD_NAME;
1631 }
1632
1633 break;
1634 }
1635
1636 return NS_OK;
1637 }
1638
AddRefObject()1639 void EventSourceImpl::AddRefObject() { AddRef(); }
1640
ReleaseObject()1641 void EventSourceImpl::ReleaseObject() { Release(); }
1642
1643 namespace {
1644 class EventSourceWorkerHolder final : public WorkerHolder {
1645 public:
EventSourceWorkerHolder(EventSourceImpl * aEventSourceImpl)1646 explicit EventSourceWorkerHolder(EventSourceImpl* aEventSourceImpl)
1647 : WorkerHolder("EventSourceWorkerHolder"),
1648 mEventSourceImpl(aEventSourceImpl) {}
1649
Notify(WorkerStatus aStatus)1650 bool Notify(WorkerStatus aStatus) override {
1651 MOZ_ASSERT(aStatus > Running);
1652 if (aStatus >= Canceling) {
1653 mEventSourceImpl->Close();
1654 }
1655 return true;
1656 }
1657
1658 private:
1659 // Raw pointer because the EventSourceImpl object keeps alive the holder.
1660 EventSourceImpl* mEventSourceImpl;
1661 };
1662
1663 class WorkerRunnableDispatcher final : public WorkerRunnable {
1664 RefPtr<EventSourceImpl> mEventSourceImpl;
1665
1666 public:
WorkerRunnableDispatcher(EventSourceImpl * aImpl,WorkerPrivate * aWorkerPrivate,already_AddRefed<nsIRunnable> aEvent)1667 WorkerRunnableDispatcher(EventSourceImpl* aImpl,
1668 WorkerPrivate* aWorkerPrivate,
1669 already_AddRefed<nsIRunnable> aEvent)
1670 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1671 mEventSourceImpl(aImpl),
1672 mEvent(Move(aEvent)) {}
1673
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1674 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1675 aWorkerPrivate->AssertIsOnWorkerThread();
1676 return !NS_FAILED(mEvent->Run());
1677 }
1678
PostRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aRunResult)1679 void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1680 bool aRunResult) override {}
1681
PreDispatch(WorkerPrivate * aWorkerPrivate)1682 bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
1683 // We don't call WorkerRunnable::PreDispatch because it would assert the
1684 // wrong thing about which thread we're on. We're on whichever thread the
1685 // channel implementation is running on (probably the main thread or
1686 // transport thread).
1687 return true;
1688 }
1689
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)1690 void PostDispatch(WorkerPrivate* aWorkerPrivate,
1691 bool aDispatchResult) override {
1692 // We don't call WorkerRunnable::PostDispatch because it would assert the
1693 // wrong thing about which thread we're on. We're on whichever thread the
1694 // channel implementation is running on (probably the main thread or
1695 // transport thread).
1696 }
1697
1698 private:
1699 nsCOMPtr<nsIRunnable> mEvent;
1700 };
1701
1702 } // namespace
1703
RegisterWorkerHolder()1704 bool EventSourceImpl::RegisterWorkerHolder() {
1705 MOZ_ASSERT(!IsShutDown());
1706 MOZ_ASSERT(mWorkerPrivate);
1707 mWorkerPrivate->AssertIsOnWorkerThread();
1708 MOZ_ASSERT(!mWorkerHolder);
1709 mWorkerHolder = new EventSourceWorkerHolder(this);
1710 if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
1711 mWorkerHolder = nullptr;
1712 return false;
1713 }
1714 return true;
1715 }
1716
UnregisterWorkerHolder()1717 void EventSourceImpl::UnregisterWorkerHolder() {
1718 // RegisterWorkerHolder fail will destroy EventSourceImpl and invoke
1719 // UnregisterWorkerHolder.
1720 MOZ_ASSERT(IsClosed());
1721 MOZ_ASSERT(mWorkerPrivate);
1722 mWorkerPrivate->AssertIsOnWorkerThread();
1723 // The DTOR of this WorkerHolder will release the worker for us.
1724 mWorkerHolder = nullptr;
1725 mWorkerPrivate = nullptr;
1726 }
1727
1728 //-----------------------------------------------------------------------------
1729 // EventSourceImpl::nsIEventTarget
1730 //-----------------------------------------------------------------------------
1731 NS_IMETHODIMP
DispatchFromScript(nsIRunnable * aEvent,uint32_t aFlags)1732 EventSourceImpl::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
1733 nsCOMPtr<nsIRunnable> event(aEvent);
1734 return Dispatch(event.forget(), aFlags);
1735 }
1736
1737 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aFlags)1738 EventSourceImpl::Dispatch(already_AddRefed<nsIRunnable> aEvent,
1739 uint32_t aFlags) {
1740 nsCOMPtr<nsIRunnable> event_ref(aEvent);
1741 if (mIsMainThread) {
1742 return NS_DispatchToMainThread(event_ref.forget());
1743 }
1744
1745 if (IsShutDown()) {
1746 return NS_OK;
1747 }
1748 MOZ_ASSERT(mWorkerPrivate);
1749 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
1750 // runnable.
1751 RefPtr<WorkerRunnableDispatcher> event =
1752 new WorkerRunnableDispatcher(this, mWorkerPrivate, event_ref.forget());
1753
1754 if (!event->Dispatch()) {
1755 return NS_ERROR_FAILURE;
1756 }
1757 return NS_OK;
1758 }
1759
1760 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aDelayMs)1761 EventSourceImpl::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
1762 uint32_t aDelayMs) {
1763 return NS_ERROR_NOT_IMPLEMENTED;
1764 }
1765
1766 //-----------------------------------------------------------------------------
1767 // EventSourceImpl::nsIThreadRetargetableStreamListener
1768 //-----------------------------------------------------------------------------
1769 NS_IMETHODIMP
CheckListenerChain()1770 EventSourceImpl::CheckListenerChain() {
1771 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
1772 return NS_OK;
1773 }
1774 ////////////////////////////////////////////////////////////////////////////////
1775 // EventSource
1776 ////////////////////////////////////////////////////////////////////////////////
1777
EventSource(nsPIDOMWindowInner * aOwnerWindow,bool aWithCredentials)1778 EventSource::EventSource(nsPIDOMWindowInner* aOwnerWindow,
1779 bool aWithCredentials)
1780 : DOMEventTargetHelper(aOwnerWindow),
1781 mWithCredentials(aWithCredentials),
1782 mIsMainThread(true),
1783 mKeepingAlive(false) {
1784 mImpl = new EventSourceImpl(this);
1785 }
1786
~EventSource()1787 EventSource::~EventSource() {}
1788
CreateAndDispatchSimpleEvent(const nsAString & aName)1789 nsresult EventSource::CreateAndDispatchSimpleEvent(const nsAString& aName) {
1790 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
1791 // it doesn't bubble, and it isn't cancelable
1792 event->InitEvent(aName, false, false);
1793 event->SetTrusted(true);
1794 bool dummy;
1795 return DispatchEvent(event, &dummy);
1796 }
1797
Constructor(const GlobalObject & aGlobal,const nsAString & aURL,const EventSourceInit & aEventSourceInitDict,ErrorResult & aRv)1798 /* static */ already_AddRefed<EventSource> EventSource::Constructor(
1799 const GlobalObject& aGlobal, const nsAString& aURL,
1800 const EventSourceInit& aEventSourceInitDict, ErrorResult& aRv) {
1801 nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
1802 do_QueryInterface(aGlobal.GetAsSupports());
1803
1804 MOZ_ASSERT(!NS_IsMainThread() || ownerWindow);
1805
1806 RefPtr<EventSource> eventSource =
1807 new EventSource(ownerWindow, aEventSourceInitDict.mWithCredentials);
1808 RefPtr<EventSourceImpl> eventSourceImp = eventSource->mImpl;
1809
1810 if (NS_IsMainThread()) {
1811 // Get principal from document and init EventSourceImpl
1812 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
1813 do_QueryInterface(aGlobal.GetAsSupports());
1814 if (!scriptPrincipal) {
1815 aRv.Throw(NS_ERROR_FAILURE);
1816 return nullptr;
1817 }
1818 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
1819 if (!principal) {
1820 aRv.Throw(NS_ERROR_FAILURE);
1821 return nullptr;
1822 }
1823 eventSourceImp->Init(principal, aURL, aRv);
1824 } else {
1825 // In workers we have to keep the worker alive using a WorkerHolder in order
1826 // to dispatch messages correctly.
1827 if (!eventSourceImp->RegisterWorkerHolder()) {
1828 aRv.Throw(NS_ERROR_FAILURE);
1829 return nullptr;
1830 }
1831 RefPtr<InitRunnable> runnable = new InitRunnable(eventSourceImp, aURL);
1832 runnable->Dispatch(Terminating, aRv);
1833 if (NS_WARN_IF(aRv.Failed())) {
1834 return nullptr;
1835 }
1836 aRv = runnable->ErrorCode();
1837 }
1838 if (NS_WARN_IF(aRv.Failed())) {
1839 return nullptr;
1840 }
1841 return eventSource.forget();
1842 }
1843
1844 // nsWrapperCache
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)1845 JSObject* EventSource::WrapObject(JSContext* aCx,
1846 JS::Handle<JSObject*> aGivenProto) {
1847 return EventSourceBinding::Wrap(aCx, this, aGivenProto);
1848 }
1849
Close()1850 void EventSource::Close() {
1851 AssertIsOnTargetThread();
1852 if (mImpl) {
1853 mImpl->Close();
1854 }
1855 }
1856
UpdateMustKeepAlive()1857 void EventSource::UpdateMustKeepAlive() {
1858 MOZ_ASSERT(NS_IsMainThread());
1859 MOZ_ASSERT(mImpl);
1860 if (mKeepingAlive) {
1861 return;
1862 }
1863 mKeepingAlive = true;
1864 mImpl->AddRefObject();
1865 }
1866
UpdateDontKeepAlive()1867 void EventSource::UpdateDontKeepAlive() {
1868 // Here we could not have mImpl.
1869 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
1870 if (mKeepingAlive) {
1871 MOZ_ASSERT(mImpl);
1872 mKeepingAlive = false;
1873 mImpl->mEventSource = nullptr;
1874 mImpl->ReleaseObject();
1875 }
1876 mImpl = nullptr;
1877 }
1878
1879 //-----------------------------------------------------------------------------
1880 // EventSource::nsISupports
1881 //-----------------------------------------------------------------------------
1882
1883 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource)
1884
1885 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource,
1886 DOMEventTargetHelper)
1887 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1888
1889 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource,
1890 DOMEventTargetHelper)
1891 if (tmp->mImpl) {
1892 tmp->mImpl->Close();
1893 MOZ_ASSERT(!tmp->mImpl);
1894 }
1895 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1896
IsCertainlyAliveForCC() const1897 bool EventSource::IsCertainlyAliveForCC() const { return mKeepingAlive; }
1898
1899 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventSource)
1900 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
1901
1902 NS_IMPL_ADDREF_INHERITED(EventSource, DOMEventTargetHelper)
1903 NS_IMPL_RELEASE_INHERITED(EventSource, DOMEventTargetHelper)
1904
1905 } // namespace dom
1906 } // namespace mozilla
1907