| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom-features.h" |
| #include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom-forward.h" |
| #include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom-shared.h" |
| #include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom.h" |
| |
| #include <utility> |
| |
| #include "base/dcheck_is_on.h" |
| #include "base/feature_list.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "mojo/public/cpp/bindings/associated_receiver.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h" |
| #include "mojo/public/cpp/bindings/generic_pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_associated_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_associated_remote.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/receiver_set.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/bindings/remote_set.h" |
| #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" |
| #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "mojo/public/cpp/bindings/service_factory.h" |
| #include "mojo/public/cpp/bindings/shared_associated_remote.h" |
| #include "mojo/public/cpp/bindings/shared_remote.h" |
| #include "mojo/public/cpp/bindings/tests/bindings_test_base.h" |
| #include "mojo/public/cpp/system/functions.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace mojo::test::feature_unittest { |
| |
| namespace { |
| |
| //// Mojom impls //// |
| |
| class DefaultDeniedImpl : public mojom::DefaultDenied { |
| public: |
| explicit DefaultDeniedImpl( |
| mojo::PendingReceiver<mojom::DefaultDenied> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| DefaultDeniedImpl() : receiver_(this) {} |
| ~DefaultDeniedImpl() override = default; |
| |
| DefaultDeniedImpl(const DefaultDeniedImpl&) = delete; |
| DefaultDeniedImpl& operator=(const DefaultDeniedImpl&) = delete; |
| |
| // mojom::DefaultDenied overrides. |
| void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); } |
| |
| mojo::Receiver<mojom::DefaultDenied>& receiver() { return receiver_; } |
| |
| // Helper for ServiceFactory test. |
| static auto RunService(PendingReceiver<mojom::DefaultDenied> receiver) { |
| return std::make_unique<DefaultDeniedImpl>(std::move(receiver)); |
| } |
| |
| private: |
| mojo::Receiver<mojom::DefaultDenied> receiver_; |
| }; |
| |
| class OtherDeniedImpl : public mojom::OtherDenied { |
| public: |
| explicit OtherDeniedImpl(mojo::PendingReceiver<mojom::OtherDenied> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| OtherDeniedImpl() : receiver_(this) {} |
| ~OtherDeniedImpl() override = default; |
| |
| OtherDeniedImpl(const OtherDeniedImpl&) = delete; |
| OtherDeniedImpl& operator=(const OtherDeniedImpl&) = delete; |
| |
| // mojom::OtherDenied overrides. |
| void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); } |
| |
| mojo::Receiver<mojom::OtherDenied>& receiver() { return receiver_; } |
| |
| private: |
| mojo::Receiver<mojom::OtherDenied> receiver_; |
| }; |
| |
| class OtherAllowedImpl : public mojom::OtherAllowed { |
| public: |
| explicit OtherAllowedImpl(mojo::PendingReceiver<mojom::OtherAllowed> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| OtherAllowedImpl() : receiver_(this) {} |
| ~OtherAllowedImpl() override = default; |
| |
| OtherAllowedImpl(const OtherAllowedImpl&) = delete; |
| OtherAllowedImpl& operator=(const OtherAllowedImpl&) = delete; |
| |
| // mojom::OtherAllowed overrides. |
| void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); } |
| |
| mojo::Receiver<mojom::OtherAllowed>& receiver() { return receiver_; } |
| |
| private: |
| mojo::Receiver<mojom::OtherAllowed> receiver_; |
| }; |
| |
| class DefaultDeniedSelfOwnedImpl : public mojom::DefaultDenied { |
| public: |
| DefaultDeniedSelfOwnedImpl() = default; |
| ~DefaultDeniedSelfOwnedImpl() override = default; |
| |
| DefaultDeniedSelfOwnedImpl(const DefaultDeniedSelfOwnedImpl&) = delete; |
| DefaultDeniedSelfOwnedImpl& operator=(const DefaultDeniedSelfOwnedImpl&) = |
| delete; |
| |
| // mojom::DefaultDenied overrides. |
| void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); } |
| }; |
| |
| class DefaultAllowedSelfOwnedImpl : public mojom::DefaultAllowed { |
| public: |
| DefaultAllowedSelfOwnedImpl() = default; |
| ~DefaultAllowedSelfOwnedImpl() override = default; |
| |
| DefaultAllowedSelfOwnedImpl(const DefaultAllowedSelfOwnedImpl&) = delete; |
| DefaultAllowedSelfOwnedImpl& operator=(const DefaultAllowedSelfOwnedImpl&) = |
| delete; |
| |
| // mojom::DefaultAllowed overrides. |
| void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); } |
| }; |
| |
| class DefaultAllowedImpl : mojom::DefaultAllowed { |
| public: |
| explicit DefaultAllowedImpl( |
| mojo::PendingReceiver<mojom::DefaultAllowed> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| ~DefaultAllowedImpl() override = default; |
| |
| DefaultAllowedImpl(const DefaultAllowedImpl&) = delete; |
| DefaultAllowedImpl& operator=(const DefaultAllowedImpl&) = delete; |
| |
| // mojom::DefaultAllowed overrides. |
| void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); } |
| |
| mojo::Receiver<mojom::DefaultAllowed>& receiver() { return receiver_; } |
| |
| // Helper for ServiceFactory test. |
| static auto RunService(PendingReceiver<mojom::DefaultAllowed> receiver) { |
| return std::make_unique<DefaultAllowedImpl>(std::move(receiver)); |
| } |
| |
| private: |
| mojo::Receiver<mojom::DefaultAllowed> receiver_; |
| }; |
| |
| class FeaturesOnMethodsImpl : public mojom::FeaturesOnMethods { |
| public: |
| explicit FeaturesOnMethodsImpl( |
| mojo::PendingReceiver<mojom::FeaturesOnMethods> receiver) |
| : receiver_(this, std::move(receiver)), assoc_receiver_(nullptr) {} |
| explicit FeaturesOnMethodsImpl( |
| mojo::PendingAssociatedReceiver<mojom::FeaturesOnMethods> receiver) |
| : receiver_(nullptr), assoc_receiver_(this, std::move(receiver)) {} |
| |
| ~FeaturesOnMethodsImpl() override = default; |
| |
| FeaturesOnMethodsImpl(const FeaturesOnMethodsImpl&) = delete; |
| FeaturesOnMethodsImpl& operator=(const FeaturesOnMethodsImpl&) = delete; |
| |
| // mojom::FeaturesOnMethods overrides. |
| void DefaultDenied(DefaultDeniedCallback callback) override { |
| std::move(callback).Run(1); |
| } |
| void DefaultDeniedSync(DefaultDeniedCallback callback) override { |
| std::move(callback).Run(1); |
| } |
| void DefaultAllowed(DefaultAllowedCallback callback) override { |
| std::move(callback).Run(1); |
| } |
| void Normal(NormalCallback callback) override { std::move(callback).Run(1); } |
| |
| private: |
| mojo::Receiver<mojom::FeaturesOnMethods> receiver_; |
| mojo::AssociatedReceiver<mojom::FeaturesOnMethods> assoc_receiver_; |
| }; |
| |
| class PassesInterfacesImpl : public mojom::PassesInterfaces { |
| public: |
| explicit PassesInterfacesImpl( |
| mojo::PendingReceiver<mojom::PassesInterfaces> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| ~PassesInterfacesImpl() override = default; |
| |
| PassesInterfacesImpl(const PassesInterfacesImpl&) = delete; |
| PassesInterfacesImpl& operator=(const PassesInterfacesImpl&) = delete; |
| |
| // Callback to service responses from a mojom::DefaultAllowed remote. |
| void OnGetInt(PassPendingOptionalRemoteAllowedCallback callback, int val) { |
| std::move(callback).Run(val == 1); |
| } |
| |
| // mojom::PassesInterfaces overrides. |
| void PassPendingOptionalRemoteAllowed( |
| mojo::PendingRemote<mojom::DefaultAllowed> iface, |
| PassPendingOptionalRemoteAllowedCallback callback) override { |
| allowed_remote_.Bind(std::move(iface)); |
| ASSERT_TRUE(allowed_remote_); |
| allowed_remote_->GetInt(base::BindOnce(&PassesInterfacesImpl::OnGetInt, |
| base::Unretained(this), |
| std::move(callback))); |
| } |
| |
| void PassPendingOptionalRemoteDisabled( |
| mojo::PendingRemote<mojom::DefaultDenied> iface, |
| PassPendingOptionalRemoteDisabledCallback callback) override { |
| // As disabled interfaces are serialized as if an unbound pipe was |
| // passed this method is called but it does not receive a pipe to bind. |
| ASSERT_FALSE(iface.is_valid()); |
| std::move(callback).Run(true); |
| } |
| |
| void PassPendingOptionalReceiverAllowed( |
| mojo::PendingReceiver<mojom::DefaultAllowed> iface) override { |
| allowed_impl_ = std::make_unique<DefaultAllowedImpl>(std::move(iface)); |
| ASSERT_TRUE(allowed_impl_->receiver().is_bound()); |
| } |
| |
| void PassPendingOptionalReceiverDisabled( |
| mojo::PendingReceiver<mojom::DefaultDenied> iface, |
| PassPendingOptionalReceiverDisabledCallback callback) override { |
| denied_impl_ = std::make_unique<DefaultDeniedImpl>(std::move(iface)); |
| ASSERT_FALSE(denied_impl_->receiver().is_bound()); |
| std::move(callback).Run(true); |
| } |
| |
| private: |
| mojo::Receiver<mojom::PassesInterfaces> receiver_; |
| |
| mojo::Remote<mojom::DefaultAllowed> allowed_remote_; |
| mojo::Remote<mojom::DefaultDenied> denied_remote_; |
| std::unique_ptr<DefaultAllowedImpl> allowed_impl_; |
| std::unique_ptr<DefaultDeniedImpl> denied_impl_; |
| }; |
| |
| } // namespace |
| |
| //// Test runners //// |
| |
| class FeatureBindingsTest : public BindingsTestBase { |
| public: |
| void SetUp() override { |
| mojo::SetDefaultProcessErrorHandler(base::BindRepeating( |
| &FeatureBindingsTest::OnProcessError, base::Unretained(this))); |
| } |
| |
| void TearDown() override { |
| mojo::SetDefaultProcessErrorHandler(base::NullCallback()); |
| } |
| |
| void SetErrorHandler(base::OnceClosure handler) { |
| error_handler_ = std::move(handler); |
| } |
| |
| private: |
| void OnProcessError(const std::string& error) { |
| if (error_handler_) { |
| std::move(error_handler_).Run(); |
| } |
| } |
| |
| base::OnceClosure error_handler_; |
| }; |
| |
| //// Tests ///// |
| |
| TEST(FeatureTest, FeatureBasics) { |
| EXPECT_TRUE(base::FeatureList::IsEnabled( |
| mojo::test::feature_unittest::mojom::TestFeatureOn)); |
| EXPECT_FALSE(base::FeatureList::IsEnabled( |
| mojo::test::feature_unittest::mojom::TestFeatureOff)); |
| } |
| |
| TEST(FeatureTest, ScopedFeatures) { |
| base::test::ScopedFeatureList feature_list1; |
| // --enable-features=TestFeatureOff --disable-features=TestFeatureOn. |
| feature_list1.InitFromCommandLine("TestFeatureOff", "TestFeatureOn"); |
| |
| // Check state is affected by the command line. |
| EXPECT_FALSE(base::FeatureList::IsEnabled( |
| mojo::test::feature_unittest::mojom::TestFeatureOn)); |
| EXPECT_TRUE(base::FeatureList::IsEnabled( |
| mojo::test::feature_unittest::mojom::TestFeatureOff)); |
| } |
| |
| //// Tests that enabled features allow normal behavior. //// |
| |
| TEST_P(FeatureBindingsTest, DefaultAllowed) { |
| // Validate that allowed interfaces on `DefaultAllowed` can be called. |
| bool error = false; |
| SetErrorHandler(base::BindLambdaForTesting([&] { error = true; })); |
| |
| Remote<mojom::DefaultAllowed> remote; |
| DefaultAllowedImpl impl(remote.BindNewPipeAndPassReceiver()); |
| EXPECT_TRUE(remote); |
| |
| base::RunLoop run_loop; |
| remote->GetInt(base::BindLambdaForTesting([&](int) { run_loop.Quit(); })); |
| run_loop.Run(); |
| EXPECT_FALSE(error); |
| } |
| |
| TEST_P(FeatureBindingsTest, OtherAllowed) { |
| // Validate that allowed interfaces on `OtherAllowed` can be called. This |
| // interface imports its features from another file. |
| bool error = false; |
| SetErrorHandler(base::BindLambdaForTesting([&] { error = true; })); |
| |
| Remote<mojom::OtherAllowed> remote; |
| OtherAllowedImpl impl(remote.BindNewPipeAndPassReceiver()); |
| EXPECT_TRUE(remote); |
| |
| base::RunLoop run_loop; |
| remote->GetInt(base::BindLambdaForTesting([&](int) { run_loop.Quit(); })); |
| run_loop.Run(); |
| EXPECT_FALSE(error); |
| } |
| |
| TEST_P(FeatureBindingsTest, FeaturesOnMethodsAllowed) { |
| // Validate that allowed interfaces on `FeaturesOnMethods` can be called. |
| bool error = false; |
| SetErrorHandler(base::BindLambdaForTesting([&] { error = true; })); |
| |
| Remote<mojom::FeaturesOnMethods> remote; |
| EXPECT_FALSE(remote); |
| FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver()); |
| EXPECT_TRUE(remote); |
| |
| base::RunLoop run_loop; |
| remote->DefaultAllowed( |
| base::BindLambdaForTesting([&](int) { run_loop.Quit(); })); |
| run_loop.Run(); |
| |
| base::RunLoop run_loop2; |
| remote->Normal(base::BindLambdaForTesting([&](int) { run_loop2.Quit(); })); |
| run_loop2.Run(); |
| EXPECT_FALSE(error); |
| } |
| |
| TEST_P(FeatureBindingsTest, FeaturesOnReceiverDenied) { |
| if (GetParam() == mojo::BindingsTestSerializationMode::kNeverSerialize) { |
| return; |
| } |
| // Validate that disabled methods on `FeaturesOnMethods` cannot be called. |
| bool called = false; |
| bool called_disconnect = false; |
| base::RunLoop run_loop; |
| |
| Remote<mojom::FeaturesOnMethods> remote; |
| auto pending_receiver = remote.BindNewPipeAndPassReceiver(); |
| |
| remote.set_disconnect_handler(base::BindLambdaForTesting([&] { |
| called_disconnect = true; |
| run_loop.Quit(); |
| })); |
| |
| { |
| // Queue to the pending_receiver with the feature enabled. |
| // --enable-features=TestFeatureOff. |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitFromCommandLine("TestFeatureOff", ""); |
| remote->DefaultDenied( |
| base::BindLambdaForTesting([&](int) { called = true; })); |
| } |
| // Receiver can now process messages and TestFeatureOff is now disabled. |
| FeaturesOnMethodsImpl impl(std::move(pending_receiver)); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(called); |
| EXPECT_TRUE(called_disconnect); |
| } |
| |
| TEST_P(FeatureBindingsTest, PassesInterfacesAllowed) { |
| // Validate that feature-protected interfaces can be passed over mojo methods. |
| bool error = false; |
| SetErrorHandler(base::BindLambdaForTesting([&] { error = true; })); |
| |
| Remote<mojom::PassesInterfaces> remote; |
| EXPECT_FALSE(remote); |
| PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver()); |
| EXPECT_TRUE(remote); |
| |
| { |
| // Can call methods via a passed allowed pending remote. |
| base::RunLoop run_loop; |
| MessagePipe pipe; |
| PendingRemote<mojom::DefaultAllowed> allowed_pending_remote( |
| std::move(pipe.handle0), 0); |
| PendingReceiver<mojom::DefaultAllowed> allowed_pending_receiver( |
| std::move(pipe.handle1)); |
| bool cb_val = false; |
| remote->PassPendingOptionalRemoteAllowed( |
| std::move(allowed_pending_remote), |
| base::BindLambdaForTesting([&](bool val) { |
| cb_val = val; |
| run_loop.Quit(); |
| })); |
| DefaultAllowedImpl allowed_impl(std::move(allowed_pending_receiver)); |
| run_loop.Run(); |
| ASSERT_TRUE(cb_val); |
| } |
| |
| { |
| // Can call methods via a passed allowed pending receiver. |
| base::RunLoop run_loop; |
| MessagePipe pipe; |
| PendingRemote<mojom::DefaultAllowed> allowed_pending_remote( |
| std::move(pipe.handle0), 0); |
| PendingReceiver<mojom::DefaultAllowed> allowed_pending_receiver( |
| std::move(pipe.handle1)); |
| bool cb_val = false; |
| remote->PassPendingOptionalReceiverAllowed( |
| std::move(allowed_pending_receiver)); |
| Remote<mojom::DefaultAllowed> allowed(std::move(allowed_pending_remote)); |
| allowed->GetInt(base::BindLambdaForTesting([&](int val) { |
| cb_val = val == 1; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| ASSERT_TRUE(cb_val); |
| } |
| } |
| |
| TEST_P(FeatureBindingsTest, SetsAllowed) { |
| // Features intervene at the set.Add() stage so validate that these work. |
| Remote<mojom::DefaultAllowed> remote_a; |
| Remote<mojom::DefaultAllowed> remote_b; |
| DefaultAllowedImpl impl_a(remote_a.BindNewPipeAndPassReceiver()); |
| DefaultAllowedImpl impl_b(remote_b.BindNewPipeAndPassReceiver()); |
| |
| PendingRemote<mojom::DefaultAllowed> pending_remote_c; |
| auto pending_receiver_c = pending_remote_c.InitWithNewPipeAndPassReceiver(); |
| PendingRemote<mojom::DefaultAllowed> pending_remote_d; |
| auto pending_receiver_d = pending_remote_d.InitWithNewPipeAndPassReceiver(); |
| auto impl_c = std::make_unique<DefaultAllowedSelfOwnedImpl>(); |
| auto impl_d = std::make_unique<DefaultAllowedSelfOwnedImpl>(); |
| |
| ReceiverSet<mojom::DefaultAllowed> receiver_set; |
| // Every .Add() variant routes to AddImpl in an internal class. |
| receiver_set.Add(impl_c.get(), std::move(pending_receiver_c)); |
| receiver_set.Add(impl_d.get(), std::move(pending_receiver_d)); |
| |
| RemoteSet<mojom::DefaultAllowed> remote_set; |
| remote_set.Add(std::move(remote_a)); |
| remote_set.Add(std::move(remote_b)); |
| remote_set.Add(std::move(pending_remote_c)); |
| remote_set.Add(std::move(pending_remote_d)); |
| } |
| |
| TEST_P(FeatureBindingsTest, ServiceFactory) { |
| // Service factory quietly skips Adding disabled helpers. |
| ServiceFactory services; |
| MessagePipe pipe_allowed; |
| MessagePipe pipe_denied; |
| |
| services.Add(DefaultAllowedImpl::RunService); |
| services.Add(DefaultDeniedImpl::RunService); |
| |
| GenericPendingReceiver gpr_allowed(mojom::DefaultAllowed::Name_, |
| std::move(pipe_allowed.handle0)); |
| GenericPendingReceiver gpr_denied(mojom::DefaultDenied::Name_, |
| std::move(pipe_denied.handle0)); |
| |
| EXPECT_TRUE(services.CanRunService(gpr_allowed)); |
| EXPECT_FALSE(services.CanRunService(gpr_denied)); |
| } |
| |
| //// |
| // Test that disabled features prevent binding or sending messages. |
| // |
| // These tests DCHECK pretty early (in the same helper that inspects feature |
| // state) so we cannot meaningfully run them in DCHECK builds. |
| //// |
| #if !DCHECK_IS_ON() |
| |
| //// mojo::(Associated)?Remote<> //// |
| |
| TEST_P(FeatureBindingsTest, InertPendingRemoteWontInit) { |
| // Inert pipes still cannot be initialized. |
| PendingRemote<mojom::DefaultDenied> pending_remote; |
| EXPECT_FALSE(pending_remote); |
| PendingReceiver<mojom::DefaultDenied> pending_receiver = |
| pending_remote.InitWithNewPipeAndPassReceiver(); |
| EXPECT_FALSE(pending_receiver); |
| EXPECT_FALSE(pending_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, InertPendingReceiverWontInit) { |
| PendingReceiver<mojom::DefaultDenied> pending_receiver; |
| EXPECT_FALSE(pending_receiver); |
| PendingRemote<mojom::DefaultDenied> pending_remote = |
| pending_receiver.InitWithNewPipeAndPassRemote(); |
| EXPECT_FALSE(pending_receiver); |
| EXPECT_FALSE(pending_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, InertPendingAssociatedRemoteWontInit) { |
| // Inert pipes still cannot be initialized. |
| PendingAssociatedRemote<mojom::DefaultDenied> pending_remote; |
| EXPECT_FALSE(pending_remote); |
| PendingAssociatedReceiver<mojom::DefaultDenied> pending_receiver = |
| pending_remote.InitWithNewEndpointAndPassReceiver(); |
| EXPECT_FALSE(pending_receiver); |
| EXPECT_FALSE(pending_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, InertPendingAssociatedReceiverWontInit) { |
| PendingAssociatedReceiver<mojom::DefaultDenied> pending_receiver; |
| EXPECT_FALSE(pending_receiver); |
| PendingAssociatedRemote<mojom::DefaultDenied> pending_remote = |
| pending_receiver.InitWithNewEndpointAndPassRemote(); |
| EXPECT_FALSE(pending_receiver); |
| EXPECT_FALSE(pending_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, RemoteWontBindNewPipe) { |
| Remote<mojom::DefaultDenied> remote; |
| EXPECT_FALSE(remote); |
| PendingReceiver<mojom::DefaultDenied> pending_receiver = |
| remote.BindNewPipeAndPassReceiver(); |
| EXPECT_FALSE(pending_receiver); |
| } |
| |
| TEST_P(FeatureBindingsTest, RemoteWontBindPendingRemote) { |
| MessagePipe pipe; |
| PendingRemote<mojom::DefaultDenied> pending_remote(std::move(pipe.handle0), |
| 0); |
| Remote<mojom::DefaultDenied> remote; |
| EXPECT_FALSE(remote); |
| remote.Bind(std::move(pending_remote)); |
| EXPECT_FALSE(remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, AssociatedRemoteWontBind) { |
| PendingAssociatedRemote<mojom::DefaultDenied> pa_remote; |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver; |
| { |
| base::test::ScopedFeatureList feature_list1; |
| // --enable-features=TestFeatureOff. |
| feature_list1.InitFromCommandLine("TestFeatureOff", ""); |
| pa_receiver = pa_remote.InitWithNewEndpointAndPassReceiver(); |
| } |
| EXPECT_TRUE(pa_remote); |
| AssociatedRemote<mojom::DefaultDenied> a_remote; |
| a_remote.Bind(std::move(pa_remote)); |
| EXPECT_FALSE(a_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, AssociatedRemoteWontBindNewEndpoint) { |
| AssociatedRemote<mojom::DefaultDenied> a_remote; |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver = |
| a_remote.BindNewEndpointAndPassReceiver(); |
| EXPECT_FALSE(a_remote); |
| EXPECT_FALSE(pa_receiver); |
| } |
| |
| TEST_P(FeatureBindingsTest, AssociatedRemoteWontBindDedicated) { |
| AssociatedRemote<mojom::DefaultDenied> a_remote; |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver = |
| a_remote.BindNewEndpointAndPassDedicatedReceiver(); |
| EXPECT_FALSE(a_remote); |
| EXPECT_FALSE(pa_receiver); |
| } |
| |
| //// mojo::(Associated)?Receiver<> //// |
| |
| TEST_P(FeatureBindingsTest, ReceiverWontBindNewPipe) { |
| DefaultDeniedImpl impl; |
| PendingRemote<mojom::DefaultDenied> pending_remote = |
| impl.receiver().BindNewPipeAndPassRemote(); |
| EXPECT_FALSE(pending_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, ReceiverWontBindPendingReceiver) { |
| MessagePipe pipe; |
| PendingReceiver<mojom::DefaultDenied> pending_receiver( |
| std::move(pipe.handle0)); |
| EXPECT_TRUE(pending_receiver); |
| DefaultDeniedImpl impl(std::move(pending_receiver)); |
| EXPECT_FALSE(impl.receiver().is_bound()); |
| } |
| |
| TEST_P(FeatureBindingsTest, AssociatedReceiverWontBind) { |
| PendingAssociatedRemote<mojom::DefaultDenied> pa_remote; |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver = |
| pa_remote.InitWithNewEndpointAndPassReceiver(); |
| DefaultDeniedSelfOwnedImpl impl; |
| AssociatedReceiver<mojom::DefaultDenied> a_receiver(&impl); |
| a_receiver.Bind(std::move(pa_receiver), nullptr); |
| EXPECT_FALSE(a_receiver.is_bound()); |
| } |
| |
| TEST_P(FeatureBindingsTest, AssociatedReceiverWontBindNewEndpoint) { |
| DefaultDeniedSelfOwnedImpl impl; |
| AssociatedReceiver<mojom::DefaultDenied> a_receiver(&impl); |
| auto a_remote = a_receiver.BindNewEndpointAndPassRemote(nullptr); |
| EXPECT_FALSE(a_receiver); |
| EXPECT_FALSE(a_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, AssociatedReceiverWontBindDedicated) { |
| DefaultDeniedSelfOwnedImpl impl; |
| AssociatedReceiver<mojom::DefaultDenied> a_receiver(&impl); |
| auto a_remote = a_receiver.BindNewEndpointAndPassDedicatedRemote(); |
| EXPECT_FALSE(a_receiver); |
| EXPECT_FALSE(a_remote); |
| } |
| |
| //// |
| // mojo::Selfowned(Associated)?Receiver bail out without binding the underlying |
| // receivers. |
| //// |
| |
| TEST_P(FeatureBindingsTest, SelfOwnedReceiver) { |
| MessagePipe pipe; |
| PendingReceiver<mojom::DefaultDenied> pending_receiver( |
| std::move(pipe.handle0)); |
| auto impl = std::make_unique<DefaultDeniedSelfOwnedImpl>(); |
| auto weak_ref = |
| MakeSelfOwnedReceiver(std::move(impl), std::move(pending_receiver)); |
| EXPECT_FALSE(weak_ref); |
| } |
| |
| TEST_P(FeatureBindingsTest, SelfOwnedAssociatedReceiver) { |
| PendingAssociatedRemote<mojom::DefaultDenied> pa_remote; |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver = |
| pa_remote.InitWithNewEndpointAndPassReceiver(); |
| auto impl = std::make_unique<DefaultDeniedSelfOwnedImpl>(); |
| auto weak_ref = |
| MakeSelfOwnedAssociatedReceiver(std::move(impl), std::move(pa_receiver)); |
| EXPECT_FALSE(weak_ref); |
| } |
| |
| //// |
| // mojo::Shared* bail out appropriately. |
| //// |
| TEST_P(FeatureBindingsTest, SharedRemoteWontBindNewPipe) { |
| SharedRemote<mojom::DefaultDenied> s_remote; |
| EXPECT_FALSE(s_remote); |
| PendingReceiver<mojom::DefaultDenied> p_receiver = |
| s_remote.BindNewPipeAndPassReceiver(); |
| EXPECT_FALSE(s_remote); |
| EXPECT_FALSE(p_receiver); |
| } |
| |
| TEST_P(FeatureBindingsTest, SharedRemoteWontBind) { |
| PendingRemote<mojom::DefaultDenied> pa_remote; |
| PendingReceiver<mojom::DefaultDenied> pa_receiver; |
| { |
| base::test::ScopedFeatureList feature_list1; |
| // --enable-features=TestFeatureOff. |
| feature_list1.InitFromCommandLine("TestFeatureOff", ""); |
| pa_receiver = pa_remote.InitWithNewPipeAndPassReceiver(); |
| } |
| EXPECT_TRUE(pa_remote); |
| SharedRemote<mojom::DefaultDenied> s_remote; |
| s_remote.Bind(std::move(pa_remote), nullptr); |
| EXPECT_FALSE(s_remote); |
| } |
| |
| TEST_P(FeatureBindingsTest, SharedAssociatedRemoteWontBindNewEndpoint) { |
| SharedAssociatedRemote<mojom::DefaultDenied> sa_remote; |
| EXPECT_FALSE(sa_remote); |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver = |
| sa_remote.BindNewEndpointAndPassReceiver(); |
| EXPECT_FALSE(sa_remote); |
| EXPECT_FALSE(pa_receiver); |
| } |
| |
| TEST_P(FeatureBindingsTest, SharedAssociatedRemoteWontBind) { |
| PendingAssociatedRemote<mojom::DefaultDenied> pa_remote; |
| PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver; |
| { |
| base::test::ScopedFeatureList feature_list1; |
| // --enable-features=TestFeatureOff. |
| feature_list1.InitFromCommandLine("TestFeatureOff", ""); |
| pa_receiver = pa_remote.InitWithNewEndpointAndPassReceiver(); |
| } |
| EXPECT_TRUE(pa_remote); |
| SharedAssociatedRemote<mojom::DefaultDenied> sa_remote; |
| sa_remote.Bind(std::move(pa_remote), nullptr); |
| EXPECT_FALSE(sa_remote); |
| } |
| |
| //// |
| // Generic*Receiver As<disabled> won't "cast" but allowed interfaces can. |
| //// |
| TEST_P(FeatureBindingsTest, GenericPendingReceivers) { |
| MessagePipe pipe_a; |
| ScopedInterfaceEndpointHandle end_a; |
| ScopedInterfaceEndpointHandle end_b; |
| ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&end_a, &end_b); |
| |
| GenericPendingReceiver gpr_allowed(mojom::DefaultAllowed::Name_, |
| std::move(pipe_a.handle0)); |
| GenericPendingReceiver gpr_denied(mojom::DefaultDenied::Name_, |
| std::move(pipe_a.handle1)); |
| GenericPendingAssociatedReceiver gpar_allowed(mojom::DefaultAllowed::Name_, |
| std::move(end_a)); |
| GenericPendingAssociatedReceiver gpar_denied(mojom::DefaultDenied::Name_, |
| std::move(end_b)); |
| |
| auto p_allowed = gpr_allowed.As<mojom::DefaultAllowed>(); |
| auto p_denied = gpr_denied.As<mojom::DefaultDenied>(); |
| auto pa_allowed = gpar_allowed.As<mojom::DefaultAllowed>(); |
| auto pa_denied = gpar_denied.As<mojom::DefaultDenied>(); |
| |
| EXPECT_TRUE(p_allowed); |
| EXPECT_TRUE(pa_allowed); |
| EXPECT_FALSE(p_denied); |
| EXPECT_FALSE(pa_denied); |
| } |
| |
| //// |
| // Test that pending_* cannot be transmitted by methods. This prevents a |
| // process from receiving an endpoint from a compromised process. Optional |
| // remotes and receivers are treated as being empty. |
| //// |
| |
| TEST_P(FeatureBindingsTest, PassesOptionalInterfacesRemoteDenied) { |
| if (GetParam() == BindingsTestSerializationMode::kNeverSerialize) { |
| return; |
| } |
| Remote<mojom::PassesInterfaces> remote; |
| PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver()); |
| |
| base::RunLoop run_loop; |
| MessagePipe pipe; |
| PendingRemote<mojom::DefaultDenied> denied_pending_remote( |
| std::move(pipe.handle0), 0); |
| |
| bool error = false; |
| SetErrorHandler(base::BindLambdaForTesting([&] { |
| error = true; |
| run_loop.Quit(); |
| })); |
| |
| bool called_empty = false; |
| remote->PassPendingOptionalRemoteDisabled( |
| std::move(denied_pending_remote), |
| base::BindLambdaForTesting([&](bool val) { |
| called_empty = val; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| ASSERT_FALSE(error); |
| ASSERT_TRUE(called_empty); |
| } |
| |
| TEST_P(FeatureBindingsTest, PassesOptionalInterfacesReceiverDenied) { |
| if (GetParam() == mojo::BindingsTestSerializationMode::kNeverSerialize) { |
| return; |
| } |
| Remote<mojom::PassesInterfaces> remote; |
| PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver()); |
| |
| base::RunLoop run_loop; |
| MessagePipe pipe; |
| PendingReceiver<mojom::DefaultDenied> denied_pending_receiver( |
| std::move(pipe.handle1)); |
| |
| bool error = false; |
| SetErrorHandler(base::BindLambdaForTesting([&] { |
| error = true; |
| run_loop.Quit(); |
| })); |
| |
| bool called_empty = false; |
| remote->PassPendingOptionalReceiverDisabled( |
| std::move(denied_pending_receiver), |
| base::BindLambdaForTesting([&](bool val) { |
| called_empty = val; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| ASSERT_FALSE(error); |
| ASSERT_TRUE(called_empty); |
| } |
| |
| //// |
| // Tests that features from a different mojom file work (not exhaustive) - as |
| // features are implemented by templates this demonstrates that the generated |
| // headers are correctly imported in the main interface's files. |
| //// |
| |
| TEST_P(FeatureBindingsTest, RemoteWontBindNewPipeImported) { |
| // Interface is runtime disabled - so cannot bind. |
| Remote<mojom::OtherDenied> remote; |
| PendingReceiver<mojom::OtherDenied> pending_receiver = |
| remote.BindNewPipeAndPassReceiver(); |
| EXPECT_FALSE(pending_receiver); |
| } |
| |
| TEST_P(FeatureBindingsTest, ReceiverWontBindPendingReceiverImported) { |
| // Interface is runtime disabled - so cannot bind. |
| MessagePipe pipe; |
| PendingReceiver<mojom::OtherDenied> pending_receiver(std::move(pipe.handle0)); |
| OtherDeniedImpl impl(std::move(pending_receiver)); |
| EXPECT_FALSE(impl.receiver().is_bound()); |
| } |
| |
| // Validate that the various Sets return nullopt if a disabled interface is |
| // provided. |
| |
| TEST_P(FeatureBindingsTest, ReceiverSetDenied) { |
| MessagePipe pipe_a; |
| PendingReceiver<mojom::DefaultDenied> pending_receiver_a( |
| std::move(pipe_a.handle0)); |
| auto impl = std::make_unique<DefaultDeniedSelfOwnedImpl>(); |
| |
| ReceiverSet<mojom::DefaultDenied> receiver_set; |
| EXPECT_EQ(receiver_set.Add(impl.get(), std::move(pending_receiver_a)), |
| std::nullopt); |
| } |
| |
| TEST_P(FeatureBindingsTest, RemoteSetDenied) { |
| PendingRemote<mojom::DefaultDenied> pending_remote; |
| auto pending_receiver = pending_remote.InitWithNewPipeAndPassReceiver(); |
| |
| RemoteSet<mojom::DefaultDenied> remote_set; |
| // Do not test the non-pending case as disabled remote<> cannot be bound. |
| EXPECT_EQ(remote_set.Add(std::move(pending_remote)), std::nullopt); |
| } |
| |
| #endif // !DCHECK_IS_ON() |
| |
| //// |
| // Death tests - these are flaky on Android. |
| //// |
| #if defined(GTEST_HAS_DEATH_TEST) && !BUILDFLAG(IS_ANDROID) |
| using FeatureBindingsDeathTest = FeatureBindingsTest; |
| |
| TEST_P(FeatureBindingsDeathTest, MethodsOnRemoteDenied) { |
| // Validate that disabled methods on `FeaturesOnMethods` cannot be called. |
| bool called = false; |
| Remote<mojom::FeaturesOnMethods> remote; |
| FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver()); |
| EXPECT_DEATH(remote->DefaultDenied( |
| base::BindLambdaForTesting([&](int) { called = true; })), |
| ""); |
| EXPECT_FALSE(called); |
| } |
| |
| TEST_P(FeatureBindingsDeathTest, MethodsOnRemoteDeniedSync) { |
| // Validate that disabled sync methods on remotes cannot be called. |
| int result = 0; |
| Remote<mojom::FeaturesOnMethods> remote; |
| FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver()); |
| EXPECT_DEATH(remote->DefaultDeniedSync(&result), ""); |
| EXPECT_EQ(result, 0); |
| } |
| |
| TEST_P(FeatureBindingsDeathTest, MethodsOnAssociatedRemoteDenied) { |
| bool called = false; |
| // Validate that disabled methods on associated remotes cannot be called. |
| AssociatedRemote<mojom::FeaturesOnMethods> remote; |
| FeaturesOnMethodsImpl impl(remote.BindNewEndpointAndPassDedicatedReceiver()); |
| EXPECT_DEATH(remote->DefaultDenied( |
| base::BindLambdaForTesting([&](int) { called = true; })), |
| ""); |
| EXPECT_FALSE(called); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| FeatureBindingsDeathTest, |
| testing::Values(mojo::BindingsTestSerializationMode::kNeverSerialize)); |
| |
| #endif // defined(GTEST_HAS_DEATH_TEST) && !BUILDFLAG(IS_ANDROID) |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| FeatureBindingsTest, |
| testing::Values( |
| mojo::BindingsTestSerializationMode::kSerializeBeforeSend, |
| mojo::BindingsTestSerializationMode::kSerializeBeforeDispatch, |
| mojo::BindingsTestSerializationMode::kNeverSerialize)); |
| |
| } // namespace mojo::test::feature_unittest |