TestGMPRemoveAndDelete.cpp (13808B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=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 "GMPService.h" 8 #include "GMPServiceParent.h" 9 #include "GMPTestMonitor.h" 10 #include "GMPUtils.h" 11 #include "GMPVideoDecoderProxy.h" 12 #include "gmp-api/gmp-video-host.h" 13 #include "gtest/gtest.h" 14 #include "mozilla/Services.h" 15 #include "mozilla/StaticPtr.h" 16 #include "nsDirectoryServiceDefs.h" 17 #include "nsIObserverService.h" 18 19 #define GMP_DIR_NAME u"gmp-fakeopenh264"_ns 20 #define GMP_OLD_VERSION u"1.0"_ns 21 #define GMP_NEW_VERSION u"1.1"_ns 22 23 #define GMP_DELETED_TOPIC "gmp-directory-deleted" 24 25 #define EXPECT_OK(X) EXPECT_TRUE(NS_SUCCEEDED(X)) 26 27 using namespace mozilla; 28 using namespace mozilla::gmp; 29 30 class GMPRemoveTest : public nsIObserver, public GMPVideoDecoderCallbackProxy { 31 public: 32 GMPRemoveTest(); 33 34 NS_DECL_THREADSAFE_ISUPPORTS 35 36 // Called when a GMP plugin directory has been successfully deleted. 37 // |aData| will contain the directory path. 38 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, 39 const char16_t* aData) override; 40 41 // Create a new GMP plugin directory that we can trash and add it to the GMP 42 // service. Remove the original plugin directory. Original plugin directory 43 // gets re-added at destruction. 44 void Setup(); 45 46 bool CreateVideoDecoder(nsCString aNodeId = ""_ns); 47 void CloseVideoDecoder(); 48 49 void DeletePluginDirectory(bool aCanDefer); 50 51 // Decode a dummy frame. 52 GMPErr Decode(); 53 54 // Wait until TestMonitor has been signaled. 55 void Wait(); 56 57 // Did we get a Terminated() callback from the plugin? 58 bool IsTerminated(); 59 60 // From GMPVideoDecoderCallbackProxy 61 // Set mDecodeResult; unblock TestMonitor. 62 virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override; 63 virtual void Error(GMPErr aError) override; 64 65 // From GMPVideoDecoderCallbackProxy 66 // We expect this to be called when a plugin has been forcibly closed. 67 virtual void Terminated() override; 68 69 // Ignored GMPVideoDecoderCallbackProxy members 70 virtual void ReceivedDecodedReferenceFrame( 71 const uint64_t aPictureId) override {} 72 virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override {} 73 virtual void InputDataExhausted() override {} 74 virtual void DrainComplete() override {} 75 virtual void ResetComplete() override {} 76 77 private: 78 virtual ~GMPRemoveTest(); 79 80 void gmp_Decode(); 81 void gmp_GetVideoDecoder(nsCString aNodeId, 82 GMPVideoDecoderProxy** aOutDecoder, 83 GMPVideoHost** aOutHost); 84 void GeneratePlugin(); 85 86 GMPTestMonitor mTestMonitor; 87 nsCOMPtr<nsIThread> mGMPThread; 88 89 bool mIsTerminated; 90 91 // Path to the cloned GMP we have created. 92 nsString mTmpPath; 93 nsCOMPtr<nsIFile> mTmpDir; 94 95 // Path to the original GMP. Store so that we can re-add it after we're done 96 // testing. 97 nsString mOriginalPath; 98 99 GMPVideoDecoderProxy* mDecoder; 100 GMPVideoHost* mHost; 101 GMPErr mDecodeResult; 102 }; 103 104 /* 105 * Simple test that the plugin is deleted when forcibly removed and deleted. 106 */ 107 TEST(GeckoMediaPlugins, RemoveAndDeleteForcedSimple) 108 { 109 RefPtr<GMPRemoveTest> test(new GMPRemoveTest()); 110 111 test->Setup(); 112 test->DeletePluginDirectory(false /* force immediate */); 113 test->Wait(); 114 } 115 116 /* 117 * Simple test that the plugin is deleted when deferred deletion is allowed. 118 */ 119 TEST(GeckoMediaPlugins, RemoveAndDeleteDeferredSimple) 120 { 121 RefPtr<GMPRemoveTest> test(new GMPRemoveTest()); 122 123 test->Setup(); 124 test->DeletePluginDirectory(true /* can defer */); 125 test->Wait(); 126 } 127 128 /* 129 * Test that the plugin is unavailable immediately after a forced 130 * RemoveAndDelete, and that the plugin is deleted afterwards. 131 */ 132 // Bug 1115253 - disable test in win64 to reduce failure rate 133 #if !defined(_WIN64) 134 TEST(GeckoMediaPlugins, RemoveAndDeleteForcedInUse) 135 { 136 RefPtr<GMPRemoveTest> test(new GMPRemoveTest()); 137 138 test->Setup(); 139 EXPECT_TRUE(test->CreateVideoDecoder("thisOrigin"_ns)); 140 141 // Test that we can decode a frame. 142 GMPErr err = test->Decode(); 143 EXPECT_EQ(err, GMPNoErr); 144 145 test->DeletePluginDirectory(false /* force immediate */); 146 test->Wait(); 147 148 // Test that the VideoDecoder is no longer available. 149 EXPECT_FALSE(test->CreateVideoDecoder("thisOrigin"_ns)); 150 151 // Test that we were notified of the plugin's destruction. 152 EXPECT_TRUE(test->IsTerminated()); 153 } 154 155 /* 156 * Test that the plugin is still usable after a deferred RemoveAndDelete, and 157 * that the plugin is deleted afterwards. 158 */ 159 TEST(GeckoMediaPlugins, RemoveAndDeleteDeferredInUse) 160 { 161 RefPtr<GMPRemoveTest> test(new GMPRemoveTest()); 162 163 test->Setup(); 164 EXPECT_TRUE(test->CreateVideoDecoder("thisOrigin"_ns)); 165 166 // Make sure decoding works before we do anything. 167 GMPErr err = test->Decode(); 168 EXPECT_EQ(err, GMPNoErr); 169 170 test->DeletePluginDirectory(true /* can defer */); 171 172 // Test that decoding still works. 173 err = test->Decode(); 174 EXPECT_EQ(err, GMPNoErr); 175 176 // Test that this origin is still able to fetch the video decoder. 177 EXPECT_TRUE(test->CreateVideoDecoder("thisOrigin"_ns)); 178 179 test->CloseVideoDecoder(); 180 test->Wait(); 181 } 182 #endif 183 184 static StaticRefPtr<GeckoMediaPluginService> gService; 185 static StaticRefPtr<GeckoMediaPluginServiceParent> gServiceParent; 186 187 static GeckoMediaPluginService* GetService() { 188 if (!gService) { 189 RefPtr<GeckoMediaPluginService> service = 190 GeckoMediaPluginService::GetGeckoMediaPluginService(); 191 gService = service; 192 } 193 194 return gService.get(); 195 } 196 197 static GeckoMediaPluginServiceParent* GetServiceParent() { 198 if (!gServiceParent) { 199 RefPtr<GeckoMediaPluginServiceParent> parent = 200 GeckoMediaPluginServiceParent::GetSingleton(); 201 gServiceParent = parent; 202 } 203 204 return gServiceParent.get(); 205 } 206 207 NS_IMPL_ISUPPORTS(GMPRemoveTest, nsIObserver) 208 209 GMPRemoveTest::GMPRemoveTest() 210 : mIsTerminated(false), mDecoder(nullptr), mHost(nullptr) {} 211 212 GMPRemoveTest::~GMPRemoveTest() { 213 bool exists; 214 EXPECT_TRUE(NS_SUCCEEDED(mTmpDir->Exists(&exists)) && !exists); 215 216 EXPECT_OK(GetServiceParent()->AddPluginDirectory(mOriginalPath)); 217 } 218 219 void GMPRemoveTest::Setup() { 220 GeneratePlugin(); 221 GetService()->GetThread(getter_AddRefs(mGMPThread)); 222 223 // Spin the event loop until the GMP service has had a chance to complete 224 // adding GMPs from MOZ_GMP_PATH. Otherwise, the RemovePluginDirectory() 225 // below may complete before we're finished adding GMPs from MOZ_GMP_PATH, 226 // and we'll end up not removing the GMP, and the test will fail. 227 nsCOMPtr<nsISerialEventTarget> thread(GetServiceParent()->GetGMPThread()); 228 EXPECT_TRUE(thread); 229 GMPTestMonitor* mon = &mTestMonitor; 230 GetServiceParent()->EnsureInitialized()->Then( 231 thread, __func__, [mon]() { mon->SetFinished(); }, 232 [mon]() { mon->SetFinished(); }); 233 mTestMonitor.AwaitFinished(); 234 235 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 236 obs->AddObserver(this, GMP_DELETED_TOPIC, false /* strong ref */); 237 EXPECT_OK(GetServiceParent()->RemovePluginDirectory(mOriginalPath)); 238 239 GetServiceParent()->AsyncAddPluginDirectory(mTmpPath)->Then( 240 thread, __func__, [mon]() { mon->SetFinished(); }, 241 [mon]() { mon->SetFinished(); }); 242 mTestMonitor.AwaitFinished(); 243 } 244 245 bool GMPRemoveTest::CreateVideoDecoder(nsCString aNodeId) { 246 GMPVideoHost* host; 247 GMPVideoDecoderProxy* decoder = nullptr; 248 249 mGMPThread->Dispatch( 250 NewNonOwningRunnableMethod<nsCString, GMPVideoDecoderProxy**, 251 GMPVideoHost**>( 252 "GMPRemoveTest::gmp_GetVideoDecoder", this, 253 &GMPRemoveTest::gmp_GetVideoDecoder, aNodeId, &decoder, &host), 254 NS_DISPATCH_NORMAL); 255 256 mTestMonitor.AwaitFinished(); 257 258 if (!decoder) { 259 return false; 260 } 261 262 GMPVideoCodec codec; 263 memset(&codec, 0, sizeof(codec)); 264 codec.mGMPApiVersion = 33; 265 266 nsTArray<uint8_t> empty; 267 NS_DispatchAndSpinEventLoopUntilComplete( 268 "GMPVideoDecoderProxy::InitDecode"_ns, mGMPThread, 269 NewNonOwningRunnableMethod<const GMPVideoCodec&, const nsTArray<uint8_t>&, 270 GMPVideoDecoderCallbackProxy*, int32_t>( 271 "GMPVideoDecoderProxy::InitDecode", decoder, 272 &GMPVideoDecoderProxy::InitDecode, codec, empty, this, 273 1 /* core count */)); 274 275 if (mDecoder) { 276 CloseVideoDecoder(); 277 } 278 279 mDecoder = decoder; 280 mHost = host; 281 282 return true; 283 } 284 285 void GMPRemoveTest::gmp_GetVideoDecoder(nsCString aNodeId, 286 GMPVideoDecoderProxy** aOutDecoder, 287 GMPVideoHost** aOutHost) { 288 nsTArray<nsCString> tags; 289 tags.AppendElement("h264"_ns); 290 tags.AppendElement("fake"_ns); 291 292 class Callback : public GetGMPVideoDecoderCallback { 293 public: 294 Callback(GMPTestMonitor* aMonitor, GMPVideoDecoderProxy** aDecoder, 295 GMPVideoHost** aHost) 296 : mMonitor(aMonitor), mDecoder(aDecoder), mHost(aHost) {} 297 virtual void Done(GMPVideoDecoderProxy* aDecoder, 298 GMPVideoHost* aHost) override { 299 *mDecoder = aDecoder; 300 *mHost = aHost; 301 mMonitor->SetFinished(); 302 } 303 304 private: 305 GMPTestMonitor* mMonitor; 306 GMPVideoDecoderProxy** mDecoder; 307 GMPVideoHost** mHost; 308 }; 309 310 UniquePtr<GetGMPVideoDecoderCallback> cb( 311 new Callback(&mTestMonitor, aOutDecoder, aOutHost)); 312 313 if (NS_FAILED(GetService()->GetGMPVideoDecoder(nullptr, &tags, aNodeId, 314 std::move(cb)))) { 315 mTestMonitor.SetFinished(); 316 } 317 } 318 319 void GMPRemoveTest::CloseVideoDecoder() { 320 NS_DispatchAndSpinEventLoopUntilComplete( 321 "GMPVideoDecoderProxy::Close"_ns, mGMPThread, 322 NewNonOwningRunnableMethod("GMPVideoDecoderProxy::Close", mDecoder, 323 &GMPVideoDecoderProxy::Close)); 324 325 mDecoder = nullptr; 326 mHost = nullptr; 327 } 328 329 void GMPRemoveTest::DeletePluginDirectory(bool aCanDefer) { 330 GetServiceParent()->RemoveAndDeletePluginDirectory(mTmpPath, aCanDefer); 331 } 332 333 GMPErr GMPRemoveTest::Decode() { 334 mGMPThread->Dispatch( 335 NewNonOwningRunnableMethod("GMPRemoveTest::gmp_Decode", this, 336 &GMPRemoveTest::gmp_Decode), 337 NS_DISPATCH_NORMAL); 338 339 mTestMonitor.AwaitFinished(); 340 return mDecodeResult; 341 } 342 343 void GMPRemoveTest::gmp_Decode() { 344 // from gmp-fake.cpp 345 #pragma pack(push, 1) 346 struct EncodedFrame { 347 struct SPSNalu { 348 uint32_t size_; 349 uint8_t payload[14]; 350 } sps_nalu; 351 struct PPSNalu { 352 uint32_t size_; 353 uint8_t payload[4]; 354 } pps_nalu; 355 struct IDRNalu { 356 uint32_t size_; 357 uint8_t h264_compat_; 358 uint32_t magic_; 359 uint32_t width_; 360 uint32_t height_; 361 uint8_t y_; 362 uint8_t u_; 363 uint8_t v_; 364 uint64_t timestamp_; 365 } idr_nalu; 366 }; 367 #pragma pack(pop) 368 369 GMPVideoFrame* absFrame; 370 GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &absFrame); 371 EXPECT_EQ(err, GMPNoErr); 372 373 GMPUniquePtr<GMPVideoEncodedFrame> frame( 374 static_cast<GMPVideoEncodedFrame*>(absFrame)); 375 err = frame->CreateEmptyFrame(sizeof(EncodedFrame) /* size */); 376 EXPECT_EQ(err, GMPNoErr); 377 378 EncodedFrame* frameData = reinterpret_cast<EncodedFrame*>(frame->Buffer()); 379 frameData->sps_nalu.size_ = sizeof(EncodedFrame::SPSNalu) - sizeof(uint32_t); 380 frameData->pps_nalu.size_ = sizeof(EncodedFrame::PPSNalu) - sizeof(uint32_t); 381 frameData->idr_nalu.size_ = sizeof(EncodedFrame::IDRNalu) - sizeof(uint32_t); 382 frameData->idr_nalu.h264_compat_ = 5; 383 frameData->idr_nalu.magic_ = 0x004000b8; 384 frameData->idr_nalu.width_ = frameData->idr_nalu.height_ = 16; 385 386 nsTArray<uint8_t> empty; 387 nsresult rv = 388 mDecoder->Decode(std::move(frame), false /* aMissingFrames */, empty); 389 EXPECT_OK(rv); 390 } 391 392 void GMPRemoveTest::Wait() { mTestMonitor.AwaitFinished(); } 393 394 bool GMPRemoveTest::IsTerminated() { return mIsTerminated; } 395 396 // nsIObserver 397 NS_IMETHODIMP 398 GMPRemoveTest::Observe(nsISupports* aSubject, const char* aTopic, 399 const char16_t* aData) { 400 EXPECT_TRUE(!strcmp(GMP_DELETED_TOPIC, aTopic)); 401 402 nsString data(aData); 403 if (mTmpPath.Equals(data)) { 404 mTestMonitor.SetFinished(); 405 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 406 obs->RemoveObserver(this, GMP_DELETED_TOPIC); 407 } 408 409 return NS_OK; 410 } 411 412 // GMPVideoDecoderCallbackProxy 413 void GMPRemoveTest::Decoded(GMPVideoi420Frame* aDecodedFrame) { 414 aDecodedFrame->Destroy(); 415 mDecodeResult = GMPNoErr; 416 mTestMonitor.SetFinished(); 417 } 418 419 // GMPVideoDecoderCallbackProxy 420 void GMPRemoveTest::Error(GMPErr aError) { 421 mDecodeResult = aError; 422 mTestMonitor.SetFinished(); 423 } 424 425 // GMPVideoDecoderCallbackProxy 426 void GMPRemoveTest::Terminated() { 427 mIsTerminated = true; 428 if (mDecoder) { 429 mDecoder->Close(); 430 mDecoder = nullptr; 431 } 432 } 433 434 void GMPRemoveTest::GeneratePlugin() { 435 nsresult rv; 436 nsCOMPtr<nsIFile> gmpDir; 437 nsCOMPtr<nsIFile> origDir; 438 nsCOMPtr<nsIFile> tmpDir; 439 440 rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(gmpDir)); 441 EXPECT_OK(rv); 442 rv = gmpDir->Append(GMP_DIR_NAME); 443 EXPECT_OK(rv); 444 445 rv = gmpDir->Clone(getter_AddRefs(origDir)); 446 EXPECT_OK(rv); 447 rv = origDir->Append(GMP_OLD_VERSION); 448 EXPECT_OK(rv); 449 450 rv = gmpDir->Clone(getter_AddRefs(tmpDir)); 451 EXPECT_OK(rv); 452 rv = tmpDir->Append(GMP_NEW_VERSION); 453 EXPECT_OK(rv); 454 bool exists = false; 455 rv = tmpDir->Exists(&exists); 456 EXPECT_OK(rv); 457 if (exists) { 458 rv = tmpDir->Remove(true); 459 EXPECT_OK(rv); 460 } 461 rv = origDir->CopyTo(gmpDir, GMP_NEW_VERSION); 462 EXPECT_OK(rv); 463 464 rv = gmpDir->Clone(getter_AddRefs(tmpDir)); 465 EXPECT_OK(rv); 466 rv = tmpDir->Append(GMP_NEW_VERSION); 467 EXPECT_OK(rv); 468 469 EXPECT_OK(origDir->GetPath(mOriginalPath)); 470 EXPECT_OK(tmpDir->GetPath(mTmpPath)); 471 mTmpDir = tmpDir; 472 }