tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }