Coverage Report

Created: 2018-09-25 14:53

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