Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "GMPService.h"
7 : #include "GMPServiceParent.h"
8 : #include "GMPServiceChild.h"
9 : #include "GMPContentParent.h"
10 : #include "prio.h"
11 : #include "mozilla/Logging.h"
12 : #include "GMPParent.h"
13 : #include "GMPVideoDecoderParent.h"
14 : #include "nsIObserverService.h"
15 : #include "GeckoChildProcessHost.h"
16 : #include "mozilla/ClearOnShutdown.h"
17 : #include "mozilla/SyncRunnable.h"
18 : #include "nsXPCOMPrivate.h"
19 : #include "mozilla/Services.h"
20 : #include "nsNativeCharsetUtils.h"
21 : #include "nsIXULAppInfo.h"
22 : #include "nsIConsoleService.h"
23 : #include "mozilla/Unused.h"
24 : #include "GMPDecryptorParent.h"
25 : #include "nsComponentManagerUtils.h"
26 : #include "runnable_utils.h"
27 : #include "VideoUtils.h"
28 : #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
29 : #include "mozilla/SandboxInfo.h"
30 : #endif
31 : #include "nsAppDirectoryServiceDefs.h"
32 : #include "nsDirectoryServiceUtils.h"
33 : #include "nsDirectoryServiceDefs.h"
34 : #include "nsHashKeys.h"
35 : #include "nsIFile.h"
36 : #include "nsISimpleEnumerator.h"
37 : #include "nsThreadUtils.h"
38 : #include "GMPCrashHelper.h"
39 :
40 : #include "mozilla/dom/PluginCrashedEvent.h"
41 : #include "mozilla/EventDispatcher.h"
42 : #include "mozilla/Attributes.h"
43 :
44 : namespace mozilla {
45 :
46 : #ifdef LOG
47 : #undef LOG
48 : #endif
49 :
50 : LogModule*
51 10 : GetGMPLog()
52 : {
53 : static LazyLogModule sLog("GMP");
54 10 : return sLog;
55 : }
56 :
57 : #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
58 : #define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
59 :
60 : #ifdef __CLASS__
61 : #undef __CLASS__
62 : #endif
63 : #define __CLASS__ "GMPService"
64 :
65 : namespace gmp {
66 :
67 3 : static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
68 :
69 : class GMPServiceCreateHelper final : public mozilla::Runnable
70 : {
71 : RefPtr<GeckoMediaPluginService> mService;
72 :
73 : public:
74 : static already_AddRefed<GeckoMediaPluginService>
75 3 : GetOrCreate()
76 : {
77 6 : RefPtr<GeckoMediaPluginService> service;
78 :
79 3 : if (NS_IsMainThread()) {
80 3 : service = GetOrCreateOnMainThread();
81 : } else {
82 0 : RefPtr<GMPServiceCreateHelper> createHelper = new GMPServiceCreateHelper();
83 :
84 : mozilla::SyncRunnable::DispatchToThread(
85 0 : SystemGroup::EventTargetFor(mozilla::TaskCategory::Other),
86 0 : createHelper, true);
87 :
88 0 : service = createHelper->mService.forget();
89 : }
90 :
91 6 : return service.forget();
92 : }
93 :
94 : private:
95 0 : GMPServiceCreateHelper()
96 0 : : Runnable("GMPServiceCreateHelper")
97 : {
98 0 : }
99 :
100 0 : ~GMPServiceCreateHelper()
101 0 : {
102 0 : MOZ_ASSERT(!mService);
103 0 : }
104 :
105 : static already_AddRefed<GeckoMediaPluginService>
106 3 : GetOrCreateOnMainThread()
107 : {
108 3 : MOZ_ASSERT(NS_IsMainThread());
109 :
110 3 : if (!sSingletonService) {
111 1 : if (XRE_IsParentProcess()) {
112 : RefPtr<GeckoMediaPluginServiceParent> service =
113 2 : new GeckoMediaPluginServiceParent();
114 1 : service->Init();
115 1 : sSingletonService = service;
116 : } else {
117 : RefPtr<GeckoMediaPluginServiceChild> service =
118 0 : new GeckoMediaPluginServiceChild();
119 0 : service->Init();
120 0 : sSingletonService = service;
121 : }
122 :
123 1 : ClearOnShutdown(&sSingletonService);
124 : }
125 :
126 6 : RefPtr<GeckoMediaPluginService> service = sSingletonService.get();
127 6 : return service.forget();
128 : }
129 :
130 : NS_IMETHOD
131 0 : Run() override
132 : {
133 0 : MOZ_ASSERT(NS_IsMainThread());
134 :
135 0 : mService = GetOrCreateOnMainThread();
136 0 : return NS_OK;
137 : }
138 : };
139 :
140 : already_AddRefed<GeckoMediaPluginService>
141 3 : GeckoMediaPluginService::GetGeckoMediaPluginService()
142 : {
143 3 : return GMPServiceCreateHelper::GetOrCreate();
144 : }
145 :
146 102 : NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
147 :
148 1 : GeckoMediaPluginService::GeckoMediaPluginService()
149 : : mMutex("GeckoMediaPluginService::mMutex")
150 : , mGMPThreadShutdown(false)
151 1 : , mShuttingDownOnGMPThread(false)
152 : {
153 1 : MOZ_ASSERT(NS_IsMainThread());
154 :
155 2 : nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
156 1 : if (appInfo) {
157 2 : nsAutoCString version;
158 2 : nsAutoCString buildID;
159 2 : if (NS_SUCCEEDED(appInfo->GetVersion(version)) &&
160 1 : NS_SUCCEEDED(appInfo->GetAppBuildID(buildID))) {
161 1 : LOGD(("GeckoMediaPluginService created; Gecko version=%s buildID=%s",
162 : version.get(), buildID.get()));
163 : }
164 : }
165 1 : }
166 :
167 0 : GeckoMediaPluginService::~GeckoMediaPluginService()
168 : {
169 0 : }
170 :
171 : NS_IMETHODIMP
172 0 : GeckoMediaPluginService::RunPluginCrashCallbacks(uint32_t aPluginId,
173 : const nsACString& aPluginName)
174 : {
175 0 : MOZ_ASSERT(NS_IsMainThread());
176 0 : LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
177 :
178 0 : nsAutoPtr<nsTArray<RefPtr<GMPCrashHelper>>> helpers;
179 : {
180 0 : MutexAutoLock lock(mMutex);
181 0 : mPluginCrashHelpers.Remove(aPluginId, &helpers);
182 : }
183 0 : if (!helpers) {
184 0 : LOGD(("%s::%s(%i) No crash helpers, not handling crash.", __CLASS__, __FUNCTION__, aPluginId));
185 0 : return NS_OK;
186 : }
187 :
188 0 : for (const auto& helper : *helpers) {
189 0 : nsCOMPtr<nsPIDOMWindowInner> window = helper->GetPluginCrashedEventTarget();
190 0 : if (NS_WARN_IF(!window)) {
191 0 : continue;
192 : }
193 0 : nsCOMPtr<nsIDocument> document(window->GetExtantDoc());
194 0 : if (NS_WARN_IF(!document)) {
195 0 : continue;
196 : }
197 :
198 0 : dom::PluginCrashedEventInit init;
199 0 : init.mPluginID = aPluginId;
200 0 : init.mBubbles = true;
201 0 : init.mCancelable = true;
202 0 : init.mGmpPlugin = true;
203 0 : CopyUTF8toUTF16(aPluginName, init.mPluginName);
204 0 : init.mSubmittedCrashReport = false;
205 : RefPtr<dom::PluginCrashedEvent> event =
206 0 : dom::PluginCrashedEvent::Constructor(document,
207 0 : NS_LITERAL_STRING("PluginCrashed"),
208 0 : init);
209 0 : event->SetTrusted(true);
210 0 : event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
211 :
212 0 : EventDispatcher::DispatchDOMEvent(window, nullptr, event, nullptr, nullptr);
213 : }
214 :
215 0 : return NS_OK;
216 : }
217 :
218 : nsresult
219 1 : GeckoMediaPluginService::Init()
220 : {
221 1 : MOZ_ASSERT(NS_IsMainThread());
222 :
223 2 : nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
224 1 : MOZ_ASSERT(obsService);
225 1 : MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false));
226 :
227 : // Kick off scanning for plugins
228 2 : nsCOMPtr<nsIThread> thread;
229 2 : return GetThread(getter_AddRefs(thread));
230 : }
231 :
232 : RefPtr<GetCDMParentPromise>
233 0 : GeckoMediaPluginService::GetCDM(const NodeId& aNodeId,
234 : nsTArray<nsCString> aTags,
235 : GMPCrashHelper* aHelper)
236 : {
237 0 : MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
238 :
239 0 : if (mShuttingDownOnGMPThread || aTags.IsEmpty()) {
240 0 : return GetCDMParentPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
241 : }
242 :
243 : typedef MozPromiseHolder<GetCDMParentPromise> PromiseHolder;
244 0 : PromiseHolder* rawHolder(new PromiseHolder());
245 0 : RefPtr<GetCDMParentPromise> promise = rawHolder->Ensure(__func__);
246 0 : RefPtr<AbstractThread> thread(GetAbstractGMPThread());
247 0 : RefPtr<GMPCrashHelper> helper(aHelper);
248 0 : GetContentParent(
249 0 : aHelper, aNodeId, NS_LITERAL_CSTRING(CHROMIUM_CDM_API), aTags)
250 : ->Then(thread,
251 : __func__,
252 0 : [rawHolder, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
253 0 : RefPtr<GMPContentParent> parent = wrapper->mParent;
254 0 : UniquePtr<PromiseHolder> holder(rawHolder);
255 0 : RefPtr<ChromiumCDMParent> cdm = parent->GetChromiumCDM();
256 0 : if (!parent) {
257 0 : holder->Reject(NS_ERROR_FAILURE, __func__);
258 0 : return;
259 : }
260 0 : if (helper) {
261 0 : cdm->SetCrashHelper(helper);
262 : }
263 0 : holder->Resolve(cdm, __func__);
264 : },
265 0 : [rawHolder] {
266 0 : UniquePtr<PromiseHolder> holder(rawHolder);
267 0 : holder->Reject(NS_ERROR_FAILURE, __func__);
268 0 : });
269 :
270 0 : return promise;
271 : }
272 :
273 : void
274 0 : GeckoMediaPluginService::ShutdownGMPThread()
275 : {
276 0 : LOGD(("%s::%s", __CLASS__, __FUNCTION__));
277 0 : nsCOMPtr<nsIThread> gmpThread;
278 : {
279 0 : MutexAutoLock lock(mMutex);
280 0 : mGMPThreadShutdown = true;
281 0 : mGMPThread.swap(gmpThread);
282 0 : mAbstractGMPThread = nullptr;
283 : }
284 :
285 0 : if (gmpThread) {
286 0 : gmpThread->Shutdown();
287 : }
288 0 : }
289 :
290 : nsresult
291 0 : GeckoMediaPluginService::GMPDispatch(nsIRunnable* event,
292 : uint32_t flags)
293 : {
294 0 : nsCOMPtr<nsIRunnable> r(event);
295 0 : return GMPDispatch(r.forget());
296 : }
297 :
298 : nsresult
299 0 : GeckoMediaPluginService::GMPDispatch(already_AddRefed<nsIRunnable> event,
300 : uint32_t flags)
301 : {
302 0 : nsCOMPtr<nsIRunnable> r(event);
303 0 : nsCOMPtr<nsIThread> thread;
304 0 : nsresult rv = GetThread(getter_AddRefs(thread));
305 0 : if (NS_FAILED(rv)) {
306 0 : return rv;
307 : }
308 0 : return thread->Dispatch(r, flags);
309 : }
310 :
311 : // always call with getter_AddRefs, because it does
312 : NS_IMETHODIMP
313 3 : GeckoMediaPluginService::GetThread(nsIThread** aThread)
314 : {
315 3 : MOZ_ASSERT(aThread);
316 :
317 : // This can be called from any thread.
318 6 : MutexAutoLock lock(mMutex);
319 :
320 3 : if (!mGMPThread) {
321 : // Don't allow the thread to be created after shutdown has started.
322 1 : if (mGMPThreadShutdown) {
323 0 : return NS_ERROR_FAILURE;
324 : }
325 :
326 1 : nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
327 1 : if (NS_FAILED(rv)) {
328 0 : return rv;
329 : }
330 :
331 1 : mAbstractGMPThread = AbstractThread::CreateXPCOMThreadWrapper(mGMPThread, false);
332 :
333 : // Tell the thread to initialize plugins
334 1 : InitializePlugins(mAbstractGMPThread.get());
335 : }
336 :
337 6 : nsCOMPtr<nsIThread> copy = mGMPThread;
338 3 : copy.forget(aThread);
339 :
340 3 : return NS_OK;
341 : }
342 :
343 : RefPtr<AbstractThread>
344 3 : GeckoMediaPluginService::GetAbstractGMPThread()
345 : {
346 6 : MutexAutoLock lock(mMutex);
347 6 : return mAbstractGMPThread;
348 : }
349 :
350 : NS_IMETHODIMP
351 0 : GeckoMediaPluginService::GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
352 : nsTArray<nsCString>* aTags,
353 : const nsACString& aNodeId,
354 : UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
355 : uint32_t aDecryptorId)
356 : {
357 0 : MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
358 0 : NS_ENSURE_ARG(aTags && aTags->Length() > 0);
359 0 : NS_ENSURE_ARG(aCallback);
360 :
361 0 : if (mShuttingDownOnGMPThread) {
362 0 : return NS_ERROR_FAILURE;
363 : }
364 :
365 0 : GetGMPVideoDecoderCallback* rawCallback = aCallback.release();
366 0 : RefPtr<AbstractThread> thread(GetAbstractGMPThread());
367 0 : RefPtr<GMPCrashHelper> helper(aHelper);
368 0 : GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER), *aTags)
369 : ->Then(thread, __func__,
370 0 : [rawCallback, helper, aDecryptorId](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
371 0 : RefPtr<GMPContentParent> parent = wrapper->mParent;
372 0 : UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
373 0 : GMPVideoDecoderParent* actor = nullptr;
374 0 : GMPVideoHostImpl* host = nullptr;
375 0 : if (parent && NS_SUCCEEDED(parent->GetGMPVideoDecoder(&actor, aDecryptorId))) {
376 0 : host = &(actor->Host());
377 0 : actor->SetCrashHelper(helper);
378 : }
379 0 : callback->Done(actor, host);
380 0 : },
381 0 : [rawCallback] {
382 0 : UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
383 0 : callback->Done(nullptr, nullptr);
384 0 : });
385 :
386 0 : return NS_OK;
387 : }
388 :
389 : NS_IMETHODIMP
390 0 : GeckoMediaPluginService::GetGMPVideoEncoder(GMPCrashHelper* aHelper,
391 : nsTArray<nsCString>* aTags,
392 : const nsACString& aNodeId,
393 : UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
394 : {
395 0 : MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
396 0 : NS_ENSURE_ARG(aTags && aTags->Length() > 0);
397 0 : NS_ENSURE_ARG(aCallback);
398 :
399 0 : if (mShuttingDownOnGMPThread) {
400 0 : return NS_ERROR_FAILURE;
401 : }
402 :
403 0 : GetGMPVideoEncoderCallback* rawCallback = aCallback.release();
404 0 : RefPtr<AbstractThread> thread(GetAbstractGMPThread());
405 0 : RefPtr<GMPCrashHelper> helper(aHelper);
406 0 : GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER), *aTags)
407 : ->Then(thread, __func__,
408 0 : [rawCallback, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
409 0 : RefPtr<GMPContentParent> parent = wrapper->mParent;
410 0 : UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
411 0 : GMPVideoEncoderParent* actor = nullptr;
412 0 : GMPVideoHostImpl* host = nullptr;
413 0 : if (parent && NS_SUCCEEDED(parent->GetGMPVideoEncoder(&actor))) {
414 0 : host = &(actor->Host());
415 0 : actor->SetCrashHelper(helper);
416 : }
417 0 : callback->Done(actor, host);
418 0 : },
419 0 : [rawCallback] {
420 0 : UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
421 0 : callback->Done(nullptr, nullptr);
422 0 : });
423 :
424 0 : return NS_OK;
425 : }
426 :
427 : NS_IMETHODIMP
428 0 : GeckoMediaPluginService::GetGMPDecryptor(GMPCrashHelper* aHelper,
429 : nsTArray<nsCString>* aTags,
430 : const nsACString& aNodeId,
431 : UniquePtr<GetGMPDecryptorCallback>&& aCallback)
432 : {
433 : #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
434 : if (!SandboxInfo::Get().CanSandboxMedia()) {
435 : NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
436 : "EME decryption not available without sandboxing support.");
437 : return NS_ERROR_NOT_AVAILABLE;
438 : }
439 : #endif
440 :
441 0 : MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
442 0 : NS_ENSURE_ARG(aTags && aTags->Length() > 0);
443 0 : NS_ENSURE_ARG(aCallback);
444 :
445 0 : if (mShuttingDownOnGMPThread) {
446 0 : return NS_ERROR_FAILURE;
447 : }
448 :
449 0 : GetGMPDecryptorCallback* rawCallback = aCallback.release();
450 0 : RefPtr<AbstractThread> thread(GetAbstractGMPThread());
451 0 : RefPtr<GMPCrashHelper> helper(aHelper);
452 0 : GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_DECRYPTOR), *aTags)
453 : ->Then(thread, __func__,
454 0 : [rawCallback, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
455 0 : RefPtr<GMPContentParent> parent = wrapper->mParent;
456 0 : UniquePtr<GetGMPDecryptorCallback> callback(rawCallback);
457 0 : GMPDecryptorParent* actor = nullptr;
458 0 : if (parent && NS_SUCCEEDED(parent->GetGMPDecryptor(&actor))) {
459 0 : actor->SetCrashHelper(helper);
460 : }
461 0 : callback->Done(actor);
462 0 : },
463 0 : [rawCallback] {
464 0 : UniquePtr<GetGMPDecryptorCallback> callback(rawCallback);
465 0 : callback->Done(nullptr);
466 0 : });
467 :
468 0 : return NS_OK;
469 : }
470 :
471 : void
472 0 : GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper)
473 : {
474 0 : if (!aHelper) {
475 0 : return;
476 : }
477 0 : MutexAutoLock lock(mMutex);
478 : nsTArray<RefPtr<GMPCrashHelper>>* helpers;
479 0 : if (!mPluginCrashHelpers.Get(aPluginId, &helpers)) {
480 0 : helpers = new nsTArray<RefPtr<GMPCrashHelper>>();
481 0 : mPluginCrashHelpers.Put(aPluginId, helpers);
482 0 : } else if (helpers->Contains(aHelper)) {
483 0 : return;
484 : }
485 0 : helpers->AppendElement(aHelper);
486 : }
487 :
488 0 : void GeckoMediaPluginService::DisconnectCrashHelper(GMPCrashHelper* aHelper)
489 : {
490 0 : if (!aHelper) {
491 0 : return;
492 : }
493 0 : MutexAutoLock lock(mMutex);
494 0 : for (auto iter = mPluginCrashHelpers.Iter(); !iter.Done(); iter.Next()) {
495 0 : nsTArray<RefPtr<GMPCrashHelper>>* helpers = iter.Data();
496 0 : if (!helpers->Contains(aHelper)) {
497 0 : continue;
498 : }
499 0 : helpers->RemoveElement(aHelper);
500 0 : MOZ_ASSERT(!helpers->Contains(aHelper)); // Ensure there aren't duplicates.
501 0 : if (helpers->IsEmpty()) {
502 0 : iter.Remove();
503 : }
504 : }
505 : }
506 :
507 : } // namespace gmp
508 : } // namespace mozilla
|