Bug 1118574 - test case for "Forget This Site" command. r=cpearce
authorJW Wang <jwwang@mozilla.com>
Tue, 06 Jan 2015 18:51:00 +0100
changeset 248204 bcbedf706c4389796da093b7067573da275df733
parent 248203 97da2ad0174adf91da5f8855e8a5244439c17489
child 248205 35faf9a888ae139ef53ae0e3a55f04b33bcdbacd
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1118574
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1118574 - test case for "Forget This Site" command. r=cpearce
dom/media/gmp/GMPService.cpp
dom/media/gmp/GMPService.h
dom/media/gtest/TestGMPCrossOrigin.cpp
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -927,18 +927,18 @@ WriteToFile(nsIFile* aPath,
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 static nsresult
 ReadFromFile(nsIFile* aPath,
-             const nsCString& aFileName,
-             nsCString& aOutData,
+             const nsACString& aFileName,
+             nsACString& aOutData,
              int32_t aMaxLength)
 {
   nsCOMPtr<nsIFile> path;
   nsresult rv = aPath->Clone(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -965,18 +965,18 @@ ReadFromFile(nsIFile* aPath,
   PR_Close(f);
   if (NS_WARN_IF(len != size)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-static nsresult
-ReadSalt(nsIFile* aPath, nsCString& aOutData)
+nsresult
+ReadSalt(nsIFile* aPath, nsACString& aOutData)
 {
   return ReadFromFile(aPath, NS_LITERAL_CSTRING("salt"),
                       aOutData, NodeIdSaltLength);
 
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::IsPersistentStorageAllowed(const nsACString& aNodeId,
@@ -1136,17 +1136,17 @@ GeckoMediaPluginService::GetNodeId(const
   }
 
   aOutId = salt;
   mPersistentStorageAllowed.Put(salt, true);
 
   return NS_OK;
 }
 
-static bool
+bool
 MatchOrigin(nsIFile* aPath, const nsACString& aOrigin)
 {
   // http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
   static const uint32_t MaxDomainLength = 253;
 
   nsresult rv;
   nsCString str;
   rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("origin"), str, MaxDomainLength);
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -139,12 +139,15 @@ private:
   // non-persistent sessions.
   nsClassHashtable<nsUint32HashKey, nsCString> mTempNodeIds;
 
   // Hashes node id to whether that node id is allowed to store data
   // persistently on disk.
   nsDataHashtable<nsCStringHashKey, bool> mPersistentStorageAllowed;
 };
 
+nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
+bool MatchOrigin(nsIFile* aPath, const nsACString& aOrigin);
+
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPService_h_
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -115,16 +115,52 @@ GetGMPThread()
 {
   nsRefPtr<GeckoMediaPluginService> service =
     GeckoMediaPluginService::GetGeckoMediaPluginService();
   nsCOMPtr<nsIThread> thread;
   EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
   return thread.forget();
 }
 
+template<typename T>
+static nsresult
+EnumerateDir(const nsACString& aDir, T&& aDirIter)
+{
+  nsRefPtr<GeckoMediaPluginService> service =
+    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  MOZ_ASSERT(service);
+
+  // $profileDir/gmp/
+  nsCOMPtr<nsIFile> path;
+  nsresult rv = service->GetStorageDir(getter_AddRefs(path));
+  NS_ENSURE_SUCCESS(rv, rv);
+  // $profileDir/gmp/$aDir/
+  rv = path->AppendNative(aDir);
+  NS_ENSURE_SUCCESS(rv, rv);
+  // Iterate all sub-folders of $profileDir/gmp/$aDir/
+  nsCOMPtr<nsISimpleEnumerator> iter;
+  rv = path->GetDirectoryEntries(getter_AddRefs(iter));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool hasMore = false;
+  while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
+    nsCOMPtr<nsISupports> supports;
+    rv = iter->GetNext(getter_AddRefs(supports));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+    nsCOMPtr<nsIFile> entry(do_QueryInterface(supports, &rv));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+    aDirIter(entry);
+  }
+  return NS_OK;
+}
+
 class GMPShutdownObserver : public nsIRunnable
                           , public nsIObserver {
 public:
   GMPShutdownObserver(nsIRunnable* aShutdownTask,
                       nsIRunnable* Continuation,
                       const nsACString& aNodeId)
     : mShutdownTask(aShutdownTask)
     , mContinuation(Continuation)
@@ -407,16 +443,148 @@ class GMPStorageTest : public GMPDecrypt
     // Send a message to the fake GMP for it to run its own tests internally.
     // It sends us a "test-storage complete" message when its passed, or
     // some other message if its tests fail.
     Expect(NS_LITERAL_CSTRING("test-storage complete"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
     Update(NS_LITERAL_CSTRING("test-storage"));
   }
 
+  /**
+   * 1. Generate storage data for some sites.
+   * 2. Forget about one of the sites.
+   * 3. Check if the storage data for the forgotten site are erased correctly.
+   * 4. Check if the storage data for other sites remain unchanged.
+   */
+  void TestForgetThisSite() {
+    AssertIsOnGMPThread();
+    EXPECT_TRUE(IsGMPStorageIsEmpty());
+
+    // Generate storage data for some site.
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false);
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
+    Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
+    Update(NS_LITERAL_CSTRING("test-storage"));
+  }
+
+  void TestForgetThisSite_AnotherSite() {
+    Shutdown();
+
+    // Generate storage data for another site.
+    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
+                    NS_LITERAL_STRING("example4.com"),
+                    false);
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
+    Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
+    Update(NS_LITERAL_CSTRING("test-storage"));
+  }
+
+  struct NodeInfo {
+    explicit NodeInfo(const nsACString& aSite) : siteToForget(aSite) {}
+    nsCString siteToForget;
+    nsTArray<nsCString> expectedRemainingNodeIds;
+  };
+
+  class NodeIdCollector {
+  public:
+    explicit NodeIdCollector(NodeInfo* aInfo) : mNodeInfo(aInfo) {}
+    void operator()(nsIFile* aFile) {
+      nsCString salt;
+      nsresult rv = ReadSalt(aFile, salt);
+      ASSERT_TRUE(NS_SUCCEEDED(rv));
+      if (!MatchOrigin(aFile, mNodeInfo->siteToForget)) {
+        mNodeInfo->expectedRemainingNodeIds.AppendElement(salt);
+      }
+    }
+  private:
+    NodeInfo* mNodeInfo;
+  };
+
+  void TestForgetThisSite_CollectSiteInfo() {
+    nsAutoPtr<NodeInfo> siteInfo(
+        new NodeInfo(NS_LITERAL_CSTRING("example1.com")));
+    // Collect nodeIds that are expected to remain for later comparison.
+    EnumerateDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
+    // Invoke "Forget this site" on the main thread.
+    NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
+        this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo));
+  }
+
+  void TestForgetThisSite_Forget(nsAutoPtr<NodeInfo> aSiteInfo) {
+    nsRefPtr<GeckoMediaPluginService> service =
+        GeckoMediaPluginService::GetGeckoMediaPluginService();
+    service->ForgetThisSite(NS_ConvertUTF8toUTF16(aSiteInfo->siteToForget));
+
+    nsCOMPtr<nsIThread> thread;
+    service->GetThread(getter_AddRefs(thread));
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
+        this, &GMPStorageTest::TestForgetThisSite_Verify, aSiteInfo);
+    thread->Dispatch(r, NS_DISPATCH_NORMAL);
+
+    nsCOMPtr<nsIRunnable> f = NS_NewRunnableMethod(
+        this, &GMPStorageTest::SetFinished);
+    thread->Dispatch(f, NS_DISPATCH_NORMAL);
+  }
+
+  class NodeIdVerifier {
+  public:
+    explicit NodeIdVerifier(const NodeInfo* aInfo)
+      : mNodeInfo(aInfo)
+      , mExpectedRemainingNodeIds(aInfo->expectedRemainingNodeIds) {}
+    void operator()(nsIFile* aFile) {
+      nsCString salt;
+      nsresult rv = ReadSalt(aFile, salt);
+      ASSERT_TRUE(NS_SUCCEEDED(rv));
+      // Shouldn't match the origin if we clear correctly.
+      EXPECT_FALSE(MatchOrigin(aFile, mNodeInfo->siteToForget));
+      // Check if remaining nodeIDs are as expected.
+      EXPECT_TRUE(mExpectedRemainingNodeIds.RemoveElement(salt));
+    }
+    ~NodeIdVerifier() {
+      EXPECT_TRUE(mExpectedRemainingNodeIds.IsEmpty());
+    }
+  private:
+    const NodeInfo* mNodeInfo;
+    nsTArray<nsCString> mExpectedRemainingNodeIds;
+  };
+
+  class StorageVerifier {
+  public:
+    explicit StorageVerifier(const NodeInfo* aInfo)
+      : mExpectedRemainingNodeIds(aInfo->expectedRemainingNodeIds) {}
+    void operator()(nsIFile* aFile) {
+      nsCString salt;
+      nsresult rv = aFile->GetNativeLeafName(salt);
+      ASSERT_TRUE(NS_SUCCEEDED(rv));
+      EXPECT_TRUE(mExpectedRemainingNodeIds.RemoveElement(salt));
+    }
+    ~StorageVerifier() {
+      EXPECT_TRUE(mExpectedRemainingNodeIds.IsEmpty());
+    }
+  private:
+    nsTArray<nsCString> mExpectedRemainingNodeIds;
+  };
+
+  void TestForgetThisSite_Verify(nsAutoPtr<NodeInfo> aSiteInfo) {
+    nsresult rv = EnumerateDir(
+        NS_LITERAL_CSTRING("id"), NodeIdVerifier(aSiteInfo));
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+    rv = EnumerateDir(
+        NS_LITERAL_CSTRING("storage"), StorageVerifier(aSiteInfo));
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+  }
+
   void TestCrossOriginStorage() {
     EXPECT_TRUE(!mDecryptor);
 
     // Open decryptor on one, origin, write a record, and test that that
     // record can't be read on another origin.
     CreateDecryptor(NS_LITERAL_STRING("example3.com"),
                     NS_LITERAL_STRING("example4.com"),
                     false);
@@ -813,16 +981,21 @@ TEST(GeckoMediaPlugins, GMPStorageGetNod
   runner->DoTest(&GMPStorageTest::TestGetNodeId);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageBasic) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestBasicStorage);
 }
 
+TEST(GeckoMediaPlugins, GMPStorageForgetThisSite) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestForgetThisSite);
+}
+
 TEST(GeckoMediaPlugins, GMPStorageCrossOrigin) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestCrossOriginStorage);
 }
 
 TEST(GeckoMediaPlugins, GMPStoragePrivateBrowsing) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestPBStorage);