media_drm_storage_impl_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/cdm/browser/media_drm_storage_impl.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/run_loop.h"
  9. #include "base/threading/thread_task_runner_handle.h"
  10. #include "base/unguessable_token.h"
  11. #include "components/prefs/testing_pref_service.h"
  12. #include "content/public/browser/web_contents.h"
  13. #include "content/public/test/navigation_simulator.h"
  14. #include "content/public/test/test_renderer_host.h"
  15. #include "media/mojo/services/mojo_media_drm_storage.h"
  16. #include "mojo/public/cpp/bindings/pending_remote.h"
  17. #include "testing/gtest/include/gtest/gtest.h"
  18. #include "third_party/abseil-cpp/absl/types/optional.h"
  19. #include "url/gurl.h"
  20. #include "url/origin.h"
  21. namespace cdm {
  22. namespace {
  23. const char kTestOrigin[] = "https://www.testorigin.com:80";
  24. const char kTestOrigin2[] = "https://www.testorigin2.com:80";
  25. using MediaDrmOriginId = MediaDrmStorageImpl::MediaDrmOriginId;
  26. void OnMediaDrmStorageInit(bool expected_success,
  27. MediaDrmOriginId* out_origin_id,
  28. bool success,
  29. const MediaDrmOriginId& origin_id) {
  30. DCHECK(out_origin_id);
  31. DCHECK_EQ(success, expected_success);
  32. *out_origin_id = origin_id;
  33. }
  34. void CreateOriginId(MediaDrmStorageImpl::OriginIdObtainedCB callback) {
  35. std::move(callback).Run(true, base::UnguessableToken::Create());
  36. }
  37. void CreateEmptyOriginId(MediaDrmStorageImpl::OriginIdObtainedCB callback) {
  38. // |callback| has to fail in order to check if empty origin ID allowed.
  39. std::move(callback).Run(false, absl::nullopt);
  40. }
  41. void CreateOriginIdAsync(MediaDrmStorageImpl::OriginIdObtainedCB callback) {
  42. base::ThreadTaskRunnerHandle::Get()->PostTask(
  43. FROM_HERE, base::BindOnce(&CreateOriginId, std::move(callback)));
  44. }
  45. void AllowEmptyOriginId(base::OnceCallback<void(bool)> callback) {
  46. std::move(callback).Run(true);
  47. }
  48. void DisallowEmptyOriginId(base::OnceCallback<void(bool)> callback) {
  49. std::move(callback).Run(false);
  50. }
  51. } // namespace
  52. class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness {
  53. public:
  54. MediaDrmStorageImplTest() {}
  55. void SetUp() override {
  56. RenderViewHostTestHarness::SetUp();
  57. pref_service_ = std::make_unique<TestingPrefServiceSimple>();
  58. PrefRegistrySimple* registry = pref_service_->registry();
  59. MediaDrmStorageImpl::RegisterProfilePrefs(registry);
  60. media_drm_storage_ =
  61. CreateAndInitMediaDrmStorage(GURL(kTestOrigin), &origin_id_);
  62. }
  63. void TearDown() override {
  64. media_drm_storage_.reset();
  65. base::RunLoop().RunUntilIdle();
  66. RenderViewHostTestHarness::TearDown();
  67. }
  68. protected:
  69. using SessionData = media::MediaDrmStorage::SessionData;
  70. std::unique_ptr<media::MediaDrmStorage> CreateMediaDrmStorage(
  71. content::RenderFrameHost* rfh,
  72. MediaDrmStorageImpl::GetOriginIdCB get_origin_id_cb,
  73. MediaDrmStorageImpl::AllowEmptyOriginIdCB allow_empty_cb =
  74. base::BindRepeating(&AllowEmptyOriginId)) {
  75. mojo::PendingRemote<media::mojom::MediaDrmStorage>
  76. pending_media_drm_storage;
  77. auto receiver = pending_media_drm_storage.InitWithNewPipeAndPassReceiver();
  78. auto media_drm_storage = std::make_unique<media::MojoMediaDrmStorage>(
  79. std::move(pending_media_drm_storage));
  80. // The created object will be destroyed on connection error.
  81. new MediaDrmStorageImpl(*rfh, pref_service_.get(),
  82. std::move(get_origin_id_cb),
  83. std::move(allow_empty_cb), std::move(receiver));
  84. return std::move(media_drm_storage);
  85. }
  86. std::unique_ptr<media::MediaDrmStorage> CreateAndInitMediaDrmStorage(
  87. const GURL& origin,
  88. MediaDrmOriginId* origin_id) {
  89. DCHECK(origin_id);
  90. std::unique_ptr<media::MediaDrmStorage> media_drm_storage =
  91. CreateMediaDrmStorage(SimulateNavigation(origin),
  92. base::BindRepeating(&CreateOriginId));
  93. media_drm_storage->Initialize(
  94. base::BindOnce(OnMediaDrmStorageInit, true, origin_id));
  95. base::RunLoop().RunUntilIdle();
  96. // Verify the origin dictionary is created.
  97. const base::Value::Dict& storage_dict =
  98. pref_service_->GetValueDict(prefs::kMediaDrmStorage);
  99. EXPECT_TRUE(storage_dict.Find(kTestOrigin));
  100. DCHECK(*origin_id);
  101. return media_drm_storage;
  102. }
  103. content::RenderFrameHost* SimulateNavigation(const GURL& url) {
  104. content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
  105. content::RenderFrameHostTester::For(rfh)->InitializeRenderFrameIfNeeded();
  106. auto navigation_simulator =
  107. content::NavigationSimulator::CreateRendererInitiated(url, rfh);
  108. navigation_simulator->Commit();
  109. return navigation_simulator->GetFinalRenderFrameHost();
  110. }
  111. void OnProvisioned() {
  112. media_drm_storage_->OnProvisioned(ExpectResult(true));
  113. }
  114. void SavePersistentSession(const std::string& session_id,
  115. const std::vector<uint8_t>& key_set_id,
  116. const std::string& mime_type,
  117. bool success = true) {
  118. media_drm_storage_->SavePersistentSession(
  119. session_id,
  120. SessionData(key_set_id, mime_type, media::MediaDrmKeyType::OFFLINE),
  121. ExpectResult(success));
  122. }
  123. void LoadPersistentSession(const std::string& session_id,
  124. const std::vector<uint8_t>& expected_key_set_id,
  125. const std::string& expected_mime_type) {
  126. media_drm_storage_->LoadPersistentSession(
  127. session_id, ExpectResult(std::make_unique<SessionData>(
  128. expected_key_set_id, expected_mime_type,
  129. media::MediaDrmKeyType::OFFLINE)));
  130. }
  131. void LoadPersistentSessionAndExpectFailure(const std::string& session_id) {
  132. media_drm_storage_->LoadPersistentSession(
  133. session_id, ExpectResult(std::unique_ptr<SessionData>()));
  134. }
  135. void RemovePersistentSession(const std::string& session_id,
  136. bool success = true) {
  137. media_drm_storage_->RemovePersistentSession(session_id,
  138. ExpectResult(success));
  139. }
  140. void SaveAndLoadPersistentSession(const std::string& session_id,
  141. const std::vector<uint8_t>& key_set_id,
  142. const std::string& mime_type) {
  143. SavePersistentSession(session_id, key_set_id, mime_type);
  144. LoadPersistentSession(session_id, key_set_id, mime_type);
  145. }
  146. media::MediaDrmStorage::ResultCB ExpectResult(bool expected_result) {
  147. return base::BindOnce(&MediaDrmStorageImplTest::CheckResult,
  148. base::Unretained(this), expected_result);
  149. }
  150. media::MediaDrmStorage::LoadPersistentSessionCB ExpectResult(
  151. std::unique_ptr<SessionData> expected_session_data) {
  152. return base::BindOnce(&MediaDrmStorageImplTest::CheckLoadedSession,
  153. base::Unretained(this),
  154. std::move(expected_session_data));
  155. }
  156. void CheckResult(bool expected_result, bool result) {
  157. EXPECT_EQ(expected_result, result);
  158. }
  159. void CheckLoadedSession(std::unique_ptr<SessionData> expected_session_data,
  160. std::unique_ptr<SessionData> session_data) {
  161. if (!expected_session_data) {
  162. EXPECT_FALSE(session_data);
  163. return;
  164. }
  165. EXPECT_EQ(expected_session_data->key_set_id, session_data->key_set_id);
  166. EXPECT_EQ(expected_session_data->mime_type, session_data->mime_type);
  167. }
  168. std::unique_ptr<TestingPrefServiceSimple> pref_service_;
  169. std::unique_ptr<media::MediaDrmStorage> media_drm_storage_;
  170. MediaDrmOriginId origin_id_;
  171. };
  172. // MediaDrmStorageImpl should write origin ID to persistent storage when
  173. // Initialize is called. Later call to Initialize should return the same origin
  174. // ID. The second MediaDrmStorage won't call Initialize until the first one is
  175. // fully initialized.
  176. // TODO(yucliu): Test origin ID is re-generated after clearing licenses.
  177. TEST_F(MediaDrmStorageImplTest, Initialize_OriginIdNotChanged) {
  178. MediaDrmOriginId original_origin_id = origin_id_;
  179. ASSERT_TRUE(original_origin_id);
  180. MediaDrmOriginId origin_id;
  181. std::unique_ptr<media::MediaDrmStorage> storage =
  182. CreateAndInitMediaDrmStorage(GURL(kTestOrigin), &origin_id);
  183. EXPECT_EQ(origin_id, original_origin_id);
  184. }
  185. // Two MediaDrmStorage call Initialize concurrently. The second MediaDrmStorage
  186. // will NOT wait for the first one to be initialized. Both instances should get
  187. // the same origin ID.
  188. TEST_F(MediaDrmStorageImplTest, Initialize_Concurrent) {
  189. content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2));
  190. std::unique_ptr<media::MediaDrmStorage> storage1 =
  191. CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginId));
  192. std::unique_ptr<media::MediaDrmStorage> storage2 =
  193. CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginId));
  194. MediaDrmOriginId origin_id_1;
  195. storage1->Initialize(
  196. base::BindOnce(OnMediaDrmStorageInit, true, &origin_id_1));
  197. MediaDrmOriginId origin_id_2;
  198. storage2->Initialize(
  199. base::BindOnce(OnMediaDrmStorageInit, true, &origin_id_2));
  200. base::RunLoop().RunUntilIdle();
  201. EXPECT_TRUE(origin_id_1);
  202. EXPECT_TRUE(origin_id_2);
  203. EXPECT_EQ(origin_id_1, origin_id_2);
  204. }
  205. TEST_F(MediaDrmStorageImplTest, Initialize_Concurrent_Async) {
  206. content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2));
  207. std::unique_ptr<media::MediaDrmStorage> storage1 =
  208. CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginIdAsync));
  209. std::unique_ptr<media::MediaDrmStorage> storage2 =
  210. CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateOriginIdAsync));
  211. MediaDrmOriginId origin_id_1;
  212. storage1->Initialize(
  213. base::BindOnce(OnMediaDrmStorageInit, true, &origin_id_1));
  214. MediaDrmOriginId origin_id_2;
  215. storage2->Initialize(
  216. base::BindOnce(OnMediaDrmStorageInit, true, &origin_id_2));
  217. base::RunLoop().RunUntilIdle();
  218. EXPECT_TRUE(origin_id_1);
  219. EXPECT_TRUE(origin_id_2);
  220. EXPECT_EQ(origin_id_1, origin_id_2);
  221. }
  222. TEST_F(MediaDrmStorageImplTest, Initialize_DifferentOrigins) {
  223. MediaDrmOriginId origin_id_1 = origin_id_;
  224. ASSERT_TRUE(origin_id_1);
  225. MediaDrmOriginId origin_id_2;
  226. auto storage2 =
  227. CreateAndInitMediaDrmStorage(GURL(kTestOrigin2), &origin_id_2);
  228. ASSERT_TRUE(origin_id_2);
  229. EXPECT_NE(origin_id_1, origin_id_2);
  230. }
  231. TEST_F(MediaDrmStorageImplTest, OnProvisioned) {
  232. OnProvisioned();
  233. base::RunLoop().RunUntilIdle();
  234. // Verify the origin dictionary is created.
  235. const base::Value::Dict& storage_dict =
  236. pref_service_->GetValueDict(prefs::kMediaDrmStorage);
  237. EXPECT_TRUE(storage_dict.Find(kTestOrigin));
  238. }
  239. TEST_F(MediaDrmStorageImplTest, OnProvisioned_Twice) {
  240. OnProvisioned();
  241. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime/type1");
  242. // Provisioning again will clear everything associated with the origin.
  243. OnProvisioned();
  244. LoadPersistentSessionAndExpectFailure("session_id");
  245. base::RunLoop().RunUntilIdle();
  246. }
  247. TEST_F(MediaDrmStorageImplTest, SaveSession_Unprovisioned) {
  248. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime/type1");
  249. base::RunLoop().RunUntilIdle();
  250. }
  251. TEST_F(MediaDrmStorageImplTest, SaveSession_SaveTwice) {
  252. OnProvisioned();
  253. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime/type1");
  254. SaveAndLoadPersistentSession("session_id", {2, 3}, "mime/type2");
  255. base::RunLoop().RunUntilIdle();
  256. }
  257. TEST_F(MediaDrmStorageImplTest, SaveAndLoadSession_LoadTwice) {
  258. OnProvisioned();
  259. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime/type");
  260. LoadPersistentSession("session_id", {1, 0}, "mime/type");
  261. base::RunLoop().RunUntilIdle();
  262. }
  263. TEST_F(MediaDrmStorageImplTest, SaveAndLoadSession_SpecialCharacters) {
  264. OnProvisioned();
  265. SaveAndLoadPersistentSession("session.id", {1, 0}, "mime.type");
  266. SaveAndLoadPersistentSession("session/id", {1, 0}, "mime/type");
  267. SaveAndLoadPersistentSession("session-id", {1, 0}, "mime-type");
  268. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime_type");
  269. SaveAndLoadPersistentSession("session,id", {1, 0}, "mime,type");
  270. base::RunLoop().RunUntilIdle();
  271. }
  272. TEST_F(MediaDrmStorageImplTest, LoadSession_Unprovisioned) {
  273. LoadPersistentSessionAndExpectFailure("session_id");
  274. base::RunLoop().RunUntilIdle();
  275. }
  276. TEST_F(MediaDrmStorageImplTest, RemoveSession_Success) {
  277. OnProvisioned();
  278. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime/type");
  279. RemovePersistentSession("session_id", true);
  280. LoadPersistentSessionAndExpectFailure("session_id");
  281. base::RunLoop().RunUntilIdle();
  282. }
  283. TEST_F(MediaDrmStorageImplTest, RemoveSession_InvalidSession) {
  284. OnProvisioned();
  285. SaveAndLoadPersistentSession("session_id", {1, 0}, "mime/type");
  286. RemovePersistentSession("invalid_session_id", true);
  287. // Can still load "session_id" session.
  288. LoadPersistentSession("session_id", {1, 0}, "mime/type");
  289. base::RunLoop().RunUntilIdle();
  290. }
  291. TEST_F(MediaDrmStorageImplTest, GetAllOrigins) {
  292. OnProvisioned();
  293. base::RunLoop().RunUntilIdle();
  294. // Verify the origin is found.
  295. std::set<GURL> origins =
  296. MediaDrmStorageImpl::GetAllOrigins(pref_service_.get());
  297. EXPECT_EQ(1u, origins.count(GURL(kTestOrigin)));
  298. }
  299. TEST_F(MediaDrmStorageImplTest, GetOriginsModifiedBetween) {
  300. base::Time start_time = base::Time::Now();
  301. OnProvisioned();
  302. base::RunLoop().RunUntilIdle();
  303. // Should not be found before |start_time|.
  304. std::vector<GURL> origins0 = MediaDrmStorageImpl::GetOriginsModifiedBetween(
  305. pref_service_.get(), base::Time(), start_time);
  306. EXPECT_EQ(origins0, std::vector<GURL>{});
  307. // Verify the origin is found from all time.
  308. std::vector<GURL> origins1 = MediaDrmStorageImpl::GetOriginsModifiedBetween(
  309. pref_service_.get(), base::Time(), base::Time::Max());
  310. EXPECT_EQ(origins1, std::vector<GURL>{GURL(kTestOrigin)});
  311. // Should also be found from |start_time|.
  312. std::vector<GURL> origins2 = MediaDrmStorageImpl::GetOriginsModifiedBetween(
  313. pref_service_.get(), start_time, base::Time::Max());
  314. EXPECT_EQ(origins2, std::vector<GURL>{GURL(kTestOrigin)});
  315. // Should not be found from Now().
  316. base::Time check_time = base::Time::Now();
  317. EXPECT_GT(check_time, start_time);
  318. std::vector<GURL> origins3 = MediaDrmStorageImpl::GetOriginsModifiedBetween(
  319. pref_service_.get(), check_time, base::Time::Max());
  320. EXPECT_EQ(origins3, std::vector<GURL>{});
  321. // Save a new session.
  322. SavePersistentSession("session_id", {1, 0}, "mime/type");
  323. base::RunLoop().RunUntilIdle();
  324. // Now that a new session has been added, the origin should be found.
  325. std::vector<GURL> origins4 = MediaDrmStorageImpl::GetOriginsModifiedBetween(
  326. pref_service_.get(), check_time, base::Time::Max());
  327. EXPECT_EQ(origins4, std::vector<GURL>{GURL(kTestOrigin)});
  328. // Now that a new session has been added, the origin should be found between
  329. // two points in time.
  330. base::Time second_check_time = base::Time::Now();
  331. std::vector<GURL> origins5 = MediaDrmStorageImpl::GetOriginsModifiedBetween(
  332. pref_service_.get(), check_time, second_check_time);
  333. EXPECT_EQ(origins5, std::vector<GURL>{GURL(kTestOrigin)});
  334. }
  335. TEST_F(MediaDrmStorageImplTest, AllowEmptyOriginId) {
  336. content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2));
  337. std::unique_ptr<media::MediaDrmStorage> storage =
  338. CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateEmptyOriginId));
  339. MediaDrmOriginId origin_id;
  340. storage->Initialize(base::BindOnce(OnMediaDrmStorageInit, true, &origin_id));
  341. base::RunLoop().RunUntilIdle();
  342. EXPECT_FALSE(origin_id);
  343. }
  344. TEST_F(MediaDrmStorageImplTest, DisallowEmptyOriginId) {
  345. content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2));
  346. std::unique_ptr<media::MediaDrmStorage> storage =
  347. CreateMediaDrmStorage(rfh, base::BindRepeating(&CreateEmptyOriginId),
  348. base::BindRepeating(&DisallowEmptyOriginId));
  349. MediaDrmOriginId origin_id;
  350. storage->Initialize(base::BindOnce(OnMediaDrmStorageInit, false, &origin_id));
  351. base::RunLoop().RunUntilIdle();
  352. EXPECT_FALSE(origin_id);
  353. }
  354. } // namespace cdm