BroadcastChannelService.cpp (5349B)
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 "BroadcastChannelService.h" 8 9 #include "BroadcastChannelParent.h" 10 #include "mozilla/dom/BlobImpl.h" 11 #include "mozilla/dom/File.h" 12 #include "mozilla/dom/IPCBlobUtils.h" 13 #include "mozilla/ipc/BackgroundParent.h" 14 15 #ifdef XP_WIN 16 # undef PostMessage 17 #endif 18 19 namespace mozilla { 20 21 using namespace ipc; 22 23 namespace dom { 24 25 namespace { 26 27 BroadcastChannelService* sInstance = nullptr; 28 29 ClonedMessageData CloneClonedMessageData(const ClonedMessageData& aOther) { 30 auto cloneData = SerializedStructuredCloneBuffer{}; 31 cloneData.data.initScope(aOther.data().data.scope()); 32 const bool res = cloneData.data.Append(aOther.data().data); 33 MOZ_RELEASE_ASSERT(res, "out of memory"); 34 return {std::move(cloneData), aOther.blobs(), aOther.inputStreams(), 35 aOther.identifiers()}; 36 } 37 38 MessageData CloneMessageData(const MessageData& aOther) { 39 switch (aOther.data().type()) { 40 case MessageDataType::TClonedMessageData: 41 return {aOther.agentClusterId(), 42 CloneClonedMessageData(aOther.data().get_ClonedMessageData())}; 43 case MessageDataType::TRefMessageData: 44 return {aOther.agentClusterId(), aOther.data().get_RefMessageData()}; 45 default: 46 MOZ_CRASH("Unexpected MessageDataType type"); 47 } 48 } 49 50 } // namespace 51 52 BroadcastChannelService::BroadcastChannelService() { 53 AssertIsOnBackgroundThread(); 54 55 // sInstance is a raw BroadcastChannelService*. 56 MOZ_ASSERT(!sInstance); 57 sInstance = this; 58 } 59 60 BroadcastChannelService::~BroadcastChannelService() { 61 AssertIsOnBackgroundThread(); 62 MOZ_ASSERT(sInstance == this); 63 MOZ_ASSERT(mAgents.Count() == 0); 64 65 sInstance = nullptr; 66 } 67 68 // static 69 already_AddRefed<BroadcastChannelService> 70 BroadcastChannelService::GetOrCreate() { 71 AssertIsOnBackgroundThread(); 72 73 RefPtr<BroadcastChannelService> instance = sInstance; 74 if (!instance) { 75 instance = new BroadcastChannelService(); 76 } 77 return instance.forget(); 78 } 79 80 void BroadcastChannelService::RegisterActor( 81 BroadcastChannelParent* aParent, const nsAString& aOriginChannelKey) { 82 AssertIsOnBackgroundThread(); 83 MOZ_ASSERT(aParent); 84 85 auto* const parents = mAgents.GetOrInsertNew(aOriginChannelKey); 86 87 MOZ_ASSERT(!parents->Contains(aParent)); 88 parents->AppendElement(aParent); 89 } 90 91 void BroadcastChannelService::UnregisterActor( 92 BroadcastChannelParent* aParent, const nsAString& aOriginChannelKey) { 93 AssertIsOnBackgroundThread(); 94 MOZ_ASSERT(aParent); 95 96 if (auto entry = mAgents.Lookup(aOriginChannelKey)) { 97 entry.Data()->RemoveElement(aParent); 98 // remove the entry if the array is now empty 99 if (entry.Data()->IsEmpty()) { 100 entry.Remove(); 101 } 102 } else { 103 MOZ_CRASH("Invalid state"); 104 } 105 } 106 107 void BroadcastChannelService::PostMessage(BroadcastChannelParent* aParent, 108 const MessageData& aData, 109 const nsAString& aOriginChannelKey) { 110 AssertIsOnBackgroundThread(); 111 MOZ_ASSERT(aParent); 112 113 nsTArray<BroadcastChannelParent*>* parents; 114 if (!mAgents.Get(aOriginChannelKey, &parents)) { 115 MOZ_CRASH("Invalid state"); 116 } 117 118 // We need to keep the array alive for the life-time of this operation. 119 nsTArray<RefPtr<BlobImpl>> blobImpls; 120 if (aData.data().type() == MessageDataType::TClonedMessageData) { 121 const nsTArray<IPCBlob>& blobs = 122 aData.data().get_ClonedMessageData().blobs(); 123 if (!blobs.IsEmpty()) { 124 blobImpls.SetCapacity(blobs.Length()); 125 126 for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) { 127 RefPtr<BlobImpl> impl = IPCBlobUtils::Deserialize(blobs[i]); 128 129 MOZ_ASSERT(impl); 130 blobImpls.AppendElement(impl); 131 } 132 } 133 } 134 135 uint32_t selectedActorsOnSamePid = 0; 136 137 // For each parent actor, we notify the message. 138 for (uint32_t i = 0; i < parents->Length(); ++i) { 139 BroadcastChannelParent* parent = parents->ElementAt(i); 140 MOZ_ASSERT(parent); 141 142 if (parent == aParent) { 143 continue; 144 } 145 146 if (parent->OtherChildID() == aParent->OtherChildID()) { 147 ++selectedActorsOnSamePid; 148 } 149 150 // We need to have a copy of the data for this parent. 151 MessageData newData = CloneMessageData(aData); 152 MOZ_ASSERT(newData.data().type() == aData.data().type()); 153 154 if (!blobImpls.IsEmpty()) { 155 nsTArray<IPCBlob>& newBlobImpls = 156 newData.data().get_ClonedMessageData().blobs(); 157 MOZ_ASSERT(blobImpls.Length() == newBlobImpls.Length()); 158 159 // Serialize Blob objects for this message. 160 for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) { 161 nsresult rv = IPCBlobUtils::Serialize(blobImpls[i], newBlobImpls[i]); 162 if (NS_WARN_IF(NS_FAILED(rv))) { 163 return; 164 } 165 } 166 } 167 168 (void)parent->SendNotify(newData); 169 } 170 171 // If this is a refMessageData, we need to know when it can be released. 172 if (aData.data().type() == MessageDataType::TRefMessageData) { 173 (void)aParent->SendRefMessageDelivered( 174 aData.data().get_RefMessageData().uuid(), selectedActorsOnSamePid); 175 } 176 } 177 178 } // namespace dom 179 } // namespace mozilla