Commit 15303d0c authored by Xiaohan Wang's avatar Xiaohan Wang Committed by Commit Bot

Add FrameServiceBase: base class for document bound mojo services

This CL adds FrameServiceBase which is a base class for mojo interface
implementations that is tied to the lifetime of a document.

Also updates MediaDrmStorageImpl, OutputProtectionImpl and
PlatformVerificationImpl to use the new FrameServiceBase class.

TBR=halliwell@chromium.org,mnissler@chromium.org
BUG=707335
TEST=New unit tests for FrameServiceBase

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_site_isolation
Change-Id: I7f69f70a3da24c784267ecc1d49fd681bbf1a71a
Reviewed-on: https://chromium-review.googlesource.com/678438
Commit-Queue: Xiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJohn Rummell <jrummell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#506979}
parent 05c0c8a8
......@@ -3488,7 +3488,7 @@ split_static_library("browser") {
]
}
if (enable_mojo_media) {
if (enable_library_cdms) {
sources += [
"media/output_protection_impl.cc",
"media/output_protection_impl.h",
......
......@@ -2977,21 +2977,20 @@ void ChromeContentBrowserClient::ExposeInterfacesToRenderer(
void ChromeContentBrowserClient::ExposeInterfacesToMediaService(
service_manager::BinderRegistry* registry,
content::RenderFrameHost* render_frame_host) {
// TODO(xhwang): Only register this when ENABLE_MOJO_MEDIA.
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
registry->AddInterface(
base::Bind(&OutputProtectionImpl::Create, render_frame_host));
#if defined(OS_CHROMEOS)
registry->AddInterface(
base::Bind(&chromeos::attestation::PlatformVerificationImpl::Create,
render_frame_host));
#endif // defined(OS_CHROMEOS)
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
#if BUILDFLAG(ENABLE_MOJO_MEDIA)
registry->AddInterface(
base::Bind(&OutputProtectionImpl::Create, render_frame_host));
#if BUILDFLAG(ENABLE_MOJO_CDM) && defined(OS_ANDROID)
registry->AddInterface(
base::Bind(&chrome::CreateMediaDrmStorage, render_frame_host));
#endif
#endif // BUILDFLAG(ENABLE_MOJO_MEDIA)
}
void ChromeContentBrowserClient::BindInterfaceRequestFromFrame(
......
......@@ -21,20 +21,24 @@ void PlatformVerificationImpl::Create(
content::RenderFrameHost* render_frame_host,
media::mojom::PlatformVerificationRequest request) {
DVLOG(2) << __FUNCTION__;
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(render_frame_host);
mojo::MakeStrongBinding(
base::MakeUnique<PlatformVerificationImpl>(render_frame_host),
std::move(request));
// PlatformVerificationFlow requires to run on the UI thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// The object is bound to the lifetime of |render_frame_host| and the mojo
// connection. See FrameServiceBase for details.
new PlatformVerificationImpl(render_frame_host, std::move(request));
}
PlatformVerificationImpl::PlatformVerificationImpl(
content::RenderFrameHost* render_frame_host)
: render_frame_host_(render_frame_host), weak_factory_(this) {
DCHECK(render_frame_host);
}
content::RenderFrameHost* render_frame_host,
media::mojom::PlatformVerificationRequest request)
: FrameServiceBase(render_frame_host, std::move(request)),
weak_factory_(this) {}
PlatformVerificationImpl::~PlatformVerificationImpl() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
void PlatformVerificationImpl::ChallengePlatform(
......@@ -48,8 +52,7 @@ void PlatformVerificationImpl::ChallengePlatform(
platform_verification_flow_ = new PlatformVerificationFlow();
platform_verification_flow_->ChallengePlatformKey(
content::WebContents::FromRenderFrameHost(render_frame_host_), service_id,
challenge,
web_contents(), service_id, challenge,
base::Bind(&PlatformVerificationImpl::OnPlatformChallenged,
weak_factory_.GetWeakPtr(), base::Passed(&callback)));
}
......
......@@ -10,7 +10,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/chromeos/attestation/platform_verification_flow.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/frame_service_base.h"
#include "media/mojo/interfaces/platform_verification.mojom.h"
namespace chromeos {
......@@ -19,21 +19,24 @@ namespace attestation {
// Implements media::mojom::PlatformVerification on ChromeOS using
// PlatformVerificationFlow. Can only be used on the UI thread because
// PlatformVerificationFlow lives on the UI thread.
class PlatformVerificationImpl : public media::mojom::PlatformVerification {
class PlatformVerificationImpl final
: public content::FrameServiceBase<media::mojom::PlatformVerification> {
public:
static void Create(content::RenderFrameHost* render_frame_host,
media::mojom::PlatformVerificationRequest request);
explicit PlatformVerificationImpl(
content::RenderFrameHost* render_frame_host);
~PlatformVerificationImpl() override;
PlatformVerificationImpl(content::RenderFrameHost* render_frame_host,
media::mojom::PlatformVerificationRequest request);
// mojo::InterfaceImpl<PlatformVerification> implementation.
void ChallengePlatform(const std::string& service_id,
const std::string& challenge,
ChallengePlatformCallback callback) override;
ChallengePlatformCallback callback) final;
private:
// |this| can only be destructed as a FrameServiceBase.
~PlatformVerificationImpl() final;
using Result = PlatformVerificationFlow::Result;
void OnPlatformChallenged(ChallengePlatformCallback callback,
......@@ -42,10 +45,7 @@ class PlatformVerificationImpl : public media::mojom::PlatformVerification {
const std::string& signature,
const std::string& platform_key_certificate);
content::RenderFrameHost* const render_frame_host_;
scoped_refptr<PlatformVerificationFlow> platform_verification_flow_;
base::WeakPtrFactory<PlatformVerificationImpl> weak_factory_;
};
......
......@@ -38,15 +38,14 @@ void CreateMediaDrmStorage(content::RenderFrameHost* render_frame_host,
PrefService* pref_service = profile->GetPrefs();
DCHECK(pref_service) << "PrefService not available.";
url::Origin origin = render_frame_host->GetLastCommittedOrigin();
if (origin.unique()) {
if (render_frame_host->GetLastCommittedOrigin().unique()) {
DVLOG(1) << __func__ << ": Unique origin.";
return;
}
// The object will be deleted on connection error, or when the frame navigates
// away.
new cdm::MediaDrmStorageImpl(render_frame_host, pref_service, origin,
// away. See FrameServiceBase for details.
new cdm::MediaDrmStorageImpl(render_frame_host, pref_service,
std::move(request));
}
......
......@@ -4,8 +4,9 @@
#include "chrome/browser/media/output_protection_impl.h"
#include <memory>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "chrome/browser/media/output_protection_proxy.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
......@@ -16,22 +17,24 @@ void OutputProtectionImpl::Create(
content::RenderFrameHost* render_frame_host,
media::mojom::OutputProtectionRequest request) {
DVLOG(2) << __func__;
// OutputProtectionProxy requires to run on the UI thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(render_frame_host);
mojo::MakeStrongBinding(base::MakeUnique<OutputProtectionImpl>(
render_frame_host->GetProcess()->GetID(),
render_frame_host->GetRoutingID()),
std::move(request));
}
OutputProtectionImpl::OutputProtectionImpl(int render_process_id,
int render_frame_id)
: render_process_id_(render_process_id),
render_frame_id_(render_frame_id),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// The object is bound to the lifetime of |render_frame_host| and the mojo
// connection. See FrameServiceBase for details.
new OutputProtectionImpl(render_frame_host, std::move(request));
}
OutputProtectionImpl::OutputProtectionImpl(
content::RenderFrameHost* render_frame_host,
media::mojom::OutputProtectionRequest request)
: FrameServiceBase(render_frame_host, std::move(request)),
render_process_id_(render_frame_host->GetProcess()->GetID()),
render_frame_id_(render_frame_host->GetRoutingID()),
weak_factory_(this) {}
OutputProtectionImpl::~OutputProtectionImpl() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
......@@ -63,6 +66,7 @@ void OutputProtectionImpl::OnQueryStatusResult(QueryStatusCallback callback,
DVLOG(2) << __func__ << ": success=" << success << ", link_mask=" << link_mask
<< ", protection_mask=" << protection_mask;
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::move(callback).Run(success, link_mask, protection_mask);
}
......@@ -71,13 +75,14 @@ void OutputProtectionImpl::OnEnableProtectionResult(
bool success) {
DVLOG(2) << __func__ << ": success=" << success;
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::move(callback).Run(success);
}
// Helper function to lazily create the |proxy_| and return it.
chrome::OutputProtectionProxy* OutputProtectionImpl::GetProxy() {
if (!proxy_) {
proxy_ = base::MakeUnique<chrome::OutputProtectionProxy>(render_process_id_,
proxy_ = std::make_unique<chrome::OutputProtectionProxy>(render_process_id_,
render_frame_id_);
}
......
......@@ -5,23 +5,28 @@
#ifndef CHROME_BROWSER_MEDIA_OUTPUT_PROTECTION_IMPL_H_
#define CHROME_BROWSER_MEDIA_OUTPUT_PROTECTION_IMPL_H_
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/frame_service_base.h"
#include "media/mojo/interfaces/output_protection.mojom.h"
namespace chrome {
class OutputProtectionProxy;
}
namespace content {
class RenderFrameHost;
}
// Implements media::mojom::OutputProtection to check display links and
// their statuses. On all platforms we'll check the network links. On ChromeOS
// we'll also check the hardware links. Can only be used on the UI thread.
class OutputProtectionImpl : public media::mojom::OutputProtection {
class OutputProtectionImpl final
: public content::FrameServiceBase<media::mojom::OutputProtection> {
public:
static void Create(content::RenderFrameHost* render_frame_host,
media::mojom::OutputProtectionRequest request);
OutputProtectionImpl(int render_process_id, int render_frame_id);
~OutputProtectionImpl() final;
OutputProtectionImpl(content::RenderFrameHost* render_frame_host,
media::mojom::OutputProtectionRequest request);
// media::mojom::OutputProtection implementation.
void QueryStatus(QueryStatusCallback callback) final;
......@@ -29,6 +34,9 @@ class OutputProtectionImpl : public media::mojom::OutputProtection {
EnableProtectionCallback callback) final;
private:
// |this| can only be destructed as a FrameServiceBase.
~OutputProtectionImpl() final;
// Callbacks for QueryStatus and EnableProtection results.
// Note: These are bound using weak pointers so that we won't fire |callback|
// after the binding is destroyed.
......@@ -42,6 +50,8 @@ class OutputProtectionImpl : public media::mojom::OutputProtection {
// Helper function to lazily create the |proxy_| and return it.
chrome::OutputProtectionProxy* GetProxy();
// TODO(crbug.com/770958): Remove these IDs after OutputProtection PPAPI is
// deprecated.
const int render_process_id_;
const int render_frame_id_;
......
......@@ -119,15 +119,14 @@ void CreateMediaDrmStorage(content::RenderFrameHost* render_frame_host,
PrefService* pref_service = CastBrowserProcess::GetInstance()->pref_service();
DCHECK(pref_service);
url::Origin origin = render_frame_host->GetLastCommittedOrigin();
if (origin.unique()) {
if (render_frame_host->GetLastCommittedOrigin().unique()) {
DVLOG(1) << __func__ << ": Unique origin.";
return;
}
// The object will be deleted on connection error, or when the frame navigates
// away.
new cdm::MediaDrmStorageImpl(render_frame_host, pref_service, origin,
new cdm::MediaDrmStorageImpl(render_frame_host, pref_service,
std::move(request));
}
#endif // defined(OS_ANDROID) && !BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
......
......@@ -35,6 +35,7 @@ source_set("unit_tests") {
":browser",
"//base/test:test_support",
"//components/prefs:test_support",
"//content/test:test_support",
"//media/mojo/services",
"//testing/gtest",
]
......
......@@ -5,4 +5,8 @@ include_rules = [
"+media/media_features.h",
"+media/mojo",
"+mojo/public/cpp/bindings",
# For testing
"+content/public/test",
"+content/test",
]
......@@ -363,33 +363,28 @@ std::vector<base::UnguessableToken> MediaDrmStorageImpl::ClearMatchingLicenses(
return ClearMatchingLicenseData(update.Get(), start, end, filter);
}
// MediaDrmStorageImpl
MediaDrmStorageImpl::MediaDrmStorageImpl(
content::RenderFrameHost* render_frame_host,
PrefService* pref_service,
const url::Origin& origin,
media::mojom::MediaDrmStorageRequest request)
: render_frame_host_(render_frame_host),
pref_service_(pref_service),
origin_string_(origin.Serialize()),
binding_(this, std::move(request)) {
DVLOG(1) << __func__ << ": origin = " << origin;
DCHECK(thread_checker_.CalledOnValidThread());
: FrameServiceBase(render_frame_host, std::move(request)),
pref_service_(pref_service) {
DVLOG(1) << __func__ << ": origin = " << origin();
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(pref_service_);
DCHECK(!origin_string_.empty());
// |this| owns |binding_|, so unretained is safe.
binding_.set_connection_error_handler(
base::Bind(&MediaDrmStorageImpl::Close, base::Unretained(this)));
DCHECK(!origin().unique());
}
MediaDrmStorageImpl::~MediaDrmStorageImpl() {
DVLOG(1) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void MediaDrmStorageImpl::Initialize(InitializeCallback callback) {
DVLOG(1) << __func__ << ": origin = " << origin_string_;
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << __func__ << ": origin = " << origin();
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!origin_id_);
const base::DictionaryValue* storage_dict =
......@@ -398,7 +393,7 @@ void MediaDrmStorageImpl::Initialize(InitializeCallback callback) {
const base::DictionaryValue* origin_dict = nullptr;
// The origin string may contain dots. Do not use path expansion.
bool exist = storage_dict && storage_dict->GetDictionaryWithoutPathExpansion(
origin_string_, &origin_dict);
origin().Serialize(), &origin_dict);
base::UnguessableToken origin_id;
if (exist) {
......@@ -423,7 +418,7 @@ void MediaDrmStorageImpl::Initialize(InitializeCallback callback) {
void MediaDrmStorageImpl::OnProvisioned(OnProvisionedCallback callback) {
DVLOG(1) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!IsInitialized()) {
DVLOG(1) << __func__ << ": Not initialized.";
......@@ -436,11 +431,11 @@ void MediaDrmStorageImpl::OnProvisioned(OnProvisionedCallback callback) {
DCHECK(storage_dict);
// The origin string may contain dots. Do not use path expansion.
DVLOG_IF(1, HasEntry(*storage_dict, origin_string_))
<< __func__ << ": Entry for origin " << origin_string_
DVLOG_IF(1, HasEntry(*storage_dict, origin().Serialize()))
<< __func__ << ": Entry for origin " << origin()
<< " already exists and will be cleared";
CreateOriginDictAndReturnSessionsDict(storage_dict, origin_string_,
CreateOriginDictAndReturnSessionsDict(storage_dict, origin().Serialize(),
origin_id_);
std::move(callback).Run(true);
}
......@@ -450,7 +445,7 @@ void MediaDrmStorageImpl::SavePersistentSession(
media::mojom::SessionDataPtr session_data,
SavePersistentSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!IsInitialized()) {
DVLOG(1) << __func__ << ": Not initialized.";
......@@ -463,17 +458,17 @@ void MediaDrmStorageImpl::SavePersistentSession(
DCHECK(storage_dict);
base::DictionaryValue* sessions_dict =
GetSessionsDictFromStorageDict<base::DictionaryValue>(storage_dict,
origin_string_);
GetSessionsDictFromStorageDict<base::DictionaryValue>(
storage_dict, origin().Serialize());
// This could happen if the profile is removed, but the device is still
// provisioned for the origin. In this case, just create a new entry.
// Since we're using random origin ID in MediaDrm, it's rare to enter the if
// branch. Deleting the profile causes reprovisioning of the origin.
if (!sessions_dict) {
DVLOG(1) << __func__ << ": No entry for origin " << origin_string_;
DVLOG(1) << __func__ << ": No entry for origin " << origin();
sessions_dict = CreateOriginDictAndReturnSessionsDict(
storage_dict, origin_string_, origin_id_);
storage_dict, origin().Serialize(), origin_id_);
DCHECK(sessions_dict);
}
......@@ -491,7 +486,7 @@ void MediaDrmStorageImpl::LoadPersistentSession(
const std::string& session_id,
LoadPersistentSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!IsInitialized()) {
DVLOG(1) << __func__ << ": Not initialized.";
......@@ -501,7 +496,7 @@ void MediaDrmStorageImpl::LoadPersistentSession(
const base::DictionaryValue* sessions_dict =
GetSessionsDictFromStorageDict<const base::DictionaryValue>(
pref_service_->GetDictionary(kMediaDrmStorage), origin_string_);
pref_service_->GetDictionary(kMediaDrmStorage), origin().Serialize());
if (!sessions_dict) {
std::move(callback).Run(nullptr);
return;
......@@ -511,7 +506,7 @@ void MediaDrmStorageImpl::LoadPersistentSession(
if (!sessions_dict->GetDictionaryWithoutPathExpansion(session_id,
&session_dict)) {
DVLOG(1) << __func__ << ": No session " << session_id << " for origin "
<< origin_string_;
<< origin();
std::move(callback).Run(nullptr);
return;
}
......@@ -531,7 +526,7 @@ void MediaDrmStorageImpl::RemovePersistentSession(
const std::string& session_id,
RemovePersistentSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!IsInitialized()) {
DVLOG(1) << __func__ << ": Not initialized.";
......@@ -542,8 +537,8 @@ void MediaDrmStorageImpl::RemovePersistentSession(
DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage);
base::DictionaryValue* sessions_dict =
GetSessionsDictFromStorageDict<base::DictionaryValue>(update.Get(),
origin_string_);
GetSessionsDictFromStorageDict<base::DictionaryValue>(
update.Get(), origin().Serialize());
if (!sessions_dict) {
std::move(callback).Run(true);
......@@ -554,31 +549,4 @@ void MediaDrmStorageImpl::RemovePersistentSession(
std::move(callback).Run(true);
}
void MediaDrmStorageImpl::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
DCHECK(thread_checker_.CalledOnValidThread());
if (render_frame_host == render_frame_host_) {
DVLOG(1) << __func__ << ": RenderFrame destroyed.";
Close();
}
}
void MediaDrmStorageImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
DCHECK(thread_checker_.CalledOnValidThread());
if (navigation_handle->GetRenderFrameHost() == render_frame_host_) {
DVLOG(1) << __func__ << ": Close connection on navigation.";
Close();
}
}
void MediaDrmStorageImpl::Close() {
DVLOG(1) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
delete this;
}
} // namespace cdm
......@@ -12,6 +12,7 @@
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "content/public/browser/frame_service_base.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
#include "media/mojo/interfaces/media_drm_storage.mojom.h"
......@@ -31,8 +32,8 @@ namespace cdm {
// Implements media::mojom::MediaDrmStorage using PrefService.
// This file is located under components/ so that it can be shared by multiple
// content embedders (e.g. chrome and chromecast).
class MediaDrmStorageImpl final : public media::mojom::MediaDrmStorage,
public content::WebContentsObserver {
class MediaDrmStorageImpl final
: public content::FrameServiceBase<media::mojom::MediaDrmStorage> {
public:
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
......@@ -55,9 +56,7 @@ class MediaDrmStorageImpl final : public media::mojom::MediaDrmStorage,
MediaDrmStorageImpl(content::RenderFrameHost* render_frame_host,
PrefService* pref_service,
const url::Origin& origin,
media::mojom::MediaDrmStorageRequest request);
~MediaDrmStorageImpl() final;
// media::mojom::MediaDrmStorage implementation.
void Initialize(InitializeCallback callback) final;
......@@ -70,30 +69,17 @@ class MediaDrmStorageImpl final : public media::mojom::MediaDrmStorage,
void RemovePersistentSession(const std::string& session_id,
RemovePersistentSessionCallback callback) final;
// content::WebContentsObserver implementation.
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) final;
void DidFinishNavigation(content::NavigationHandle* navigation_handle) final;
bool IsInitialized() const { return !!origin_id_; }
private:
base::ThreadChecker thread_checker_;
// Stops observing WebContents and delete |this|.
void Close();
// |this| can only be destructed as a FrameServiceBase.
~MediaDrmStorageImpl() final;
content::RenderFrameHost* const render_frame_host_ = nullptr;
PrefService* const pref_service_ = nullptr;
// String for the current origin. It will be used as a key in storage
// dictionary.
const std::string origin_string_;
// ID for the current origin. Per EME spec on individualization,
// implementation should not expose application-specific information.
base::UnguessableToken origin_id_;
mojo::Binding<media::mojom::MediaDrmStorage> binding_;
};
} // namespace cdm
......
......@@ -7,9 +7,10 @@
#include <memory>
#include "base/run_loop.h"
#include "base/test/test_message_loop.h"
#include "base/unguessable_token.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/navigation_simulator.h"
#include "content/test/test_render_frame_host.h"
#include "media/mojo/services/mojo_media_drm_storage.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
......@@ -19,14 +20,28 @@
namespace cdm {
namespace {
const char kMediaDrmStorage[] = "media.media_drm_storage";
const char kTestOrigin[] = "https://www.testorigin.com:80";
class MediaDrmStorageImplTest : public ::testing::Test {
content::RenderFrameHost* SimulateNavigation(content::RenderFrameHost* rfh,
const GURL& url) {
auto navigation_simulator =
content::NavigationSimulator::CreateRendererInitiated(url, rfh);
navigation_simulator->Commit();
return navigation_simulator->GetFinalRenderFrameHost();
}
} // namespace
class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness {
public:
MediaDrmStorageImplTest() {}
void SetUp() override {
RenderViewHostTestHarness::SetUp();
pref_service_.reset(new TestingPrefServiceSimple());
PrefRegistrySimple* registry = pref_service_->registry();
MediaDrmStorageImpl::RegisterProfilePrefs(registry);
......@@ -52,10 +67,12 @@ class MediaDrmStorageImplTest : public ::testing::Test {
auto media_drm_storage = base::MakeUnique<media::MojoMediaDrmStorage>(
std::move(media_drm_storage_ptr));
content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
content::RenderFrameHostTester::For(rfh)->InitializeRenderFrameIfNeeded();
rfh = SimulateNavigation(rfh, GURL(kTestOrigin));
// The created object will be destroyed on connection error.
new MediaDrmStorageImpl(nullptr, // Use null RenderFrameHost for testing.
pref_service_.get(), url::Origin(GURL(kTestOrigin)),
std::move(request));
new MediaDrmStorageImpl(rfh, pref_service_.get(), std::move(request));
media_drm_storage->Initialize(base::BindOnce(
[](base::UnguessableToken* out_origin_id,
......@@ -136,7 +153,6 @@ class MediaDrmStorageImplTest : public ::testing::Test {
EXPECT_EQ(expected_session_data->mime_type, session_data->mime_type);
}
base::TestMessageLoop message_loop_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<media::MediaDrmStorage> media_drm_storage_;
base::UnguessableToken origin_id_;
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/frame_service_base.h"
#include "base/run_loop.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/test/echo.mojom.h"
#include "content/test/test_render_frame_host.h"
#include "url/gurl.h"
// Unit test for FrameServiceBase in content/public/browser.
namespace content {
namespace {
const char kFooOrigin[] = "https://foo.com";
const char kBarOrigin[] = "https://bar.com";
// Subclass of FrameServiceBase for test.
class EchoImpl final : public FrameServiceBase<mojom::Echo> {
public:
EchoImpl(RenderFrameHost* render_frame_host,
mojo::InterfaceRequest<mojom::Echo> request,
base::OnceClosure destruction_cb)
: FrameServiceBase(render_frame_host, std::move(request)),
destruction_cb_(std::move(destruction_cb)) {}
~EchoImpl() final { std::move(destruction_cb_).Run(); }
// mojom::Echo implementation
void EchoString(const std::string& input, EchoStringCallback callback) final {
std::move(callback).Run(input);
}
private:
base::OnceClosure destruction_cb_;
};
// Help functions to manipulate RenderFrameHosts.
// Simulates navigation and returns the final RenderFrameHost.
RenderFrameHost* SimulateNavigation(RenderFrameHost* rfh, const GURL& url) {
auto navigation_simulator =
NavigationSimulator::CreateRendererInitiated(url, rfh);
navigation_simulator->Commit();
return navigation_simulator->GetFinalRenderFrameHost();
}
RenderFrameHost* AddChildFrame(RenderFrameHost* rfh, const GURL& url) {
RenderFrameHost* child_rfh = RenderFrameHostTester::For(rfh)->AppendChild("");
RenderFrameHostTester::For(child_rfh)->InitializeRenderFrameIfNeeded();
return SimulateNavigation(child_rfh, url);
}
void DetachFrame(RenderFrameHost* rfh) {
RenderFrameHostTester::For(rfh)->Detach();
}
} // namespace
class FrameServiceBaseTest : public RenderViewHostTestHarness {
protected:
void SetUp() final {
RenderViewHostTestHarness::SetUp();
Initialize();
}
void Initialize() {
RenderFrameHost* main_rfh = web_contents()->GetMainFrame();
RenderFrameHostTester::For(main_rfh)->InitializeRenderFrameIfNeeded();
main_rfh_ = SimulateNavigation(main_rfh, GURL(kFooOrigin));
}
void CreateEchoImpl(RenderFrameHost* rfh) {
DCHECK(!is_echo_impl_alive_);
new EchoImpl(rfh, mojo::MakeRequest(&echo_ptr_),
base::Bind(&FrameServiceBaseTest::OnEchoImplDestructed,
base::Unretained(this)));
is_echo_impl_alive_ = true;
}
void OnEchoImplDestructed() {
DCHECK(is_echo_impl_alive_);
is_echo_impl_alive_ = false;
}
void ResetConnection() {
echo_ptr_.reset();
base::RunLoop().RunUntilIdle();
}
RenderFrameHost* main_rfh_ = nullptr;
mojom::EchoPtr echo_ptr_;
bool is_echo_impl_alive_ = false;
};
TEST_F(FrameServiceBaseTest, ConnectionError) {
CreateEchoImpl(main_rfh_);
ResetConnection();
EXPECT_FALSE(is_echo_impl_alive_);
}
TEST_F(FrameServiceBaseTest, RenderFrameDeleted) {
// Needs to create a child frame so we can delete it using DetachFrame()
// because it is not allowed to detach the main frame.
RenderFrameHost* child_rfh = AddChildFrame(main_rfh_, GURL(kBarOrigin));
CreateEchoImpl(child_rfh);
DetachFrame(child_rfh);
EXPECT_FALSE(is_echo_impl_alive_);
}
TEST_F(FrameServiceBaseTest, DidFinishNavigation) {
CreateEchoImpl(main_rfh_);
SimulateNavigation(main_rfh_, GURL(kBarOrigin));
EXPECT_FALSE(is_echo_impl_alive_);
}
TEST_F(FrameServiceBaseTest, SameDocumentNavigation) {
CreateEchoImpl(main_rfh_);
// Must use the same origin to simulate same document navigation.
auto navigation_simulator =
NavigationSimulator::CreateRendererInitiated(GURL(kFooOrigin), main_rfh_);
navigation_simulator->CommitSameDocument();
DCHECK_EQ(main_rfh_, navigation_simulator->GetFinalRenderFrameHost());
EXPECT_TRUE(is_echo_impl_alive_);
}
TEST_F(FrameServiceBaseTest, FailedNavigation) {
CreateEchoImpl(main_rfh_);
auto navigation_simulator =
NavigationSimulator::CreateRendererInitiated(GURL(kFooOrigin), main_rfh_);
navigation_simulator->Fail(net::ERR_TIMED_OUT);
navigation_simulator->CommitErrorPage();
EXPECT_FALSE(is_echo_impl_alive_);
}
TEST_F(FrameServiceBaseTest, DeleteContents) {
CreateEchoImpl(main_rfh_);
DeleteContents();
EXPECT_FALSE(is_echo_impl_alive_);
}
} // namespace content
......@@ -123,6 +123,7 @@ source_set("browser_sources") {
"favicon_status.h",
"focused_node_details.h",
"font_list_async.h",
"frame_service_base.h",
"global_request_id.h",
"gpu_data_manager.h",
"gpu_data_manager_observer.h",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_FRAME_SERVICE_BASE_H_
#define CONTENT_PUBLIC_BROWSER_FRAME_SERVICE_BASE_H_
#include "base/logging.h"
#include "base/threading/thread_checker.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "url/origin.h"
namespace content {
class NavigationHandle;
class RenderFrameHost;
// Base class for mojo interface implementations tied to a document's lifetime.
// The service will be destroyed when any of the following happens:
// 1. mojo interface connection error happened,
// 2. the RenderFrameHost was deleted, or
// 3. navigation was committed on the RenderFrameHost (not same document).
//
// WARNING: To avoid race conditions, subclasses MUST only get the origin via
// origin() instead of from |render_frame_host| passed in the constructor.
// See https://crbug.com/769189 for an example of such a race.
template <typename Interface>
class FrameServiceBase : public Interface, public WebContentsObserver {
public:
FrameServiceBase(RenderFrameHost* render_frame_host,
mojo::InterfaceRequest<Interface> request)
: WebContentsObserver(
WebContents::FromRenderFrameHost(render_frame_host)),
render_frame_host_(render_frame_host),
origin_(render_frame_host_->GetLastCommittedOrigin()),
binding_(this, std::move(request)) {
// |this| owns |binding_|, so unretained is safe.
binding_.set_connection_error_handler(
base::Bind(&FrameServiceBase::Close, base::Unretained(this)));
}
protected:
// Make the destructor private since |this| can only be deleted by Close().
virtual ~FrameServiceBase() = default;
// All subclasses should use this function to obtain the origin instead of
// trying to get it from the RenderFrameHost pointer directly.
const url::Origin& origin() const { return origin_; }
// Subclasses can use this to check thread safety.
// For example: DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
THREAD_CHECKER(thread_checker_);
private:
// WebContentsObserver implementation.
void RenderFrameDeleted(RenderFrameHost* render_frame_host) final {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (render_frame_host == render_frame_host_) {
DVLOG(1) << __func__ << ": RenderFrame destroyed.";
Close();
}
}
void DidFinishNavigation(NavigationHandle* navigation_handle) final {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
if (navigation_handle->GetRenderFrameHost() == render_frame_host_) {
DVLOG(1) << __func__ << ": Close connection on navigation.";
Close();
}
}
// Stops observing WebContents and delete |this|.
void Close() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DVLOG(1) << __func__;
delete this;
}
RenderFrameHost* const render_frame_host_ = nullptr;
const url::Origin origin_;
mojo::Binding<Interface> binding_;
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_FRAME_SERVICE_BASE_H_
......@@ -531,6 +531,13 @@ mojom("mojo_layouttest_bindings") {
]
}
mojom("test_interfaces") {
testonly = true
sources = [
"echo.mojom",
]
}
static_library("layouttest_support") {
testonly = true
......@@ -1192,6 +1199,7 @@ test("content_unittests") {
"../browser/fileapi/fileapi_message_filter_unittest.cc",
"../browser/fileapi/upload_file_system_file_element_reader_unittest.cc",
"../browser/frame_host/ancestor_throttle_unittest.cc",
"../browser/frame_host/frame_service_base_unittest.cc",
"../browser/frame_host/frame_tree_node_blame_context_unittest.cc",
"../browser/frame_host/frame_tree_unittest.cc",
"../browser/frame_host/mixed_content_navigation_throttle_unittest.cc",
......@@ -1571,6 +1579,7 @@ test("content_unittests") {
deps = [
":content_test_mojo_bindings",
":content_unittests_catalog_source",
":test_interfaces",
":test_support",
"//base/test:test_support",
"//base/third_party/dynamic_annotations",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module content.mojom;
// Echos its input.
interface Echo {
// Echos the passed-in string.
EchoString(string input) => (string echoed_input);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment