Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/broadcastchannel/BroadcastChannel.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 "BroadcastChannel.h"
8
#include "BroadcastChannelChild.h"
9
#include "mozilla/dom/BroadcastChannelBinding.h"
10
#include "mozilla/dom/Navigator.h"
11
#include "mozilla/dom/File.h"
12
#include "mozilla/dom/StructuredCloneHolder.h"
13
#include "mozilla/dom/ipc/StructuredCloneData.h"
14
#include "mozilla/dom/WorkerPrivate.h"
15
#include "mozilla/dom/WorkerRef.h"
16
#include "mozilla/dom/WorkerRunnable.h"
17
#include "mozilla/ipc/BackgroundChild.h"
18
#include "mozilla/ipc/BackgroundUtils.h"
19
#include "mozilla/ipc/PBackgroundChild.h"
20
#include "nsContentUtils.h"
21
22
#include "nsIBFCacheEntry.h"
23
#include "nsICookieService.h"
24
#include "nsIDocument.h"
25
#include "nsISupportsPrimitives.h"
26
27
#ifdef XP_WIN
28
#undef PostMessage
29
#endif
30
31
namespace mozilla {
32
33
using namespace ipc;
34
35
namespace dom {
36
37
using namespace ipc;
38
39
class BroadcastChannelMessage final : public StructuredCloneDataNoTransfers
40
{
41
public:
42
  NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
43
44
  BroadcastChannelMessage()
45
    : StructuredCloneDataNoTransfers()
46
0
  {}
47
48
private:
49
  ~BroadcastChannelMessage()
50
0
  {}
51
};
52
53
namespace {
54
55
nsIPrincipal*
56
GetPrincipalFromThreadSafeWorkerRef(ThreadSafeWorkerRef* aWorkerRef)
57
0
{
58
0
  nsIPrincipal* principal = aWorkerRef->Private()->GetPrincipal();
59
0
  if (principal) {
60
0
    return principal;
61
0
  }
62
0
63
0
  // Walk up to our containing page
64
0
  WorkerPrivate* wp = aWorkerRef->Private();
65
0
  while (wp->GetParent()) {
66
0
    wp = wp->GetParent();
67
0
  }
68
0
69
0
  return wp->GetPrincipal();
70
0
}
71
72
class InitializeRunnable final : public WorkerMainThreadRunnable
73
{
74
public:
75
  InitializeRunnable(ThreadSafeWorkerRef* aWorkerRef, nsACString& aOrigin,
76
                     PrincipalInfo& aPrincipalInfo, bool* aThirdPartyWindow,
77
                     ErrorResult& aRv)
78
    : WorkerMainThreadRunnable(aWorkerRef->Private(),
79
                               NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
80
    , mWorkerRef(aWorkerRef)
81
    , mOrigin(aOrigin)
82
    , mPrincipalInfo(aPrincipalInfo)
83
    , mThirdPartyWindow(aThirdPartyWindow)
84
    , mRv(aRv)
85
0
  {
86
0
    MOZ_ASSERT(mWorkerRef);
87
0
  }
88
89
  bool MainThreadRun() override
90
0
  {
91
0
    MOZ_ASSERT(NS_IsMainThread());
92
0
93
0
    nsIPrincipal* principal = GetPrincipalFromThreadSafeWorkerRef(mWorkerRef);
94
0
    if (!principal) {
95
0
      mRv.Throw(NS_ERROR_FAILURE);
96
0
      return true;
97
0
    }
98
0
99
0
    mRv = PrincipalToPrincipalInfo(principal, &mPrincipalInfo);
100
0
    if (NS_WARN_IF(mRv.Failed())) {
101
0
      return true;
102
0
    }
103
0
104
0
    mRv = principal->GetOrigin(mOrigin);
105
0
    if (NS_WARN_IF(mRv.Failed())) {
106
0
      return true;
107
0
    }
108
0
109
0
    // Walk up to our containing page
110
0
    WorkerPrivate* wp = mWorkerRef->Private();
111
0
    while (wp->GetParent()) {
112
0
      wp = wp->GetParent();
113
0
    }
114
0
115
0
    // Window doesn't exist for some kind of workers (eg: SharedWorkers)
116
0
    nsPIDOMWindowInner* window = wp->GetWindow();
117
0
    if (!window) {
118
0
      return true;
119
0
    }
120
0
121
0
    *mThirdPartyWindow =
122
0
      nsContentUtils::IsThirdPartyWindowOrChannel(window, nullptr, nullptr);
123
0
124
0
    return true;
125
0
  }
126
127
private:
128
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
129
  nsACString& mOrigin;
130
  PrincipalInfo& mPrincipalInfo;
131
  bool* mThirdPartyWindow;
132
  ErrorResult& mRv;
133
};
134
135
class CloseRunnable final : public nsIRunnable,
136
                            public nsICancelableRunnable
137
{
138
public:
139
  NS_DECL_ISUPPORTS
140
141
  explicit CloseRunnable(BroadcastChannel* aBC)
142
    : mBC(aBC)
143
0
  {
144
0
    MOZ_ASSERT(mBC);
145
0
  }
146
147
  NS_IMETHOD Run() override
148
0
  {
149
0
    mBC->Shutdown();
150
0
    return NS_OK;
151
0
  }
152
153
  nsresult Cancel() override
154
0
  {
155
0
    return NS_OK;
156
0
  }
157
158
private:
159
0
  ~CloseRunnable() {}
160
161
  RefPtr<BroadcastChannel> mBC;
162
};
163
164
NS_IMPL_ISUPPORTS(CloseRunnable, nsICancelableRunnable, nsIRunnable)
165
166
class TeardownRunnable
167
{
168
protected:
169
  explicit TeardownRunnable(BroadcastChannelChild* aActor)
170
    : mActor(aActor)
171
0
  {
172
0
    MOZ_ASSERT(mActor);
173
0
  }
174
175
  void RunInternal()
176
0
  {
177
0
    MOZ_ASSERT(mActor);
178
0
    if (!mActor->IsActorDestroyed()) {
179
0
      mActor->SendClose();
180
0
    }
181
0
  }
182
183
protected:
184
0
  virtual ~TeardownRunnable() = default;
185
186
private:
187
  RefPtr<BroadcastChannelChild> mActor;
188
};
189
190
class TeardownRunnableOnMainThread final : public Runnable
191
                                         , public TeardownRunnable
192
{
193
public:
194
  explicit TeardownRunnableOnMainThread(BroadcastChannelChild* aActor)
195
    : Runnable("TeardownRunnableOnMainThread")
196
    , TeardownRunnable(aActor)
197
0
  {
198
0
  }
199
200
  NS_IMETHOD Run() override
201
0
  {
202
0
    RunInternal();
203
0
    return NS_OK;
204
0
  }
205
};
206
207
class TeardownRunnableOnWorker final : public WorkerControlRunnable
208
                                     , public TeardownRunnable
209
{
210
public:
211
  TeardownRunnableOnWorker(WorkerPrivate* aWorkerPrivate,
212
                           BroadcastChannelChild* aActor)
213
    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
214
    , TeardownRunnable(aActor)
215
0
  {
216
0
  }
217
218
  bool WorkerRun(JSContext*, WorkerPrivate*) override
219
0
  {
220
0
    RunInternal();
221
0
    return true;
222
0
  }
223
224
  bool
225
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
226
0
  {
227
0
    return true;
228
0
  }
229
230
  void
231
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
232
0
  {}
233
234
  bool
235
  PreRun(WorkerPrivate* aWorkerPrivate) override
236
0
  {
237
0
    return true;
238
0
  }
239
240
  void
241
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
242
          bool aRunResult) override
243
0
  {}
244
};
245
246
247
} // namespace
248
249
BroadcastChannel::BroadcastChannel(nsPIDOMWindowInner* aWindow,
250
                                   const nsAString& aChannel)
251
  : DOMEventTargetHelper(aWindow)
252
  , mChannel(aChannel)
253
  , mState(StateActive)
254
0
{
255
0
  // Window can be null in workers
256
0
257
0
  KeepAliveIfHasListenersFor(NS_LITERAL_STRING("message"));
258
0
}
259
260
BroadcastChannel::~BroadcastChannel()
261
0
{
262
0
  Shutdown();
263
0
  MOZ_ASSERT(!mWorkerRef);
264
0
}
265
266
JSObject*
267
BroadcastChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
268
0
{
269
0
  return BroadcastChannel_Binding::Wrap(aCx, this, aGivenProto);
270
0
}
271
272
/* static */ already_AddRefed<BroadcastChannel>
273
BroadcastChannel::Constructor(const GlobalObject& aGlobal,
274
                              const nsAString& aChannel,
275
                              ErrorResult& aRv)
276
0
{
277
0
  nsCOMPtr<nsPIDOMWindowInner> window =
278
0
    do_QueryInterface(aGlobal.GetAsSupports());
279
0
  // Window is null in workers.
280
0
281
0
  RefPtr<BroadcastChannel> bc = new BroadcastChannel(window, aChannel);
282
0
283
0
  nsAutoCString origin;
284
0
  PrincipalInfo principalInfo;
285
0
286
0
  if (NS_IsMainThread()) {
287
0
    nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal();
288
0
289
0
    if (!incumbent) {
290
0
      aRv.Throw(NS_ERROR_FAILURE);
291
0
      return nullptr;
292
0
    }
293
0
294
0
    nsIPrincipal* principal = incumbent->PrincipalOrNull();
295
0
    if (!principal) {
296
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
297
0
      return nullptr;
298
0
    }
299
0
300
0
    aRv = principal->GetOrigin(origin);
301
0
    if (NS_WARN_IF(aRv.Failed())) {
302
0
      return nullptr;
303
0
    }
304
0
305
0
    aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
306
0
    if (NS_WARN_IF(aRv.Failed())) {
307
0
      return nullptr;
308
0
    }
309
0
310
0
    if (nsContentUtils::IsThirdPartyWindowOrChannel(window, nullptr,
311
0
                                                    nullptr) &&
312
0
        nsContentUtils::StorageAllowedForWindow(window) !=
313
0
          nsContentUtils::StorageAccess::eAllow) {
314
0
      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
315
0
      return nullptr;
316
0
    }
317
0
  } else {
318
0
    JSContext* cx = aGlobal.Context();
319
0
320
0
    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
321
0
    MOZ_ASSERT(workerPrivate);
322
0
323
0
    RefPtr<StrongWorkerRef> workerRef =
324
0
      StrongWorkerRef::Create(workerPrivate, "BroadcastChannel",
325
0
                              [bc] () { bc->Shutdown(); });
326
0
    // We are already shutting down the worker. Let's return a non-active
327
0
    // object.
328
0
    if (NS_WARN_IF(!workerRef)) {
329
0
      aRv.Throw(NS_ERROR_FAILURE);
330
0
      return nullptr;
331
0
    }
332
0
333
0
    RefPtr<ThreadSafeWorkerRef> tsr = new ThreadSafeWorkerRef(workerRef);
334
0
335
0
    bool thirdPartyWindow = false;
336
0
337
0
    RefPtr<InitializeRunnable> runnable =
338
0
      new InitializeRunnable(tsr, origin, principalInfo, &thirdPartyWindow,
339
0
                             aRv);
340
0
    runnable->Dispatch(Canceling, aRv);
341
0
    if (aRv.Failed()) {
342
0
      return nullptr;
343
0
    }
344
0
345
0
    if (thirdPartyWindow && !workerPrivate->IsStorageAllowed()) {
346
0
      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
347
0
      return nullptr;
348
0
    }
349
0
350
0
    bc->mWorkerRef = std::move(workerRef);
351
0
  }
352
0
353
0
  // Register this component to PBackground.
354
0
  PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
355
0
  if (NS_WARN_IF(!actorChild)) {
356
0
    // Firefox is probably shutting down. Let's return a 'generic' error.
357
0
    aRv.Throw(NS_ERROR_FAILURE);
358
0
    return nullptr;
359
0
  }
360
0
361
0
  PBroadcastChannelChild* actor =
362
0
    actorChild->SendPBroadcastChannelConstructor(principalInfo, origin,
363
0
                                                 nsString(aChannel));
364
0
365
0
  bc->mActor = static_cast<BroadcastChannelChild*>(actor);
366
0
  MOZ_ASSERT(bc->mActor);
367
0
368
0
  bc->mActor->SetParent(bc);
369
0
370
0
  return bc.forget();
371
0
}
372
373
void
374
BroadcastChannel::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
375
                              ErrorResult& aRv)
376
0
{
377
0
  if (mState != StateActive) {
378
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
379
0
    return;
380
0
  }
381
0
382
0
  RefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
383
0
384
0
  data->Write(aCx, aMessage, aRv);
385
0
  if (NS_WARN_IF(aRv.Failed())) {
386
0
    return;
387
0
  }
388
0
389
0
  RemoveDocFromBFCache();
390
0
391
0
  ClonedMessageData message;
392
0
  data->BuildClonedMessageDataForBackgroundChild(mActor->Manager(), message);
393
0
  mActor->SendPostMessage(message);
394
0
}
395
396
void
397
BroadcastChannel::Close()
398
0
{
399
0
  if (mState != StateActive) {
400
0
    return;
401
0
  }
402
0
403
0
  // We cannot call Shutdown() immediatelly because we could have some
404
0
  // postMessage runnable already dispatched. Instead, we change the state to
405
0
  // StateClosed and we shutdown the actor asynchrounsly.
406
0
407
0
  mState = StateClosed;
408
0
  RefPtr<CloseRunnable> runnable = new CloseRunnable(this);
409
0
410
0
  if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
411
0
    NS_WARNING("Failed to dispatch to the current thread!");
412
0
  }
413
0
}
414
415
void
416
BroadcastChannel::Shutdown()
417
0
{
418
0
  mState = StateClosed;
419
0
420
0
  // The DTOR of this WorkerRef will release the worker for us.
421
0
  mWorkerRef = nullptr;
422
0
423
0
  if (mActor) {
424
0
    mActor->SetParent(nullptr);
425
0
426
0
    if (NS_IsMainThread()) {
427
0
      RefPtr<TeardownRunnableOnMainThread> runnable =
428
0
        new TeardownRunnableOnMainThread(mActor);
429
0
      NS_DispatchToCurrentThread(runnable);
430
0
    } else {
431
0
      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
432
0
      MOZ_ASSERT(workerPrivate);
433
0
434
0
      RefPtr<TeardownRunnableOnWorker> runnable =
435
0
        new TeardownRunnableOnWorker(workerPrivate, mActor);
436
0
      runnable->Dispatch();
437
0
    }
438
0
439
0
    mActor = nullptr;
440
0
  }
441
0
442
0
  IgnoreKeepAliveIfHasListenersFor(NS_LITERAL_STRING("message"));
443
0
}
444
445
void
446
BroadcastChannel::RemoveDocFromBFCache()
447
0
{
448
0
  if (!NS_IsMainThread()) {
449
0
    return;
450
0
  }
451
0
452
0
  nsPIDOMWindowInner* window = GetOwner();
453
0
  if (!window) {
454
0
    return;
455
0
  }
456
0
457
0
  nsIDocument* doc = window->GetExtantDoc();
458
0
  if (!doc) {
459
0
    return;
460
0
  }
461
0
462
0
  nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
463
0
  if (!bfCacheEntry) {
464
0
    return;
465
0
  }
466
0
467
0
  bfCacheEntry->RemoveFromBFCacheSync();
468
0
}
469
470
void
471
BroadcastChannel::DisconnectFromOwner()
472
0
{
473
0
  Shutdown();
474
0
  DOMEventTargetHelper::DisconnectFromOwner();
475
0
}
476
477
NS_IMPL_CYCLE_COLLECTION_CLASS(BroadcastChannel)
478
479
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BroadcastChannel,
480
0
                                                  DOMEventTargetHelper)
481
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
482
483
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BroadcastChannel,
484
0
                                                DOMEventTargetHelper)
485
0
  tmp->Shutdown();
486
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
487
488
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BroadcastChannel)
489
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
490
491
NS_IMPL_ADDREF_INHERITED(BroadcastChannel, DOMEventTargetHelper)
492
NS_IMPL_RELEASE_INHERITED(BroadcastChannel, DOMEventTargetHelper)
493
494
} // namespace dom
495
} // namespace mozilla