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