Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support widevine in linux by bundling #1475

Closed
wants to merge 11 commits into from

Conversation

simonhong
Copy link
Member

@simonhong simonhong commented Jan 28, 2019

In linux, WidevineCdm library is bundled instead of installing it
by component updating during the runtime. In linux, we can't load
cdm library during the runtime because processes except browser
process are forked from zygote process not created from brave.exe.
So, cdm should be loaded(not initialized) into zygote process space
before zygote enters the sandbox.

Chrome browser also uses bundling for linux.

This PR should be merged with brave/brave-browser#3037

Fix brave/brave-browser#413

Submitter Checklist:

  • Submitted a ticket for my issue if one did not already exist.
  • Used Github auto-closing keywords in the commit message.
  • Added/updated tests for this change (for new code or code which already has tests).
  • Verified that these changes build without errors on
    • Windows
    • macOS
    • Linux
  • Verified that these changes pass automated tests (npm test brave_unit_tests && npm test brave_browser_tests) on
    • Windows
    • macOS
    • Linux
  • Ran git rebase master (if needed).
  • Ran git rebase -i to squash commits (if needed).
  • Tagged reviewers and labelled the pull request as needed.
  • Request a security/privacy review as needed.
  • Add appropriate QA labels (QA/Yes or QA/No) to include the closed issue in milestone

Test Plan:

  1. Start browser with clean profile
  2. Load https://bitmovin.com/demos/drm
  3. Check No drm
  4. Install widevine via content settings bubble
  5. Check widevine is enabled
  6. Restart browser and check widevine is enabled

yarn test brave_unittest --filter=CdmRegistryImplTest.WidevineCdmExcludeTest

Reviewer Checklist:

  • New files have MPL-2.0 license header.
  • Request a security/privacy review as needed.
  • Adequate test coverage exists to prevent regressions
  • Verify test plan is specified in PR before merging to source

@simonhong
Copy link
Member Author

simonhong commented Jan 28, 2019

When widevine cdm is bundled, widevine cdm library is loaded into zygote process and
it is registered to CdmRegistry at startup.
Because of this, widevine is initialized regardless of the value of kWidevineOptedIn.
So, need to find how prevent initializing widevine when user doesn't accept explicitly.

To handle this, I tried to get kWidevineOptedIn from CdmRegistry via ContentBrowserClient() when renderer asks whether widevine is enabled or not.
However, there are some difficulties - it is ambiguous which profile should be used and profile manager is only allowed to access by main thread.

If we also want to show content settings bubble in linux, I need to find how to prevent cdm initialization. Or if we can disable content bubble in linux, only first & second commits in this PR are needed.
@bbondy @iefremov Any advice would be welcome!

@iefremov
Copy link
Contributor

@simonhong
Maybe we can ignore the fact that different profiles may have different values of kWidevineOptedIn during Widevine initialization? It's hard to skip loading the binary if the user has one profile that opted int and another that didn't.

This way, if any of user profiles has this preference enabled, we can somehow persist the fact that the binary should be loaded (e.g. create a marker file) and, for example, check it in ChromeContentClient::AddContentDecryptionModules.

@simonhong
Copy link
Member Author

simonhong commented Jan 28, 2019

With bundling, cdm loading and initialization is in separated phase.
Loading is done during the startup by PreloadLibraryCdms().
And initialization is done by CdmModule::Initialize() when cdm service is launched.
This launching is triggered by renderer that tries to play contents.

@iefremov As you said, we can't avoid loading. But, I thought we can control cdm service launching.
For having same UX with mac/win, my initial idea was modifying CdmRegistryImpl::GetAllRegisteredCdms().
In this method, I tried to exclude widevine CdmInfo when kWidevineOptedIn is not set.
With this, I think we could make renderer think widevine is not available for now.

However, I met multi profile issue and accessing profile_manager from io thread.
If we must have same UX with other platforms, we should find the way and I think it's not impossible but would introduce many patches.

So, I also want to know we should have same UX. If not, it can be achieved with simple patch. Only first & second commits are needed as I said earlier.

IMO, current content setting bubble is not suitable because cdm library is already installed and loaded into process. It is just not initialized.

@simonhong
Copy link
Member Author

simonhong commented Jan 29, 2019

With the 4th commit, widevine is only initialized after users allowed to use widevine via content settings bubble.
One remained issue is widevine isn't initialized by reloading. To use widevine after user accept, contents page should be loaded in the new tab or restart. I'm trying to find the reason now...

@simonhong simonhong changed the title WIP: Support widevine in linux by bundling Support widevine in linux by bundling Jan 29, 2019
@simonhong simonhong added this to the 0.62.x - Nightly milestone Jan 29, 2019
@simonhong
Copy link
Member Author

I think this PR is ready to review.
With this PR, linux has same widevine UX with mac/win.

@simonhong
Copy link
Member Author

simonhong commented Jan 30, 2019

@bbondy Due to the restriction of zygote in linux, I enabled widevine by bundling.
That means, widevine is loaded into zygote process by default but not initialized.
It is only initialized and cmd service is launched when user accepts via content settings bubble.
If we don't load it by default, we would need restart to use widevine after user accepts.
If loading by default is not allowed by our privacy policy, please let me know.

@@ -14,4 +15,7 @@ BraveBrowserMainExtraParts::~BraveBrowserMainExtraParts() {

void BraveBrowserMainExtraParts::PreMainMessageLoopRun() {
brave::AutoImportMuon();
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM)
RegisterWidevineCdmToCdmRegistry();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename -> MaybeRegister... ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also skip ToCdmRegistry, sounds obvious :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. it's more clear 👍

@@ -7,7 +7,9 @@
#include "brave/browser/ui/content_settings/brave_autoplay_blocked_image_model.h"
#include "third_party/widevine/cdm/buildflags.h"

#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
// On Windows and MacOS, widevine is supported by component updater.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the comment is relevant here, maybe move it to the widevine.gni patch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, widevine.gni is better place for this comment.
Done.

void CdmRegistryImpl::Init() {
+ // In linux, we want to register widevine cdm to CdmRegistry when users opted
+ // in. If not, widevine is initialized by default.
+ // Instead, we tries to register it in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a native speaker, but probably it should be "on linux", "when users opt in", "if not" -> otherwise, "we tries" -> "we try"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

+ // Instead, we tries to register it in
+ // BraveBrowserMainExtraParts::PreMainMessageLoopRun() by checking opted in
+ // prefs.
+#if defined(BRAVE_CHROMIUM_BUILD) && !defined(OS_LINUX)
Copy link
Contributor

@iefremov iefremov Jan 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENABLE_WIDEVINE_CDM_COMPONENT instead of !OS_LINUX ?
nevermind

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering whether we potentially block CDMs other than Widevine on Linux?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch.
Actually, widevine alone is used for now but don't know in the future whether chrome registers more cdms.
So, fixed to exclude widevine only after getting cdms list.

@iefremov
Copy link
Contributor

Generally LGTM with small nits

@simonhong
Copy link
Member Author

Created f/u issue - brave/brave-browser#3172

@@ -1,4 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this change announced somewhere? A think, if it was then we should fix all sources at once, otherwise its better to fix the linter. @bbondy could you please clarify?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iefremov it is enforced by the linter. There was a discussion in #brave-core

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bridiver maybe we can fix the whole codebase by a single PR then? I can do one

bundle_widevine_cdm = enable_library_widevine_cdm && is_chrome_branded
+if (brave_chromium_build) {
+ # On brave linux, widevine is supported by bundle for now.
+ # TODO(simonhong): Support by component update.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind placing a follow-up issue number here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has this been vetted by @bbondy? We're specifically prohibited from bundling on other platforms

Copy link
Member Author

@simonhong simonhong Jan 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I discussed in dm and he agreed to do it as a next step. (long term)


const auto& cdms = cdm_registry->GetAllRegisteredCdms();
#if defined(OS_LINUX)
EXPECT_EQ(cdms.size(), 0U);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in gtest macroses expected argument is the first, i.e. EXPECT_EQ(0u, cdms.size());

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

};

// This test checks CdmRegistryImpl erases widevine from cdm list after fetching
// cdm list from content client. This erase only happened on linux.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This erasure happens only on Linux

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

iefremov
iefremov previously approved these changes Jan 31, 2019
Copy link
Member Author

@simonhong simonhong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All done. PTAL.
Also this one - brave/brave-browser#3037

};

// This test checks CdmRegistryImpl erases widevine from cdm list after fetching
// cdm list from content client. This erase only happened on linux.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


const auto& cdms = cdm_registry->GetAllRegisteredCdms();
#if defined(OS_LINUX)
EXPECT_EQ(cdms.size(), 0U);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

bundle_widevine_cdm = enable_library_widevine_cdm && is_chrome_branded
+if (brave_chromium_build) {
+ # On brave linux, widevine is supported by bundle for now.
+ # TODO(simonhong): Support by component update.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

iefremov
iefremov previously approved these changes Jan 31, 2019
// Let embedders register CDMs.
GetContentClient()->AddContentDecryptionModules(&cdms_, nullptr);
+#if defined(BRAVE_CHROMIUM_BUILD) && defined(OS_LINUX)
+ // On linux, we want to register widevine cdm to CdmRegistry when users opt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please do not add comments to patches. We want to minimize the size of patches

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this patch file was deleted by overriding.

#include "third_party/widevine/cdm/buildflags.h"

-#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
+#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) || BUILDFLAG(BUNDLE_WIDEVINE_CDM)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we're patching this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W/o this patch in this file, reloading doesn't enable widevine after user accepts.

+ // in. Also, we try to register it during the startup in
+ // BraveBrowserMainExtraParts::PreMainMessageLoopRun() by checking opted in
+ // prefs.
+ cdms_.erase(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we're doing this here, why wouldn't we do it in AddContentDecryptionModules where we don't have to patch anything?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems my reply was deleted after force push.
There are some places where original cmd list is needed like PreloadLibraryCdms().
So, we can't override ChromeContentClient::AddContentDecryptionModules().
Also, this patch is removed by file overriding.

content::CdmCapability capability;
const base::Version cdm_version(WIDEVINE_CDM_VERSION_STRING);

// Add the supported codecs as if they came from the component manifest.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems very fragile to me. Where did this list of codecs come from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so. I copied this logic from IsWidevineAvailable() in chrome_content_client.cc.

Copy link
Member Author

@simonhong simonhong Jan 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah,, I think this can be improved by storing excluded cdminfo in CdmRegistry in at some place and can be reused again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this log is deleted and used cached info from CdmRegistryImpl.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All duplicated code is deleted by caching CdmInfo that created by upstream.
81ca1a1

void CdmRegistryImpl::Init() {
Init_ChromiumImpl();

#if defined(OS_LINUX)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're missing what I was trying to say here. It calls GetContentClient()->AddContentDecryptionModules and we can implement AddContentDecryptionModules in BraveContentClient

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that first.
But, other places needs original list like PreloadLibraryCdms() in content_main_runner_impl.cc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand, the result would be identical because it passes a reference that can be modified

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChromeContentClient::AddContentDecryptionModules() fills cdms to passed pointer.
Caller of above method will have different instance.
Of course, client of CdmRegistry will see same list.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, many comments were deleted..
Caller of AddContentDecryptionMoudles maintains it's own cmd list.
Clients of CdmRegistry see same list because CdmRegistry::GetAllRegisteredCdms() returns list reference.

#include "brave/browser/ui/content_settings/brave_widevine_blocked_image_model.h"
#endif

void BraveGenerateContentSettingImageModels(
std::vector<std::unique_ptr<ContentSettingImageModel>>& result) {
std::vector<std::unique_ptr<ContentSettingImageModel>>* result) {
// lint complains to use pointer or const referencc for |result|.
Copy link
Collaborator

@bridiver bridiver Jan 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* is always preferable to & because it's more clear to the caller that you're passing something which might be modified when the method returns. If you're looking at a caller using ref, it's indistinguishable from passing by const ref or value unless you look at the method sig

Copy link
Member Author

@simonhong simonhong Jan 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, totally agree about that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically, this rule is fixed in CC, so we don't even need to have comments like this in the codebase :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. using like this is natural in chromium project :) Removed.

@simonhong simonhong force-pushed the widevine_support_in_linux branch 3 times, most recently from 0b8a725 to 15bcb7c Compare February 1, 2019 02:43
In linux, WidevineCdm library is bundled instead of installing it
by component updating during the runtime. In linux, we can't load
cdm library during the runtime because processes except browser
process are forked from zygote process not created from brave.exe.
So, cdm should be loaded(not initialized) into zygote process space
before zygote enters the sandbox.

Chrome browser also uses bundling for linux.
In linux, widevine should be enabled only when user accpeted.
With this, widevine cdm is initialized when users allowed to use
widevine cdm via content settings bubble.
In linux, bundled widevine cdm is loaded by default but not initialized
by default. It is only initialized when user allows it.
Then, widevine cdm is added to CdmRegistry. That means renderer should
also update supported KeySystems during the runtime.
We only want to exclude widevine. So, exclude it explicitly from list.
This test checks CdmRegistryImpl erases widevine from cdm list after
fetching cdm list from content client. This erase only happened on linux.
Instead, use cached CdmInfo from CdmRegistryImpl.
@simonhong
Copy link
Member Author

New PR (#1606) is opened for this.

@simonhong simonhong closed this Feb 7, 2019
@bsclifton bsclifton deleted the widevine_support_in_linux branch January 15, 2020 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants