GMPStorageChild.cpp (6820B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "GMPStorageChild.h" 7 8 #include "GMPChild.h" 9 #include "base/task.h" 10 #include "gmp-storage.h" 11 12 #define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current()) 13 14 #define CALL_ON_GMP_THREAD(_func, ...) \ 15 do { \ 16 if (ON_GMP_THREAD()) { \ 17 _func(__VA_ARGS__); \ 18 } else { \ 19 mPlugin->GMPMessageLoop()->PostTask( \ 20 dont_add_new_uses_of_this::NewRunnableMethod( \ 21 this, &GMPStorageChild::_func, ##__VA_ARGS__)); \ 22 } \ 23 } while (false) 24 25 namespace mozilla::gmp { 26 27 static nsTArray<uint8_t> ToArray(const uint8_t* aData, uint32_t aDataSize) { 28 nsTArray<uint8_t> data; 29 data.AppendElements(aData, aDataSize); 30 return data; 31 } 32 33 GMPRecordImpl::GMPRecordImpl(GMPStorageChild* aOwner, const nsCString& aName, 34 GMPRecordClient* aClient) 35 : mName(aName), mClient(aClient), mOwner(aOwner) {} 36 37 GMPErr GMPRecordImpl::Open() { return mOwner->Open(this); } 38 39 void GMPRecordImpl::OpenComplete(GMPErr aStatus) { 40 mClient->OpenComplete(aStatus); 41 } 42 43 GMPErr GMPRecordImpl::Read() { return mOwner->Read(this); } 44 45 void GMPRecordImpl::ReadComplete(GMPErr aStatus, const uint8_t* aBytes, 46 uint32_t aLength) { 47 mClient->ReadComplete(aStatus, aBytes, aLength); 48 } 49 50 GMPErr GMPRecordImpl::Write(const uint8_t* aData, uint32_t aDataSize) { 51 return mOwner->Write(this, aData, aDataSize); 52 } 53 54 void GMPRecordImpl::WriteComplete(GMPErr aStatus) { 55 mClient->WriteComplete(aStatus); 56 } 57 58 GMPErr GMPRecordImpl::Close() { 59 RefPtr<GMPRecordImpl> kungfuDeathGrip(this); 60 // Delete our self reference. 61 Release(); 62 mOwner->Close(this->Name()); 63 return GMPNoErr; 64 } 65 66 GMPStorageChild::GMPStorageChild(GMPChild* aPlugin) 67 : mMonitor("GMPStorageChild"), mPlugin(aPlugin), mShutdown(false) { 68 MOZ_ASSERT(ON_GMP_THREAD()); 69 } 70 71 GMPErr GMPStorageChild::CreateRecord(const nsCString& aRecordName, 72 GMPRecord** aOutRecord, 73 GMPRecordClient* aClient) { 74 MonitorAutoLock lock(mMonitor); 75 76 if (mShutdown) { 77 NS_WARNING("GMPStorage used after it's been shutdown!"); 78 return GMPClosedErr; 79 } 80 81 MOZ_ASSERT(aRecordName.Length() && aOutRecord); 82 83 if (HasRecord(aRecordName)) { 84 return GMPRecordInUse; 85 } 86 87 RefPtr<GMPRecordImpl> record(new GMPRecordImpl(this, aRecordName, aClient)); 88 mRecords.InsertOrUpdate(aRecordName, RefPtr{record}); // Addrefs 89 90 // The GMPRecord holds a self reference until the GMP calls Close() on 91 // it. This means the object is always valid (even if neutered) while 92 // the GMP expects it to be. 93 record.forget(aOutRecord); 94 95 return GMPNoErr; 96 } 97 98 bool GMPStorageChild::HasRecord(const nsCString& aRecordName) { 99 mMonitor.AssertCurrentThreadOwns(); 100 return mRecords.Contains(aRecordName); 101 } 102 103 already_AddRefed<GMPRecordImpl> GMPStorageChild::GetRecord( 104 const nsCString& aRecordName) { 105 MonitorAutoLock lock(mMonitor); 106 RefPtr<GMPRecordImpl> record; 107 mRecords.Get(aRecordName, getter_AddRefs(record)); 108 return record.forget(); 109 } 110 111 GMPErr GMPStorageChild::Open(GMPRecordImpl* aRecord) { 112 MonitorAutoLock lock(mMonitor); 113 114 if (mShutdown) { 115 NS_WARNING("GMPStorage used after it's been shutdown!"); 116 return GMPClosedErr; 117 } 118 119 if (!HasRecord(aRecord->Name())) { 120 // Trying to re-open a record that has already been closed. 121 return GMPClosedErr; 122 } 123 124 CALL_ON_GMP_THREAD(SendOpen, aRecord->Name()); 125 126 return GMPNoErr; 127 } 128 129 GMPErr GMPStorageChild::Read(GMPRecordImpl* aRecord) { 130 MonitorAutoLock lock(mMonitor); 131 132 if (mShutdown) { 133 NS_WARNING("GMPStorage used after it's been shutdown!"); 134 return GMPClosedErr; 135 } 136 137 if (!HasRecord(aRecord->Name())) { 138 // Record not opened. 139 return GMPClosedErr; 140 } 141 142 CALL_ON_GMP_THREAD(SendRead, aRecord->Name()); 143 144 return GMPNoErr; 145 } 146 147 GMPErr GMPStorageChild::Write(GMPRecordImpl* aRecord, const uint8_t* aData, 148 uint32_t aDataSize) { 149 if (aDataSize > GMP_MAX_RECORD_SIZE) { 150 return GMPQuotaExceededErr; 151 } 152 153 MonitorAutoLock lock(mMonitor); 154 155 if (mShutdown) { 156 NS_WARNING("GMPStorage used after it's been shutdown!"); 157 return GMPClosedErr; 158 } 159 160 if (!HasRecord(aRecord->Name())) { 161 // Record not opened. 162 return GMPClosedErr; 163 } 164 165 CALL_ON_GMP_THREAD(SendWrite, aRecord->Name(), ToArray(aData, aDataSize)); 166 167 return GMPNoErr; 168 } 169 170 GMPErr GMPStorageChild::Close(const nsCString& aRecordName) { 171 MonitorAutoLock lock(mMonitor); 172 173 if (!HasRecord(aRecordName)) { 174 // Already closed. 175 return GMPClosedErr; 176 } 177 178 mRecords.Remove(aRecordName); 179 180 if (!mShutdown) { 181 CALL_ON_GMP_THREAD(SendClose, aRecordName); 182 } 183 184 return GMPNoErr; 185 } 186 187 mozilla::ipc::IPCResult GMPStorageChild::RecvOpenComplete( 188 const nsCString& aRecordName, const GMPErr& aStatus) { 189 // We don't need a lock to read |mShutdown| since it is only changed in 190 // the GMP thread. 191 if (mShutdown) { 192 return IPC_OK(); 193 } 194 RefPtr<GMPRecordImpl> record = GetRecord(aRecordName); 195 if (!record) { 196 // Not fatal. 197 return IPC_OK(); 198 } 199 record->OpenComplete(aStatus); 200 return IPC_OK(); 201 } 202 203 mozilla::ipc::IPCResult GMPStorageChild::RecvReadComplete( 204 const nsCString& aRecordName, const GMPErr& aStatus, 205 nsTArray<uint8_t>&& aBytes) { 206 if (mShutdown) { 207 return IPC_OK(); 208 } 209 RefPtr<GMPRecordImpl> record = GetRecord(aRecordName); 210 if (!record) { 211 // Not fatal. 212 return IPC_OK(); 213 } 214 record->ReadComplete(aStatus, aBytes.Elements(), aBytes.Length()); 215 return IPC_OK(); 216 } 217 218 mozilla::ipc::IPCResult GMPStorageChild::RecvWriteComplete( 219 const nsCString& aRecordName, const GMPErr& aStatus) { 220 if (mShutdown) { 221 return IPC_OK(); 222 } 223 RefPtr<GMPRecordImpl> record = GetRecord(aRecordName); 224 if (!record) { 225 // Not fatal. 226 return IPC_OK(); 227 } 228 record->WriteComplete(aStatus); 229 return IPC_OK(); 230 } 231 232 mozilla::ipc::IPCResult GMPStorageChild::RecvShutdown() { 233 // Block any new storage requests, and thus any messages back to the 234 // parent. We don't delete any objects here, as that may invalidate 235 // GMPRecord pointers held by the GMP. 236 MonitorAutoLock lock(mMonitor); 237 mShutdown = true; 238 return IPC_OK(); 239 } 240 241 } // namespace mozilla::gmp 242 243 // avoid redefined macro in unified build 244 #undef ON_GMP_THREAD 245 #undef CALL_ON_GMP_THREAD