Bug 1726269: part 1) Repeatedly call `::OleSetClipboard` for the Windows-specific...
[gecko.git] / dom / broadcastchannel / BroadcastChannel.cpp
blobd215ed9f9f433043d961af357973d4cd9762c496
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/. */
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/MessageEvent.h"
13 #include "mozilla/dom/MessageEventBinding.h"
14 #include "mozilla/dom/StructuredCloneHolder.h"
15 #include "mozilla/dom/ipc/StructuredCloneData.h"
16 #include "mozilla/dom/RefMessageBodyService.h"
17 #include "mozilla/dom/SharedMessageBody.h"
18 #include "mozilla/dom/WorkerPrivate.h"
19 #include "mozilla/dom/WorkerScope.h"
20 #include "mozilla/dom/WorkerRef.h"
21 #include "mozilla/dom/WorkerRunnable.h"
22 #include "mozilla/ipc/BackgroundChild.h"
23 #include "mozilla/ipc/BackgroundUtils.h"
24 #include "mozilla/ipc/PBackgroundChild.h"
25 #include "mozilla/StorageAccess.h"
26 #include "nsContentUtils.h"
28 #include "nsIBFCacheEntry.h"
29 #include "nsICookieJarSettings.h"
30 #include "mozilla/dom/Document.h"
32 #ifdef XP_WIN
33 # undef PostMessage
34 #endif
36 namespace mozilla {
38 using namespace ipc;
40 namespace dom {
42 using namespace ipc;
44 namespace {
46 class CloseRunnable final : public DiscardableRunnable {
47 public:
48 explicit CloseRunnable(BroadcastChannel* aBC)
49 : DiscardableRunnable("BroadcastChannel CloseRunnable"), mBC(aBC) {
50 MOZ_ASSERT(mBC);
53 NS_IMETHOD Run() override {
54 mBC->Shutdown();
55 return NS_OK;
58 private:
59 ~CloseRunnable() = default;
61 RefPtr<BroadcastChannel> mBC;
64 class TeardownRunnable {
65 protected:
66 explicit TeardownRunnable(BroadcastChannelChild* aActor) : mActor(aActor) {
67 MOZ_ASSERT(mActor);
70 void RunInternal() {
71 MOZ_ASSERT(mActor);
72 if (!mActor->IsActorDestroyed()) {
73 mActor->SendClose();
77 protected:
78 virtual ~TeardownRunnable() = default;
80 private:
81 RefPtr<BroadcastChannelChild> mActor;
84 class TeardownRunnableOnMainThread final : public Runnable,
85 public TeardownRunnable {
86 public:
87 explicit TeardownRunnableOnMainThread(BroadcastChannelChild* aActor)
88 : Runnable("TeardownRunnableOnMainThread"), TeardownRunnable(aActor) {}
90 NS_IMETHOD Run() override {
91 RunInternal();
92 return NS_OK;
96 class TeardownRunnableOnWorker final : public WorkerControlRunnable,
97 public TeardownRunnable {
98 public:
99 TeardownRunnableOnWorker(WorkerPrivate* aWorkerPrivate,
100 BroadcastChannelChild* aActor)
101 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
102 TeardownRunnable(aActor) {}
104 bool WorkerRun(JSContext*, WorkerPrivate*) override {
105 RunInternal();
106 return true;
109 bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
111 void PostDispatch(WorkerPrivate* aWorkerPrivate,
112 bool aDispatchResult) override {}
114 bool PreRun(WorkerPrivate* aWorkerPrivate) override { return true; }
116 void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
117 bool aRunResult) override {}
120 } // namespace
122 BroadcastChannel::BroadcastChannel(nsIGlobalObject* aGlobal,
123 const nsAString& aChannel,
124 const nsID& aPortUUID)
125 : DOMEventTargetHelper(aGlobal),
126 mRefMessageBodyService(RefMessageBodyService::GetOrCreate()),
127 mChannel(aChannel),
128 mState(StateActive),
129 mPortUUID(aPortUUID) {
130 MOZ_ASSERT(aGlobal);
131 KeepAliveIfHasListenersFor(u"message"_ns);
134 BroadcastChannel::~BroadcastChannel() {
135 Shutdown();
136 MOZ_ASSERT(!mWorkerRef);
139 JSObject* BroadcastChannel::WrapObject(JSContext* aCx,
140 JS::Handle<JSObject*> aGivenProto) {
141 return BroadcastChannel_Binding::Wrap(aCx, this, aGivenProto);
144 /* static */
145 already_AddRefed<BroadcastChannel> BroadcastChannel::Constructor(
146 const GlobalObject& aGlobal, const nsAString& aChannel, ErrorResult& aRv) {
147 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
148 if (NS_WARN_IF(!global)) {
149 aRv.Throw(NS_ERROR_FAILURE);
150 return nullptr;
153 nsID portUUID = {};
154 aRv = nsContentUtils::GenerateUUIDInPlace(portUUID);
155 if (aRv.Failed()) {
156 return nullptr;
159 RefPtr<BroadcastChannel> bc =
160 new BroadcastChannel(global, aChannel, portUUID);
162 nsAutoCString origin;
163 nsAutoString originNoSuffix;
164 PrincipalInfo storagePrincipalInfo;
166 StorageAccess storageAccess;
168 nsCOMPtr<nsICookieJarSettings> cjs;
169 if (NS_IsMainThread()) {
170 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
171 if (NS_WARN_IF(!window)) {
172 aRv.Throw(NS_ERROR_FAILURE);
173 return nullptr;
176 nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal();
178 if (!incumbent) {
179 aRv.Throw(NS_ERROR_FAILURE);
180 return nullptr;
183 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(incumbent);
184 if (NS_WARN_IF(!sop)) {
185 aRv.Throw(NS_ERROR_FAILURE);
186 return nullptr;
189 nsIPrincipal* storagePrincipal = sop->GetEffectiveStoragePrincipal();
190 if (!storagePrincipal) {
191 aRv.Throw(NS_ERROR_UNEXPECTED);
192 return nullptr;
195 aRv = storagePrincipal->GetOrigin(origin);
196 if (NS_WARN_IF(aRv.Failed())) {
197 return nullptr;
200 nsAutoCString originNoSuffix8;
201 aRv = storagePrincipal->GetOriginNoSuffix(originNoSuffix8);
202 if (NS_WARN_IF(aRv.Failed())) {
203 return nullptr;
205 CopyUTF8toUTF16(originNoSuffix8, originNoSuffix);
207 aRv = PrincipalToPrincipalInfo(storagePrincipal, &storagePrincipalInfo);
208 if (NS_WARN_IF(aRv.Failed())) {
209 return nullptr;
212 storageAccess = StorageAllowedForWindow(window);
214 Document* doc = window->GetExtantDoc();
215 if (doc) {
216 cjs = doc->CookieJarSettings();
218 } else {
219 JSContext* cx = aGlobal.Context();
221 WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
222 MOZ_ASSERT(workerPrivate);
224 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
225 workerPrivate, "BroadcastChannel", [bc]() { bc->Shutdown(); });
226 // We are already shutting down the worker. Let's return a non-active
227 // object.
228 if (NS_WARN_IF(!workerRef)) {
229 aRv.Throw(NS_ERROR_FAILURE);
230 return nullptr;
233 storageAccess = workerPrivate->StorageAccess();
234 storagePrincipalInfo = workerPrivate->GetEffectiveStoragePrincipalInfo();
235 origin = workerPrivate->EffectiveStoragePrincipalOrigin();
237 originNoSuffix = workerPrivate->GetLocationInfo().mOrigin;
239 bc->mWorkerRef = workerRef;
241 cjs = workerPrivate->CookieJarSettings();
244 // We want to allow opaque origins.
245 if (storagePrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo &&
246 (storageAccess == StorageAccess::eDeny ||
247 (ShouldPartitionStorage(storageAccess) &&
248 !StoragePartitioningEnabled(storageAccess, cjs)))) {
249 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
250 return nullptr;
253 // Register this component to PBackground.
254 PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
255 if (NS_WARN_IF(!actorChild)) {
256 // Firefox is probably shutting down. Let's return a 'generic' error.
257 aRv.Throw(NS_ERROR_FAILURE);
258 return nullptr;
261 PBroadcastChannelChild* actor = actorChild->SendPBroadcastChannelConstructor(
262 storagePrincipalInfo, origin, nsString(aChannel));
264 bc->mActor = static_cast<BroadcastChannelChild*>(actor);
265 MOZ_ASSERT(bc->mActor);
267 bc->mActor->SetParent(bc);
268 bc->mOriginNoSuffix = originNoSuffix;
270 return bc.forget();
273 void BroadcastChannel::PostMessage(JSContext* aCx,
274 JS::Handle<JS::Value> aMessage,
275 ErrorResult& aRv) {
276 if (mState != StateActive) {
277 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
278 return;
281 Maybe<nsID> agentClusterId;
282 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
283 MOZ_ASSERT(global);
284 if (global) {
285 agentClusterId = global->GetAgentClusterId();
288 RefPtr<SharedMessageBody> data = new SharedMessageBody(
289 StructuredCloneHolder::TransferringNotSupported, agentClusterId);
291 data->Write(aCx, aMessage, JS::UndefinedHandleValue, mPortUUID,
292 mRefMessageBodyService, aRv);
293 if (NS_WARN_IF(aRv.Failed())) {
294 return;
297 RemoveDocFromBFCache();
299 MessageData message;
300 SharedMessageBody::FromSharedToMessageChild(mActor->Manager(), data, message);
301 mActor->SendPostMessage(message);
304 void BroadcastChannel::Close() {
305 if (mState != StateActive) {
306 return;
309 // We cannot call Shutdown() immediatelly because we could have some
310 // postMessage runnable already dispatched. Instead, we change the state to
311 // StateClosed and we shutdown the actor asynchrounsly.
313 mState = StateClosed;
314 RefPtr<CloseRunnable> runnable = new CloseRunnable(this);
316 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
317 NS_WARNING("Failed to dispatch to the current thread!");
321 void BroadcastChannel::Shutdown() {
322 mState = StateClosed;
324 // The DTOR of this WorkerRef will release the worker for us.
325 mWorkerRef = nullptr;
327 if (mActor) {
328 mActor->SetParent(nullptr);
330 if (NS_IsMainThread()) {
331 RefPtr<TeardownRunnableOnMainThread> runnable =
332 new TeardownRunnableOnMainThread(mActor);
333 NS_DispatchToCurrentThread(runnable);
334 } else {
335 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
336 MOZ_ASSERT(workerPrivate);
338 RefPtr<TeardownRunnableOnWorker> runnable =
339 new TeardownRunnableOnWorker(workerPrivate, mActor);
340 runnable->Dispatch();
343 mActor = nullptr;
346 IgnoreKeepAliveIfHasListenersFor(u"message"_ns);
349 void BroadcastChannel::RemoveDocFromBFCache() {
350 if (!NS_IsMainThread()) {
351 return;
354 if (nsPIDOMWindowInner* window = GetOwner()) {
355 window->RemoveFromBFCacheSync();
359 void BroadcastChannel::DisconnectFromOwner() {
360 Shutdown();
361 DOMEventTargetHelper::DisconnectFromOwner();
364 void BroadcastChannel::MessageReceived(const MessageData& aData) {
365 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
366 RemoveDocFromBFCache();
367 return;
370 // Let's ignore messages after a close/shutdown.
371 if (mState != StateActive) {
372 return;
375 nsCOMPtr<nsIGlobalObject> globalObject;
377 if (NS_IsMainThread()) {
378 globalObject = GetParentObject();
379 } else {
380 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
381 MOZ_ASSERT(workerPrivate);
382 globalObject = workerPrivate->GlobalScope();
385 AutoJSAPI jsapi;
386 if (!globalObject || !jsapi.Init(globalObject)) {
387 NS_WARNING("Failed to initialize AutoJSAPI object.");
388 return;
391 JSContext* cx = jsapi.cx();
393 RefPtr<SharedMessageBody> data = SharedMessageBody::FromMessageToSharedChild(
394 aData, StructuredCloneHolder::TransferringNotSupported);
395 if (NS_WARN_IF(!data)) {
396 DispatchError(cx);
397 return;
400 IgnoredErrorResult rv;
401 JS::Rooted<JS::Value> value(cx);
403 data->Read(cx, &value, mRefMessageBodyService,
404 SharedMessageBody::ReadMethod::KeepRefMessageBody, rv);
405 if (NS_WARN_IF(rv.Failed())) {
406 JS_ClearPendingException(cx);
407 DispatchError(cx);
408 return;
411 RemoveDocFromBFCache();
413 RootedDictionary<MessageEventInit> init(cx);
414 init.mBubbles = false;
415 init.mCancelable = false;
416 init.mOrigin = mOriginNoSuffix;
417 init.mData = value;
419 RefPtr<MessageEvent> event =
420 MessageEvent::Constructor(this, u"message"_ns, init);
422 event->SetTrusted(true);
424 DispatchEvent(*event);
427 void BroadcastChannel::MessageDelivered(const nsID& aMessageID,
428 uint32_t aOtherBCs) {
429 mRefMessageBodyService->SetMaxCount(aMessageID, aOtherBCs);
432 void BroadcastChannel::DispatchError(JSContext* aCx) {
433 RootedDictionary<MessageEventInit> init(aCx);
434 init.mBubbles = false;
435 init.mCancelable = false;
436 init.mOrigin = mOriginNoSuffix;
438 RefPtr<Event> event =
439 MessageEvent::Constructor(this, u"messageerror"_ns, init);
440 event->SetTrusted(true);
442 DispatchEvent(*event);
445 NS_IMPL_CYCLE_COLLECTION_CLASS(BroadcastChannel)
447 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BroadcastChannel,
448 DOMEventTargetHelper)
449 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
451 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BroadcastChannel,
452 DOMEventTargetHelper)
453 tmp->Shutdown();
454 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
456 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BroadcastChannel)
457 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
459 NS_IMPL_ADDREF_INHERITED(BroadcastChannel, DOMEventTargetHelper)
460 NS_IMPL_RELEASE_INHERITED(BroadcastChannel, DOMEventTargetHelper)
462 } // namespace dom
463 } // namespace mozilla