/src/mozilla-central/dom/media/gtest/TestCDMStorage.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=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 "gtest/gtest.h" |
8 | | |
9 | | #include "mozilla/RefPtr.h" |
10 | | |
11 | | #include "ChromiumCDMCallback.h" |
12 | | #include "GMPTestMonitor.h" |
13 | | #include "GMPServiceParent.h" |
14 | | #include "MediaResult.h" |
15 | | #include "nsIFile.h" |
16 | | #include "nsISimpleEnumerator.h" |
17 | | #include "nsNSSComponent.h" //For EnsureNSSInitializedChromeOrContent |
18 | | #include "nsThreadUtils.h" |
19 | | |
20 | | using namespace mozilla; |
21 | | using namespace mozilla::gmp; |
22 | | |
23 | | static already_AddRefed<nsIThread> |
24 | | GetGMPThread() |
25 | 0 | { |
26 | 0 | RefPtr<GeckoMediaPluginService> service = |
27 | 0 | GeckoMediaPluginService::GetGeckoMediaPluginService(); |
28 | 0 | nsCOMPtr<nsIThread> thread; |
29 | 0 | EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread)))); |
30 | 0 | return thread.forget(); |
31 | 0 | } |
32 | | |
33 | | static RefPtr<AbstractThread> |
34 | | GetAbstractGMPThread() |
35 | 0 | { |
36 | 0 | RefPtr<GeckoMediaPluginService> service = |
37 | 0 | GeckoMediaPluginService::GetGeckoMediaPluginService(); |
38 | 0 | return service->GetAbstractGMPThread(); |
39 | 0 | } |
40 | | /** |
41 | | * Enumerate files under |aPath| (non-recursive). |
42 | | */ |
43 | | template<typename T> |
44 | | static nsresult |
45 | | EnumerateDir(nsIFile* aPath, T&& aDirIter) |
46 | 0 | { |
47 | 0 | nsCOMPtr<nsIDirectoryEnumerator> iter; |
48 | 0 | nsresult rv = aPath->GetDirectoryEntries(getter_AddRefs(iter)); |
49 | 0 | if (NS_FAILED(rv)) { |
50 | 0 | return rv; |
51 | 0 | } |
52 | 0 | |
53 | 0 | nsCOMPtr<nsIFile> entry; |
54 | 0 | while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(entry))) && entry) { |
55 | 0 | aDirIter(entry); |
56 | 0 | } |
57 | 0 | return NS_OK; |
58 | 0 | } Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateDir<CDMStorageTest::NodeIdCollector&>(nsIFile*, CDMStorageTest::NodeIdCollector&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateDir<CDMStorageTest::NodeIdVerifier&>(nsIFile*, CDMStorageTest::NodeIdVerifier&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateDir<CDMStorageTest::StorageVerifier&>(nsIFile*, CDMStorageTest::StorageVerifier&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateDir<CDMStorageTest::MaxMTimeFinder&>(nsIFile*, CDMStorageTest::MaxMTimeFinder&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateDir<CDMStorageTest::FileCounter&>(nsIFile*, CDMStorageTest::FileCounter&) |
59 | | |
60 | | /** |
61 | | * Enumerate files under $profileDir/gmp/$platform/gmp-fake/$aDir/ (non-recursive). |
62 | | */ |
63 | | template<typename T> |
64 | | static nsresult |
65 | | EnumerateCDMStorageDir(const nsACString& aDir, T&& aDirIter) |
66 | 0 | { |
67 | 0 | RefPtr<GeckoMediaPluginServiceParent> service = |
68 | 0 | GeckoMediaPluginServiceParent::GetSingleton(); |
69 | 0 | MOZ_ASSERT(service); |
70 | 0 |
|
71 | 0 | // $profileDir/gmp/$platform/ |
72 | 0 | nsCOMPtr<nsIFile> path; |
73 | 0 | nsresult rv = service->GetStorageDir(getter_AddRefs(path)); |
74 | 0 | if (NS_FAILED(rv)) { |
75 | 0 | return rv; |
76 | 0 | } |
77 | 0 | |
78 | 0 | |
79 | 0 | // $profileDir/gmp/$platform/gmp-fake/ |
80 | 0 | rv = path->Append(NS_LITERAL_STRING("gmp-fake")); |
81 | 0 | if (NS_FAILED(rv)) { |
82 | 0 | return rv; |
83 | 0 | } |
84 | 0 | |
85 | 0 | // $profileDir/gmp/$platform/gmp-fake/$aDir/ |
86 | 0 | rv = path->AppendNative(aDir); |
87 | 0 | if (NS_FAILED(rv)) { |
88 | 0 | return rv; |
89 | 0 | } |
90 | 0 | |
91 | 0 | return EnumerateDir(path, aDirIter); |
92 | 0 | } Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateCDMStorageDir<CDMStorageTest::NodeIdCollector>(nsTSubstring<char> const&, CDMStorageTest::NodeIdCollector&&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateCDMStorageDir<CDMStorageTest::NodeIdVerifier>(nsTSubstring<char> const&, CDMStorageTest::NodeIdVerifier&&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateCDMStorageDir<CDMStorageTest::StorageVerifier>(nsTSubstring<char> const&, CDMStorageTest::StorageVerifier&&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateCDMStorageDir<CDMStorageTest::MaxMTimeFinder&>(nsTSubstring<char> const&, CDMStorageTest::MaxMTimeFinder&) Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:nsresult EnumerateCDMStorageDir<CDMStorageTest::FileCounter&>(nsTSubstring<char> const&, CDMStorageTest::FileCounter&) |
93 | | |
94 | | class GMPShutdownObserver : public nsIRunnable |
95 | | , public nsIObserver { |
96 | | public: |
97 | | GMPShutdownObserver(already_AddRefed<nsIRunnable> aShutdownTask, |
98 | | already_AddRefed<nsIRunnable> Continuation, |
99 | | const nsACString& aNodeId) |
100 | | : mShutdownTask(aShutdownTask) |
101 | | , mContinuation(Continuation) |
102 | | , mNodeId(NS_ConvertUTF8toUTF16(aNodeId)) |
103 | 0 | {} |
104 | | |
105 | | NS_DECL_THREADSAFE_ISUPPORTS |
106 | | |
107 | 0 | NS_IMETHOD Run() override { |
108 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
109 | 0 | nsCOMPtr<nsIObserverService> observerService = |
110 | 0 | mozilla::services::GetObserverService(); |
111 | 0 | EXPECT_TRUE(observerService); |
112 | 0 | observerService->AddObserver(this, "gmp-shutdown", false); |
113 | 0 |
|
114 | 0 | nsCOMPtr<nsIThread> thread(GetGMPThread()); |
115 | 0 | thread->Dispatch(mShutdownTask, NS_DISPATCH_NORMAL); |
116 | 0 | return NS_OK; |
117 | 0 | } |
118 | | |
119 | | NS_IMETHOD Observe(nsISupports* aSubject, |
120 | | const char* aTopic, |
121 | | const char16_t* aSomeData) override |
122 | 0 | { |
123 | 0 | if (!strcmp(aTopic, "gmp-shutdown") && |
124 | 0 | mNodeId.Equals(nsDependentString(aSomeData))) { |
125 | 0 | nsCOMPtr<nsIObserverService> observerService = |
126 | 0 | mozilla::services::GetObserverService(); |
127 | 0 | EXPECT_TRUE(observerService); |
128 | 0 | observerService->RemoveObserver(this, "gmp-shutdown"); |
129 | 0 | nsCOMPtr<nsIThread> thread(GetGMPThread()); |
130 | 0 | thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL); |
131 | 0 | } |
132 | 0 | return NS_OK; |
133 | 0 | } |
134 | | |
135 | | private: |
136 | 0 | virtual ~GMPShutdownObserver() {} |
137 | | nsCOMPtr<nsIRunnable> mShutdownTask; |
138 | | nsCOMPtr<nsIRunnable> mContinuation; |
139 | | const nsString mNodeId; |
140 | | }; |
141 | | |
142 | | NS_IMPL_ISUPPORTS(GMPShutdownObserver, nsIRunnable, nsIObserver) |
143 | | |
144 | | class NotifyObserversTask : public Runnable { |
145 | | public: |
146 | | explicit NotifyObserversTask(const char* aTopic) |
147 | | : mozilla::Runnable("NotifyObserversTask") |
148 | | , mTopic(aTopic) |
149 | 0 | {} |
150 | 0 | NS_IMETHOD Run() override { |
151 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
152 | 0 | nsCOMPtr<nsIObserverService> observerService = |
153 | 0 | mozilla::services::GetObserverService(); |
154 | 0 | if (observerService) { |
155 | 0 | observerService->NotifyObservers(nullptr, mTopic, nullptr); |
156 | 0 | } |
157 | 0 | return NS_OK; |
158 | 0 | } |
159 | | const char* mTopic; |
160 | | }; |
161 | | |
162 | | class ClearCDMStorageTask : public nsIRunnable |
163 | | , public nsIObserver { |
164 | | public: |
165 | | ClearCDMStorageTask(already_AddRefed<nsIRunnable> Continuation, |
166 | | nsIThread* aTarget, PRTime aSince) |
167 | | : mContinuation(Continuation) |
168 | | , mTarget(aTarget) |
169 | | , mSince(aSince) |
170 | 0 | {} |
171 | | |
172 | | NS_DECL_THREADSAFE_ISUPPORTS |
173 | | |
174 | 0 | NS_IMETHOD Run() override { |
175 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
176 | 0 | nsCOMPtr<nsIObserverService> observerService = |
177 | 0 | mozilla::services::GetObserverService(); |
178 | 0 | EXPECT_TRUE(observerService); |
179 | 0 | observerService->AddObserver(this, "gmp-clear-storage-complete", false); |
180 | 0 | if (observerService) { |
181 | 0 | nsAutoString str; |
182 | 0 | if (mSince >= 0) { |
183 | 0 | str.AppendInt(static_cast<int64_t>(mSince)); |
184 | 0 | } |
185 | 0 | observerService->NotifyObservers( |
186 | 0 | nullptr, "browser:purge-session-history", str.Data()); |
187 | 0 | } |
188 | 0 | return NS_OK; |
189 | 0 | } |
190 | | |
191 | | NS_IMETHOD Observe(nsISupports* aSubject, |
192 | | const char* aTopic, |
193 | | const char16_t* aSomeData) override |
194 | 0 | { |
195 | 0 | if (!strcmp(aTopic, "gmp-clear-storage-complete")) { |
196 | 0 | nsCOMPtr<nsIObserverService> observerService = |
197 | 0 | mozilla::services::GetObserverService(); |
198 | 0 | EXPECT_TRUE(observerService); |
199 | 0 | observerService->RemoveObserver(this, "gmp-clear-storage-complete"); |
200 | 0 | mTarget->Dispatch(mContinuation, NS_DISPATCH_NORMAL); |
201 | 0 | } |
202 | 0 | return NS_OK; |
203 | 0 | } |
204 | | |
205 | | private: |
206 | 0 | virtual ~ClearCDMStorageTask() {} |
207 | | nsCOMPtr<nsIRunnable> mContinuation; |
208 | | nsCOMPtr<nsIThread> mTarget; |
209 | | const PRTime mSince; |
210 | | }; |
211 | | |
212 | | NS_IMPL_ISUPPORTS(ClearCDMStorageTask, nsIRunnable, nsIObserver) |
213 | | |
214 | | static void |
215 | | ClearCDMStorage(already_AddRefed<nsIRunnable> aContinuation, |
216 | | nsIThread* aTarget, PRTime aSince = -1) |
217 | 0 | { |
218 | 0 | RefPtr<ClearCDMStorageTask> task( |
219 | 0 | new ClearCDMStorageTask(std::move(aContinuation), aTarget, aSince)); |
220 | 0 | SystemGroup::Dispatch(TaskCategory::Other, task.forget()); |
221 | 0 | } |
222 | | |
223 | | static void |
224 | | SimulatePBModeExit() |
225 | 0 | { |
226 | 0 | // SystemGroup::EventTargetFor() doesn't support NS_DISPATCH_SYNC. |
227 | 0 | NS_DispatchToMainThread(new NotifyObserversTask("last-pb-context-exited"), NS_DISPATCH_SYNC); |
228 | 0 | } |
229 | | |
230 | | class TestGetNodeIdCallback : public GetNodeIdCallback |
231 | | { |
232 | | public: |
233 | | TestGetNodeIdCallback(nsCString& aNodeId, nsresult& aResult) |
234 | | : mNodeId(aNodeId), |
235 | | mResult(aResult) |
236 | 0 | { |
237 | 0 | } |
238 | | |
239 | | void Done(nsresult aResult, const nsACString& aNodeId) |
240 | 0 | { |
241 | 0 | mResult = aResult; |
242 | 0 | mNodeId = aNodeId; |
243 | 0 | } |
244 | | |
245 | | private: |
246 | | nsCString& mNodeId; |
247 | | nsresult& mResult; |
248 | | }; |
249 | | |
250 | | static NodeId |
251 | | GetNodeId(const nsAString& aOrigin, |
252 | | const nsAString& aTopLevelOrigin, |
253 | | const nsAString & aGmpName, |
254 | | bool aInPBMode) |
255 | 0 | { |
256 | 0 | OriginAttributes attrs; |
257 | 0 | attrs.mPrivateBrowsingId = aInPBMode ? 1 : 0; |
258 | 0 |
|
259 | 0 | nsAutoCString suffix; |
260 | 0 | attrs.CreateSuffix(suffix); |
261 | 0 |
|
262 | 0 | nsAutoString origin; |
263 | 0 | origin.Assign(aOrigin); |
264 | 0 | origin.Append(NS_ConvertUTF8toUTF16(suffix)); |
265 | 0 |
|
266 | 0 | nsAutoString topLevelOrigin; |
267 | 0 | topLevelOrigin.Assign(aTopLevelOrigin); |
268 | 0 | topLevelOrigin.Append(NS_ConvertUTF8toUTF16(suffix)); |
269 | 0 | return NodeId(origin, topLevelOrigin, aGmpName); |
270 | 0 | } |
271 | | |
272 | | static nsCString |
273 | | GetNodeId(const nsAString& aOrigin, |
274 | | const nsAString& aTopLevelOrigin, |
275 | | bool aInPBMode) |
276 | 0 | { |
277 | 0 | RefPtr<GeckoMediaPluginServiceParent> service = |
278 | 0 | GeckoMediaPluginServiceParent::GetSingleton(); |
279 | 0 | EXPECT_TRUE(service); |
280 | 0 | nsCString nodeId; |
281 | 0 | nsresult result; |
282 | 0 | UniquePtr<GetNodeIdCallback> callback(new TestGetNodeIdCallback(nodeId, |
283 | 0 | result)); |
284 | 0 |
|
285 | 0 | OriginAttributes attrs; |
286 | 0 | attrs.mPrivateBrowsingId = aInPBMode ? 1 : 0; |
287 | 0 |
|
288 | 0 | nsAutoCString suffix; |
289 | 0 | attrs.CreateSuffix(suffix); |
290 | 0 |
|
291 | 0 | nsAutoString origin; |
292 | 0 | origin.Assign(aOrigin); |
293 | 0 | origin.Append(NS_ConvertUTF8toUTF16(suffix)); |
294 | 0 |
|
295 | 0 | nsAutoString topLevelOrigin; |
296 | 0 | topLevelOrigin.Assign(aTopLevelOrigin); |
297 | 0 | topLevelOrigin.Append(NS_ConvertUTF8toUTF16(suffix)); |
298 | 0 |
|
299 | 0 | // We rely on the fact that the GetNodeId implementation for |
300 | 0 | // GeckoMediaPluginServiceParent is synchronous. |
301 | 0 | nsresult rv = service->GetNodeId(origin, |
302 | 0 | topLevelOrigin, |
303 | 0 | NS_LITERAL_STRING("gmp-fake"), |
304 | 0 | std::move(callback)); |
305 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv) && NS_SUCCEEDED(result)); |
306 | 0 | return nodeId; |
307 | 0 | } |
308 | | |
309 | | static bool |
310 | | IsCDMStorageIsEmpty() |
311 | 0 | { |
312 | 0 | RefPtr<GeckoMediaPluginServiceParent> service = |
313 | 0 | GeckoMediaPluginServiceParent::GetSingleton(); |
314 | 0 | MOZ_ASSERT(service); |
315 | 0 | nsCOMPtr<nsIFile> storage; |
316 | 0 | nsresult rv = service->GetStorageDir(getter_AddRefs(storage)); |
317 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
318 | 0 | bool exists = false; |
319 | 0 | if (storage) { |
320 | 0 | storage->Exists(&exists); |
321 | 0 | } |
322 | 0 | return !exists; |
323 | 0 | } |
324 | | |
325 | | static void |
326 | | AssertIsOnGMPThread() |
327 | 0 | { |
328 | 0 | RefPtr<GeckoMediaPluginService> service = |
329 | 0 | GeckoMediaPluginService::GetGeckoMediaPluginService(); |
330 | 0 | MOZ_ASSERT(service); |
331 | 0 | nsCOMPtr<nsIThread> thread; |
332 | 0 | service->GetThread(getter_AddRefs(thread)); |
333 | 0 | MOZ_ASSERT(thread); |
334 | 0 | nsCOMPtr<nsIThread> currentThread; |
335 | 0 | DebugOnly<nsresult> rv = NS_GetCurrentThread(getter_AddRefs(currentThread)); |
336 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
337 | 0 | MOZ_ASSERT(currentThread == thread); |
338 | 0 | } |
339 | | |
340 | | class CDMStorageTest |
341 | | { |
342 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CDMStorageTest) |
343 | | |
344 | 0 | void DoTest(void (CDMStorageTest::*aTestMethod)()) { |
345 | 0 | EnsureNSSInitializedChromeOrContent(); |
346 | 0 | nsCOMPtr<nsIThread> thread(GetGMPThread()); |
347 | 0 | ClearCDMStorage( |
348 | 0 | NewRunnableMethod("CDMStorageTest::DoTest", this, aTestMethod), thread); |
349 | 0 | AwaitFinished(); |
350 | 0 | } |
351 | | |
352 | | CDMStorageTest() |
353 | | : mMonitor("CDMStorageTest") |
354 | | , mFinished(false) |
355 | 0 | { |
356 | 0 | } |
357 | | |
358 | | void |
359 | | Update(const nsCString& aMessage) |
360 | 0 | { |
361 | 0 | nsTArray<uint8_t> msg; |
362 | 0 | msg.AppendElements(aMessage.get(), aMessage.Length()); |
363 | 0 | mCDM->UpdateSession(NS_LITERAL_CSTRING("fake-session-id"), 1, msg); |
364 | 0 | } |
365 | | |
366 | | void TestGetNodeId() |
367 | 0 | { |
368 | 0 | AssertIsOnGMPThread(); |
369 | 0 |
|
370 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
371 | 0 |
|
372 | 0 | const nsString origin1 = NS_LITERAL_STRING("http://example1.com"); |
373 | 0 | const nsString origin2 = NS_LITERAL_STRING("http://example2.org"); |
374 | 0 |
|
375 | 0 | nsCString PBnodeId1 = GetNodeId(origin1, origin2, true); |
376 | 0 | nsCString PBnodeId2 = GetNodeId(origin1, origin2, true); |
377 | 0 |
|
378 | 0 | // Node ids for the same origins should be the same in PB mode. |
379 | 0 | EXPECT_TRUE(PBnodeId1.Equals(PBnodeId2)); |
380 | 0 |
|
381 | 0 | nsCString PBnodeId3 = GetNodeId(origin2, origin1, true); |
382 | 0 |
|
383 | 0 | // Node ids with origin and top level origin swapped should be different. |
384 | 0 | EXPECT_TRUE(!PBnodeId3.Equals(PBnodeId1)); |
385 | 0 |
|
386 | 0 | // Getting node ids in PB mode should not result in the node id being stored. |
387 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
388 | 0 |
|
389 | 0 | nsCString nodeId1 = GetNodeId(origin1, origin2, false); |
390 | 0 | nsCString nodeId2 = GetNodeId(origin1, origin2, false); |
391 | 0 |
|
392 | 0 | // NodeIds for the same origin pair in non-pb mode should be the same. |
393 | 0 | EXPECT_TRUE(nodeId1.Equals(nodeId2)); |
394 | 0 |
|
395 | 0 | // Node ids for a given origin pair should be different for the PB origins should be the same in PB mode. |
396 | 0 | EXPECT_TRUE(!PBnodeId1.Equals(nodeId1)); |
397 | 0 | EXPECT_TRUE(!PBnodeId2.Equals(nodeId2)); |
398 | 0 |
|
399 | 0 | nsCOMPtr<nsIThread> thread(GetGMPThread()); |
400 | 0 | ClearCDMStorage( |
401 | 0 | NewRunnableMethod<nsCString>("CDMStorageTest::TestGetNodeId_Continuation", |
402 | 0 | this, |
403 | 0 | &CDMStorageTest::TestGetNodeId_Continuation, |
404 | 0 | nodeId1), |
405 | 0 | thread); |
406 | 0 | } |
407 | | |
408 | 0 | void TestGetNodeId_Continuation(nsCString aNodeId1) { |
409 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
410 | 0 |
|
411 | 0 | // Once we clear storage, the node ids generated for the same origin-pair |
412 | 0 | // should be different. |
413 | 0 | const nsString origin1 = NS_LITERAL_STRING("http://example1.com"); |
414 | 0 | const nsString origin2 = NS_LITERAL_STRING("http://example2.org"); |
415 | 0 | nsCString nodeId3 = GetNodeId(origin1, origin2, false); |
416 | 0 | EXPECT_TRUE(!aNodeId1.Equals(nodeId3)); |
417 | 0 |
|
418 | 0 | SetFinished(); |
419 | 0 | } |
420 | | |
421 | | void CreateDecryptor(const nsAString& aOrigin, |
422 | | const nsAString& aTopLevelOrigin, |
423 | | bool aInPBMode, |
424 | | const nsCString& aUpdate) |
425 | 0 | { |
426 | 0 | nsTArray<nsCString> updates; |
427 | 0 | updates.AppendElement(aUpdate); |
428 | 0 | CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, std::move(updates)); |
429 | 0 | } |
430 | | |
431 | | void CreateDecryptor(const nsAString& aOrigin, |
432 | | const nsAString& aTopLevelOrigin, |
433 | | bool aInPBMode, |
434 | 0 | nsTArray<nsCString>&& aUpdates) { |
435 | 0 | CreateDecryptor(GetNodeId(aOrigin, aTopLevelOrigin, NS_LITERAL_STRING("gmp-fake"), aInPBMode), std::move(aUpdates)); |
436 | 0 | } |
437 | | |
438 | | void CreateDecryptor(const NodeId& aNodeId, |
439 | 0 | nsTArray<nsCString>&& aUpdates) { |
440 | 0 | RefPtr<GeckoMediaPluginService> service = |
441 | 0 | GeckoMediaPluginService::GetGeckoMediaPluginService(); |
442 | 0 | EXPECT_TRUE(service); |
443 | 0 |
|
444 | 0 | nsTArray<nsCString> tags; |
445 | 0 | tags.AppendElement(NS_LITERAL_CSTRING("fake")); |
446 | 0 |
|
447 | 0 | RefPtr<CDMStorageTest> self = this; |
448 | 0 | RefPtr<gmp::GetCDMParentPromise> promise = |
449 | 0 | service->GetCDM(aNodeId, std::move(tags), nullptr); |
450 | 0 | auto thread = GetAbstractGMPThread(); |
451 | 0 | promise->Then(thread, |
452 | 0 | __func__, |
453 | 0 | [self, aUpdates](RefPtr<gmp::ChromiumCDMParent> cdm) { |
454 | 0 | self->mCDM = cdm; |
455 | 0 | EXPECT_TRUE(!!self->mCDM); |
456 | 0 | self->mCallback.reset(new CallbackProxy(self)); |
457 | 0 | nsCString failureReason; |
458 | 0 | self->mCDM->Init(self->mCallback.get(), |
459 | 0 | false, |
460 | 0 | true, |
461 | 0 | GetMainThreadEventTarget(), |
462 | 0 | failureReason); |
463 | 0 |
|
464 | 0 | for (auto& update : aUpdates) { |
465 | 0 | self->Update(update); |
466 | 0 | } |
467 | 0 | }, |
468 | 0 | [](MediaResult rv) { EXPECT_TRUE(false); }); |
469 | 0 | } |
470 | | |
471 | 0 | void TestBasicStorage() { |
472 | 0 | AssertIsOnGMPThread(); |
473 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
474 | 0 |
|
475 | 0 | RefPtr<GeckoMediaPluginService> service = |
476 | 0 | GeckoMediaPluginService::GetGeckoMediaPluginService(); |
477 | 0 |
|
478 | 0 | // Send a message to the fake GMP for it to run its own tests internally. |
479 | 0 | // It sends us a "test-storage complete" message when its passed, or |
480 | 0 | // some other message if its tests fail. |
481 | 0 | Expect(NS_LITERAL_CSTRING("test-storage complete"), |
482 | 0 | NewRunnableMethod("CDMStorageTest::SetFinished", |
483 | 0 | this, |
484 | 0 | &CDMStorageTest::SetFinished)); |
485 | 0 |
|
486 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), |
487 | 0 | NS_LITERAL_STRING("http://example2.com"), |
488 | 0 | false, |
489 | 0 | NS_LITERAL_CSTRING("test-storage")); |
490 | 0 | } |
491 | | |
492 | | /** |
493 | | * 1. Generate storage data for some sites. |
494 | | * 2. Forget about one of the sites. |
495 | | * 3. Check if the storage data for the forgotten site are erased correctly. |
496 | | * 4. Check if the storage data for other sites remain unchanged. |
497 | | */ |
498 | 0 | void TestForgetThisSite() { |
499 | 0 | AssertIsOnGMPThread(); |
500 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
501 | 0 |
|
502 | 0 | // Generate storage data for some site. |
503 | 0 | nsCOMPtr<nsIRunnable> r = |
504 | 0 | NewRunnableMethod("CDMStorageTest::TestForgetThisSite_AnotherSite", |
505 | 0 | this, |
506 | 0 | &CDMStorageTest::TestForgetThisSite_AnotherSite); |
507 | 0 | Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); |
508 | 0 |
|
509 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), |
510 | 0 | NS_LITERAL_STRING("http://example2.com"), |
511 | 0 | false, |
512 | 0 | NS_LITERAL_CSTRING("test-storage")); |
513 | 0 | } |
514 | | |
515 | 0 | void TestForgetThisSite_AnotherSite() { |
516 | 0 | Shutdown(); |
517 | 0 |
|
518 | 0 | // Generate storage data for another site. |
519 | 0 | nsCOMPtr<nsIRunnable> r = |
520 | 0 | NewRunnableMethod("CDMStorageTest::TestForgetThisSite_CollectSiteInfo", |
521 | 0 | this, |
522 | 0 | &CDMStorageTest::TestForgetThisSite_CollectSiteInfo); |
523 | 0 | Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); |
524 | 0 |
|
525 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example3.com"), |
526 | 0 | NS_LITERAL_STRING("http://example4.com"), |
527 | 0 | false, |
528 | 0 | NS_LITERAL_CSTRING("test-storage")); |
529 | 0 | } |
530 | | |
531 | | struct NodeInfo { |
532 | | explicit NodeInfo(const nsACString& aSite, |
533 | | const mozilla::OriginAttributesPattern& aPattern) |
534 | | : siteToForget(aSite) |
535 | | , mPattern(aPattern) |
536 | 0 | { } |
537 | | nsCString siteToForget; |
538 | | mozilla::OriginAttributesPattern mPattern; |
539 | | nsTArray<nsCString> expectedRemainingNodeIds; |
540 | | }; |
541 | | |
542 | | class NodeIdCollector { |
543 | | public: |
544 | 0 | explicit NodeIdCollector(NodeInfo* aInfo) : mNodeInfo(aInfo) {} |
545 | 0 | void operator()(nsIFile* aFile) { |
546 | 0 | nsCString salt; |
547 | 0 | nsresult rv = ReadSalt(aFile, salt); |
548 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
549 | 0 | if (!MatchOrigin(aFile, mNodeInfo->siteToForget, mNodeInfo->mPattern)) { |
550 | 0 | mNodeInfo->expectedRemainingNodeIds.AppendElement(salt); |
551 | 0 | } |
552 | 0 | } |
553 | | private: |
554 | | NodeInfo* mNodeInfo; |
555 | | }; |
556 | | |
557 | 0 | void TestForgetThisSite_CollectSiteInfo() { |
558 | 0 | mozilla::OriginAttributesPattern pattern; |
559 | 0 |
|
560 | 0 | UniquePtr<NodeInfo> siteInfo( |
561 | 0 | new NodeInfo(NS_LITERAL_CSTRING("http://example1.com"), |
562 | 0 | pattern)); |
563 | 0 | // Collect nodeIds that are expected to remain for later comparison. |
564 | 0 | EnumerateCDMStorageDir(NS_LITERAL_CSTRING("id"), |
565 | 0 | NodeIdCollector(siteInfo.get())); |
566 | 0 | // Invoke "Forget this site" on the main thread. |
567 | 0 | SystemGroup::Dispatch(TaskCategory::Other, |
568 | 0 | NewRunnableMethod<UniquePtr<NodeInfo>&&>( |
569 | 0 | "CDMStorageTest::TestForgetThisSite_Forget", |
570 | 0 | this, |
571 | 0 | &CDMStorageTest::TestForgetThisSite_Forget, |
572 | 0 | std::move(siteInfo))); |
573 | 0 | } |
574 | | |
575 | 0 | void TestForgetThisSite_Forget(UniquePtr<NodeInfo>&& aSiteInfo) { |
576 | 0 | RefPtr<GeckoMediaPluginServiceParent> service = |
577 | 0 | GeckoMediaPluginServiceParent::GetSingleton(); |
578 | 0 | service->ForgetThisSiteNative(NS_ConvertUTF8toUTF16(aSiteInfo->siteToForget), |
579 | 0 | aSiteInfo->mPattern); |
580 | 0 |
|
581 | 0 | nsCOMPtr<nsIThread> thread; |
582 | 0 | service->GetThread(getter_AddRefs(thread)); |
583 | 0 |
|
584 | 0 | nsCOMPtr<nsIRunnable> r = NewRunnableMethod<UniquePtr<NodeInfo>&&>( |
585 | 0 | "CDMStorageTest::TestForgetThisSite_Verify", |
586 | 0 | this, |
587 | 0 | &CDMStorageTest::TestForgetThisSite_Verify, |
588 | 0 | std::move(aSiteInfo)); |
589 | 0 | thread->Dispatch(r, NS_DISPATCH_NORMAL); |
590 | 0 |
|
591 | 0 | nsCOMPtr<nsIRunnable> f = NewRunnableMethod( |
592 | 0 | "CDMStorageTest::SetFinished", this, &CDMStorageTest::SetFinished); |
593 | 0 | thread->Dispatch(f, NS_DISPATCH_NORMAL); |
594 | 0 | } |
595 | | |
596 | | class NodeIdVerifier { |
597 | | public: |
598 | | explicit NodeIdVerifier(const NodeInfo* aInfo) |
599 | | : mNodeInfo(aInfo) |
600 | 0 | , mExpectedRemainingNodeIds(aInfo->expectedRemainingNodeIds) {} |
601 | 0 | void operator()(nsIFile* aFile) { |
602 | 0 | nsCString salt; |
603 | 0 | nsresult rv = ReadSalt(aFile, salt); |
604 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
605 | 0 | // Shouldn't match the origin if we clear correctly. |
606 | 0 | EXPECT_FALSE(MatchOrigin(aFile, mNodeInfo->siteToForget, mNodeInfo->mPattern)); |
607 | 0 | // Check if remaining nodeIDs are as expected. |
608 | 0 | EXPECT_TRUE(mExpectedRemainingNodeIds.RemoveElement(salt)); |
609 | 0 | } |
610 | 0 | ~NodeIdVerifier() { |
611 | 0 | EXPECT_TRUE(mExpectedRemainingNodeIds.IsEmpty()); |
612 | 0 | } |
613 | | private: |
614 | | const NodeInfo* mNodeInfo; |
615 | | nsTArray<nsCString> mExpectedRemainingNodeIds; |
616 | | }; |
617 | | |
618 | | class StorageVerifier { |
619 | | public: |
620 | | explicit StorageVerifier(const NodeInfo* aInfo) |
621 | 0 | : mExpectedRemainingNodeIds(aInfo->expectedRemainingNodeIds) {} |
622 | 0 | void operator()(nsIFile* aFile) { |
623 | 0 | nsCString salt; |
624 | 0 | nsresult rv = aFile->GetNativeLeafName(salt); |
625 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
626 | 0 | EXPECT_TRUE(mExpectedRemainingNodeIds.RemoveElement(salt)); |
627 | 0 | } |
628 | 0 | ~StorageVerifier() { |
629 | 0 | EXPECT_TRUE(mExpectedRemainingNodeIds.IsEmpty()); |
630 | 0 | } |
631 | | private: |
632 | | nsTArray<nsCString> mExpectedRemainingNodeIds; |
633 | | }; |
634 | | |
635 | 0 | void TestForgetThisSite_Verify(UniquePtr<NodeInfo>&& aSiteInfo) { |
636 | 0 | nsresult rv = EnumerateCDMStorageDir( |
637 | 0 | NS_LITERAL_CSTRING("id"), NodeIdVerifier(aSiteInfo.get())); |
638 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
639 | 0 |
|
640 | 0 | rv = EnumerateCDMStorageDir( |
641 | 0 | NS_LITERAL_CSTRING("storage"), StorageVerifier(aSiteInfo.get())); |
642 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
643 | 0 | } |
644 | | |
645 | | /** |
646 | | * 1. Generate some storage data. |
647 | | * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/id/. |
648 | | * 3. Pass |t| to clear recent history. |
649 | | * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and |
650 | | * $profileDir/gmp/$platform/gmp-fake/storage are removed. |
651 | | */ |
652 | 0 | void TestClearRecentHistory1() { |
653 | 0 | AssertIsOnGMPThread(); |
654 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
655 | 0 |
|
656 | 0 | // Generate storage data for some site. |
657 | 0 | nsCOMPtr<nsIRunnable> r = |
658 | 0 | NewRunnableMethod("CDMStorageTest::TestClearRecentHistory1_Clear", |
659 | 0 | this, |
660 | 0 | &CDMStorageTest::TestClearRecentHistory1_Clear); |
661 | 0 | Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); |
662 | 0 |
|
663 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), |
664 | 0 | NS_LITERAL_STRING("http://example2.com"), |
665 | 0 | false, |
666 | 0 | NS_LITERAL_CSTRING("test-storage")); |
667 | 0 | } |
668 | | |
669 | | /** |
670 | | * 1. Generate some storage data. |
671 | | * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/. |
672 | | * 3. Pass |t| to clear recent history. |
673 | | * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and |
674 | | * $profileDir/gmp/$platform/gmp-fake/storage are removed. |
675 | | */ |
676 | 0 | void TestClearRecentHistory2() { |
677 | 0 | AssertIsOnGMPThread(); |
678 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
679 | 0 |
|
680 | 0 | // Generate storage data for some site. |
681 | 0 | nsCOMPtr<nsIRunnable> r = |
682 | 0 | NewRunnableMethod("CDMStorageTest::TestClearRecentHistory2_Clear", |
683 | 0 | this, |
684 | 0 | &CDMStorageTest::TestClearRecentHistory2_Clear); |
685 | 0 | Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); |
686 | 0 |
|
687 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), |
688 | 0 | NS_LITERAL_STRING("http://example2.com"), |
689 | 0 | false, |
690 | 0 | NS_LITERAL_CSTRING("test-storage")); |
691 | 0 | } |
692 | | |
693 | | /** |
694 | | * 1. Generate some storage data. |
695 | | * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/. |
696 | | * 3. Pass |t+1| to clear recent history. |
697 | | * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and |
698 | | * $profileDir/gmp/$platform/gmp-fake/storage remain unchanged. |
699 | | */ |
700 | 0 | void TestClearRecentHistory3() { |
701 | 0 | AssertIsOnGMPThread(); |
702 | 0 | EXPECT_TRUE(IsCDMStorageIsEmpty()); |
703 | 0 |
|
704 | 0 | // Generate storage data for some site. |
705 | 0 | nsCOMPtr<nsIRunnable> r = |
706 | 0 | NewRunnableMethod("CDMStorageTest::TestClearRecentHistory3_Clear", |
707 | 0 | this, |
708 | 0 | &CDMStorageTest::TestClearRecentHistory3_Clear); |
709 | 0 | Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); |
710 | 0 |
|
711 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), |
712 | 0 | NS_LITERAL_STRING("http://example2.com"), |
713 | 0 | false, |
714 | 0 | NS_LITERAL_CSTRING("test-storage")); |
715 | 0 | } |
716 | | |
717 | | class MaxMTimeFinder { |
718 | | public: |
719 | 0 | MaxMTimeFinder() : mMaxTime(0) {} |
720 | 0 | void operator()(nsIFile* aFile) { |
721 | 0 | PRTime lastModified; |
722 | 0 | nsresult rv = aFile->GetLastModifiedTime(&lastModified); |
723 | 0 | if (NS_SUCCEEDED(rv) && lastModified > mMaxTime) { |
724 | 0 | mMaxTime = lastModified; |
725 | 0 | } |
726 | 0 | EnumerateDir(aFile, *this); |
727 | 0 | } |
728 | 0 | PRTime GetResult() const { return mMaxTime; } |
729 | | private: |
730 | | PRTime mMaxTime; |
731 | | }; |
732 | | |
733 | 0 | void TestClearRecentHistory1_Clear() { |
734 | 0 | MaxMTimeFinder f; |
735 | 0 | nsresult rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("id"), f); |
736 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
737 | 0 |
|
738 | 0 | nsCOMPtr<nsIRunnable> r = |
739 | 0 | NewRunnableMethod("CDMStorageTest::TestClearRecentHistory_CheckEmpty", |
740 | 0 | this, |
741 | 0 | &CDMStorageTest::TestClearRecentHistory_CheckEmpty); |
742 | 0 | nsCOMPtr<nsIThread> t(GetGMPThread()); |
743 | 0 | ClearCDMStorage(r.forget(), t, f.GetResult()); |
744 | 0 | } |
745 | | |
746 | 0 | void TestClearRecentHistory2_Clear() { |
747 | 0 | MaxMTimeFinder f; |
748 | 0 | nsresult rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("storage"), f); |
749 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
750 | 0 |
|
751 | 0 | nsCOMPtr<nsIRunnable> r = |
752 | 0 | NewRunnableMethod("CDMStorageTest::TestClearRecentHistory_CheckEmpty", |
753 | 0 | this, |
754 | 0 | &CDMStorageTest::TestClearRecentHistory_CheckEmpty); |
755 | 0 | nsCOMPtr<nsIThread> t(GetGMPThread()); |
756 | 0 | ClearCDMStorage(r.forget(), t, f.GetResult()); |
757 | 0 | } |
758 | | |
759 | 0 | void TestClearRecentHistory3_Clear() { |
760 | 0 | MaxMTimeFinder f; |
761 | 0 | nsresult rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("storage"), f); |
762 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
763 | 0 |
|
764 | 0 | nsCOMPtr<nsIRunnable> r = |
765 | 0 | NewRunnableMethod("CDMStorageTest::TestClearRecentHistory_CheckNonEmpty", |
766 | 0 | this, |
767 | 0 | &CDMStorageTest::TestClearRecentHistory_CheckNonEmpty); |
768 | 0 | nsCOMPtr<nsIThread> t(GetGMPThread()); |
769 | 0 | ClearCDMStorage(r.forget(), t, f.GetResult() + 1); |
770 | 0 | } |
771 | | |
772 | | class FileCounter { |
773 | | public: |
774 | 0 | FileCounter() : mCount(0) {} |
775 | 0 | void operator()(nsIFile* aFile) { |
776 | 0 | ++mCount; |
777 | 0 | } |
778 | 0 | int GetCount() const { return mCount; } |
779 | | private: |
780 | | int mCount; |
781 | | }; |
782 | | |
783 | 0 | void TestClearRecentHistory_CheckEmpty() { |
784 | 0 | FileCounter c1; |
785 | 0 | nsresult rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("id"), c1); |
786 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
787 | 0 | // There should be no files under $profileDir/gmp/$platform/gmp-fake/id/ |
788 | 0 | EXPECT_EQ(c1.GetCount(), 0); |
789 | 0 |
|
790 | 0 | FileCounter c2; |
791 | 0 | rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("storage"), c2); |
792 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
793 | 0 | // There should be no files under $profileDir/gmp/$platform/gmp-fake/storage/ |
794 | 0 | EXPECT_EQ(c2.GetCount(), 0); |
795 | 0 |
|
796 | 0 | SetFinished(); |
797 | 0 | } |
798 | | |
799 | 0 | void TestClearRecentHistory_CheckNonEmpty() { |
800 | 0 | FileCounter c1; |
801 | 0 | nsresult rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("id"), c1); |
802 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
803 | 0 | // There should be one directory under $profileDir/gmp/$platform/gmp-fake/id/ |
804 | 0 | EXPECT_EQ(c1.GetCount(), 1); |
805 | 0 |
|
806 | 0 | FileCounter c2; |
807 | 0 | rv = EnumerateCDMStorageDir(NS_LITERAL_CSTRING("storage"), c2); |
808 | 0 | EXPECT_TRUE(NS_SUCCEEDED(rv)); |
809 | 0 | // There should be one directory under $profileDir/gmp/$platform/gmp-fake/storage/ |
810 | 0 | EXPECT_EQ(c2.GetCount(), 1); |
811 | 0 |
|
812 | 0 | SetFinished(); |
813 | 0 | } |
814 | | |
815 | 0 | void TestCrossOriginStorage() { |
816 | 0 | EXPECT_TRUE(!mCDM); |
817 | 0 |
|
818 | 0 | // Send the decryptor the message "store recordid $time" |
819 | 0 | // Wait for the decrytor to send us "stored recordid $time" |
820 | 0 | auto t = time(0); |
821 | 0 | nsCString response("stored crossOriginTestRecordId "); |
822 | 0 | response.AppendInt((int64_t)t); |
823 | 0 | Expect(response, |
824 | 0 | NewRunnableMethod( |
825 | 0 | "CDMStorageTest::TestCrossOriginStorage_RecordStoredContinuation", |
826 | 0 | this, |
827 | 0 | &CDMStorageTest::TestCrossOriginStorage_RecordStoredContinuation)); |
828 | 0 |
|
829 | 0 | nsCString update("store crossOriginTestRecordId "); |
830 | 0 | update.AppendInt((int64_t)t); |
831 | 0 |
|
832 | 0 | // Open decryptor on one, origin, write a record, and test that that |
833 | 0 | // record can't be read on another origin. |
834 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example3.com"), |
835 | 0 | NS_LITERAL_STRING("http://example4.com"), |
836 | 0 | false, |
837 | 0 | update); |
838 | 0 | } |
839 | | |
840 | 0 | void TestCrossOriginStorage_RecordStoredContinuation() { |
841 | 0 | // Close the old decryptor, and create a new one on a different origin, |
842 | 0 | // and try to read the record. |
843 | 0 | Shutdown(); |
844 | 0 |
|
845 | 0 | Expect(NS_LITERAL_CSTRING( |
846 | 0 | "retrieve crossOriginTestRecordId succeeded (length 0 bytes)"), |
847 | 0 | NewRunnableMethod("CDMStorageTest::SetFinished", |
848 | 0 | this, |
849 | 0 | &CDMStorageTest::SetFinished)); |
850 | 0 |
|
851 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://example5.com"), |
852 | 0 | NS_LITERAL_STRING("http://example6.com"), |
853 | 0 | false, |
854 | 0 | NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId")); |
855 | 0 | } |
856 | | |
857 | 0 | void TestPBStorage() { |
858 | 0 | // Send the decryptor the message "store recordid $time" |
859 | 0 | // Wait for the decrytor to send us "stored recordid $time" |
860 | 0 | nsCString response("stored pbdata test-pb-data"); |
861 | 0 | Expect(response, |
862 | 0 | NewRunnableMethod( |
863 | 0 | "CDMStorageTest::TestPBStorage_RecordStoredContinuation", |
864 | 0 | this, |
865 | 0 | &CDMStorageTest::TestPBStorage_RecordStoredContinuation)); |
866 | 0 |
|
867 | 0 | // Open decryptor on one, origin, write a record, close decryptor, |
868 | 0 | // open another, and test that record can be read, close decryptor, |
869 | 0 | // then send pb-last-context-closed notification, then open decryptor |
870 | 0 | // and check that it can't read that data; it should have been purged. |
871 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"), |
872 | 0 | NS_LITERAL_STRING("http://pb2.com"), |
873 | 0 | true, |
874 | 0 | NS_LITERAL_CSTRING("store pbdata test-pb-data")); |
875 | 0 | } |
876 | | |
877 | 0 | void TestPBStorage_RecordStoredContinuation() { |
878 | 0 | Shutdown(); |
879 | 0 |
|
880 | 0 | Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"), |
881 | 0 | NewRunnableMethod( |
882 | 0 | "CDMStorageTest::TestPBStorage_RecordRetrievedContinuation", |
883 | 0 | this, |
884 | 0 | &CDMStorageTest::TestPBStorage_RecordRetrievedContinuation)); |
885 | 0 |
|
886 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"), |
887 | 0 | NS_LITERAL_STRING("http://pb2.com"), |
888 | 0 | true, |
889 | 0 | NS_LITERAL_CSTRING("retrieve pbdata")); |
890 | 0 | } |
891 | | |
892 | 0 | void TestPBStorage_RecordRetrievedContinuation() { |
893 | 0 | Shutdown(); |
894 | 0 | SimulatePBModeExit(); |
895 | 0 |
|
896 | 0 | Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"), |
897 | 0 | NewRunnableMethod("CDMStorageTest::SetFinished", |
898 | 0 | this, |
899 | 0 | &CDMStorageTest::SetFinished)); |
900 | 0 |
|
901 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"), |
902 | 0 | NS_LITERAL_STRING("http://pb2.com"), |
903 | 0 | true, |
904 | 0 | NS_LITERAL_CSTRING("retrieve pbdata")); |
905 | 0 | } |
906 | | |
907 | | #if defined(XP_WIN) |
908 | | void TestOutputProtection() { |
909 | | Shutdown(); |
910 | | |
911 | | Expect(NS_LITERAL_CSTRING("OP tests completed"), |
912 | | NewRunnableMethod("CDMStorageTest::SetFinished", |
913 | | this, &CDMStorageTest::SetFinished)); |
914 | | |
915 | | CreateDecryptor(NS_LITERAL_STRING("http://example15.com"), |
916 | | NS_LITERAL_STRING("http://example16.com"), |
917 | | false, |
918 | | NS_LITERAL_CSTRING("test-op-apis")); |
919 | | } |
920 | | #endif |
921 | | |
922 | 0 | void TestLongRecordNames() { |
923 | 0 | NS_NAMED_LITERAL_CSTRING(longRecordName, |
924 | 0 | "A_" |
925 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
926 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
927 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
928 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
929 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
930 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
931 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
932 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
933 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
934 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
935 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
936 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
937 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
938 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
939 | 0 | "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_" |
940 | 0 | "long_record_name"); |
941 | 0 |
|
942 | 0 | NS_NAMED_LITERAL_CSTRING(data, "Just_some_arbitrary_data."); |
943 | 0 |
|
944 | 0 | MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE); |
945 | 0 | MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH |
946 | 0 |
|
947 | 0 | nsCString response("stored "); |
948 | 0 | response.Append(longRecordName); |
949 | 0 | response.AppendLiteral(" "); |
950 | 0 | response.Append(data); |
951 | 0 | Expect(response, |
952 | 0 | NewRunnableMethod("CDMStorageTest::SetFinished", |
953 | 0 | this, |
954 | 0 | &CDMStorageTest::SetFinished)); |
955 | 0 |
|
956 | 0 | nsCString update("store "); |
957 | 0 | update.Append(longRecordName); |
958 | 0 | update.AppendLiteral(" "); |
959 | 0 | update.Append(data); |
960 | 0 | CreateDecryptor(NS_LITERAL_STRING("http://fuz.com"), |
961 | 0 | NS_LITERAL_STRING("http://baz.com"), |
962 | 0 | false, |
963 | 0 | update); |
964 | 0 | } |
965 | | |
966 | 0 | void Expect(const nsCString& aMessage, already_AddRefed<nsIRunnable> aContinuation) { |
967 | 0 | mExpected.AppendElement(ExpectedMessage(aMessage, std::move(aContinuation))); |
968 | 0 | } |
969 | | |
970 | 0 | void AwaitFinished() { |
971 | 0 | mozilla::SpinEventLoopUntil([&]() -> bool { return mFinished; }); |
972 | 0 | mFinished = false; |
973 | 0 | } |
974 | | |
975 | 0 | void ShutdownThen(already_AddRefed<nsIRunnable> aContinuation) { |
976 | 0 | EXPECT_TRUE(!!mCDM); |
977 | 0 | if (!mCDM) { |
978 | 0 | return; |
979 | 0 | } |
980 | 0 | EXPECT_FALSE(mNodeId.IsEmpty()); |
981 | 0 | RefPtr<GMPShutdownObserver> task(new GMPShutdownObserver( |
982 | 0 | NewRunnableMethod( |
983 | 0 | "CDMStorageTest::Shutdown", this, &CDMStorageTest::Shutdown), |
984 | 0 | std::move(aContinuation), |
985 | 0 | mNodeId)); |
986 | 0 | SystemGroup::Dispatch(TaskCategory::Other, task.forget()); |
987 | 0 | } |
988 | | |
989 | 0 | void Shutdown() { |
990 | 0 | if (mCDM) { |
991 | 0 | mCDM->Shutdown(); |
992 | 0 | mCDM = nullptr; |
993 | 0 | mNodeId = EmptyCString(); |
994 | 0 | } |
995 | 0 | } |
996 | | |
997 | 0 | void Dummy() { |
998 | 0 | } |
999 | | |
1000 | 0 | void SetFinished() { |
1001 | 0 | mFinished = true; |
1002 | 0 | Shutdown(); |
1003 | 0 | nsCOMPtr<nsIRunnable> task = |
1004 | 0 | NewRunnableMethod("CDMStorageTest::Dummy", this, &CDMStorageTest::Dummy); |
1005 | 0 | SystemGroup::Dispatch(TaskCategory::Other, task.forget()); |
1006 | 0 | } |
1007 | | |
1008 | | void SessionMessage(const nsACString& aSessionId, |
1009 | | uint32_t aMessageType, |
1010 | | const nsTArray<uint8_t>& aMessage) |
1011 | 0 | { |
1012 | 0 | MonitorAutoLock mon(mMonitor); |
1013 | 0 |
|
1014 | 0 | nsCString msg((const char*)aMessage.Elements(), aMessage.Length()); |
1015 | 0 | EXPECT_TRUE(mExpected.Length() > 0); |
1016 | 0 | bool matches = mExpected[0].mMessage.Equals(msg); |
1017 | 0 | EXPECT_STREQ(mExpected[0].mMessage.get(), msg.get()); |
1018 | 0 | if (mExpected.Length() > 0 && matches) { |
1019 | 0 | nsCOMPtr<nsIRunnable> continuation = mExpected[0].mContinuation; |
1020 | 0 | mExpected.RemoveElementAt(0); |
1021 | 0 | if (continuation) { |
1022 | 0 | NS_DispatchToCurrentThread(continuation); |
1023 | 0 | } |
1024 | 0 | } |
1025 | 0 | } |
1026 | | |
1027 | 0 | void Terminated() { |
1028 | 0 | if (mCDM) { |
1029 | 0 | mCDM->Shutdown(); |
1030 | 0 | mCDM = nullptr; |
1031 | 0 | } |
1032 | 0 | } |
1033 | | |
1034 | | private: |
1035 | 0 | ~CDMStorageTest() { } |
1036 | | |
1037 | | struct ExpectedMessage { |
1038 | | ExpectedMessage(const nsCString& aMessage, already_AddRefed<nsIRunnable> aContinuation) |
1039 | | : mMessage(aMessage) |
1040 | | , mContinuation(aContinuation) |
1041 | 0 | {} |
1042 | | nsCString mMessage; |
1043 | | nsCOMPtr<nsIRunnable> mContinuation; |
1044 | | }; |
1045 | | |
1046 | | nsTArray<ExpectedMessage> mExpected; |
1047 | | |
1048 | | RefPtr<nsIRunnable> mSetDecryptorIdContinuation; |
1049 | | |
1050 | | RefPtr<gmp::ChromiumCDMParent> mCDM; |
1051 | | Monitor mMonitor; |
1052 | | Atomic<bool> mFinished; |
1053 | | nsCString mNodeId; |
1054 | | |
1055 | | class CallbackProxy : public ChromiumCDMCallback { |
1056 | | public: |
1057 | | |
1058 | | explicit CallbackProxy(CDMStorageTest* aRunner) |
1059 | | : mRunner(aRunner) |
1060 | 0 | { |
1061 | 0 | } |
1062 | | |
1063 | | void SetSessionId(uint32_t aPromiseId, |
1064 | 0 | const nsCString& aSessionId) override { } |
1065 | | |
1066 | | void ResolveLoadSessionPromise(uint32_t aPromiseId, |
1067 | 0 | bool aSuccessful) override { } |
1068 | | |
1069 | | void ResolvePromiseWithKeyStatus(uint32_t aPromiseId, |
1070 | 0 | uint32_t aKeyStatus) override { } |
1071 | | |
1072 | 0 | void ResolvePromise(uint32_t aPromiseId) override { } |
1073 | | |
1074 | | void RejectPromise(uint32_t aPromiseId, |
1075 | | nsresult aError, |
1076 | 0 | const nsCString& aErrorMessage) override { } |
1077 | | |
1078 | | void SessionMessage(const nsACString& aSessionId, |
1079 | | uint32_t aMessageType, |
1080 | | nsTArray<uint8_t>&& aMessage) override |
1081 | 0 | { |
1082 | 0 | mRunner->SessionMessage(aSessionId, aMessageType, std::move(aMessage)); |
1083 | 0 | } |
1084 | | |
1085 | | void SessionKeysChange(const nsCString& aSessionId, |
1086 | 0 | nsTArray<mozilla::gmp::CDMKeyInformation>&& aKeysInfo) override { } |
1087 | | |
1088 | | void ExpirationChange(const nsCString& aSessionId, |
1089 | 0 | double aSecondsSinceEpoch) override { } |
1090 | | |
1091 | 0 | void SessionClosed(const nsCString& aSessionId) override { } |
1092 | | |
1093 | 0 | void Terminated() override { mRunner->Terminated(); } |
1094 | | |
1095 | 0 | void Shutdown() override { mRunner->Shutdown(); } |
1096 | | |
1097 | | private: |
1098 | | |
1099 | | // Warning: Weak ref. |
1100 | | CDMStorageTest* mRunner; |
1101 | | }; |
1102 | | |
1103 | | UniquePtr<CallbackProxy> mCallback; |
1104 | | }; // class CDMStorageTest |
1105 | | |
1106 | | |
1107 | 0 | TEST(GeckoMediaPlugins, CDMStorageGetNodeId) { |
1108 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1109 | 0 | runner->DoTest(&CDMStorageTest::TestGetNodeId); |
1110 | 0 | } |
1111 | | |
1112 | 0 | TEST(GeckoMediaPlugins, CDMStorageBasic) { |
1113 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1114 | 0 | runner->DoTest(&CDMStorageTest::TestBasicStorage); |
1115 | 0 | } |
1116 | | |
1117 | 0 | TEST(GeckoMediaPlugins, CDMStorageForgetThisSite) { |
1118 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1119 | 0 | runner->DoTest(&CDMStorageTest::TestForgetThisSite); |
1120 | 0 | } |
1121 | | |
1122 | 0 | TEST(GeckoMediaPlugins, CDMStorageClearRecentHistory1) { |
1123 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1124 | 0 | runner->DoTest(&CDMStorageTest::TestClearRecentHistory1); |
1125 | 0 | } |
1126 | | |
1127 | 0 | TEST(GeckoMediaPlugins, CDMStorageClearRecentHistory2) { |
1128 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1129 | 0 | runner->DoTest(&CDMStorageTest::TestClearRecentHistory2); |
1130 | 0 | } |
1131 | | |
1132 | 0 | TEST(GeckoMediaPlugins, CDMStorageClearRecentHistory3) { |
1133 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1134 | 0 | runner->DoTest(&CDMStorageTest::TestClearRecentHistory3); |
1135 | 0 | } |
1136 | | |
1137 | 0 | TEST(GeckoMediaPlugins, CDMStorageCrossOrigin) { |
1138 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1139 | 0 | runner->DoTest(&CDMStorageTest::TestCrossOriginStorage); |
1140 | 0 | } |
1141 | | |
1142 | 0 | TEST(GeckoMediaPlugins, CDMStoragePrivateBrowsing) { |
1143 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1144 | 0 | runner->DoTest(&CDMStorageTest::TestPBStorage); |
1145 | 0 | } |
1146 | | |
1147 | | #if defined(XP_WIN) |
1148 | | TEST(GeckoMediaPlugins, GMPOutputProtection) { |
1149 | | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1150 | | runner->DoTest(&CDMStorageTest::TestOutputProtection); |
1151 | | } |
1152 | | #endif |
1153 | | |
1154 | 0 | TEST(GeckoMediaPlugins, CDMStorageLongRecordNames) { |
1155 | 0 | RefPtr<CDMStorageTest> runner = new CDMStorageTest(); |
1156 | 0 | runner->DoTest(&CDMStorageTest::TestLongRecordNames); |
1157 | 0 | } |