7
7
#include " mozilla/dom/EventSource.h"
8
8
9
9
#include " mozilla/ArrayUtils.h"
10
+ #include " mozilla/DataMutex.h"
10
11
#include " mozilla/DebugOnly.h"
11
12
#include " mozilla/LoadInfo.h"
12
13
#include " mozilla/DOMEventTargetHelper.h"
@@ -147,25 +148,29 @@ class EventSourceImpl final : public nsIObserver,
147
148
bool IsTargetThread () const { return NS_GetCurrentThread() == mTargetThread ; }
148
149
149
150
uint16_t ReadyState () {
150
- MutexAutoLock lock ( mMutex );
151
- if (mEventSource ) {
152
- return mEventSource ->mReadyState ;
151
+ auto lock = mSharedData . Lock ( );
152
+ if (lock-> mEventSource ) {
153
+ return lock-> mEventSource ->mReadyState ;
153
154
}
154
155
// EventSourceImpl keeps EventSource alive. If mEventSource is null, it
155
156
// means that the EventSource has been closed.
156
157
return CLOSED;
157
158
}
158
159
159
160
void SetReadyState (uint16_t aReadyState) {
160
- MutexAutoLock lock ( mMutex );
161
- MOZ_ASSERT (mEventSource );
161
+ auto lock = mSharedData . Lock ( );
162
+ MOZ_ASSERT (lock-> mEventSource );
162
163
MOZ_ASSERT (!mIsShutDown );
163
- mEventSource ->mReadyState = aReadyState;
164
+ lock-> mEventSource ->mReadyState = aReadyState;
164
165
}
165
166
166
167
bool IsClosed () { return ReadyState () == CLOSED; }
167
168
168
- RefPtr<EventSource> mEventSource ;
169
+ RefPtr<EventSource> GetEventSource () {
170
+ AssertIsOnTargetThread ();
171
+ auto lock = mSharedData .Lock ();
172
+ return lock->mEventSource ;
173
+ }
169
174
170
175
/* *
171
176
* A simple state machine used to manage the event-source's line buffer
@@ -249,9 +254,6 @@ class EventSourceImpl final : public nsIObserver,
249
254
// EventSourceImpl internal states.
250
255
// WorkerRef to keep the worker alive. (accessed on worker thread only)
251
256
RefPtr<ThreadSafeWorkerRef> mWorkerRef ;
252
- // This mutex protects mServiceNotifier and mEventSource->mReadyState that are
253
- // used in different threads.
254
- mozilla::Mutex mMutex ;
255
257
// Whether the window is frozen. May be set on main thread and read on target
256
258
// thread.
257
259
Atomic<bool > mFrozen ;
@@ -311,7 +313,13 @@ class EventSourceImpl final : public nsIObserver,
311
313
bool mConnectionOpened ;
312
314
};
313
315
314
- UniquePtr<EventSourceServiceNotifier> mServiceNotifier ;
316
+ struct SharedData {
317
+ RefPtr<EventSource> mEventSource ;
318
+ UniquePtr<EventSourceServiceNotifier> mServiceNotifier ;
319
+ };
320
+
321
+ DataMutex<SharedData> mSharedData ;
322
+
315
323
// Event Source owner information:
316
324
// - the script file name
317
325
// - source code line number and column number where the Event Source object
@@ -355,20 +363,19 @@ NS_IMPL_ISUPPORTS(EventSourceImpl, nsIObserver, nsIStreamListener,
355
363
356
364
EventSourceImpl::EventSourceImpl (EventSource* aEventSource,
357
365
nsICookieJarSettings* aCookieJarSettings)
358
- : mEventSource (aEventSource),
359
- mReconnectionTime (0 ),
366
+ : mReconnectionTime (0 ),
360
367
mStatus (PARSE_STATE_OFF),
361
- mMutex(" EventSourceImpl::mMutex" ),
362
368
mFrozen(false ),
363
369
mGoingToDispatchAllMessages(false ),
364
370
mIsMainThread(NS_IsMainThread()),
365
371
mIsShutDown(false ),
372
+ mSharedData(SharedData{aEventSource}, " EventSourceImpl::mSharedData" ),
366
373
mScriptLine(0 ),
367
374
mScriptColumn(0 ),
368
375
mInnerWindowID(0 ),
369
376
mCookieJarSettings(aCookieJarSettings),
370
377
mTargetThread(NS_GetCurrentThread()) {
371
- MOZ_ASSERT (mEventSource );
378
+ MOZ_ASSERT (aEventSource );
372
379
SetReadyState (CONNECTING);
373
380
}
374
381
@@ -412,13 +419,13 @@ void EventSourceImpl::CloseInternal() {
412
419
413
420
RefPtr<EventSource> myES;
414
421
{
415
- MutexAutoLock lock ( mMutex );
422
+ auto lock = mSharedData . Lock ( );
416
423
// We want to ensure to release ourself even if we have
417
424
// the shutdown case, thus we put aside a pointer
418
425
// to the EventSource and null it out right now.
419
- myES = std::move (mEventSource );
420
- mEventSource = nullptr ;
421
- mServiceNotifier = nullptr ;
426
+ myES = std::move (lock-> mEventSource );
427
+ lock-> mEventSource = nullptr ;
428
+ lock-> mServiceNotifier = nullptr ;
422
429
}
423
430
424
431
MOZ_ASSERT (!mIsShutDown );
@@ -569,7 +576,12 @@ nsresult EventSourceImpl::ParseURL(const nsAString& aURL) {
569
576
// is only ever called from EventSourceImpl::Init(), which is either called
570
577
// directly if mEventSource was created on the main thread, or via a
571
578
// synchronous runnable if it was created on a worker thread.
572
- mEventSource ->mOriginalURL = NS_ConvertUTF8toUTF16(spec);
579
+ {
580
+ // We can't use GetEventSource() here because it would modify the refcount,
581
+ // and that's not allowed off the owning thread.
582
+ auto lock = mSharedData .Lock ();
583
+ lock->mEventSource ->mOriginalURL = NS_ConvertUTF8toUTF16(spec);
584
+ }
573
585
mSrc = srcURI;
574
586
mOrigin = origin;
575
587
return NS_OK;
@@ -649,8 +661,12 @@ EventSourceImpl::Observe(nsISupports* aSubject, const char* aTopic,
649
661
650
662
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface (aSubject);
651
663
MOZ_ASSERT (mIsMainThread );
652
- if (!mEventSource ->GetOwner () || window != mEventSource ->GetOwner ()) {
653
- return NS_OK;
664
+ {
665
+ auto lock = mSharedData .Lock ();
666
+ if (!lock->mEventSource ->GetOwner () ||
667
+ window != lock->mEventSource ->GetOwner ()) {
668
+ return NS_OK;
669
+ }
654
670
}
655
671
656
672
DebugOnly<nsresult> rv;
@@ -723,8 +739,8 @@ EventSourceImpl::OnStartRequest(nsIRequest* aRequest) {
723
739
}
724
740
725
741
{
726
- MutexAutoLock lock ( mMutex );
727
- mServiceNotifier = MakeUnique<EventSourceServiceNotifier>(
742
+ auto lock = mSharedData . Lock ( );
743
+ lock-> mServiceNotifier = MakeUnique<EventSourceServiceNotifier>(
728
744
this , mHttpChannel ->ChannelId (), mInnerWindowID );
729
745
}
730
746
rv = Dispatch (NewRunnableMethod (" dom::EventSourceImpl::AnnounceConnection" ,
@@ -867,7 +883,8 @@ EventSourceImpl::AsyncOnChannelRedirect(
867
883
868
884
bool isValidScheme = newURI->SchemeIs (" http" ) || newURI->SchemeIs (" https" );
869
885
870
- rv = mIsMainThread ? mEventSource ->CheckCurrentGlobalCorrectness () : NS_OK;
886
+ rv =
887
+ mIsMainThread ? GetEventSource ()->CheckCurrentGlobalCorrectness () : NS_OK;
871
888
if (NS_FAILED(rv) || !isValidScheme) {
872
889
DispatchFailConnection ();
873
890
return NS_ERROR_DOM_SECURITY_ERR;
@@ -922,11 +939,12 @@ EventSourceImpl::GetInterface(const nsIID& aIID, void** aResult) {
922
939
// To avoid a data race we may only access the event target if it lives on
923
940
// the main thread.
924
941
if (mIsMainThread ) {
925
- rv = mEventSource ->CheckCurrentGlobalCorrectness ();
942
+ auto lock = mSharedData .Lock ();
943
+ rv = lock->mEventSource ->CheckCurrentGlobalCorrectness ();
926
944
NS_ENSURE_SUCCESS (rv, NS_ERROR_UNEXPECTED);
927
945
928
- if (mEventSource ->GetOwner ()) {
929
- window = mEventSource ->GetOwner ()->GetOuterWindow ();
946
+ if (lock-> mEventSource ->GetOwner ()) {
947
+ window = lock-> mEventSource ->GetOwner ()->GetOuterWindow ();
930
948
}
931
949
}
932
950
@@ -959,7 +977,7 @@ nsresult EventSourceImpl::GetBaseURI(nsIURI** aBaseURI) {
959
977
960
978
// first we try from document->GetBaseURI()
961
979
nsCOMPtr<Document> doc =
962
- mIsMainThread ? mEventSource ->GetDocumentIfCurrent () : nullptr ;
980
+ mIsMainThread ? GetEventSource () ->GetDocumentIfCurrent () : nullptr ;
963
981
if (doc) {
964
982
baseURI = doc->GetBaseURI ();
965
983
}
@@ -1029,31 +1047,41 @@ nsresult EventSourceImpl::InitChannelAndRequestEventSource(
1029
1047
bool isValidScheme = mSrc ->SchemeIs (" http" ) || mSrc ->SchemeIs (" https" );
1030
1048
1031
1049
MOZ_ASSERT_IF (mIsMainThread , aEventTargetAccessAllowed);
1050
+
1032
1051
nsresult rv = aEventTargetAccessAllowed
1033
- ? mEventSource ->CheckCurrentGlobalCorrectness ()
1052
+ ? [this ]() {
1053
+ // We can't call GetEventSource() because we're not
1054
+ // allowed to touch the refcount off the worker thread
1055
+ // due to an assertion, event if it would have otherwise
1056
+ // been safe.
1057
+ auto lock = mSharedData .Lock ();
1058
+ return lock->mEventSource ->CheckCurrentGlobalCorrectness ();
1059
+ }()
1034
1060
: NS_OK;
1035
1061
if (NS_FAILED(rv) || !isValidScheme) {
1036
1062
DispatchFailConnection ();
1037
1063
return NS_ERROR_DOM_SECURITY_ERR;
1038
1064
}
1039
1065
1066
+ nsCOMPtr<Document> doc;
1067
+ nsSecurityFlags securityFlags =
1068
+ nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
1069
+ {
1070
+ auto lock = mSharedData .Lock ();
1071
+ doc = aEventTargetAccessAllowed ? lock->mEventSource ->GetDocumentIfCurrent ()
1072
+ : nullptr ;
1073
+
1074
+ if (lock->mEventSource ->mWithCredentials ) {
1075
+ securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
1076
+ }
1077
+ }
1078
+
1040
1079
// The html spec requires we use fetch cache mode of "no-store". This
1041
1080
// maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
1042
1081
nsLoadFlags loadFlags;
1043
1082
loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE |
1044
1083
nsIRequest::INHIBIT_CACHING;
1045
1084
1046
- const nsCOMPtr<Document> doc = aEventTargetAccessAllowed
1047
- ? mEventSource ->GetDocumentIfCurrent ()
1048
- : nullptr ;
1049
-
1050
- nsSecurityFlags securityFlags =
1051
- nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
1052
-
1053
- if (mEventSource ->mWithCredentials ) {
1054
- securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
1055
- }
1056
-
1057
1085
nsCOMPtr<nsIChannel> channel;
1058
1086
// If we have the document, use it
1059
1087
if (doc) {
@@ -1115,9 +1143,9 @@ void EventSourceImpl::AnnounceConnection() {
1115
1143
}
1116
1144
1117
1145
{
1118
- MutexAutoLock lock ( mMutex );
1119
- if (mServiceNotifier ) {
1120
- mServiceNotifier ->ConnectionOpened ();
1146
+ auto lock = mSharedData . Lock ( );
1147
+ if (lock-> mServiceNotifier ) {
1148
+ lock-> mServiceNotifier ->ConnectionOpened ();
1121
1149
}
1122
1150
}
1123
1151
@@ -1127,11 +1155,13 @@ void EventSourceImpl::AnnounceConnection() {
1127
1155
1128
1156
SetReadyState (OPEN);
1129
1157
1130
- nsresult rv = mEventSource ->CheckCurrentGlobalCorrectness ();
1158
+ nsresult rv = GetEventSource () ->CheckCurrentGlobalCorrectness ();
1131
1159
if (NS_FAILED(rv)) {
1132
1160
return ;
1133
1161
}
1134
- rv = mEventSource ->CreateAndDispatchSimpleEvent (u" open" _ns);
1162
+ // We can't hold the mutex while dispatching the event because the mutex is
1163
+ // not reentrant, and content might call back into our code.
1164
+ rv = GetEventSource ()->CreateAndDispatchSimpleEvent (u" open" _ns);
1135
1165
if (NS_FAILED(rv)) {
1136
1166
NS_WARNING (" Failed to dispatch the error event!!!" );
1137
1167
return ;
@@ -1212,14 +1242,16 @@ void EventSourceImpl::ReestablishConnection() {
1212
1242
return ;
1213
1243
}
1214
1244
1215
- rv = mEventSource ->CheckCurrentGlobalCorrectness ();
1245
+ rv = GetEventSource () ->CheckCurrentGlobalCorrectness ();
1216
1246
if (NS_FAILED(rv)) {
1217
1247
return ;
1218
1248
}
1219
1249
1220
1250
SetReadyState (CONNECTING);
1221
1251
ResetDecoder ();
1222
- rv = mEventSource ->CreateAndDispatchSimpleEvent (u" error" _ns);
1252
+ // We can't hold the mutex while dispatching the event because the mutex is
1253
+ // not reentrant, and content might call back into our code.
1254
+ rv = GetEventSource ()->CreateAndDispatchSimpleEvent (u" error" _ns);
1223
1255
if (NS_FAILED(rv)) {
1224
1256
NS_WARNING (" Failed to dispatch the error event!!!" );
1225
1257
return ;
@@ -1338,9 +1370,11 @@ void EventSourceImpl::FailConnection() {
1338
1370
// When a user agent is to fail the connection, the user agent must set the
1339
1371
// readyState attribute to CLOSED and queue a task to fire a simple event
1340
1372
// named error at the EventSource object.
1341
- nsresult rv = mEventSource ->CheckCurrentGlobalCorrectness ();
1373
+ nsresult rv = GetEventSource () ->CheckCurrentGlobalCorrectness ();
1342
1374
if (NS_SUCCEEDED(rv)) {
1343
- rv = mEventSource ->CreateAndDispatchSimpleEvent (u" error" _ns);
1375
+ // We can't hold the mutex while dispatching the event because the mutex
1376
+ // is not reentrant, and content might call back into our code.
1377
+ rv = GetEventSource ()->CreateAndDispatchSimpleEvent (u" error" _ns);
1344
1378
if (NS_FAILED(rv)) {
1345
1379
NS_WARNING (" Failed to dispatch the error event!!!" );
1346
1380
}
@@ -1449,14 +1483,18 @@ void EventSourceImpl::DispatchAllMessageEvents() {
1449
1483
return ;
1450
1484
}
1451
1485
1452
- nsresult rv = mEventSource ->CheckCurrentGlobalCorrectness ();
1453
- if (NS_FAILED(rv)) {
1454
- return ;
1455
- }
1456
-
1486
+ nsresult rv;
1457
1487
AutoJSAPI jsapi;
1458
- if (NS_WARN_IF(!jsapi.Init (mEventSource ->GetOwnerGlobal ()))) {
1459
- return ;
1488
+ {
1489
+ auto lock = mSharedData .Lock ();
1490
+ rv = lock->mEventSource ->CheckCurrentGlobalCorrectness ();
1491
+ if (NS_FAILED(rv)) {
1492
+ return ;
1493
+ }
1494
+
1495
+ if (NS_WARN_IF(!jsapi.Init (lock->mEventSource ->GetOwnerGlobal ()))) {
1496
+ return ;
1497
+ }
1460
1498
}
1461
1499
1462
1500
JSContext* cx = jsapi.cx ();
@@ -1473,11 +1511,11 @@ void EventSourceImpl::DispatchAllMessageEvents() {
1473
1511
}
1474
1512
1475
1513
{
1476
- MutexAutoLock lock ( mMutex );
1477
- if (mServiceNotifier ) {
1478
- mServiceNotifier ->EventReceived (message->mEventName , mLastEventID ,
1479
- message->mData , mReconnectionTime ,
1480
- PR_Now ());
1514
+ auto lock = mSharedData . Lock ( );
1515
+ if (lock-> mServiceNotifier ) {
1516
+ lock-> mServiceNotifier ->EventReceived (message->mEventName , mLastEventID ,
1517
+ message->mData , mReconnectionTime ,
1518
+ PR_Now ());
1481
1519
}
1482
1520
}
1483
1521
@@ -1495,16 +1533,19 @@ void EventSourceImpl::DispatchAllMessageEvents() {
1495
1533
// create an event that uses the MessageEvent interface,
1496
1534
// which does not bubble, is not cancelable, and has no default action
1497
1535
1536
+ RefPtr<EventSource> eventSource = GetEventSource ();
1498
1537
RefPtr<MessageEvent> event =
1499
- new MessageEvent (mEventSource , nullptr , nullptr );
1538
+ new MessageEvent (eventSource , nullptr , nullptr );
1500
1539
1501
1540
event->InitMessageEvent (nullptr , message->mEventName , CanBubble::eNo,
1502
1541
Cancelable::eNo, jsData, mOrigin , mLastEventID ,
1503
1542
nullptr , Sequence<OwningNonNull<MessagePort>>());
1504
1543
event->SetTrusted (true );
1505
1544
1545
+ // We can't hold the mutex while dispatching the event because the mutex is
1546
+ // not reentrant, and content might call back into our code.
1506
1547
IgnoredErrorResult err;
1507
- mEventSource ->DispatchEvent (*event, err);
1548
+ eventSource ->DispatchEvent (*event, err);
1508
1549
if (err.Failed ()) {
1509
1550
NS_WARNING (" Failed to dispatch the message event!!!" );
1510
1551
return ;
@@ -2055,7 +2096,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2055
2096
2056
2097
bool EventSource::IsCertainlyAliveForCC () const {
2057
2098
// Until we are double linked forth and back, we want to stay alive.
2058
- return mESImpl != nullptr && mESImpl ->mEventSource == this ;
2099
+ if (!mESImpl ) {
2100
+ return false ;
2101
+ }
2102
+ auto lock = mESImpl ->mSharedData .Lock ();
2103
+ return lock->mEventSource == this ;
2059
2104
}
2060
2105
2061
2106
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION (EventSource)
0 commit comments