GMPParent.cpp (49742B)
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 "GMPParent.h" 7 8 #include "CDMStorageIdProvider.h" 9 #include "ChromiumCDMAdapter.h" 10 #include "GMPContentParent.h" 11 #include "GMPLog.h" 12 #include "GMPTimerParent.h" 13 #include "GeckoProfiler.h" 14 #include "MediaResult.h" 15 #include "mozIGeckoMediaPluginService.h" 16 #include "mozilla/Casting.h" 17 #include "mozilla/FOGIPC.h" 18 #include "mozilla/dom/KeySystemNames.h" 19 #include "mozilla/dom/WidevineCDMManifestBinding.h" 20 #include "mozilla/ipc/CrashReporterHost.h" 21 #include "mozilla/ipc/Endpoint.h" 22 #include "mozilla/ipc/GeckoChildProcessHost.h" 23 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 24 # include "mozilla/SandboxInfo.h" 25 # include "mozilla/ipc/SharedMemoryHandle.h" 26 #endif 27 #include "ProfilerParent.h" 28 #include "mozilla/SSE.h" 29 #include "mozilla/Services.h" 30 #include "mozilla/StaticPrefs_media.h" 31 #include "mozilla/SyncRunnable.h" 32 #include "mozilla/Telemetry.h" 33 #include "mozilla/glean/IpcMetrics.h" 34 #include "nsComponentManagerUtils.h" 35 #include "nsIObserverService.h" 36 #include "nsIRunnable.h" 37 #include "nsIWritablePropertyBag2.h" 38 #include "nsPrintfCString.h" 39 #include "nsThreadUtils.h" 40 #include "runnable_utils.h" 41 #ifdef XP_WIN 42 # include "PDMFactory.h" 43 # include "WMFDecoderModule.h" 44 # include "mozilla/FileUtilsWin.h" 45 # include "mozilla/WinDllServices.h" 46 #endif 47 #if defined(MOZ_WIDGET_ANDROID) 48 # include "mozilla/java/GeckoProcessManagerWrappers.h" 49 # include "mozilla/java/GeckoProcessTypeWrappers.h" 50 #endif // defined(MOZ_WIDGET_ANDROID) 51 #if defined(XP_MACOSX) 52 # include "base/process_util.h" 53 # include "nsMacUtilsImpl.h" 54 #endif // defined(XP_MACOSX) 55 56 using mozilla::ipc::GeckoChildProcessHost; 57 58 using CrashReporter::AnnotationTable; 59 60 namespace mozilla::gmp { 61 62 #define GMP_PARENT_LOG_DEBUG(x, ...) \ 63 GMP_LOG_DEBUG("GMPParent[%p|childPid=%d] " x, this, mChildPid, ##__VA_ARGS__) 64 65 #ifdef __CLASS__ 66 # undef __CLASS__ 67 #endif 68 #define __CLASS__ "GMPParent" 69 70 GMPParent::GMPParent() 71 : mState(GMPState::NotLoaded), 72 mPluginId(GeckoChildProcessHost::GetUniqueID()), 73 mProcess(nullptr), 74 mDeleteProcessOnlyOnUnload(false), 75 mAbnormalShutdownInProgress(false), 76 mIsBlockingDeletion(false), 77 mCanDecrypt(false), 78 mGMPContentChildCount(0), 79 mChildPid(0), 80 #ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH 81 mChildLaunchArch(base::PROCESS_ARCH_INVALID), 82 #endif 83 mMainThread(GetMainThreadSerialEventTarget()) { 84 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 85 GMP_PARENT_LOG_DEBUG("GMPParent ctor id=%u", mPluginId); 86 } 87 88 GMPParent::~GMPParent() { 89 // This method is not restricted to a specific thread. 90 GMP_PARENT_LOG_DEBUG("GMPParent dtor id=%u", mPluginId); 91 MOZ_ASSERT(!mProcess); 92 } 93 94 void GMPParent::CloneFrom(const GMPParent* aOther) { 95 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 96 MOZ_ASSERT(aOther->mService); 97 98 mService = aOther->mService; 99 mDirectory = aOther->mDirectory; 100 mName = aOther->mName; 101 mVersion = aOther->mVersion; 102 mDescription = aOther->mDescription; 103 mDisplayName = aOther->mDisplayName; 104 mPluginType = aOther->mPluginType; 105 #if defined(XP_WIN) || defined(XP_LINUX) 106 mLibs = aOther->mLibs; 107 #endif 108 for (const GMPCapability& cap : aOther->mCapabilities) { 109 mCapabilities.AppendElement(cap); 110 } 111 mAdapter = aOther->mAdapter; 112 113 #ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH 114 mChildLaunchArch = aOther->mChildLaunchArch; 115 #endif 116 } 117 118 #if defined(XP_WIN) || defined(XP_MACOSX) 119 nsresult GMPParent::GetPluginFileArch(nsIFile* aPluginDir, 120 const nsString& aBaseName, 121 uint32_t& aArchSet) { 122 // Build up the plugin filename 123 # if defined(XP_MACOSX) 124 nsAutoString pluginFileName = u"lib"_ns + aBaseName + u".dylib"_ns; 125 # elif defined(XP_WIN) 126 nsAutoString pluginFileName = aBaseName + u".dll"_ns; 127 # endif 128 GMP_PARENT_LOG_DEBUG("%s: pluginFileName: %s", __FUNCTION__, 129 NS_LossyConvertUTF16toASCII(pluginFileName).get()); 130 131 // Create an nsIFile representing the plugin 132 nsCOMPtr<nsIFile> pluginFile; 133 nsresult rv = aPluginDir->Clone(getter_AddRefs(pluginFile)); 134 NS_ENSURE_SUCCESS(rv, rv); 135 pluginFile->AppendRelativePath(pluginFileName); 136 137 # if defined(XP_MACOSX) 138 // Get the full plugin path 139 nsAutoCString pluginPath; 140 rv = pluginFile->GetNativePath(pluginPath); 141 NS_ENSURE_SUCCESS(rv, rv); 142 GMP_PARENT_LOG_DEBUG("%s: pluginPath: %s", __FUNCTION__, pluginPath.get()); 143 144 rv = nsMacUtilsImpl::GetArchitecturesForBinary(pluginPath.get(), &aArchSet); 145 NS_ENSURE_SUCCESS(rv, rv); 146 147 # if defined(__aarch64__) 148 mPluginFilePath = pluginPath; 149 # endif 150 # elif defined(XP_WIN) 151 // Get the full plugin path 152 nsAutoString pluginPath; 153 rv = pluginFile->GetTarget(pluginPath); 154 NS_ENSURE_SUCCESS(rv, rv); 155 GMP_PARENT_LOG_DEBUG("%s: pluginPath: %s", __FUNCTION__, 156 NS_LossyConvertUTF16toASCII(pluginPath).get()); 157 158 aArchSet = GetExecutableArchitecture(pluginPath.get()); 159 if (aArchSet == base::PROCESS_ARCH_INVALID) { 160 return NS_ERROR_FAILURE; 161 } 162 # endif 163 164 return NS_OK; 165 } 166 #endif // defined(XP_WIN) || defined(XP_MACOSX) 167 168 #ifdef MOZ_WIDGET_ANDROID 169 void GMPParent::InitForClearkey(GeckoMediaPluginServiceParent* aService) { 170 MOZ_ASSERT(aService); 171 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 172 173 mService = aService; 174 mName = u"clearkey"_ns; 175 mDisplayName = "clearkey"_ns; 176 mVersion = "0.1"_ns; 177 mDescription = "ClearKey Gecko Media Plugin"_ns; 178 mPluginType = GMPPluginType::Clearkey; 179 mAdapter = u"chromium"_ns; 180 181 mCapabilities.SetCapacity(1); 182 auto& video = *mCapabilities.AppendElement(); 183 video.mAPIName = nsLiteralCString(CHROMIUM_CDM_API); 184 video.mAPITags.SetCapacity(2); 185 video.mAPITags.AppendElement(nsCString{kClearKeyKeySystemName}); 186 video.mAPITags.AppendElement( 187 nsCString{kClearKeyWithProtectionQueryKeySystemName}); 188 } 189 #endif 190 191 RefPtr<GenericPromise> GMPParent::Init(GeckoMediaPluginServiceParent* aService, 192 nsIFile* aPluginDir) { 193 MOZ_ASSERT(aPluginDir); 194 MOZ_ASSERT(aService); 195 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 196 197 mService = aService; 198 mDirectory = aPluginDir; 199 200 // aPluginDir is <profile-dir>/<gmp-plugin-id>/<version> 201 // where <gmp-plugin-id> should be gmp-gmpopenh264 202 nsCOMPtr<nsIFile> parent; 203 nsresult rv = aPluginDir->GetParent(getter_AddRefs(parent)); 204 if (NS_WARN_IF(NS_FAILED(rv))) { 205 return GenericPromise::CreateAndReject(rv, __func__); 206 } 207 nsAutoString parentLeafName; 208 rv = parent->GetLeafName(parentLeafName); 209 if (NS_WARN_IF(NS_FAILED(rv))) { 210 return GenericPromise::CreateAndReject(rv, __func__); 211 } 212 GMP_PARENT_LOG_DEBUG("%s: for %s", __FUNCTION__, 213 NS_LossyConvertUTF16toASCII(parentLeafName).get()); 214 215 MOZ_ASSERT(parentLeafName.Length() > 4); 216 mName = Substring(parentLeafName, 4); 217 218 #if defined(XP_WIN) || defined(XP_MACOSX) 219 uint32_t pluginArch = base::PROCESS_ARCH_INVALID; 220 rv = GetPluginFileArch( 221 aPluginDir, 222 # ifdef MOZ_WMF_CDM 223 mName.Equals(u"widevinecdm-l1"_ns) ? u"Google.Widevine.CDM"_ns : mName, 224 # else 225 mName, 226 # endif 227 pluginArch); 228 if (NS_FAILED(rv)) { 229 GMP_PARENT_LOG_DEBUG("%s: Plugin arch error: %d", __FUNCTION__, 230 uint32_t(rv)); 231 } else { 232 GMP_PARENT_LOG_DEBUG("%s: Plugin arch: 0x%x", __FUNCTION__, pluginArch); 233 } 234 235 const uint32_t x86 = base::PROCESS_ARCH_X86_64 | base::PROCESS_ARCH_I386; 236 # ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH 237 const uint32_t arm64 = base::PROCESS_ARCH_ARM_64; 238 239 mChildLaunchArch = pluginArch; 240 // When executing in an ARM64 process, if the library is x86 or x64, 241 // set |mChildLaunchArch| to x64 and allow the library to be used as long 242 // as this process is a universal binary. 243 if (!(pluginArch & arm64) && (pluginArch & x86)) { 244 bool isWidevine = parentLeafName.Find(u"widevine") != kNotFound; 245 bool isWidevineAllowed = 246 StaticPrefs::media_gmp_widevinecdm_allow_x64_plugin_on_arm64(); 247 bool isH264 = parentLeafName.Find(u"openh264") != kNotFound; 248 bool isH264Allowed = 249 StaticPrefs::media_gmp_gmpopenh264_allow_x64_plugin_on_arm64(); 250 bool isClearkey = parentLeafName.Find(u"clearkey") != kNotFound; 251 bool isClearkeyAllowed = 252 StaticPrefs::media_gmp_gmpclearkey_allow_x64_plugin_on_arm64(); 253 254 // Only allow x64 child GMP processes for Widevine, OpenH264 and Clearkey 255 if (!isWidevine && !isH264 && !isClearkey) { 256 return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, 257 __func__); 258 } 259 // And only if prefs permit it. 260 if ((isWidevine && !isWidevineAllowed) || (isH264 && !isH264Allowed) || 261 (isClearkey && !isClearkeyAllowed)) { 262 return GenericPromise::CreateAndReject(NS_ERROR_PLUGIN_DISABLED, 263 __func__); 264 } 265 266 # ifdef XP_MACOSX 267 // We have an x64 library. Get the bundle architecture to determine 268 // if we are a universal binary and hence if we can launch an x64 269 // child process to host this plugin. 270 uint32_t bundleArch = base::PROCESS_ARCH_INVALID; 271 rv = nsMacUtilsImpl::GetArchitecturesForBundle(&bundleArch); 272 if (NS_FAILED(rv)) { 273 // If we fail here, continue as if this is not a univeral binary. 274 GMP_PARENT_LOG_DEBUG("%s: Bundle arch error: %d", __FUNCTION__, 275 uint32_t(rv)); 276 } else { 277 GMP_PARENT_LOG_DEBUG("%s: Bundle arch: 0x%x", __FUNCTION__, bundleArch); 278 } 279 280 bool isUniversalBinary = (bundleArch & base::PROCESS_ARCH_X86_64) && 281 (bundleArch & base::PROCESS_ARCH_ARM_64); 282 if (isUniversalBinary) { 283 mChildLaunchArch = base::PROCESS_ARCH_X86_64; 284 PreTranslateBins(); 285 } else { 286 return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, 287 __func__); 288 } 289 # endif 290 } 291 # else 292 // When executing in a non-ARM process, if the library is not x86 or x64, 293 // remove it and return an error. This prevents a child process crash due 294 // to loading an incompatible library and forces a new plugin version to be 295 // downloaded when the check is next performed. This could occur if a profile 296 // is moved from an ARM64 system to an x64 system. 297 if ((pluginArch & x86) == 0) { 298 GMP_PARENT_LOG_DEBUG("%s: Removing plugin directory", __FUNCTION__); 299 aPluginDir->Remove(true); 300 return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); 301 } 302 # endif // defined(ALLOW_GECKO_CHILD_PROCESS_ARCH) 303 #endif // defined(XP_WIN) || defined(XP_MACOSX) 304 305 return ReadGMPMetaData(); 306 } 307 308 void GMPParent::Crash() { 309 if (mState != GMPState::NotLoaded) { 310 (void)SendCrashPluginNow(); 311 } 312 } 313 314 class NotifyGMPProcessLoadedTask : public Runnable { 315 public: 316 explicit NotifyGMPProcessLoadedTask(const ::base::ProcessId aProcessId, 317 GMPParent* aGMPParent) 318 : Runnable("NotifyGMPProcessLoadedTask"), 319 mProcessId(aProcessId), 320 mGMPParent(aGMPParent) {} 321 322 NS_IMETHOD Run() override { 323 MOZ_ASSERT(NS_IsMainThread()); 324 325 bool canProfile = true; 326 327 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 328 if (SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia) && 329 ipc::shared_memory::UsingPosixShm()) { 330 canProfile = false; 331 } 332 #endif 333 334 nsCOMPtr<nsISerialEventTarget> gmpEventTarget = 335 mGMPParent->GMPEventTarget(); 336 if (NS_WARN_IF(!gmpEventTarget)) { 337 return NS_ERROR_FAILURE; 338 } 339 340 #if defined(XP_WIN) 341 RefPtr<DllServices> dllSvc(DllServices::Get()); 342 bool isReadyForBackgroundProcessing = 343 dllSvc->IsReadyForBackgroundProcessing(); 344 gmpEventTarget->Dispatch(NewRunnableMethod<bool, bool>( 345 "GMPParent::SendInitDllServices", mGMPParent, 346 &GMPParent::SendInitDllServices, isReadyForBackgroundProcessing, 347 Telemetry::CanRecordReleaseData())); 348 #endif 349 350 if (canProfile) { 351 ipc::Endpoint<PProfilerChild> profilerParent( 352 ProfilerParent::CreateForProcess(mProcessId)); 353 354 gmpEventTarget->Dispatch( 355 NewRunnableMethod<ipc::Endpoint<mozilla::PProfilerChild>&&>( 356 "GMPParent::SendInitProfiler", mGMPParent, 357 &GMPParent::SendInitProfiler, std::move(profilerParent))); 358 } 359 360 return NS_OK; 361 } 362 363 ::base::ProcessId mProcessId; 364 const RefPtr<GMPParent> mGMPParent; 365 }; 366 367 nsresult GMPParent::LoadProcess() { 368 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 369 MOZ_ASSERT(mState == GMPState::NotLoaded); 370 371 if (NS_WARN_IF(mPluginType == GMPPluginType::WidevineL1)) { 372 GMP_PARENT_LOG_DEBUG("%s: cannot load process for WidevineL1", 373 __FUNCTION__); 374 return NS_ERROR_NOT_IMPLEMENTED; 375 } 376 377 nsAutoString path; 378 #ifdef MOZ_WIDGET_ANDROID 379 // We need to bundle any CDMs with the APK, so we can just supply the library 380 // name to the child process. 381 path = mName; 382 #else 383 if (NS_WARN_IF(!mDirectory) || 384 NS_WARN_IF(NS_FAILED(mDirectory->GetPath(path)))) { 385 return NS_ERROR_FAILURE; 386 } 387 #endif 388 GMP_PARENT_LOG_DEBUG("%s: for %s", __FUNCTION__, 389 NS_ConvertUTF16toUTF8(path).get()); 390 391 if (!mProcess) { 392 mProcess = new GMPProcessParent(NS_ConvertUTF16toUTF8(path).get()); 393 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 394 mProcess->SetRequiresWindowServer(mAdapter.EqualsLiteral("chromium")); 395 #endif 396 397 #ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH 398 mProcess->SetLaunchArchitecture(mChildLaunchArch); 399 #endif 400 401 if (!mProcess->Launch(30 * 1000)) { 402 GMP_PARENT_LOG_DEBUG("%s: Failed to launch new child process", 403 __FUNCTION__); 404 mProcess->Delete(); 405 mProcess = nullptr; 406 return NS_ERROR_FAILURE; 407 } 408 409 mChildPid = mProcess->GetChildProcessId(); 410 GMP_PARENT_LOG_DEBUG("%s: Launched new child process", __FUNCTION__); 411 412 bool opened = mProcess->TakeInitialEndpoint().Bind(this); 413 if (!opened) { 414 GMP_PARENT_LOG_DEBUG("%s: Failed to open channel to new child process", 415 __FUNCTION__); 416 mProcess->Delete(); 417 mProcess = nullptr; 418 return NS_ERROR_FAILURE; 419 } 420 GMP_PARENT_LOG_DEBUG("%s: Opened channel to new child process", 421 __FUNCTION__); 422 423 // ComputeStorageId may return empty string, we leave the error handling to 424 // CDM. The CDM will reject the promise once we provide a empty string of 425 // storage id. 426 bool ok = 427 SendProvideStorageId(CDMStorageIdProvider::ComputeStorageId(mNodeId)); 428 if (!ok) { 429 GMP_PARENT_LOG_DEBUG("%s: Failed to send storage id to child process", 430 __FUNCTION__); 431 return NS_ERROR_FAILURE; 432 } 433 GMP_PARENT_LOG_DEBUG("%s: Sent storage id to child process", __FUNCTION__); 434 435 #if defined(XP_WIN) || defined(XP_LINUX) 436 if (!mLibs.IsEmpty()) { 437 bool ok = SendPreloadLibs(mLibs); 438 if (!ok) { 439 GMP_PARENT_LOG_DEBUG("%s: Failed to send preload-libs to child process", 440 __FUNCTION__); 441 return NS_ERROR_FAILURE; 442 } 443 GMP_PARENT_LOG_DEBUG("%s: Sent preload-libs ('%s') to child process", 444 __FUNCTION__, mLibs.get()); 445 } 446 #endif 447 448 NS_DispatchToMainThread(new NotifyGMPProcessLoadedTask(OtherPid(), this)); 449 450 // Intr call to block initialization on plugin load. 451 if (!SendStartPlugin(mAdapter)) { 452 GMP_PARENT_LOG_DEBUG("%s: Failed to send start to child process", 453 __FUNCTION__); 454 return NS_ERROR_FAILURE; 455 } 456 GMP_PARENT_LOG_DEBUG("%s: Sent StartPlugin to child process", __FUNCTION__); 457 } 458 459 mState = GMPState::Loaded; 460 461 return NS_OK; 462 } 463 464 void GMPParent::OnPreferenceChange(const mozilla::dom::Pref& aPref) { 465 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 466 GMP_PARENT_LOG_DEBUG("%s", __FUNCTION__); 467 468 if (!mProcess || !mProcess->UseXPCOM()) { 469 return; 470 } 471 472 (void)SendPreferenceUpdate(aPref); 473 } 474 475 mozilla::ipc::IPCResult GMPParent::RecvPGMPContentChildDestroyed() { 476 --mGMPContentChildCount; 477 if (!IsUsed()) { 478 CloseIfUnused(); 479 } 480 return IPC_OK(); 481 } 482 483 mozilla::ipc::IPCResult GMPParent::RecvFOGData(ByteBuf&& aBuf) { 484 GMP_PARENT_LOG_DEBUG("GMPParent RecvFOGData"); 485 glean::FOGData(std::move(aBuf)); 486 return IPC_OK(); 487 } 488 489 #if defined(XP_WIN) 490 mozilla::ipc::IPCResult GMPParent::RecvGetModulesTrust( 491 ModulePaths&& aModPaths, bool aRunAtNormalPriority, 492 GetModulesTrustResolver&& aResolver) { 493 class ModulesTrustRunnable final : public Runnable { 494 public: 495 ModulesTrustRunnable(ModulePaths&& aModPaths, bool aRunAtNormalPriority, 496 GetModulesTrustResolver&& aResolver) 497 : Runnable("GMPParent::RecvGetModulesTrust::ModulesTrustRunnable"), 498 mModPaths(std::move(aModPaths)), 499 mResolver(std::move(aResolver)), 500 mEventTarget(GetCurrentSerialEventTarget()), 501 mRunAtNormalPriority(aRunAtNormalPriority) {} 502 503 NS_IMETHOD Run() override { 504 RefPtr<DllServices> dllSvc(DllServices::Get()); 505 dllSvc->GetModulesTrust(std::move(mModPaths), mRunAtNormalPriority) 506 ->Then( 507 mEventTarget, __func__, 508 [self = RefPtr{this}](ModulesMapResult&& aResult) { 509 self->mResolver(Some(ModulesMapResult(std::move(aResult)))); 510 }, 511 [self = RefPtr{this}](nsresult aRv) { 512 self->mResolver(Nothing()); 513 }); 514 return NS_OK; 515 } 516 517 private: 518 ~ModulesTrustRunnable() override = default; 519 520 ModulePaths mModPaths; 521 GetModulesTrustResolver mResolver; 522 nsCOMPtr<nsISerialEventTarget> mEventTarget; 523 bool mRunAtNormalPriority; 524 }; 525 526 NS_DispatchToMainThread(MakeAndAddRef<ModulesTrustRunnable>( 527 std::move(aModPaths), aRunAtNormalPriority, std::move(aResolver))); 528 return IPC_OK(); 529 } 530 #endif // defined(XP_WIN) 531 532 void GMPParent::CloseIfUnused() { 533 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 534 GMP_PARENT_LOG_DEBUG("%s", __FUNCTION__); 535 536 if ((mDeleteProcessOnlyOnUnload || mState == GMPState::Loaded || 537 mState == GMPState::Unloading) && 538 !IsUsed()) { 539 // Ensure all timers are killed. 540 for (uint32_t i = mTimers.Length(); i > 0; i--) { 541 mTimers[i - 1]->Shutdown(); 542 } 543 544 // Shutdown GMPStorage. Given that all protocol actors must be shutdown 545 // (!Used() is true), all storage operations should be complete. 546 GMP_PARENT_LOG_DEBUG("%p shutdown storage (sz=%zu)", this, 547 mStorage.Length()); 548 for (size_t i = mStorage.Length(); i > 0; i--) { 549 mStorage[i - 1]->Shutdown(); 550 } 551 Shutdown(); 552 } 553 } 554 555 void GMPParent::CloseActive(bool aDieWhenUnloaded) { 556 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 557 GMP_PARENT_LOG_DEBUG("%s: state %u", __FUNCTION__, 558 uint32_t(GMPState(mState))); 559 560 if (aDieWhenUnloaded) { 561 mDeleteProcessOnlyOnUnload = true; // don't allow this to go back... 562 } 563 if (mState == GMPState::Loaded) { 564 mState = GMPState::Unloading; 565 } 566 if (mState != GMPState::NotLoaded && IsUsed()) { 567 (void)SendCloseActive(); 568 CloseIfUnused(); 569 } 570 } 571 572 void GMPParent::MarkForDeletion() { 573 mDeleteProcessOnlyOnUnload = true; 574 mIsBlockingDeletion = true; 575 } 576 577 bool GMPParent::IsMarkedForDeletion() { return mIsBlockingDeletion; } 578 579 void GMPParent::Shutdown() { 580 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 581 GMP_PARENT_LOG_DEBUG("%s", __FUNCTION__); 582 583 if (mAbnormalShutdownInProgress) { 584 return; 585 } 586 587 MOZ_ASSERT(!IsUsed()); 588 switch (mState) { 589 case GMPState::NotLoaded: 590 case GMPState::Closing: 591 case GMPState::Closed: 592 return; 593 default: 594 break; 595 } 596 597 RefPtr<GMPParent> self(this); 598 DeleteProcess(); 599 600 // XXX Get rid of mDeleteProcessOnlyOnUnload and this code when 601 // Bug 1043671 is fixed 602 if (!mDeleteProcessOnlyOnUnload) { 603 // Destroy ourselves and rise from the fire to save memory 604 mService->ReAddOnGMPThread(self); 605 } // else we've been asked to die and stay dead 606 MOZ_ASSERT(mState == GMPState::NotLoaded || mState == GMPState::Closing); 607 } 608 609 class NotifyGMPShutdownTask : public Runnable { 610 public: 611 explicit NotifyGMPShutdownTask(const nsAString& aNodeId) 612 : Runnable("NotifyGMPShutdownTask"), mNodeId(aNodeId) {} 613 NS_IMETHOD Run() override { 614 MOZ_ASSERT(NS_IsMainThread()); 615 nsCOMPtr<nsIObserverService> obsService = 616 mozilla::services::GetObserverService(); 617 MOZ_ASSERT(obsService); 618 if (obsService) { 619 obsService->NotifyObservers(nullptr, "gmp-shutdown", mNodeId.get()); 620 } 621 return NS_OK; 622 } 623 nsString mNodeId; 624 }; 625 626 void GMPParent::ChildTerminated() { 627 RefPtr<GMPParent> self(this); 628 nsCOMPtr<nsISerialEventTarget> gmpEventTarget = GMPEventTarget(); 629 630 if (!gmpEventTarget) { 631 // Bug 1163239 - this can happen on shutdown. 632 // PluginTerminated removes the GMP from the GMPService. 633 // On shutdown we can have this case where it is already been 634 // removed so there is no harm in not trying to remove it again. 635 GMP_PARENT_LOG_DEBUG("%s::%s: GMPEventTarget() returned nullptr.", 636 __CLASS__, __FUNCTION__); 637 } else { 638 gmpEventTarget->Dispatch( 639 NewRunnableMethod<RefPtr<GMPParent>>( 640 "gmp::GeckoMediaPluginServiceParent::PluginTerminated", mService, 641 &GeckoMediaPluginServiceParent::PluginTerminated, self), 642 NS_DISPATCH_NORMAL); 643 } 644 } 645 646 void GMPParent::DeleteProcess() { 647 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 648 649 switch (mState) { 650 case GMPState::Closed: 651 // Closing has finished, we can proceed to destroy the process. 652 break; 653 case GMPState::Closing: 654 // Closing in progress, just waiting for the shutdown response. 655 GMP_PARENT_LOG_DEBUG("%s: Shutdown handshake in progress.", __FUNCTION__); 656 return; 657 default: { 658 // Don't Close() twice! 659 // Probably remove when bug 1043671 is resolved 660 GMP_PARENT_LOG_DEBUG("%s: Shutdown handshake starting.", __FUNCTION__); 661 662 RefPtr<GMPParent> self = this; 663 nsCOMPtr<nsISerialEventTarget> gmpEventTarget = GMPEventTarget(); 664 mState = GMPState::Closing; 665 // Let's attempt to get the profile from the child process if we can 666 // before we destroy it. This is particularly important for the GMP 667 // process because we aggressively shut it down when not in active use, so 668 // it is easy to miss the recordings during profiling. 669 SendShutdown()->Then( 670 gmpEventTarget, __func__, 671 [self](nsCString&& aProfile) { 672 GMP_LOG_DEBUG( 673 "GMPParent[%p|childPid=%d] DeleteProcess: Shutdown handshake " 674 "success, profileLen=%zu.", 675 self.get(), self->mChildPid, aProfile.Length()); 676 if (!aProfile.IsEmpty()) { 677 NS_DispatchToMainThread(NS_NewRunnableFunction( 678 "GMPParent::DeleteProcess", 679 [profile = std::move(aProfile)]() { 680 profiler_received_exit_profile(profile); 681 })); 682 } 683 self->mState = GMPState::Closed; 684 self->Close(); 685 self->DeleteProcess(); 686 }, 687 [self](const ipc::ResponseRejectReason&) { 688 // We crashed during shutdown, ActorDestroy will perform cleanup. 689 GMP_LOG_DEBUG( 690 "GMPParent[%p|childPid=%d] DeleteProcess: Shutdown handshake " 691 "error.", 692 self.get(), self->mChildPid); 693 }); 694 return; 695 } 696 } 697 698 GMP_PARENT_LOG_DEBUG("%s: Shutting down process.", __FUNCTION__); 699 mProcess->Delete(NewRunnableMethod("gmp::GMPParent::ChildTerminated", this, 700 &GMPParent::ChildTerminated)); 701 GMP_PARENT_LOG_DEBUG("%s: Shut down process", __FUNCTION__); 702 mProcess = nullptr; 703 704 #if defined(MOZ_WIDGET_ANDROID) 705 if (mState != GMPState::NotLoaded) { 706 nsCOMPtr<nsIEventTarget> launcherThread(ipc::GetIPCLauncher()); 707 MOZ_ASSERT(launcherThread); 708 709 auto procType = java::GeckoProcessType::GMPLUGIN(); 710 auto selector = 711 java::GeckoProcessManager::Selector::New(procType, OtherPid()); 712 713 launcherThread->Dispatch(NS_NewRunnableFunction( 714 "GMPParent::DeleteProcess", 715 [selector = 716 java::GeckoProcessManager::Selector::GlobalRef(selector)]() { 717 java::GeckoProcessManager::ShutdownProcess(selector); 718 })); 719 } 720 #endif // defined(MOZ_WIDGET_ANDROID) 721 722 mState = GMPState::NotLoaded; 723 724 nsCOMPtr<nsIRunnable> r = 725 new NotifyGMPShutdownTask(NS_ConvertUTF8toUTF16(mNodeId)); 726 mMainThread->Dispatch(r.forget()); 727 } 728 729 GMPState GMPParent::State() const { return mState; } 730 731 nsCOMPtr<nsISerialEventTarget> GMPParent::GMPEventTarget() { 732 nsCOMPtr<mozIGeckoMediaPluginService> mps = 733 do_GetService("@mozilla.org/gecko-media-plugin-service;1"); 734 MOZ_ASSERT(mps); 735 if (!mps) { 736 return nullptr; 737 } 738 // Note: GeckoMediaPluginService::GetThread() is threadsafe, and returns 739 // nullptr if the GeckoMediaPluginService has started shutdown. 740 nsCOMPtr<nsIThread> gmpThread; 741 mps->GetThread(getter_AddRefs(gmpThread)); 742 return gmpThread; 743 } 744 745 /* static */ 746 bool GMPCapability::Supports(const nsTArray<GMPCapability>& aCapabilities, 747 const nsACString& aAPI, 748 const nsTArray<nsCString>& aTags) { 749 for (const nsCString& tag : aTags) { 750 if (!GMPCapability::Supports(aCapabilities, aAPI, tag)) { 751 return false; 752 } 753 } 754 return true; 755 } 756 757 /* static */ 758 bool GMPCapability::Supports(const nsTArray<GMPCapability>& aCapabilities, 759 const nsACString& aAPI, const nsCString& aTag) { 760 for (const GMPCapability& capabilities : aCapabilities) { 761 if (!capabilities.mAPIName.Equals(aAPI)) { 762 continue; 763 } 764 for (const nsCString& tag : capabilities.mAPITags) { 765 if (tag.Equals(aTag)) { 766 #ifdef XP_WIN 767 // Clearkey on Windows advertises that it can decode in its GMP info 768 // file, but uses Windows Media Foundation to decode. That's not present 769 // on Windows XP, and on some Vista, Windows N, and KN variants without 770 // certain services packs. 771 if (tag.EqualsLiteral(kClearKeyKeySystemName)) { 772 if (capabilities.mAPIName.EqualsLiteral(GMP_API_VIDEO_DECODER)) { 773 auto pdmFactory = MakeRefPtr<PDMFactory>(); 774 if (pdmFactory->SupportsMimeType("video/avc"_ns).isEmpty()) { 775 continue; 776 } 777 } 778 } 779 #endif 780 return true; 781 } 782 } 783 } 784 return false; 785 } 786 787 bool GMPParent::EnsureProcessLoaded() { 788 switch (mState) { 789 case GMPState::NotLoaded: 790 return NS_SUCCEEDED(LoadProcess()); 791 case GMPState::Loaded: 792 return true; 793 case GMPState::Unloading: 794 case GMPState::Closing: 795 case GMPState::Closed: 796 return false; 797 } 798 799 MOZ_ASSERT_UNREACHABLE("Unhandled GMPState!"); 800 return false; 801 } 802 803 void GMPParent::AddCrashAnnotations() { 804 if (mCrashReporter) { 805 mCrashReporter->AddAnnotationBool(CrashReporter::Annotation::GMPPlugin, 806 true); 807 mCrashReporter->AddAnnotationNSCString( 808 CrashReporter::Annotation::PluginFilename, 809 NS_ConvertUTF16toUTF8(mName)); 810 mCrashReporter->AddAnnotationNSCString( 811 CrashReporter::Annotation::PluginName, mDisplayName); 812 mCrashReporter->AddAnnotationNSCString( 813 CrashReporter::Annotation::PluginVersion, mVersion); 814 } 815 } 816 817 void GMPParent::GetCrashID(nsString& aResult) { 818 AddCrashAnnotations(); 819 GenerateCrashReport(&aResult); 820 } 821 822 static void GMPNotifyObservers(const uint32_t aPluginID, 823 const nsACString& aPluginName, 824 const nsAString& aPluginDumpID) { 825 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 826 nsCOMPtr<nsIWritablePropertyBag2> propbag = 827 do_CreateInstance("@mozilla.org/hash-property-bag;1"); 828 if (obs && propbag) { 829 propbag->SetPropertyAsUint32(u"pluginID"_ns, aPluginID); 830 propbag->SetPropertyAsACString(u"pluginName"_ns, aPluginName); 831 propbag->SetPropertyAsAString(u"pluginDumpID"_ns, aPluginDumpID); 832 obs->NotifyObservers(propbag, "gmp-plugin-crash", nullptr); 833 } 834 835 RefPtr<gmp::GeckoMediaPluginService> service = 836 gmp::GeckoMediaPluginService::GetGeckoMediaPluginService(); 837 if (service) { 838 service->RunPluginCrashCallbacks(aPluginID, aPluginName); 839 } 840 } 841 842 void GMPParent::ActorDestroy(ActorDestroyReason aWhy) { 843 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 844 GMP_PARENT_LOG_DEBUG("%s: (%d), state=%u", __FUNCTION__, (int)aWhy, 845 uint32_t(GMPState(mState))); 846 847 if (AbnormalShutdown == aWhy) { 848 glean::subprocess::abnormal_abort.Get("gmplugin"_ns).Add(1); 849 nsString dumpID; 850 GetCrashID(dumpID); 851 if (dumpID.IsEmpty()) { 852 NS_WARNING("GMP crash without crash report"); 853 dumpID = mName; 854 dumpID += '-'; 855 AppendUTF8toUTF16(mVersion, dumpID); 856 } 857 858 // NotifyObservers is mainthread-only 859 nsCOMPtr<nsIRunnable> r = 860 WrapRunnableNM(&GMPNotifyObservers, mPluginId, mDisplayName, dumpID); 861 mMainThread->Dispatch(r.forget()); 862 } 863 864 // warn us off trying to close again 865 mState = GMPState::Closed; 866 mAbnormalShutdownInProgress = true; 867 CloseActive(false); 868 869 // Normal Shutdown() will delete the process on unwind. GMPProcessParent 870 // blocks shutdown to avoid races. 871 if (AbnormalShutdown == aWhy) { 872 RefPtr<GMPParent> self(this); 873 // Must not call Close() again in DeleteProcess(), as we'll recurse 874 // infinitely if we do. 875 MOZ_ASSERT(mState == GMPState::Closed); 876 DeleteProcess(); 877 // Note: final destruction will be Dispatched to ourself 878 mService->ReAddOnGMPThread(self); 879 } 880 } 881 882 PGMPStorageParent* GMPParent::AllocPGMPStorageParent() { 883 GMPStorageParent* p = new GMPStorageParent(mNodeId, this); 884 mStorage.AppendElement(p); // Addrefs, released in DeallocPGMPStorageParent. 885 return p; 886 } 887 888 bool GMPParent::DeallocPGMPStorageParent(PGMPStorageParent* aActor) { 889 GMPStorageParent* p = static_cast<GMPStorageParent*>(aActor); 890 p->Shutdown(); 891 mStorage.RemoveElement(p); 892 return true; 893 } 894 895 mozilla::ipc::IPCResult GMPParent::RecvPGMPStorageConstructor( 896 PGMPStorageParent* aActor) { 897 GMPStorageParent* p = (GMPStorageParent*)aActor; 898 if (NS_FAILED(p->Init())) { 899 // TODO: Verify if this is really a good reason to IPC_FAIL. 900 // There might be shutdown edge cases here. 901 return IPC_FAIL(this, 902 "GMPParent::RecvPGMPStorageConstructor: p->Init() failed."); 903 } 904 return IPC_OK(); 905 } 906 907 mozilla::ipc::IPCResult GMPParent::RecvPGMPTimerConstructor( 908 PGMPTimerParent* actor) { 909 return IPC_OK(); 910 } 911 912 PGMPTimerParent* GMPParent::AllocPGMPTimerParent() { 913 nsCOMPtr<nsISerialEventTarget> target = GMPEventTarget(); 914 GMPTimerParent* p = new GMPTimerParent(target); 915 mTimers.AppendElement( 916 p); // Released in DeallocPGMPTimerParent, or on shutdown. 917 return p; 918 } 919 920 bool GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor) { 921 GMPTimerParent* p = static_cast<GMPTimerParent*>(aActor); 922 p->Shutdown(); 923 mTimers.RemoveElement(p); 924 return true; 925 } 926 927 bool ReadInfoField(GMPInfoFileParser& aParser, const nsCString& aKey, 928 nsACString& aOutValue) { 929 if (!aParser.Contains(aKey) || aParser.Get(aKey).IsEmpty()) { 930 return false; 931 } 932 aOutValue = aParser.Get(aKey); 933 return true; 934 } 935 936 RefPtr<GenericPromise> GMPParent::ReadGMPMetaData() { 937 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 938 MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!"); 939 MOZ_ASSERT(!mName.IsEmpty(), "Plugin mName cannot be empty!"); 940 941 nsCOMPtr<nsIFile> infoFile; 942 nsresult rv = mDirectory->Clone(getter_AddRefs(infoFile)); 943 if (NS_WARN_IF(NS_FAILED(rv))) { 944 return GenericPromise::CreateAndReject(rv, __func__); 945 } 946 infoFile->AppendRelativePath(mName + u".info"_ns); 947 948 if (FileExists(infoFile)) { 949 return ReadGMPInfoFile(infoFile); 950 } 951 952 // Maybe this is the Widevine adapted plugin? 953 nsCOMPtr<nsIFile> manifestFile; 954 rv = mDirectory->Clone(getter_AddRefs(manifestFile)); 955 if (NS_WARN_IF(NS_FAILED(rv))) { 956 return GenericPromise::CreateAndReject(rv, __func__); 957 } 958 manifestFile->AppendRelativePath(u"manifest.json"_ns); 959 return ReadChromiumManifestFile(manifestFile); 960 } 961 962 #if defined(XP_LINUX) 963 static void ApplyGlibcWorkaround(nsCString& aLibs) { 964 // These glibc libraries were merged into libc.so.6 as of glibc 965 // 2.34; they now exist only as stub libraries for compatibility and 966 // newly linked code won't depend on them, so we need to ensure 967 // they're loaded for plugins that may have been linked against a 968 // different version of glibc. (See also bug 1725828.) 969 if (!aLibs.IsEmpty()) { 970 aLibs.AppendLiteral(", "); 971 } 972 aLibs.AppendLiteral("libdl.so.2, libpthread.so.0, librt.so.1"); 973 } 974 #endif 975 976 #if defined(XP_WIN) 977 static void ApplyOleaut32(nsCString& aLibs) { 978 // In the libwebrtc update in bug 1766646 an include of comdef.h for using 979 // _bstr_t was introduced. This resulted in a dependency on comsupp.lib which 980 // contains a `_variant_t vtMissing` that would get cleared in an exit 981 // handler. VariantClear is defined in oleaut32.dll, and so we'd try to load 982 // oleaut32.dll on exit but get denied by the sandbox. 983 // Note that we had includes of comdef.h before bug 1766646 but it is the use 984 // of _bstr_t that triggers the vtMissing exit handler. 985 // See bug 1788592 for details. 986 if (!aLibs.IsEmpty()) { 987 aLibs.AppendLiteral(", "); 988 } 989 aLibs.AppendLiteral("oleaut32.dll"); 990 } 991 #endif 992 993 static constexpr uint64_t MakeVersion(uint16_t aA, uint16_t aB, uint16_t aC, 994 uint16_t aD) { 995 return (static_cast<uint64_t>(aA) << 48) | (static_cast<uint64_t>(aB) << 32) | 996 (static_cast<uint64_t>(aC) << 16) | aD; 997 } 998 999 static nsresult ParseVersion(const nsACString& aVersion, 1000 uint64_t* aParsedVersion) { 1001 MOZ_ASSERT(aParsedVersion); 1002 1003 uint64_t version = 0; 1004 uint32_t fragmentCount = 0; 1005 nsresult rv = NS_OK; 1006 1007 for (const auto& fragment : aVersion.Split('.')) { 1008 ++fragmentCount; 1009 if (NS_WARN_IF(fragmentCount >= 5)) { 1010 return NS_ERROR_FAILURE; 1011 } 1012 1013 uint32_t fragmentInt = fragment.ToUnsignedInteger(&rv, /* aRadix */ 10); 1014 if (NS_WARN_IF(NS_FAILED(rv))) { 1015 return rv; 1016 } 1017 1018 version = (version << 16) | SaturatingCast<uint16_t>(fragmentInt); 1019 } 1020 1021 *aParsedVersion = version << (4 - fragmentCount) * 16; 1022 return NS_OK; 1023 } 1024 1025 RefPtr<GenericPromise> GMPParent::ReadGMPInfoFile(nsIFile* aFile) { 1026 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 1027 GMPInfoFileParser parser; 1028 if (!parser.Init(aFile)) { 1029 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1030 } 1031 1032 nsAutoCString apis; 1033 if (!ReadInfoField(parser, "name"_ns, mDisplayName) || 1034 !ReadInfoField(parser, "description"_ns, mDescription) || 1035 !ReadInfoField(parser, "version"_ns, mVersion) || 1036 !ReadInfoField(parser, "apis"_ns, apis)) { 1037 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1038 } 1039 1040 #if defined(XP_WIN) || defined(XP_LINUX) 1041 // "Libraries" field is optional. 1042 ReadInfoField(parser, "libraries"_ns, mLibs); 1043 #endif 1044 1045 UpdatePluginType(); 1046 1047 // We check the version for OpenH264 because we may need to add additional API 1048 // tags to indicate we support more advanced modes for newer versions of the 1049 // plugin. 1050 bool addMozSupportsH264Advanced = false; 1051 bool addMozSupportsH264TemporalSVC = false; 1052 if (mPluginType == GMPPluginType::OpenH264) { 1053 uint64_t parsedVersion = 0; 1054 nsresult rv = ParseVersion(mVersion, &parsedVersion); 1055 if (NS_WARN_IF(NS_FAILED(rv))) { 1056 return GenericPromise::CreateAndReject(rv, __func__); 1057 } 1058 1059 // Earlier versions only supported decoding/encoding constrained baseline. 1060 addMozSupportsH264Advanced = parsedVersion >= MakeVersion(2, 3, 2, 0); 1061 1062 // Earlier versions did not expose the encoded SVC temporal layer ID. 1063 addMozSupportsH264TemporalSVC = parsedVersion > MakeVersion(2, 5, 0, 0); 1064 } 1065 1066 #ifdef XP_LINUX 1067 // The glibc workaround (see above) isn't needed for clearkey 1068 // because it's built along with the browser. 1069 if (mPluginType != GMPPluginType::Clearkey) { 1070 ApplyGlibcWorkaround(mLibs); 1071 } 1072 #endif 1073 1074 #ifdef XP_WIN 1075 ApplyOleaut32(mLibs); 1076 #endif 1077 1078 nsTArray<nsCString> apiTokens; 1079 SplitAt(", ", apis, apiTokens); 1080 for (nsCString api : apiTokens) { 1081 int32_t tagsStart = api.FindChar('['); 1082 if (tagsStart == 0) { 1083 // Not allowed to be the first character. 1084 // API name must be at least one character. 1085 continue; 1086 } 1087 1088 GMPCapability cap; 1089 if (tagsStart == -1) { 1090 // No tags. 1091 cap.mAPIName.Assign(api); 1092 } else { 1093 auto tagsEnd = api.FindChar(']'); 1094 if (tagsEnd == -1 || tagsEnd < tagsStart) { 1095 // Invalid syntax, skip whole capability. 1096 continue; 1097 } 1098 1099 cap.mAPIName.Assign(Substring(api, 0, tagsStart)); 1100 1101 if ((tagsEnd - tagsStart) > 1) { 1102 const nsDependentCSubstring ts( 1103 Substring(api, tagsStart + 1, tagsEnd - tagsStart - 1)); 1104 nsTArray<nsCString> tagTokens; 1105 SplitAt(":", ts, tagTokens); 1106 for (nsCString tag : tagTokens) { 1107 cap.mAPITags.AppendElement(tag); 1108 } 1109 } 1110 } 1111 1112 if (mPluginType == GMPPluginType::OpenH264) { 1113 if (addMozSupportsH264Advanced && 1114 !cap.mAPITags.Contains("moz-h264-advanced"_ns)) { 1115 cap.mAPITags.AppendElement("moz-h264-advanced"_ns); 1116 } 1117 if (addMozSupportsH264TemporalSVC && 1118 !cap.mAPITags.Contains("moz-h264-temporal-svc"_ns)) { 1119 cap.mAPITags.AppendElement("moz-h264-temporal-svc"_ns); 1120 } 1121 } 1122 1123 mCapabilities.AppendElement(std::move(cap)); 1124 } 1125 1126 if (mCapabilities.IsEmpty()) { 1127 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1128 } 1129 1130 return GenericPromise::CreateAndResolve(true, __func__); 1131 } 1132 1133 RefPtr<GenericPromise> GMPParent::ReadChromiumManifestFile(nsIFile* aFile) { 1134 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 1135 nsAutoCString json; 1136 if (!ReadIntoString(aFile, json, 5 * 1024)) { 1137 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1138 } 1139 1140 // DOM JSON parsing needs to run on the main thread. 1141 return InvokeAsync(mMainThread, this, __func__, 1142 &GMPParent::ParseChromiumManifest, 1143 NS_ConvertUTF8toUTF16(json)); 1144 } 1145 1146 static bool IsCDMAPISupported( 1147 const mozilla::dom::WidevineCDMManifest& aManifest) { 1148 if (!aManifest.mX_cdm_module_versions.WasPassed() || 1149 !aManifest.mX_cdm_interface_versions.WasPassed() || 1150 !aManifest.mX_cdm_host_versions.WasPassed()) { 1151 return false; 1152 } 1153 1154 nsresult ignored; // Note: ToInteger returns 0 on failure. 1155 int32_t moduleVersion = 1156 aManifest.mX_cdm_module_versions.Value().ToInteger(&ignored); 1157 int32_t interfaceVersion = 1158 aManifest.mX_cdm_interface_versions.Value().ToInteger(&ignored); 1159 int32_t hostVersion = 1160 aManifest.mX_cdm_host_versions.Value().ToInteger(&ignored); 1161 return ChromiumCDMAdapter::Supports(moduleVersion, interfaceVersion, 1162 hostVersion); 1163 } 1164 1165 RefPtr<GenericPromise> GMPParent::ParseChromiumManifest( 1166 const nsAString& aJSON) { 1167 GMP_PARENT_LOG_DEBUG("%s: for '%s'", __FUNCTION__, 1168 NS_LossyConvertUTF16toASCII(aJSON).get()); 1169 1170 MOZ_ASSERT(NS_IsMainThread()); 1171 mozilla::dom::WidevineCDMManifest m; 1172 if (!m.Init(aJSON)) { 1173 GMP_PARENT_LOG_DEBUG("%s: Failed to initialize json parser, failing.", 1174 __FUNCTION__); 1175 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1176 } 1177 1178 CopyUTF16toUTF8(m.mName, mDisplayName); 1179 CopyUTF16toUTF8(m.mVersion, mVersion); 1180 1181 if (m.mDescription.WasPassed()) { 1182 CopyUTF16toUTF8(m.mDescription.Value(), mDescription); 1183 } 1184 1185 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 1186 if (!mozilla::SandboxInfo::Get().CanSandboxMedia()) { 1187 nsPrintfCString msg( 1188 "GMPParent::ParseChromiumManifest: Plugin \"%s\" is an EME CDM" 1189 " but this system can't sandbox it; not loading.", 1190 mDisplayName.get()); 1191 printf_stderr("%s\n", msg.get()); 1192 GMP_PARENT_LOG_DEBUG("%s", msg.get()); 1193 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1194 } 1195 #endif 1196 1197 UpdatePluginType(); 1198 1199 GMPCapability video; 1200 1201 if (IsCDMAPISupported(m)) { 1202 video.mAPIName = nsLiteralCString(CHROMIUM_CDM_API); 1203 mAdapter = u"chromium"_ns; 1204 #ifdef MOZ_WMF_CDM 1205 } else if (mPluginType == GMPPluginType::WidevineL1) { 1206 video.mAPIName = nsCString(kWidevineExperimentAPIName); 1207 mAdapter = NS_ConvertUTF8toUTF16(kWidevineExperimentAPIName); 1208 #endif 1209 } else { 1210 GMP_PARENT_LOG_DEBUG("%s: CDM API not supported, failing.", __FUNCTION__); 1211 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1212 } 1213 1214 // We hard code a few of the settings because they can't be stored in the 1215 // widevine manifest without making our API different to widevine's. 1216 switch (mPluginType) { 1217 case GMPPluginType::Clearkey: 1218 video.mAPITags.AppendElement(nsCString{kClearKeyKeySystemName}); 1219 video.mAPITags.AppendElement( 1220 nsCString{kClearKeyWithProtectionQueryKeySystemName}); 1221 #if XP_WIN 1222 mLibs = nsLiteralCString( 1223 "dxva2.dll, evr.dll, freebl3.dll, mfh264dec.dll, mfplat.dll, " 1224 "msmpeg2vdec.dll, nss3.dll, softokn3.dll"); 1225 #elif XP_LINUX 1226 mLibs = "libfreeblpriv3.so, libsoftokn3.so"_ns; 1227 #endif 1228 break; 1229 case GMPPluginType::Widevine: 1230 video.mAPITags.AppendElement(nsCString{kWidevineKeySystemName}); 1231 #if XP_WIN 1232 // psapi.dll added for GetMappedFileNameW, which could possibly be avoided 1233 // in future versions, see bug 1383611 for details. 1234 mLibs = "dxva2.dll, ole32.dll, psapi.dll, shell32.dll, winmm.dll"_ns; 1235 #endif 1236 break; 1237 #ifdef MOZ_WMF_CDM 1238 case GMPPluginType::WidevineL1: 1239 video.mAPITags.AppendElement(nsCString{kWidevineExperimentKeySystemName}); 1240 video.mAPITags.AppendElement( 1241 nsCString{kWidevineExperiment2KeySystemName}); 1242 break; 1243 #endif 1244 case GMPPluginType::Fake: 1245 // The fake CDM just exposes a key system with id "fake". 1246 video.mAPITags.AppendElement(nsCString{"fake"}); 1247 #if XP_WIN 1248 mLibs = "dxva2.dll, ole32.dll"_ns; 1249 #endif 1250 break; 1251 default: 1252 GMP_PARENT_LOG_DEBUG("%s: Unrecognized key system: %s, failing.", 1253 __FUNCTION__, mDisplayName.get()); 1254 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 1255 } 1256 1257 #ifdef XP_LINUX 1258 ApplyGlibcWorkaround(mLibs); 1259 #endif 1260 1261 #ifdef XP_WIN 1262 ApplyOleaut32(mLibs); 1263 #endif 1264 1265 nsTArray<nsCString> codecs; 1266 1267 if (m.mX_cdm_codecs.WasPassed()) { 1268 nsCString codecsString; 1269 codecsString = NS_ConvertUTF16toUTF8(m.mX_cdm_codecs.Value()); 1270 SplitAt(",", codecsString, codecs); 1271 } 1272 1273 // Parse the codec strings in the manifest and map them to strings used 1274 // internally by Gecko for capability recognition. 1275 // 1276 // Google's code to parse manifests can be used as a reference for strings 1277 // the manifest may contain 1278 // https://source.chromium.org/chromium/chromium/src/+/master:components/cdm/common/cdm_manifest.cc;l=74;drc=775880ced8a989191281e93854c7f2201f25068f 1279 // 1280 // Gecko's internal strings can be found at 1281 // https://searchfox.org/mozilla-central/rev/ea63a0888d406fae720cf24f4727d87569a8cab5/dom/media/eme/MediaKeySystemAccess.cpp#149-155 1282 for (const nsCString& chromiumCodec : codecs) { 1283 nsCString codec; 1284 if (chromiumCodec.EqualsASCII("vp8")) { 1285 codec = "vp8"_ns; 1286 } else if (chromiumCodec.EqualsASCII("vp9.0") || // Legacy string. 1287 chromiumCodec.EqualsASCII("vp09")) { 1288 codec = "vp9"_ns; 1289 } else if (chromiumCodec.EqualsASCII("avc1")) { 1290 codec = "h264"_ns; 1291 } else if (chromiumCodec.EqualsASCII("av01")) { 1292 codec = "av1"_ns; 1293 } else { 1294 GMP_PARENT_LOG_DEBUG("%s: Unrecognized codec: %s.", __FUNCTION__, 1295 chromiumCodec.get()); 1296 MOZ_ASSERT_UNREACHABLE( 1297 "Unhandled codec string! Need to add it to the parser."); 1298 continue; 1299 } 1300 1301 video.mAPITags.AppendElement(codec); 1302 } 1303 1304 mCapabilities.AppendElement(std::move(video)); 1305 1306 GMP_PARENT_LOG_DEBUG("%s: Successfully parsed manifest.", __FUNCTION__); 1307 return GenericPromise::CreateAndResolve(true, __func__); 1308 } 1309 1310 bool GMPParent::CanBeSharedCrossNodeIds() const { 1311 return mNodeId.IsEmpty() && 1312 // XXX bug 1159300 hack -- maybe remove after openh264 1.4 1313 // We don't want to use CDM decoders for non-encrypted playback 1314 // just yet; especially not for WebRTC. Don't allow CDMs to be used 1315 // without a node ID. 1316 !mCanDecrypt; 1317 } 1318 1319 bool GMPParent::CanBeUsedFrom(const nsACString& aNodeId) const { 1320 return mNodeId == aNodeId; 1321 } 1322 1323 void GMPParent::SetNodeId(const nsACString& aNodeId) { 1324 MOZ_ASSERT(!aNodeId.IsEmpty()); 1325 mNodeId = aNodeId; 1326 } 1327 1328 void GMPParent::UpdatePluginType() { 1329 if (mDisplayName.EqualsLiteral("WidevineCdm")) { 1330 mPluginType = GMPPluginType::Widevine; 1331 #ifdef MOZ_WMF_CDM 1332 } else if (mDisplayName.EqualsLiteral(kWidevineExperimentAPIName)) { 1333 mPluginType = GMPPluginType::WidevineL1; 1334 #endif 1335 } else if (mDisplayName.EqualsLiteral("gmpopenh264")) { 1336 mPluginType = GMPPluginType::OpenH264; 1337 } else if (mDisplayName.EqualsLiteral("clearkey")) { 1338 mPluginType = GMPPluginType::Clearkey; 1339 } else if (mDisplayName.EqualsLiteral("fake")) { 1340 mPluginType = GMPPluginType::Fake; 1341 } else { 1342 mPluginType = GMPPluginType::Unknown; 1343 } 1344 } 1345 1346 const nsCString& GMPParent::GetDisplayName() const { return mDisplayName; } 1347 1348 const nsCString& GMPParent::GetVersion() const { return mVersion; } 1349 1350 uint32_t GMPParent::GetPluginId() const { return mPluginId; } 1351 1352 void GMPParent::ResolveGetContentParentPromises() { 1353 nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> promises = 1354 std::move(mGetContentParentPromises); 1355 MOZ_ASSERT(mGetContentParentPromises.IsEmpty()); 1356 RefPtr<GMPContentParentCloseBlocker> blocker( 1357 new GMPContentParentCloseBlocker(mGMPContentParent)); 1358 for (auto& holder : promises) { 1359 holder->Resolve(blocker, __func__); 1360 } 1361 } 1362 1363 bool GMPParent::OpenPGMPContent() { 1364 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 1365 MOZ_ASSERT(!mGMPContentParent); 1366 1367 Endpoint<PGMPContentParent> parent; 1368 Endpoint<PGMPContentChild> child; 1369 if (NS_WARN_IF(NS_FAILED(PGMPContent::CreateEndpoints( 1370 mozilla::ipc::EndpointProcInfo::Current(), OtherEndpointProcInfo(), 1371 &parent, &child)))) { 1372 return false; 1373 } 1374 1375 mGMPContentParent = new GMPContentParent(this); 1376 1377 if (!parent.Bind(mGMPContentParent)) { 1378 return false; 1379 } 1380 1381 if (!SendInitGMPContentChild(std::move(child))) { 1382 return false; 1383 } 1384 1385 ResolveGetContentParentPromises(); 1386 1387 return true; 1388 } 1389 1390 void GMPParent::RejectGetContentParentPromises() { 1391 nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> promises = 1392 std::move(mGetContentParentPromises); 1393 MOZ_ASSERT(mGetContentParentPromises.IsEmpty()); 1394 for (auto& holder : promises) { 1395 holder->Reject(NS_ERROR_FAILURE, __func__); 1396 } 1397 } 1398 1399 void GMPParent::GetGMPContentParent( 1400 UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>&& aPromiseHolder) { 1401 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 1402 GMP_PARENT_LOG_DEBUG("%s %p", __FUNCTION__, this); 1403 1404 if (mGMPContentParent) { 1405 RefPtr<GMPContentParentCloseBlocker> blocker( 1406 new GMPContentParentCloseBlocker(mGMPContentParent)); 1407 aPromiseHolder->Resolve(blocker, __func__); 1408 } else { 1409 mGetContentParentPromises.AppendElement(std::move(aPromiseHolder)); 1410 // If we don't have a GMPContentParent and we try to get one for the first 1411 // time (mGetContentParentPromises.Length() == 1) then call 1412 // PGMPContent::Open. If more calls to GetGMPContentParent happen before 1413 // mGMPContentParent has been set then we should just store them, so that 1414 // they get called when we set mGMPContentParent as a result of the 1415 // PGMPContent::Open call. 1416 if (mGetContentParentPromises.Length() == 1) { 1417 if (!EnsureProcessLoaded() || !OpenPGMPContent()) { 1418 RejectGetContentParentPromises(); 1419 return; 1420 } 1421 // We want to increment this as soon as possible, to avoid that we'd try 1422 // to shut down the GMP process while we're still trying to get a 1423 // PGMPContentParent actor. 1424 ++mGMPContentChildCount; 1425 } 1426 } 1427 } 1428 1429 already_AddRefed<GMPContentParent> GMPParent::ForgetGMPContentParent() { 1430 MOZ_ASSERT(mGetContentParentPromises.IsEmpty()); 1431 return mGMPContentParent.forget(); 1432 } 1433 1434 bool GMPParent::EnsureProcessLoaded(base::ProcessId* aID) { 1435 if (!EnsureProcessLoaded()) { 1436 return false; 1437 } 1438 *aID = OtherPid(); 1439 return true; 1440 } 1441 1442 void GMPParent::IncrementGMPContentChildCount() { ++mGMPContentChildCount; } 1443 1444 nsString GMPParent::GetPluginBaseName() const { return u"gmp-"_ns + mName; } 1445 1446 #if defined(XP_MACOSX) && defined(__aarch64__) 1447 void GMPParent::PreTranslateBins() { 1448 nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod( 1449 "RosettaTranslation", this, &GMPParent::PreTranslateBinsWorker); 1450 1451 DebugOnly<nsresult> rv = 1452 NS_DispatchBackgroundTask(event.forget(), NS_DISPATCH_EVENT_MAY_BLOCK); 1453 1454 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1455 } 1456 1457 void GMPParent::PreTranslateBinsWorker() { 1458 int rv = nsMacUtilsImpl::PreTranslateXUL(); 1459 GMP_PARENT_LOG_DEBUG("%s: XUL translation result: %d", __FUNCTION__, rv); 1460 1461 rv = nsMacUtilsImpl::PreTranslateBinary(mPluginFilePath); 1462 GMP_PARENT_LOG_DEBUG("%s: %s translation result: %d", __FUNCTION__, 1463 mPluginFilePath.get(), rv); 1464 } 1465 #endif 1466 1467 } // namespace mozilla::gmp 1468 1469 #undef GMP_PARENT_LOG_DEBUG 1470 #undef __CLASS__