| // Copyright 2018 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 "fuchsia/engine/context_provider_impl.h" |
| |
| #include <fuchsia/sys/cpp/fidl_test_base.h> |
| #include <fuchsia/web/cpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/sys/cpp/outgoing_directory.h> |
| #include <lib/zx/socket.h> |
| #include <zircon/processargs.h> |
| #include <zircon/types.h> |
| |
| #include <functional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/base_paths_fuchsia.h" |
| #include "base/base_switches.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/files/file.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/fuchsia/file_utils.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/fuchsia/process_context.h" |
| #include "base/fuchsia/scoped_service_binding.h" |
| #include "base/fuchsia/test_component_context_for_process.h" |
| #include "base/path_service.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/bind.h" |
| #include "base/test/multiprocess_test.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_timeouts.h" |
| #include "build/build_config.h" |
| #include "fuchsia/engine/fake_context.h" |
| #include "fuchsia/engine/switches.h" |
| #include "fuchsia/engine/web_instance_host/web_instance_host.h" |
| #include "services/network/public/cpp/network_switches.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/multiprocess_func_list.h" |
| |
| namespace { |
| |
| constexpr char kTestDataFileIn[] = "DataFileIn"; |
| constexpr char kTestDataFileOut[] = "DataFileOut"; |
| |
| constexpr char kUrl[] = "chrome://:emorhc"; |
| constexpr char kTitle[] = "Palindrome"; |
| |
| constexpr uint64_t kTestQuotaBytes = 1024; |
| constexpr char kTestQuotaBytesSwitchValue[] = "1024"; |
| |
| constexpr char kCommandLineArgs[] = "command-line-args"; |
| |
| MULTIPROCESS_TEST_MAIN(SpawnContextServer) { |
| base::test::SingleThreadTaskEnvironment task_environment( |
| base::test::SingleThreadTaskEnvironment::MainThreadType::IO); |
| |
| LOG(INFO) << "SpawnContextServer test component started."; |
| |
| base::FilePath data_dir; |
| CHECK(base::PathService::Get(base::DIR_APP_DATA, &data_dir)); |
| if (base::PathExists(data_dir.AppendASCII(kTestDataFileIn))) { |
| auto out_file = data_dir.AppendASCII(kTestDataFileOut); |
| CHECK_EQ(base::WriteFile(out_file, nullptr, 0), 0); |
| } |
| |
| // Publish the fake fuchsia.web.Context implementation for the test to use. |
| FakeContext context; |
| fidl::BindingSet<fuchsia::web::Context> bindings; |
| base::ComponentContextForProcess()->outgoing()->AddPublicService( |
| bindings.GetHandler(&context), "fuchsia.web.Context"); |
| base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo(); |
| |
| // When a Frame's NavigationEventListener is bound, immediately broadcast a |
| // navigation event to its listeners. |
| context.set_on_create_frame_callback( |
| base::BindRepeating([](FakeFrame* frame) { |
| frame->set_on_set_listener_callback(base::BindOnce( |
| [](FakeFrame* frame) { |
| fuchsia::web::NavigationState state; |
| state.set_url(kUrl); |
| state.set_title(kTitle); |
| frame->listener()->OnNavigationStateChanged(std::move(state), |
| []() {}); |
| }, |
| frame)); |
| })); |
| |
| // Quit the process when the context is destroyed. |
| base::RunLoop run_loop; |
| bindings.set_empty_set_handler( |
| [quit_loop = run_loop.QuitClosure()]() { quit_loop.Run(); }); |
| run_loop.Run(); |
| |
| return 0; |
| } |
| |
| // Fake implementation of the Launcher for the isolated environment in which |
| // web instance Components are launched. |
| class FakeSysLauncher final : public fuchsia::sys::testing::Launcher_TestBase { |
| public: |
| using CreateComponentCallback = |
| base::OnceCallback<void(const base::CommandLine&)>; |
| |
| explicit FakeSysLauncher(fuchsia::sys::Launcher* real_launcher) |
| : real_launcher_(real_launcher) {} |
| ~FakeSysLauncher() override = default; |
| |
| void set_create_component_callback(CreateComponentCallback callback) { |
| create_component_callback_ = std::move(callback); |
| } |
| |
| void Bind(fidl::InterfaceRequest<fuchsia::sys::Launcher> request) { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| // fuchsia::sys::Launcher implementation. |
| void CreateComponent(fuchsia::sys::LaunchInfo launch_info, |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> |
| request) override { |
| // |arguments| should not include argv[0] (i.e. the program name), which |
| // would be empty in a no-program CommandLine instance. Verify that the |
| // |arguments| are either empty or have a non-empty first element. |
| EXPECT_TRUE(launch_info.arguments->empty() || |
| !launch_info.arguments->at(0).empty()); |
| |
| // |arguments| omits argv[0] so cannot be used directly to initialize a |
| // CommandLine, but CommandLine provides useful switch processing logic. |
| // Prepend an empty element to a copy of |arguments| and use that to create |
| // a valid CommandLine. |
| std::vector<std::string> command_line_args(*launch_info.arguments); |
| command_line_args.emplace(command_line_args.begin()); |
| const base::CommandLine command_line(command_line_args); |
| CHECK(!command_line.HasSwitch(switches::kTestChildProcess)); |
| |
| // If a create-component-callback is specified then there is no need to |
| // actually launch a component. |
| if (create_component_callback_) { |
| std::move(create_component_callback_).Run(command_line); |
| return; |
| } |
| |
| // Otherwise, launch another instance of this test executable, configured to |
| // run as a test child (similar to SpawnMultiProcessTestChild()). The |
| // test-suite's component manifest cannot be re-used for this because it |
| // specifies the "isolated-persistent-data" feature, causing the framework |
| // to populate /data, which prevents the |data_directory| supplied in the |
| // CreateContextParams from being mapped. |
| // Launch the component via a fake manifest identical to the one used for |
| // web instances, but which runs this test executable. |
| EXPECT_EQ(launch_info.url, cr_fuchsia::WebInstanceHost::kComponentUrl); |
| launch_info.url = |
| "fuchsia-pkg://fuchsia.com/web_engine_unittests#meta/" |
| "web_engine_unittests_fake_instance.cmx"; |
| launch_info.arguments->push_back(base::StrCat( |
| {"--", switches::kTestChildProcess, "=SpawnContextServer"})); |
| |
| // Bind /tmp in the new Component's flat namespace, to allow it to see |
| // the GTest flagfile, if any. |
| fuchsia::io::DirectoryHandle tmp_directory; |
| zx_status_t status = |
| fdio_open("/tmp", fuchsia::io::OPEN_RIGHT_READABLE, |
| tmp_directory.NewRequest().TakeChannel().release()); |
| ZX_CHECK(status == ZX_OK, status) << "fdio_open(/tmp)"; |
| launch_info.flat_namespace->paths.push_back("/tmp"); |
| launch_info.flat_namespace->directories.push_back( |
| tmp_directory.TakeChannel()); |
| |
| // Redirect the sub-process Component's stderr to feed into the test output. |
| launch_info.err = fuchsia::sys::FileDescriptor::New(); |
| launch_info.err->type0 = PA_FD; |
| status = fdio_fd_clone(STDERR_FILENO, |
| launch_info.err->handle0.reset_and_get_address()); |
| ZX_CHECK(status == ZX_OK, status); |
| |
| real_launcher_->CreateComponent(std::move(launch_info), std::move(request)); |
| } |
| |
| private: |
| void NotImplemented_(const std::string& name) override { |
| ADD_FAILURE() << "Unexpected call: " << name; |
| } |
| |
| fidl::BindingSet<fuchsia::sys::Launcher> bindings_; |
| fuchsia::sys::Launcher* const real_launcher_; |
| CreateComponentCallback create_component_callback_; |
| }; |
| |
| // Fake implementation of the isolated Environment created by ContextProvider. |
| class FakeNestedSysEnvironment |
| : public fuchsia::sys::testing::Environment_TestBase { |
| public: |
| explicit FakeNestedSysEnvironment(FakeSysLauncher* fake_launcher) |
| : fake_launcher_(fake_launcher) {} |
| ~FakeNestedSysEnvironment() override = default; |
| |
| void Bind(fidl::InterfaceRequest<fuchsia::sys::Environment> request) { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| // fuchsia::sys::Environment implementation. |
| void GetLauncher(fidl::InterfaceRequest<fuchsia::sys::Launcher> |
| launcher_request) override { |
| fake_launcher_->Bind(std::move(launcher_request)); |
| } |
| |
| private: |
| void NotImplemented_(const std::string& name) override { |
| ADD_FAILURE() << "Unexpected call: " << name; |
| } |
| |
| FakeSysLauncher* const fake_launcher_; |
| fidl::BindingSet<fuchsia::sys::Environment> bindings_; |
| }; |
| |
| // Fake implementation of the Environment in which the ContextProvider runs. |
| class FakeSysEnvironment final |
| : public fuchsia::sys::testing::Environment_TestBase { |
| public: |
| FakeSysEnvironment(sys::OutgoingDirectory* outgoing_directory, |
| fuchsia::sys::Launcher* real_launcher) |
| : bindings_(outgoing_directory, this), |
| fake_launcher_(real_launcher), |
| fake_nested_environment_(&fake_launcher_) {} |
| ~FakeSysEnvironment() override = default; |
| |
| FakeSysLauncher& fake_launcher() { return fake_launcher_; } |
| |
| // fuchsia::sys::Environment implementation. |
| void CreateNestedEnvironment( |
| fidl::InterfaceRequest<fuchsia::sys::Environment> environment_request, |
| fidl::InterfaceRequest<fuchsia::sys::EnvironmentController> |
| controller_request, |
| std::string label, |
| fuchsia::sys::ServiceListPtr additional_services, |
| fuchsia::sys::EnvironmentOptions options) override { |
| EXPECT_TRUE(environment_request); |
| EXPECT_TRUE(controller_request); |
| EXPECT_FALSE(label.empty()); |
| |
| // The nested environment should receive only the Loader service. |
| ASSERT_TRUE(additional_services); |
| ASSERT_EQ(additional_services->names.size(), 1u); |
| EXPECT_EQ(additional_services->names[0], "fuchsia.sys.Loader"); |
| EXPECT_TRUE(additional_services->host_directory); |
| |
| EXPECT_FALSE(options.inherit_parent_services); |
| EXPECT_FALSE(options.use_parent_runners); |
| EXPECT_TRUE(options.delete_storage_on_death); |
| |
| fake_nested_environment_.Bind(std::move(environment_request)); |
| nested_environment_controller_request_ = std::move(controller_request); |
| } |
| void GetDirectory(zx::channel request) override { |
| base::ComponentContextForProcess()->svc()->CloneChannel( |
| fidl::InterfaceRequest<fuchsia::io::Directory>(std::move(request))); |
| } |
| |
| private: |
| void NotImplemented_(const std::string& name) override { |
| ADD_FAILURE() << "Unexpected call: " << name; |
| } |
| |
| base::ScopedServiceBinding<fuchsia::sys::Environment> bindings_; |
| FakeSysLauncher fake_launcher_; |
| FakeNestedSysEnvironment fake_nested_environment_; |
| |
| fidl::InterfaceRequest<fuchsia::sys::EnvironmentController> |
| nested_environment_controller_request_; |
| }; |
| |
| fuchsia::web::CreateContextParams BuildCreateContextParams() { |
| fuchsia::web::CreateContextParams output; |
| zx_status_t result = fdio_service_connect( |
| base::kServiceDirectoryPath, |
| output.mutable_service_directory()->NewRequest().TakeChannel().release()); |
| ZX_CHECK(result == ZX_OK, result) << "Failed to open /svc"; |
| return output; |
| } |
| |
| base::Value CreateConfigWithSwitchValue(std::string switch_name, |
| std::string switch_value) { |
| base::Value config_dict(base::Value::Type::DICTIONARY); |
| base::Value args(base::Value::Type::DICTIONARY); |
| args.SetStringKey(switch_name, switch_value); |
| config_dict.SetKey(kCommandLineArgs, std::move(args)); |
| return config_dict; |
| } |
| |
| fidl::InterfaceHandle<fuchsia::io::Directory> OpenCacheDirectory() { |
| fidl::InterfaceHandle<fuchsia::io::Directory> cache_handle; |
| zx_status_t result = |
| fdio_service_connect(base::kPersistedCacheDirectoryPath, |
| cache_handle.NewRequest().TakeChannel().release()); |
| ZX_CHECK(result == ZX_OK, result) << "Failed to open /cache"; |
| return cache_handle; |
| } |
| |
| } // namespace |
| |
| class ContextProviderImplTest : public base::MultiProcessTest { |
| public: |
| ContextProviderImplTest() |
| : sys_launcher_(base::ComponentContextForProcess() |
| ->svc() |
| ->Connect<fuchsia::sys::Launcher>()), |
| fake_environment_(test_component_context_.additional_services(), |
| sys_launcher_.get()), |
| provider_(std::make_unique<ContextProviderImpl>()) { |
| bindings_.AddBinding(provider_.get(), provider_ptr_.NewRequest()); |
| } |
| |
| ~ContextProviderImplTest() override { |
| provider_ptr_.Unbind(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Check if a Context is responsive by creating a Frame from it and then |
| // listening for an event. |
| void CheckContextResponsive( |
| fidl::InterfacePtr<fuchsia::web::Context>* context) { |
| // Call a Context method and wait for it to invoke a listener call. |
| base::RunLoop run_loop; |
| context->set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| ZX_LOG(ERROR, status) << " Context lost."; |
| ADD_FAILURE(); |
| }); |
| |
| fuchsia::web::FramePtr frame_ptr; |
| frame_ptr.set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| ZX_LOG(ERROR, status) << " Frame lost."; |
| ADD_FAILURE(); |
| }); |
| (*context)->CreateFrame(frame_ptr.NewRequest()); |
| |
| // Create a Frame and expect to see a navigation event. |
| CapturingNavigationStateObserver change_listener(run_loop.QuitClosure()); |
| fidl::Binding<fuchsia::web::NavigationEventListener> |
| change_listener_binding(&change_listener); |
| frame_ptr->SetNavigationEventListener(change_listener_binding.NewBinding()); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(change_listener.captured_state()->has_url()); |
| EXPECT_EQ(change_listener.captured_state()->url(), kUrl); |
| ASSERT_TRUE(change_listener.captured_state()->has_title()); |
| EXPECT_EQ(change_listener.captured_state()->title(), kTitle); |
| } |
| |
| // Checks that the Context channel was dropped. |
| void CheckContextUnresponsive( |
| fidl::InterfacePtr<fuchsia::web::Context>* context) { |
| base::RunLoop run_loop; |
| context->set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| EXPECT_EQ(status, ZX_ERR_PEER_CLOSED); |
| }); |
| |
| fuchsia::web::FramePtr frame; |
| (*context)->CreateFrame(frame.NewRequest()); |
| |
| // The error handler should be called here. |
| run_loop.Run(); |
| } |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_{ |
| base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; |
| |
| // fuchsia.sys.Launcher member must be constructed before the test component |
| // context replaces the process' component context. |
| fuchsia::sys::LauncherPtr sys_launcher_; |
| |
| // Used to replace the process component context with one providing a fake |
| // fuchsia.sys.Environment, through which a nested Environment and fake |
| // Launcher are obtained. |
| base::TestComponentContextForProcess test_component_context_; |
| FakeSysEnvironment fake_environment_; |
| |
| std::unique_ptr<ContextProviderImpl> provider_; |
| fuchsia::web::ContextProviderPtr provider_ptr_; |
| fidl::BindingSet<fuchsia::web::ContextProvider> bindings_; |
| |
| private: |
| struct CapturingNavigationStateObserver |
| : public fuchsia::web::NavigationEventListener { |
| public: |
| explicit CapturingNavigationStateObserver(base::OnceClosure on_change_cb) |
| : on_change_cb_(std::move(on_change_cb)) {} |
| ~CapturingNavigationStateObserver() override = default; |
| |
| void OnNavigationStateChanged( |
| fuchsia::web::NavigationState change, |
| OnNavigationStateChangedCallback callback) override { |
| captured_state_ = std::move(change); |
| std::move(on_change_cb_).Run(); |
| } |
| |
| fuchsia::web::NavigationState* captured_state() { return &captured_state_; } |
| |
| private: |
| base::OnceClosure on_change_cb_; |
| fuchsia::web::NavigationState captured_state_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN(ContextProviderImplTest); |
| }; |
| |
| TEST_F(ContextProviderImplTest, CanCreateContext) { |
| // Connect to a new context process. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| CheckContextResponsive(&context); |
| } |
| |
| TEST_F(ContextProviderImplTest, CreateValidatesServiceDirectory) { |
| // Attempt to create a Context without specifying a service directory. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params; |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| base::RunLoop run_loop; |
| context.set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| EXPECT_EQ(status, ZX_ERR_INVALID_ARGS); |
| }); |
| run_loop.Run(); |
| } |
| |
| TEST_F(ContextProviderImplTest, CreateValidatesDataDirectory) { |
| // Deliberately supply the wrong kind of object as the data-directory. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| zx::socket socket1, socket2; |
| ASSERT_EQ(zx::socket::create(0, &socket1, &socket2), ZX_OK); |
| create_params.set_data_directory( |
| fidl::InterfaceHandle<fuchsia::io::Directory>( |
| zx::channel(socket1.release()))); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| base::RunLoop run_loop; |
| context.set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| EXPECT_TRUE(status == ZX_ERR_PEER_CLOSED); |
| }); |
| run_loop.Run(); |
| } |
| |
| TEST_F(ContextProviderImplTest, CreateValidatesDrmFlags) { |
| { |
| // Request Widevine DRM but do not enable VULKAN. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = |
| BuildCreateContextParams(); |
| *create_params.mutable_features() = |
| fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM; |
| *create_params.mutable_cdm_data_directory() = OpenCacheDirectory(); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| base::RunLoop run_loop; |
| context.set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| EXPECT_EQ(status, ZX_ERR_NOT_SUPPORTED); |
| }); |
| run_loop.Run(); |
| } |
| |
| { |
| // Request PlayReady DRM but do not enable VULKAN. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = |
| BuildCreateContextParams(); |
| create_params.set_playready_key_system("foo"); |
| *create_params.mutable_cdm_data_directory() = OpenCacheDirectory(); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| base::RunLoop run_loop; |
| context.set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| EXPECT_EQ(status, ZX_ERR_NOT_SUPPORTED); |
| }); |
| run_loop.Run(); |
| } |
| |
| { |
| // Requesting DRM without VULKAN is acceptable for HEADLESS Contexts. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = |
| BuildCreateContextParams(); |
| *create_params.mutable_features() = |
| fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM | |
| fuchsia::web::ContextFeatureFlags::HEADLESS; |
| *create_params.mutable_cdm_data_directory() = OpenCacheDirectory(); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| base::RunLoop run_loop; |
| context.set_error_handler( |
| [quit_loop = run_loop.QuitClosure()](zx_status_t status) { |
| quit_loop.Run(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| // Spin the loop to allow CreateContext() to be handled, and the |context| |
| // channel to be disconnected, in case of failure. |
| run_loop.RunUntilIdle(); |
| } |
| } |
| |
| TEST_F(ContextProviderImplTest, MultipleConcurrentClients) { |
| // Bind a Provider connection, and create a Context from it. |
| fuchsia::web::ContextProviderPtr provider_1_ptr; |
| bindings_.AddBinding(provider_.get(), provider_1_ptr.NewRequest()); |
| fuchsia::web::ContextPtr context_1; |
| provider_1_ptr->Create(BuildCreateContextParams(), context_1.NewRequest()); |
| |
| // Do the same on another Provider connection. |
| fuchsia::web::ContextProviderPtr provider_2_ptr; |
| bindings_.AddBinding(provider_.get(), provider_2_ptr.NewRequest()); |
| fuchsia::web::ContextPtr context_2; |
| provider_2_ptr->Create(BuildCreateContextParams(), context_2.NewRequest()); |
| |
| CheckContextResponsive(&context_1); |
| CheckContextResponsive(&context_2); |
| |
| // Ensure that the initial ContextProvider connection is still usable, by |
| // creating and verifying another Context from it. |
| fuchsia::web::ContextPtr context_3; |
| provider_2_ptr->Create(BuildCreateContextParams(), context_3.NewRequest()); |
| CheckContextResponsive(&context_3); |
| } |
| |
| TEST_F(ContextProviderImplTest, WithProfileDir) { |
| base::ScopedTempDir profile_temp_dir; |
| |
| // Connect to a new context process. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| |
| // Setup data dir. |
| ASSERT_TRUE(profile_temp_dir.CreateUniqueTempDir()); |
| ASSERT_EQ( |
| base::WriteFile(profile_temp_dir.GetPath().AppendASCII(kTestDataFileIn), |
| nullptr, 0), |
| 0); |
| |
| // Pass a handle data dir to the context. |
| create_params.set_data_directory( |
| base::OpenDirectoryHandle(profile_temp_dir.GetPath())); |
| |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| |
| CheckContextResponsive(&context); |
| |
| // Verify that the context process can write to the data dir. |
| EXPECT_TRUE(base::PathExists( |
| profile_temp_dir.GetPath().AppendASCII(kTestDataFileOut))); |
| } |
| |
| TEST_F(ContextProviderImplTest, FailsDataDirectoryIsFile) { |
| base::FilePath temp_file_path; |
| |
| // Connect to a new context process. |
| fidl::InterfacePtr<fuchsia::web::Context> context; |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| |
| // Pass in a handle to a file instead of a directory. |
| CHECK(base::CreateTemporaryFile(&temp_file_path)); |
| create_params.set_data_directory(base::OpenDirectoryHandle(temp_file_path)); |
| |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| |
| CheckContextUnresponsive(&context); |
| } |
| |
| TEST_F(ContextProviderImplTest, WithConfigWithCommandLineArgs) { |
| // Specify a configuration that sets valid args with valid strings. |
| base::Value config_dict = |
| CreateConfigWithSwitchValue("renderer-process-limit", "0"); |
| |
| base::RunLoop loop; |
| provider_->set_config_for_test(std::move(config_dict)); |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop](const base::CommandLine& command) { |
| loop.Quit(); |
| EXPECT_TRUE(command.HasSwitch("renderer-process-limit")); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| provider_ptr_->Create(BuildCreateContextParams(), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ContextProviderImplTest, WithConfigWithDisallowedCommandLineArgs) { |
| // Specify a configuration that sets a disallowed command-line argument. |
| base::Value config_dict = |
| CreateConfigWithSwitchValue("kittens-are-nice", "0"); |
| |
| base::RunLoop loop; |
| provider_->set_config_for_test(std::move(config_dict)); |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop](const base::CommandLine& command) { |
| loop.Quit(); |
| EXPECT_FALSE(command.HasSwitch("kittens-are-nice")); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| provider_ptr_->Create(BuildCreateContextParams(), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ContextProviderImplTest, WithConfigWithWronglyTypedCommandLineArgs) { |
| base::Value config_dict(base::Value::Type::DICTIONARY); |
| |
| // Specify a configuration that sets valid args with invalid value. |
| base::Value args(base::Value::Type::DICTIONARY); |
| args.SetBoolKey("renderer-process-limit", false); |
| config_dict.SetKey(kCommandLineArgs, std::move(args)); |
| |
| base::RunLoop loop; |
| provider_->set_config_for_test(std::move(config_dict)); |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop](const base::CommandLine& command) { |
| loop.Quit(); |
| ADD_FAILURE(); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| EXPECT_EQ(status, ZX_ERR_INTERNAL); |
| }); |
| provider_ptr_->Create(BuildCreateContextParams(), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| |
| // Tests that unsafely_treat_insecure_origins_as_secure properly adds the right |
| // command-line arguments to the Context process. |
| TEST_F(ContextProviderImplTest, WithInsecureOriginsAsSecure) { |
| base::RunLoop loop; |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop](const base::CommandLine& command) { |
| loop.Quit(); |
| EXPECT_TRUE(command.HasSwitch(switches::kAllowRunningInsecureContent)); |
| EXPECT_THAT(command.GetSwitchValueASCII(switches::kDisableFeatures), |
| testing::HasSubstr("AutoupgradeMixedContent")); |
| EXPECT_EQ(command.GetSwitchValueASCII( |
| network::switches::kUnsafelyTreatInsecureOriginAsSecure), |
| "http://example.com"); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| std::vector<std::string> insecure_origins; |
| insecure_origins.push_back(switches::kAllowRunningInsecureContent); |
| insecure_origins.push_back("disable-mixed-content-autoupgrade"); |
| insecure_origins.push_back("http://example.com"); |
| create_params.set_unsafely_treat_insecure_origins_as_secure( |
| std::move(insecure_origins)); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ContextProviderImplTest, WithDataQuotaBytes) { |
| base::RunLoop loop; |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop](const base::CommandLine& command) { |
| loop.Quit(); |
| EXPECT_EQ(command.GetSwitchValueASCII("data-quota-bytes"), |
| kTestQuotaBytesSwitchValue); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| base::ScopedTempDir profile_temp_dir; |
| ASSERT_TRUE(profile_temp_dir.CreateUniqueTempDir()); |
| create_params.set_data_directory( |
| base::OpenDirectoryHandle(profile_temp_dir.GetPath())); |
| create_params.set_data_quota_bytes(kTestQuotaBytes); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ContextProviderImplTest, WithGoogleApiKeyValue) { |
| constexpr char kDummyApiKey[] = "apikey123"; |
| |
| base::Value config_dict = |
| CreateConfigWithSwitchValue("google-api-key", kDummyApiKey); |
| |
| base::RunLoop loop; |
| provider_->set_config_for_test(std::move(config_dict)); |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop, kDummyApiKey]( |
| const base::CommandLine& command) { |
| loop.Quit(); |
| EXPECT_EQ(command.GetSwitchValueASCII(switches::kGoogleApiKey), |
| kDummyApiKey); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| provider_ptr_->Create(BuildCreateContextParams(), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| |
| // TODO(crbug.com/1013412): This test doesn't actually exercise DRM, so could |
| // be executed everywhere if DRM support were configurable. |
| #if defined(ARCH_CPU_ARM64) |
| TEST_F(ContextProviderImplTest, WithCdmDataQuotaBytes) { |
| base::RunLoop loop; |
| fake_environment_.fake_launcher().set_create_component_callback( |
| base::BindLambdaForTesting([&loop](const base::CommandLine& command) { |
| loop.Quit(); |
| EXPECT_EQ(command.GetSwitchValueASCII("cdm-data-quota-bytes"), |
| kTestQuotaBytesSwitchValue); |
| })); |
| |
| fuchsia::web::ContextPtr context; |
| context.set_error_handler([&loop](zx_status_t status) { |
| loop.Quit(); |
| ZX_LOG(ERROR, status); |
| ADD_FAILURE(); |
| }); |
| |
| fuchsia::web::CreateContextParams create_params = BuildCreateContextParams(); |
| base::ScopedTempDir profile_temp_dir; |
| ASSERT_TRUE(profile_temp_dir.CreateUniqueTempDir()); |
| create_params.set_cdm_data_directory( |
| base::OpenDirectoryHandle(profile_temp_dir.GetPath())); |
| create_params.set_features(fuchsia::web::ContextFeatureFlags::HEADLESS | |
| fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM); |
| create_params.set_cdm_data_quota_bytes(kTestQuotaBytes); |
| provider_ptr_->Create(std::move(create_params), context.NewRequest()); |
| |
| loop.Run(); |
| } |
| #endif // defined(ARCH_CPU_ARM64) |