| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/mediacodec/cpp/fidl.h> |
| #include <fuchsia/mem/cpp/fidl.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/rights.h> |
| #include <zircon/types.h> |
| |
| #include <string> |
| |
| #include "base/containers/contains.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/fuchsia/mem_buffer_util.h" |
| #include "base/fuchsia/process_context.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/test_future.h" |
| #include "build/build_config.h" |
| #include "components/version_info/version_info.h" |
| #include "fuchsia_web/common/test/fit_adapter.h" |
| #include "fuchsia_web/common/test/frame_test_util.h" |
| #include "fuchsia_web/common/test/test_devtools_list_fetcher.h" |
| #include "fuchsia_web/webengine/test/context_provider_for_test.h" |
| #include "fuchsia_web/webengine/web_engine_integration_test_base.h" |
| #include "media/base/media_switches.h" |
| #include "media/fuchsia/audio/fake_audio_consumer.h" |
| #include "media/fuchsia/audio/fake_audio_device_enumerator.h" |
| #include "media/fuchsia/camera/fake_fuchsia_camera.h" |
| #include "net/http/http_request_headers.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace { |
| |
| constexpr char kValidUserAgentProduct[] = "TestProduct"; |
| constexpr char kValidUserAgentVersion[] = "dev.12345"; |
| constexpr char kValidUserAgentProductAndVersion[] = "TestProduct/dev.12345"; |
| constexpr char kInvalidUserAgentProduct[] = "Test/Product"; |
| constexpr char kInvalidUserAgentVersion[] = "dev/12345"; |
| |
| constexpr char kAutoplayVp9OpusUrl[] = |
| "fuchsia-dir://testdata/play_video.html?codecs=vp9,opus&autoplay=1"; |
| constexpr char kAutoplayVp9OpusToEndUrl[] = |
| "fuchsia-dir://testdata/" |
| "play_video.html?codecs=vp9,opus&autoplay=1&reportended=1"; |
| constexpr char kLoadVp9OpusUrl[] = |
| "fuchsia-dir://testdata/play_video.html?codecs=vp9,opus"; |
| |
| } // namespace |
| |
| // Starts a WebEngine instance before running the test. |
| class WebEngineIntegrationTest : public WebEngineIntegrationTestBase { |
| protected: |
| WebEngineIntegrationTest() = default; |
| |
| ~WebEngineIntegrationTest() override { |
| // We're about to shut down the realm; unbind to unhook the error handler. |
| frame_.Unbind(); |
| context_.Unbind(); |
| } |
| |
| void StartWebEngine(base::CommandLine command_line) override { |
| context_provider_.emplace( |
| ContextProviderForTest::Create(std::move(command_line))); |
| context_provider_->ptr().set_error_handler( |
| [](zx_status_t status) { FAIL() << zx_status_get_string(status); }); |
| } |
| |
| fuchsia::web::ContextProvider* GetContextProvider() override { |
| return context_provider_->get(); |
| } |
| |
| void RunPermissionTest(bool grant); |
| |
| private: |
| absl::optional<ContextProviderForTest> context_provider_; |
| }; |
| |
| class WebEngineIntegrationUserAgentTest : public WebEngineIntegrationTest { |
| protected: |
| GURL GetEchoUserAgentUrl() { |
| static std::string echo_user_agent_header_path = |
| std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent; |
| return embedded_test_server_.GetURL(echo_user_agent_header_path); |
| } |
| |
| // Returns the expected user agent string for the current Chrome version. |
| static std::string GetExpectedUserAgentString() { |
| // The default (base) user agent string without any client modifications. |
| // Due to reduced user agent, only the major version is populated, the |
| // version number is <majorVersion>.0.0.0. |
| constexpr char kDefaultUserAgentStringWithVersionPlaceholder[] = |
| "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) " |
| "Chrome/%d.0.0.0 Safari/537.36"; |
| |
| std::string expected_ua = |
| base::StringPrintf(kDefaultUserAgentStringWithVersionPlaceholder, |
| version_info::GetMajorVersionNumberAsInt()); |
| |
| // Ensure the field was actually populated. |
| EXPECT_TRUE( |
| base::Contains(expected_ua, version_info::GetMajorVersionNumber())); |
| |
| return expected_ua; |
| } |
| |
| static std::string GetExpectedUserAgentStringWithProduct( |
| const char* product) { |
| return base::StringPrintf("%s %s", GetExpectedUserAgentString().c_str(), |
| product); |
| } |
| }; |
| |
| // Although this does not need to be an integration test, all other user agent |
| // tests are here. |
| TEST_F(WebEngineIntegrationUserAgentTest, Default) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Create a Context with no values specified. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| CreateContextAndFrameAndLoadUrl(std::move(create_params), |
| GetEchoUserAgentUrl()); |
| |
| // Query & verify that the header echoed into the document body is as |
| // expected. |
| std::string result = |
| ExecuteJavaScriptWithStringResult("document.body.innerText;"); |
| EXPECT_EQ(result, GetExpectedUserAgentString()); |
| |
| // Query & verify that the navigator.userAgent is as expected. |
| result = ExecuteJavaScriptWithStringResult("navigator.userAgent;"); |
| EXPECT_EQ(result, GetExpectedUserAgentString()); |
| } |
| |
| TEST_F(WebEngineIntegrationUserAgentTest, ValidProductOnly) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Create a Context with just an embedder product specified. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| create_params.set_user_agent_product(kValidUserAgentProduct); |
| CreateContextAndFrameAndLoadUrl(std::move(create_params), |
| GetEchoUserAgentUrl()); |
| |
| std::string expected = |
| GetExpectedUserAgentStringWithProduct(kValidUserAgentProduct); |
| |
| // Query & verify that the header echoed into the document body contains |
| // the product tag. |
| std::string result = |
| ExecuteJavaScriptWithStringResult("document.body.innerText;"); |
| EXPECT_TRUE(base::Contains(result, kValidUserAgentProduct)); |
| EXPECT_EQ(result, expected); |
| |
| // Query & verify that the navigator.userAgent contains the product tag. |
| result = ExecuteJavaScriptWithStringResult("navigator.userAgent;"); |
| EXPECT_TRUE(base::Contains(result, kValidUserAgentProduct)); |
| EXPECT_EQ(result, expected); |
| } |
| |
| TEST_F(WebEngineIntegrationUserAgentTest, ValidProductAndVersion) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Create a Context with both product and version specified. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| create_params.set_user_agent_product(kValidUserAgentProduct); |
| create_params.set_user_agent_version(kValidUserAgentVersion); |
| CreateContextAndFrameAndLoadUrl(std::move(create_params), |
| GetEchoUserAgentUrl()); |
| |
| std::string expected = |
| GetExpectedUserAgentStringWithProduct(kValidUserAgentProductAndVersion); |
| |
| // Query & verify that the header echoed into the document body contains |
| // both product & version. |
| std::string result = |
| ExecuteJavaScriptWithStringResult("document.body.innerText;"); |
| EXPECT_TRUE(base::Contains(result, kValidUserAgentProductAndVersion)); |
| EXPECT_EQ(result, expected); |
| |
| // Query & verify that the navigator.userAgent contains product & version. |
| result = ExecuteJavaScriptWithStringResult("navigator.userAgent;"); |
| EXPECT_TRUE(base::Contains(result, kValidUserAgentProductAndVersion)); |
| EXPECT_EQ(result, expected); |
| |
| // Verify navigator.platform is empty, see crbug.com/1348646. |
| EXPECT_EQ(ExecuteJavaScriptWithStringResult("navigator.platform;"), ""); |
| } |
| |
| TEST_F(WebEngineIntegrationUserAgentTest, InvalidProduct) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Try to create a Context with an invalid embedder product tag. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| create_params.set_user_agent_product(kInvalidUserAgentProduct); |
| CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST_F(WebEngineIntegrationUserAgentTest, VersionOnly) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Try to create a Context with an embedder version but no product. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| create_params.set_user_agent_version(kValidUserAgentVersion); |
| CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST_F(WebEngineIntegrationUserAgentTest, ValidProductAndInvalidVersion) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Try to create a Context with valid product tag, but invalid version. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| create_params.set_user_agent_product(kValidUserAgentProduct); |
| create_params.set_user_agent_version(kInvalidUserAgentVersion); |
| CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST_F(WebEngineIntegrationTest, CreateFrameWithUnclonableFrameParamsFails) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContext(TestContextParams()); |
| |
| zx_rights_t kReadRightsWithoutDuplicate = |
| ZX_RIGHT_TRANSFER | ZX_RIGHT_READ | ZX_RIGHT_MAP | ZX_RIGHT_GET_PROPERTY; |
| |
| // Create a buffer and remove the ability clone it by changing its rights to |
| // not include ZX_RIGHT_DUPLICATE. |
| auto buffer = base::MemBufferFromString("some data", "some name"); |
| zx::vmo unclonable_readonly_vmo; |
| EXPECT_EQ(ZX_OK, buffer.vmo.duplicate(kReadRightsWithoutDuplicate, |
| &unclonable_readonly_vmo)); |
| buffer.vmo = std::move(unclonable_readonly_vmo); |
| |
| // Creation will fail, which will be reported in the error handler below. |
| fuchsia::web::CreateFrameParams create_frame_params; |
| create_frame_params.set_explicit_sites_filter_error_page( |
| fuchsia::mem::Data::WithBuffer(std::move(buffer))); |
| context_->CreateFrameWithParams(std::move(create_frame_params), |
| frame_.NewRequest()); |
| |
| base::RunLoop loop; |
| frame_.set_error_handler( |
| [quit_loop = loop.QuitClosure()](zx_status_t status) { |
| EXPECT_EQ(status, ZX_ERR_INVALID_ARGS); |
| quit_loop.Run(); |
| }); |
| loop.Run(); |
| |
| EXPECT_FALSE(frame_); |
| } |
| |
| // Check that if the CreateContextParams has |remote_debugging_port| set then: |
| // - DevTools becomes available when the first debuggable Frame is created. |
| // - DevTools closes when the last debuggable Frame is closed. |
| TEST_F(WebEngineIntegrationTest, RemoteDebuggingPort) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Create a Context with remote debugging enabled via an ephemeral port. |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| create_params.set_remote_debugging_port(0); |
| |
| // Create a Frame with remote debugging enabled. |
| fuchsia::web::CreateFrameParams create_frame_params; |
| create_frame_params.set_enable_remote_debugging(true); |
| CreateContext(std::move(create_params)); |
| CreateFrameWithParams(std::move(create_frame_params)); |
| |
| // Expect to receive a notification of the selected DevTools port. |
| base::test::TestFuture<fuchsia::web::Context_GetRemoteDebuggingPort_Result> |
| port_receiver; |
| context_->GetRemoteDebuggingPort( |
| CallbackToFitFunction(port_receiver.GetCallback())); |
| ASSERT_TRUE(port_receiver.Wait()); |
| |
| ASSERT_TRUE(port_receiver.Get().is_response()); |
| uint16_t remote_debugging_port = port_receiver.Get().response().port; |
| ASSERT_TRUE(remote_debugging_port != 0); |
| |
| // Navigate to a URL. |
| GURL url = embedded_test_server_.GetURL("/defaultresponse"); |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(url.spec())); |
| navigation_listener()->RunUntilUrlEquals(url); |
| |
| base::Value::List devtools_list = |
| GetDevToolsListFromPort(remote_debugging_port); |
| EXPECT_EQ(devtools_list.size(), 1u); |
| |
| { |
| const auto* devtools_url = devtools_list[0].GetDict().FindString("url"); |
| ASSERT_TRUE(devtools_url); |
| EXPECT_EQ(*devtools_url, url); |
| } |
| |
| // Create a second frame, without remote debugging enabled. The remote |
| // debugging service should still report a single Frame is present. |
| fuchsia::web::FramePtr web_frame2; |
| web_frame2.set_error_handler( |
| [](zx_status_t status) { FAIL() << zx_status_get_string(status); }); |
| context()->CreateFrame(web_frame2.NewRequest()); |
| |
| devtools_list = GetDevToolsListFromPort(remote_debugging_port); |
| EXPECT_EQ(devtools_list.size(), 1u); |
| |
| { |
| const auto* devtools_url = devtools_list[0].GetDict().FindString("url"); |
| ASSERT_TRUE(devtools_url); |
| EXPECT_EQ(*devtools_url, url); |
| } |
| |
| // Tear down the debuggable Frame. The remote debugging service should have |
| // shut down. |
| base::RunLoop controller_run_loop; |
| auto navigation_controller = CreateNavigationController(); |
| navigation_controller.set_error_handler( |
| [&controller_run_loop](zx_status_t) { controller_run_loop.Quit(); }); |
| frame_.Unbind(); |
| |
| // Wait until the NavigationController shuts down to ensure WebEngine has |
| // handled the Frame tear down. |
| controller_run_loop.Run(); |
| |
| devtools_list = GetDevToolsListFromPort(remote_debugging_port); |
| EXPECT_TRUE(devtools_list.empty()); |
| } |
| |
| // Check that remote debugging requests for Frames in non-debuggable Contexts |
| // cause an error to be reported. |
| TEST_F(WebEngineIntegrationTest, RequestDebuggableFrameInNonDebuggableContext) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| fuchsia::web::CreateFrameParams create_frame_params; |
| create_frame_params.set_enable_remote_debugging(true); |
| CreateContext(TestContextParams()); |
| CreateFrameWithParams(std::move(create_frame_params)); |
| |
| base::RunLoop loop; |
| frame_.set_error_handler( |
| [quit_loop = loop.QuitClosure()](zx_status_t status) { |
| EXPECT_EQ(status, ZX_ERR_INVALID_ARGS); |
| quit_loop.Run(); |
| }); |
| loop.Run(); |
| } |
| |
| // Navigates to a resource served under the "testdata" ContentDirectory. |
| TEST_F(WebEngineIntegrationTest, ContentDirectoryProvider) { |
| const GURL kUrl("fuchsia-dir://testdata/title1.html"); |
| constexpr char kTitle[] = "title 1"; |
| |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(TestContextParamsWithTestData()); |
| |
| // Navigate to test1.html and verify that the resource was correctly |
| // downloaded and interpreted by inspecting the document title. |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(kUrl.spec())); |
| navigation_listener()->RunUntilUrlAndTitleEquals(kUrl, kTitle); |
| } |
| |
| class FakeAudioRenderer |
| : public fuchsia::media::testing::AudioRenderer_TestBase { |
| public: |
| FakeAudioRenderer() = default; |
| ~FakeAudioRenderer() override = default; |
| |
| void set_on_set_usage_callback(base::OnceClosure on_set_usage_callback) { |
| on_set_usage_callback_ = std::move(on_set_usage_callback); |
| } |
| |
| void Bind(fidl::InterfaceRequest<fuchsia::media::AudioRenderer> request) { |
| binding_.AddBinding(this, std::move(request)); |
| } |
| |
| const absl::optional<fuchsia::media::AudioRenderUsage>& usage() const { |
| return usage_; |
| } |
| |
| // AudioRenderer_TestBase overrides. |
| void SetUsage(fuchsia::media::AudioRenderUsage usage) override { |
| usage_ = usage; |
| if (on_set_usage_callback_) |
| std::move(on_set_usage_callback_).Run(); |
| } |
| void NotImplemented_(const std::string& name) override {} |
| |
| private: |
| fidl::BindingSet<fuchsia::media::AudioRenderer> binding_; |
| base::OnceClosure on_set_usage_callback_; |
| absl::optional<fuchsia::media::AudioRenderUsage> usage_; |
| }; |
| |
| class FakeAudio : public fuchsia::media::testing::Audio_TestBase { |
| public: |
| FakeAudio() = default; |
| ~FakeAudio() override = default; |
| |
| void Bind(fidl::InterfaceRequest<fuchsia::media::Audio> request) { |
| binding_.AddBinding(this, std::move(request)); |
| } |
| |
| FakeAudioRenderer& renderer() { return renderer_; } |
| |
| // Audio_TestBase overrides. |
| void CreateAudioRenderer( |
| fidl::InterfaceRequest<fuchsia::media::AudioRenderer> request) override { |
| renderer_.Bind(std::move(request)); |
| } |
| void NotImplemented_(const std::string& name) override { |
| FAIL() << "Not implemented: " << name; |
| } |
| |
| private: |
| fidl::BindingSet<fuchsia::media::Audio> binding_; |
| FakeAudioRenderer renderer_; |
| }; |
| |
| // Configures the default filtered service directory with a fake AudioConsumer |
| // service for testing. |
| class WebEngineIntegrationMediaTest : public WebEngineIntegrationTest { |
| protected: |
| WebEngineIntegrationMediaTest() |
| : fake_audio_consumer_service_(filtered_service_directory() |
| .outgoing_directory() |
| ->GetOrCreateDirectory("svc")) { |
| auto* outgoing_directory = |
| filtered_service_directory().outgoing_directory(); |
| |
| // Publish fake AudioDeviceEnumerator. |
| outgoing_directory |
| ->RemovePublicService<fuchsia::media::AudioDeviceEnumerator>(); |
| fake_audio_device_enumerator_.emplace( |
| outgoing_directory->GetOrCreateDirectory("svc")); |
| |
| // Intercept `fuchsia::media::Audio` connections in order to count them. |
| outgoing_directory->RemovePublicService<fuchsia::media::Audio>(); |
| zx_status_t status = outgoing_directory->AddPublicService( |
| fidl::InterfaceRequestHandler<fuchsia::media::Audio>( |
| [this](auto request) { |
| ++num_audio_connections_; |
| if (fake_audio_) { |
| fake_audio_->Bind(std::move(request)); |
| } else { |
| base::ComponentContextForProcess()->svc()->Connect( |
| std::move(request)); |
| } |
| })); |
| ZX_CHECK(status == ZX_OK, status) << "AddPublicService"; |
| } |
| |
| // Returns a CreateContextParams that has AUDIO feature, and the "testdata" |
| // content directory provider configured. |
| fuchsia::web::CreateContextParams ContextParamsWithAudioAndTestData() { |
| fuchsia::web::CreateContextParams create_params = |
| TestContextParamsWithTestData(); |
| *create_params.mutable_features() |= |
| fuchsia::web::ContextFeatureFlags::AUDIO; |
| return create_params; |
| } |
| |
| media::FakeAudioConsumerService fake_audio_consumer_service_; |
| absl::optional<media::FakeAudioDeviceEnumerator> |
| fake_audio_device_enumerator_; |
| absl::optional<FakeAudio> fake_audio_; |
| |
| size_t num_audio_connections_ = 0; |
| }; |
| |
| TEST_F(WebEngineIntegrationMediaTest, PlayAudioToAudioRenderer) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| LoadUrlAndExpectResponse("fuchsia-dir://testdata/play_audio.html", |
| CreateLoadUrlParamsWithUserActivation())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| |
| EXPECT_EQ(num_audio_connections_, 1U); |
| EXPECT_EQ(fake_audio_consumer_service_.num_instances(), 0U); |
| } |
| |
| TEST_F(WebEngineIntegrationMediaTest, PlayAudioToAudioRendererWithUsage) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| base::RunLoop run_loop; |
| fake_audio_.emplace(); |
| fake_audio_->renderer().set_on_set_usage_callback(run_loop.QuitClosure()); |
| |
| static const fuchsia::media::AudioRenderUsage kTestRenderUsage = |
| fuchsia::media::AudioRenderUsage::SYSTEM_AGENT; |
| fuchsia::web::FrameMediaSettings media_settings; |
| media_settings.set_renderer_usage(kTestRenderUsage); |
| frame_->SetMediaSettings(std::move(media_settings)); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| LoadUrlAndExpectResponse("fuchsia-dir://testdata/play_audio.html", |
| CreateLoadUrlParamsWithUserActivation())); |
| |
| run_loop.Run(); |
| |
| EXPECT_EQ(num_audio_connections_, 1U); |
| EXPECT_EQ(fake_audio_->renderer().usage(), kTestRenderUsage); |
| } |
| |
| TEST_F(WebEngineIntegrationMediaTest, PlayAudioToAudioConsumer) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| // Send `FrameMediaSettings` with `audio_consumer_session_id`. This enables |
| // `AudioConsumer`. |
| static const uint16_t kTestMediaSessionId = 43; |
| fuchsia::web::FrameMediaSettings media_settings; |
| media_settings.set_audio_consumer_session_id(kTestMediaSessionId); |
| frame_->SetMediaSettings(std::move(media_settings)); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| LoadUrlAndExpectResponse("fuchsia-dir://testdata/play_audio.html", |
| CreateLoadUrlParamsWithUserActivation())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| |
| EXPECT_EQ(num_audio_connections_, 0U); |
| ASSERT_EQ(fake_audio_consumer_service_.num_instances(), 1U); |
| |
| auto pos = fake_audio_consumer_service_.instance(0)->GetMediaPosition(); |
| EXPECT_GT(pos, base::Seconds(2.0)); |
| EXPECT_LT(pos, base::Seconds(2.5)); |
| |
| EXPECT_EQ(fake_audio_consumer_service_.instance(0)->session_id(), |
| kTestMediaSessionId); |
| EXPECT_EQ(fake_audio_consumer_service_.instance(0)->volume(), 1.0); |
| EXPECT_FALSE(fake_audio_consumer_service_.instance(0)->is_muted()); |
| } |
| |
| // Check that audio cannot play when the AUDIO ContextFeatureFlag is not |
| // provided. |
| TEST_F(WebEngineIntegrationMediaTest, PlayAudio_NoFlag) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Both FilteredServiceDirectory and test data are needed. |
| fuchsia::web::CreateContextParams create_params = |
| TestContextParamsWithTestData(); |
| CreateContextAndFrame(std::move(create_params)); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| LoadUrlAndExpectResponse("fuchsia-dir://testdata/play_audio.html", |
| CreateLoadUrlParamsWithUserActivation())); |
| |
| // The file is still expected to play to the end. |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| |
| EXPECT_EQ(fake_audio_consumer_service_.num_instances(), 0U); |
| EXPECT_EQ(num_audio_connections_, 0U); |
| } |
| |
| TEST_F(WebEngineIntegrationMediaTest, PlayVideo) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| kAutoplayVp9OpusToEndUrl, CreateLoadUrlParamsWithUserActivation())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| |
| // Audio should be sent to AudioRenderer (created though `fuchsia.web.Audio`). |
| EXPECT_EQ(num_audio_connections_, 1U); |
| EXPECT_EQ(fake_audio_consumer_service_.num_instances(), 0U); |
| } |
| |
| void WebEngineIntegrationTest::RunPermissionTest(bool grant) { |
| CreateContextAndFrame(TestContextParamsWithTestData()); |
| |
| if (grant) { |
| GrantPermission(fuchsia::web::PermissionType::MICROPHONE, |
| "fuchsia-dir://testdata/"); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| "fuchsia-dir://testdata/check_mic_permission.html")); |
| |
| navigation_listener()->RunUntilTitleEquals(grant ? "granted" : "denied"); |
| } |
| |
| TEST_F(WebEngineIntegrationTest, PermissionDenied) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| RunPermissionTest(false); |
| } |
| |
| TEST_F(WebEngineIntegrationTest, PermissionGranted) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| RunPermissionTest(true); |
| } |
| |
| // TODO(crbug.com/1299352): Flaky. |
| TEST_F(WebEngineIntegrationMediaTest, |
| DISABLED_MicrophoneAccess_WithPermission) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| GrantPermission( |
| fuchsia::web::PermissionType::MICROPHONE, |
| embedded_test_server_.GetURL("/").DeprecatedGetOriginAsURL().spec()); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| embedded_test_server_.GetURL("/mic.html").spec())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| } |
| |
| TEST_F(WebEngineIntegrationMediaTest, MicrophoneAccess_WithoutPermission) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| embedded_test_server_.GetURL("/mic.html?NoPermission").spec())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended-NotFoundError"); |
| } |
| |
| TEST_F(WebEngineIntegrationMediaTest, SetBlockMediaLoading_Blocked) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| frame_->SetBlockMediaLoading(true); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| kAutoplayVp9OpusUrl, CreateLoadUrlParamsWithUserActivation())); |
| |
| // Check different indicators that media has not loaded and is not playing. |
| navigation_listener()->RunUntilTitleEquals("stalled"); |
| EXPECT_EQ(0 /*HAVE_NOTHING*/, |
| ExecuteJavaScriptWithDoubleResult("bear.readyState")); |
| EXPECT_EQ(0.0, ExecuteJavaScriptWithDoubleResult("bear.currentTime")); |
| EXPECT_FALSE(ExecuteJavaScriptWithBoolResult("isMetadataLoaded")); |
| } |
| |
| // Initially, set media blocking to be true. When media is unblocked, check that |
| // it begins playing, since autoplay=true. |
| TEST_F(WebEngineIntegrationMediaTest, SetBlockMediaLoading_AfterUnblock) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| frame_->SetBlockMediaLoading(true); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| kAutoplayVp9OpusUrl, CreateLoadUrlParamsWithUserActivation())); |
| |
| // Check that media loading has been blocked. |
| navigation_listener()->RunUntilTitleEquals("stalled"); |
| |
| // Unblock media from loading and see if media loads and plays, since |
| // autoplay=true. |
| frame_->SetBlockMediaLoading(false); |
| navigation_listener()->RunUntilTitleEquals("playing"); |
| EXPECT_TRUE(ExecuteJavaScriptWithBoolResult("isMetadataLoaded")); |
| } |
| |
| // Check that when autoplay=false and media loading was blocked after the |
| // element has started loading that media will play when play() is called. |
| TEST_F(WebEngineIntegrationMediaTest, |
| SetBlockMediaLoading_SetBlockedAfterLoading) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(ContextParamsWithAudioAndTestData()); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| kLoadVp9OpusUrl, CreateLoadUrlParamsWithUserActivation())); |
| |
| navigation_listener()->RunUntilTitleEquals("loaded"); |
| frame_->SetBlockMediaLoading(true); |
| ExecuteJavaScript(frame_.get(), "bear.play()"); |
| navigation_listener()->RunUntilTitleEquals("playing"); |
| } |
| |
| TEST_F(WebEngineIntegrationTest, WebGLContextAbsentWithoutVulkanFeature) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| CreateContextAndFrame(TestContextParams()); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| embedded_test_server_.GetURL("/webgl_presence.html").spec())); |
| |
| navigation_listener()->RunUntilLoaded(); |
| |
| EXPECT_EQ(navigation_listener()->title(), "absent"); |
| } |
| |
| #if defined(ARCH_CPU_ARM_FAMILY) |
| // TODO(crbug.com/1377994): Enable on ARM64 when bots support Vulkan. |
| #define MAYBE_VulkanWebEngineIntegrationTest \ |
| DISABLED_VulkanWebEngineIntegrationTest |
| #else |
| #define MAYBE_VulkanWebEngineIntegrationTest VulkanWebEngineIntegrationTest |
| #endif |
| class MAYBE_VulkanWebEngineIntegrationTest |
| : public WebEngineIntegrationMediaTest {}; |
| |
| TEST_F(MAYBE_VulkanWebEngineIntegrationTest, |
| WebGLContextPresentWithVulkanFeature) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| *create_params.mutable_features() |= |
| fuchsia::web::ContextFeatureFlags::VULKAN; |
| CreateContextAndFrame(std::move(create_params)); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| embedded_test_server_.GetURL("/webgl_presence.html").spec())); |
| |
| navigation_listener()->RunUntilLoaded(); |
| |
| EXPECT_EQ(navigation_listener()->title(), "present"); |
| } |
| |
| class WebEngineIntegrationCameraTest : public WebEngineIntegrationTest { |
| protected: |
| WebEngineIntegrationCameraTest() = default; |
| |
| void RunCameraTest(bool grant_permission); |
| }; |
| |
| void WebEngineIntegrationCameraTest::RunCameraTest(bool grant_permission) { |
| fuchsia::web::CreateContextParams create_params = TestContextParams(); |
| |
| media::FakeCameraDeviceWatcher fake_camera_device_watcher( |
| filtered_service_directory().outgoing_directory()); |
| |
| CreateContextAndFrame(std::move(create_params)); |
| |
| if (grant_permission) { |
| GrantPermission( |
| fuchsia::web::PermissionType::CAMERA, |
| embedded_test_server_.GetURL("/").DeprecatedGetOriginAsURL().spec()); |
| } |
| |
| const char* url = |
| grant_permission ? "/camera.html" : "/camera.html?NoPermission"; |
| ASSERT_NO_FATAL_FAILURE( |
| LoadUrlAndExpectResponse(embedded_test_server_.GetURL(url).spec())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| } |
| |
| TEST_F(WebEngineIntegrationCameraTest, CameraAccess_WithPermission) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| RunCameraTest(/*grant_permission=*/true); |
| } |
| |
| TEST_F(WebEngineIntegrationCameraTest, CameraAccess_WithoutPermission) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| RunCameraTest(/*grant_permission=*/false); |
| } |
| |
| TEST_F(WebEngineIntegrationCameraTest, CameraNoVideoCaptureProcess) { |
| base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| command_line.AppendSwitchASCII("disable-features", "MojoVideoCapture"); |
| StartWebEngine(std::move(command_line)); |
| RunCameraTest(/*grant_permission=*/true); |
| } |
| |
| // Check that when the ContextFeatureFlag HARDWARE_VIDEO_DECODER is |
| // provided that the CodecFactory service is connected to. |
| TEST_F(MAYBE_VulkanWebEngineIntegrationTest, |
| HardwareVideoDecoderFlag_Provided) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| // Check that the CodecFactory service is requested. |
| base::RunLoop codec_connected_run_loop; |
| auto* outgoing_directory = filtered_service_directory().outgoing_directory(); |
| outgoing_directory->RemovePublicService<fuchsia::mediacodec::CodecFactory>(); |
| zx_status_t status = outgoing_directory->AddPublicService( |
| fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>( |
| [&codec_connected_run_loop](auto request) { |
| codec_connected_run_loop.Quit(); |
| })); |
| ZX_CHECK(status == ZX_OK, status) << "AddPublicService"; |
| |
| // The VULKAN flag is required for hardware video decoders to be available. |
| fuchsia::web::CreateContextParams create_params = |
| ContextParamsWithAudioAndTestData(); |
| *create_params.mutable_features() |= |
| fuchsia::web::ContextFeatureFlags::VULKAN | |
| fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER; |
| CreateContextAndFrame(std::move(create_params)); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| kAutoplayVp9OpusToEndUrl, CreateLoadUrlParamsWithUserActivation())); |
| codec_connected_run_loop.Run(); |
| } |
| |
| // Check that the CodecFactory service is not requested when |
| // HARDWARE_VIDEO_DECODER is not provided. |
| // The video should use software decoders and still play. |
| TEST_F(WebEngineIntegrationMediaTest, HardwareVideoDecoderFlag_NotProvided) { |
| StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM)); |
| bool is_requested = false; |
| auto* outgoing_directory = filtered_service_directory().outgoing_directory(); |
| outgoing_directory->RemovePublicService<fuchsia::mediacodec::CodecFactory>(); |
| zx_status_t status = outgoing_directory->AddPublicService( |
| fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>( |
| [&is_requested](auto request) { is_requested = true; })); |
| ZX_CHECK(status == ZX_OK, status) << "AddPublicService"; |
| |
| fuchsia::web::CreateContextParams create_params = |
| ContextParamsWithAudioAndTestData(); |
| CreateContextAndFrame(std::move(create_params)); |
| |
| ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse( |
| kAutoplayVp9OpusToEndUrl, CreateLoadUrlParamsWithUserActivation())); |
| |
| navigation_listener()->RunUntilTitleEquals("ended"); |
| |
| EXPECT_FALSE(is_requested); |
| } |