diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/fuchsia | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/fuchsia')
110 files changed, 2976 insertions, 707 deletions
diff --git a/chromium/fuchsia/BUILD.gn b/chromium/fuchsia/BUILD.gn index fe99326c784..f849593928b 100644 --- a/chromium/fuchsia/BUILD.gn +++ b/chromium/fuchsia/BUILD.gn @@ -4,8 +4,6 @@ assert(is_fuchsia) -import("//build/buildflag_header.gni") -import("//fuchsia/release_channel.gni") import("//third_party/fuchsia-sdk/sdk/build/fidl_library.gni") fidl_library("cast_fidl") { diff --git a/chromium/fuchsia/base/BUILD.gn b/chromium/fuchsia/base/BUILD.gn index 2dba9680aad..4e0d73dd164 100644 --- a/chromium/fuchsia/base/BUILD.gn +++ b/chromium/fuchsia/base/BUILD.gn @@ -4,30 +4,37 @@ assert(is_fuchsia) -import("//build/buildflag_header.gni") import("//build/config/fuchsia/generate_runner_scripts.gni") -import("//fuchsia/release_channel.gni") import("//testing/test.gni") # Integration helpers for commonly used fuchsia.* APIs. source_set("base") { sources = [ "config_reader.cc", + "feedback_registration.cc", "fuchsia_dir_scheme.cc", "init_logging.cc", + "inspect.cc", "mem_buffer_util.cc", "string_util.cc", ] public = [ "config_reader.h", + "feedback_registration.h", "fuchsia_dir_scheme.h", "init_logging.h", + "inspect.h", "mem_buffer_util.h", "string_util.h", ] deps = [ "//base", + "//components/version_info", + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.feedback", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem", + "//third_party/fuchsia-sdk/sdk/pkg/fdio", + "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", + "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp", "//url", ] } @@ -78,13 +85,6 @@ source_set("legacymetrics") { friend = [ ":*" ] } -# Used to propagate release-qualified package names to integration test code. -buildflag_header("release_channel_buildflags") { - header = "release_channel.h" - flags = [ "FUCHSIA_RELEASE_CHANNEL_SUFFIX=\"$release_channel_suffix\"" ] - visibility = [ "//fuchsia/*" ] -} - source_set("test_support") { testonly = true sources = [ @@ -106,7 +106,6 @@ source_set("test_support") { public_deps = [ ":base", ":modular", - ":release_channel_buildflags", "//base", "//net", "//net:test_support", @@ -120,6 +119,7 @@ source_set("test_support") { test("cr_fuchsia_base_unittests") { sources = [ "agent_impl_unittests.cc", + "inspect_unittest.cc", "legacymetrics_client_unittest.cc", "legacymetrics_histogram_flattener_unittest.cc", "legacymetrics_user_event_recorder_unittest.cc", @@ -132,6 +132,9 @@ test("cr_fuchsia_base_unittests") { "//base:testfidl", "//base/test:run_all_unittests", "//base/test:test_support", + "//components/version_info", "//testing/gtest", + "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", + "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp", ] } diff --git a/chromium/fuchsia/base/DEPS b/chromium/fuchsia/base/DEPS index 9455f2ca503..5ded5f1b3d8 100644 --- a/chromium/fuchsia/base/DEPS +++ b/chromium/fuchsia/base/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/version_info", "+mojo/public", "+third_party/blink/public/common/messaging", "+third_party/blink/public/mojom/messaging", diff --git a/chromium/fuchsia/base/agent_impl.cc b/chromium/fuchsia/base/agent_impl.cc index b692599f2a2..4ecd277425e 100644 --- a/chromium/fuchsia/base/agent_impl.cc +++ b/chromium/fuchsia/base/agent_impl.cc @@ -7,7 +7,6 @@ #include <lib/sys/cpp/component_context.h> #include "base/bind.h" -#include "base/fuchsia/default_context.h" namespace cr_fuchsia { diff --git a/chromium/fuchsia/base/agent_impl.h b/chromium/fuchsia/base/agent_impl.h index b630dc8bb6c..389efb30b28 100644 --- a/chromium/fuchsia/base/agent_impl.h +++ b/chromium/fuchsia/base/agent_impl.h @@ -15,7 +15,6 @@ #include "base/bind.h" #include "base/containers/flat_map.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/service_provider_impl.h" #include "base/macros.h" diff --git a/chromium/fuchsia/base/agent_manager.h b/chromium/fuchsia/base/agent_manager.h index 7f60750e471..17fd60c254c 100644 --- a/chromium/fuchsia/base/agent_manager.h +++ b/chromium/fuchsia/base/agent_manager.h @@ -10,7 +10,6 @@ #include <lib/sys/cpp/component_context.h> #include "base/containers/flat_map.h" -#include "base/fuchsia/default_context.h" #include "base/macros.h" #include "base/strings/string_piece.h" diff --git a/chromium/fuchsia/base/config_reader.cc b/chromium/fuchsia/base/config_reader.cc index 87eaab8e48c..0c73357e93a 100644 --- a/chromium/fuchsia/base/config_reader.cc +++ b/chromium/fuchsia/base/config_reader.cc @@ -29,14 +29,14 @@ base::Optional<base::Value> ReadPackageConfig() { return base::nullopt; } - base::JSONReader reader; - base::Optional<base::Value> parsed = reader.Read(file_content); - CHECK(parsed) << "Failed to parse " << path.value() << ": " - << reader.GetErrorMessage(); - CHECK(parsed->is_dict()) << "Config is not a JSON dictinary: " - << path.value(); - - return std::move(parsed.value()); + base::JSONReader::ValueWithError parsed = + base::JSONReader::ReadAndReturnValueWithError(file_content); + CHECK(parsed.value) << "Failed to parse " << path.value() << ": " + << parsed.error_message; + CHECK(parsed.value->is_dict()) + << "Config is not a JSON dictionary: " << path.value(); + + return std::move(parsed.value); } } // namespace diff --git a/chromium/fuchsia/base/context_provider_test_connector.cc b/chromium/fuchsia/base/context_provider_test_connector.cc index ba5f1234432..307f0bd9d0f 100644 --- a/chromium/fuchsia/base/context_provider_test_connector.cc +++ b/chromium/fuchsia/base/context_provider_test_connector.cc @@ -12,10 +12,8 @@ #include <zircon/processargs.h> #include <utility> -#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" -#include "base/strings/strcat.h" -#include "fuchsia/base/release_channel.h" +#include "base/fuchsia/process_context.h" namespace cr_fuchsia { @@ -24,9 +22,8 @@ fidl::InterfaceHandle<fuchsia::io::Directory> StartWebEngineForTests( component_controller_request, const base::CommandLine& command_line) { fuchsia::sys::LaunchInfo launch_info; - launch_info.url = base::StrCat({"fuchsia-pkg://fuchsia.com/web_engine", - BUILDFLAG(FUCHSIA_RELEASE_CHANNEL_SUFFIX), - "#meta/context_provider.cmx"}); + launch_info.url = + "fuchsia-pkg://fuchsia.com/web_engine#meta/context_provider.cmx"; launch_info.arguments = command_line.argv(); // Clone stderr from the current process to WebEngine and ask it to @@ -43,8 +40,7 @@ fidl::InterfaceHandle<fuchsia::io::Directory> StartWebEngineForTests( web_engine_services_dir.NewRequest().TakeChannel(); fuchsia::sys::LauncherPtr launcher; - base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect( - launcher.NewRequest()); + base::ComponentContextForProcess()->svc()->Connect(launcher.NewRequest()); launcher->CreateComponent(std::move(launch_info), std::move(component_controller_request)); diff --git a/chromium/fuchsia/base/context_provider_test_connector.h b/chromium/fuchsia/base/context_provider_test_connector.h index 0d5ca1fd707..5c4604b25bc 100644 --- a/chromium/fuchsia/base/context_provider_test_connector.h +++ b/chromium/fuchsia/base/context_provider_test_connector.h @@ -20,7 +20,6 @@ fidl::InterfaceHandle<fuchsia::io::Directory> StartWebEngineForTests( const base::CommandLine& command_line = base::CommandLine(base::CommandLine::NO_PROGRAM)); -// TODO(crbug.com/1046615): Use test manifests for package specification. fuchsia::web::ContextProviderPtr ConnectContextProvider( fidl::InterfaceRequest<fuchsia::sys::ComponentController> component_controller_request, diff --git a/chromium/fuchsia/base/feedback_registration.cc b/chromium/fuchsia/base/feedback_registration.cc new file mode 100644 index 00000000000..d8bee5ff647 --- /dev/null +++ b/chromium/fuchsia/base/feedback_registration.cc @@ -0,0 +1,30 @@ +// Copyright 2020 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/base/feedback_registration.h" + +#include <fuchsia/feedback/cpp/fidl.h> +#include <lib/sys/cpp/component_context.h> + +#include "base/fuchsia/process_context.h" +#include "base/strings/string_piece.h" +#include "components/version_info/version_info.h" + +namespace cr_fuchsia { + +void RegisterCrashReportingFields(base::StringPiece component_url, + base::StringPiece crash_product_name) { + fuchsia::feedback::CrashReportingProduct product_data; + product_data.set_name(crash_product_name.as_string()); + product_data.set_version(version_info::GetVersionNumber()); + // TODO(https://crbug.com/1077428): Use the actual channel when appropriate. + // For now, always set it to the empty string to avoid reporting "missing". + product_data.set_channel(""); + base::ComponentContextForProcess() + ->svc() + ->Connect<fuchsia::feedback::CrashReportingProductRegister>() + ->Upsert(component_url.as_string(), std::move(product_data)); +} + +} // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/feedback_registration.h b/chromium/fuchsia/base/feedback_registration.h new file mode 100644 index 00000000000..7e983bf6a70 --- /dev/null +++ b/chromium/fuchsia/base/feedback_registration.h @@ -0,0 +1,22 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_BASE_FEEDBACK_REGISTRATION_H_ +#define FUCHSIA_BASE_FEEDBACK_REGISTRATION_H_ + +#include "base/strings/string_piece_forward.h" + +namespace cr_fuchsia { + +// Overrides the default Fuchsia product info in crash reports. +// Crashes for the component |component_url| will contain |crash_product_name|, +// the version from version_info, and an appropriate value for the release +// channel. |component_url| must match the current component. The calling +// process must have access to "fuchsia.feedback.CrashReportingProductRegister". +void RegisterCrashReportingFields(base::StringPiece component_url, + base::StringPiece crash_product_name); + +} // namespace cr_fuchsia + +#endif // FUCHSIA_BASE_FEEDBACK_REGISTRATION_H_
\ No newline at end of file diff --git a/chromium/fuchsia/base/frame_test_util.cc b/chromium/fuchsia/base/frame_test_util.cc index c234b3a97fd..2150cd86360 100644 --- a/chromium/fuchsia/base/frame_test_util.cc +++ b/chromium/fuchsia/base/frame_test_util.cc @@ -54,4 +54,19 @@ fuchsia::web::LoadUrlParams CreateLoadUrlParamsWithUserActivation() { return load_url_params; } +fuchsia::web::WebMessage CreateWebMessageWithMessagePortRequest( + fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request, + fuchsia::mem::Buffer buffer) { + fuchsia::web::OutgoingTransferable outgoing; + outgoing.set_message_port(std::move(message_port_request)); + + std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; + outgoing_vector.push_back(std::move(outgoing)); + + fuchsia::web::WebMessage web_message; + web_message.set_outgoing_transfer(std::move(outgoing_vector)); + web_message.set_data(std::move(buffer)); + return web_message; +} + } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/frame_test_util.h b/chromium/fuchsia/base/frame_test_util.h index 2c2ee036456..20534eaf5e9 100644 --- a/chromium/fuchsia/base/frame_test_util.h +++ b/chromium/fuchsia/base/frame_test_util.h @@ -31,6 +31,12 @@ base::Optional<base::Value> ExecuteJavaScript(fuchsia::web::Frame* frame, // autoplay to be used, which is used by many media tests. fuchsia::web::LoadUrlParams CreateLoadUrlParamsWithUserActivation(); +// Creates a WebMessage with one outgoing transferable set to +// |message_port_request| and data set to |buffer|. +fuchsia::web::WebMessage CreateWebMessageWithMessagePortRequest( + fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request, + fuchsia::mem::Buffer buffer); + } // namespace cr_fuchsia #endif // FUCHSIA_BASE_FRAME_TEST_UTIL_H_ diff --git a/chromium/fuchsia/base/inspect.cc b/chromium/fuchsia/base/inspect.cc new file mode 100644 index 00000000000..775ae986618 --- /dev/null +++ b/chromium/fuchsia/base/inspect.cc @@ -0,0 +1,28 @@ +// Copyright 2020 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/base/inspect.h" + +#include <lib/sys/inspect/cpp/component.h> + +#include "components/version_info/version_info.h" + +namespace cr_fuchsia { + +namespace { +const char kVersion[] = "version"; +const char kLastChange[] = "last_change_revision"; +} // namespace + +void PublishVersionInfoToInspect(sys::ComponentInspector* inspector) { + // These values are managed by the inspector, since they won't be updated over + // the lifetime of the component. + // TODO(https://crbug.com/1077428): Add release channel. + inspector->root().CreateString(kVersion, version_info::GetVersionNumber(), + inspector); + inspector->root().CreateString(kLastChange, version_info::GetLastChange(), + inspector); +} + +} // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/inspect.h b/chromium/fuchsia/base/inspect.h new file mode 100644 index 00000000000..4d8d35335ee --- /dev/null +++ b/chromium/fuchsia/base/inspect.h @@ -0,0 +1,20 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_BASE_INSPECT_H_ +#define FUCHSIA_BASE_INSPECT_H_ + +namespace sys { +class ComponentInspector; +} // namespace sys + +namespace cr_fuchsia { + +// Publish the Chromium version via the Inspect API. The lifetime of +// |inspector| has to be the same as the component it belongs to. +void PublishVersionInfoToInspect(sys::ComponentInspector* inspector); + +} // namespace cr_fuchsia + +#endif // FUCHSIA_BASE_INSPECT_H_
\ No newline at end of file diff --git a/chromium/fuchsia/base/inspect_unittest.cc b/chromium/fuchsia/base/inspect_unittest.cc new file mode 100644 index 00000000000..7a2bff50c81 --- /dev/null +++ b/chromium/fuchsia/base/inspect_unittest.cc @@ -0,0 +1,95 @@ +// Copyright 2020 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/base/inspect.h" + +#include <lib/fdio/directory.h> +#include <lib/inspect/cpp/hierarchy.h> +#include <lib/inspect/cpp/reader.h> +#include <lib/inspect/service/cpp/reader.h> +#include <lib/sys/cpp/component_context.h> +#include <lib/sys/inspect/cpp/component.h> +#include <cstdint> +#include <memory> + +#include "base/task/single_thread_task_executor.h" +#include "base/test/task_environment.h" +#include "components/version_info/version_info.h" +#include "fuchsia/base/mem_buffer_util.h" +#include "fuchsia/base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cr_fuchsia { + +namespace { + +const char kVersion[] = "version"; +const char kLastChange[] = "last_change_revision"; + +class InspectTest : public ::testing::Test { + public: + InspectTest() { + fidl::InterfaceHandle<fuchsia::io::Directory> incoming_directory; + auto incoming_services = + std::make_shared<sys::ServiceDirectory>(std::move(incoming_directory)); + context_ = std::make_unique<sys::ComponentContext>( + std::move(incoming_services), + published_root_directory_.NewRequest().TakeChannel()); + inspector_ = std::make_unique<sys::ComponentInspector>(context_.get()); + base::RunLoop().RunUntilIdle(); + } + + InspectTest(const InspectTest&) = delete; + InspectTest& operator=(const InspectTest&) = delete; + + protected: + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; + std::unique_ptr<sys::ComponentContext> context_; + fidl::InterfaceHandle<fuchsia::io::Directory> published_root_directory_; + std::unique_ptr<sys::ComponentInspector> inspector_; +}; + +} // namespace + +TEST_F(InspectTest, PublishVersionInfoToInspect) { + cr_fuchsia::PublishVersionInfoToInspect(inspector_.get()); + fidl::InterfaceHandle<fuchsia::io::Directory> directory; + zx_status_t status = fdio_service_connect_at( + published_root_directory_.channel().get(), "diagnostics", + directory.NewRequest().TakeChannel().release()); + ASSERT_EQ(ZX_OK, status); + std::unique_ptr<sys::ServiceDirectory> diagnostics = + std::make_unique<sys::ServiceDirectory>(std::move(directory)); + + // Access the inspect::Tree where the data is served. |tree| is in the + // directory created for the test, not the diagnostics directory for the test + // component. + fuchsia::inspect::TreePtr tree; + diagnostics->Connect(tree.NewRequest()); + fuchsia::inspect::TreeContent content; + base::RunLoop run_loop; + tree->GetContent([&content, &run_loop](fuchsia::inspect::TreeContent c) { + content = std::move(c); + run_loop.Quit(); + }); + run_loop.Run(); + + // Parse the data as an inspect::Hierarchy. + ASSERT_TRUE(content.has_buffer()); + std::string buffer_data; + cr_fuchsia::StringFromMemBuffer(content.buffer(), &buffer_data); + inspect::Hierarchy hierarchy = + inspect::ReadFromBuffer(cr_fuchsia::StringToBytes(buffer_data)) + .take_value(); + + auto* property = + hierarchy.node().get_property<inspect::StringPropertyValue>(kVersion); + EXPECT_EQ(property->value(), version_info::GetVersionNumber()); + property = + hierarchy.node().get_property<inspect::StringPropertyValue>(kLastChange); + EXPECT_EQ(property->value(), version_info::GetLastChange()); +} + +} // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/legacymetrics_client.cc b/chromium/fuchsia/base/legacymetrics_client.cc index a2d45a83ac8..58f384124c1 100644 --- a/chromium/fuchsia/base/legacymetrics_client.cc +++ b/chromium/fuchsia/base/legacymetrics_client.cc @@ -11,8 +11,8 @@ #include <utility> #include <vector> -#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -34,11 +34,13 @@ void LegacyMetricsClient::Start(base::TimeDelta report_interval) { DCHECK(!metrics_recorder_) << "Start() called more than once."; report_interval_ = report_interval; - metrics_recorder_ = base::fuchsia::ComponentContextForCurrentProcess() + metrics_recorder_ = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::legacymetrics::MetricsRecorder>(); metrics_recorder_.set_error_handler(fit::bind_member( this, &LegacyMetricsClient::OnMetricsRecorderDisconnected)); + metrics_recorder_.events().OnCloseSoon = + fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon); user_events_recorder_ = std::make_unique<LegacyMetricsUserActionRecorder>(); ScheduleNextReport(); } @@ -54,7 +56,18 @@ void LegacyMetricsClient::SetReportAdditionalMetricsCallback( report_additional_callback_ = std::move(callback); } +void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(callback); + DCHECK(!metrics_recorder_) + << "SetNotifyFlushCallback() must be called before Start()."; + + notify_flush_callback_ = std::move(callback); +} + void LegacyMetricsClient::ScheduleNextReport() { + DCHECK(!is_flushing_); + DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds() << "seconds."; timer_.Start(FROM_HERE, report_interval_, this, @@ -91,44 +104,70 @@ void LegacyMetricsClient::Report( } } - if (events.empty()) { - ScheduleNextReport(); - return; - } + std::move(events.begin(), events.end(), std::back_inserter(to_send_)); - DrainBuffer(std::move(events)); + DrainBuffer(); } -void LegacyMetricsClient::DrainBuffer( - std::vector<fuchsia::legacymetrics::Event> buffer) { - if (buffer.empty()) { +void LegacyMetricsClient::DrainBuffer() { + DVLOG(1) << __func__ << " called."; + + if (record_ack_pending_) { + // There is a Record() call already inflight. When it is acknowledged, + // buffer draining will continue. + return; + } + + if (to_send_.empty()) { DVLOG(1) << "Buffer drained."; - ScheduleNextReport(); + + if (is_flushing_) { + metrics_recorder_.Unbind(); + } else { + ScheduleNextReport(); + } + return; } - // Since ordering doesn't matter, we can efficiently drain |buffer| by + // Since ordering doesn't matter, we can efficiently drain |to_send_| by // repeatedly sending and truncating its tail. - const size_t batch_size = std::min(buffer.size(), kMaxBatchSize); - const size_t batch_start_idx = buffer.size() - batch_size; + const size_t batch_size = std::min(to_send_.size(), kMaxBatchSize); + const size_t batch_start_idx = to_send_.size() - batch_size; std::vector<fuchsia::legacymetrics::Event> batch; batch.resize(batch_size); - std::move(buffer.begin() + batch_start_idx, buffer.end(), batch.begin()); - buffer.resize(buffer.size() - batch_size); - - metrics_recorder_->Record(std::move(batch), - [this, buffer = std::move(buffer)]() mutable { - DrainBuffer(std::move(buffer)); - }); + std::move(to_send_.begin() + batch_start_idx, to_send_.end(), batch.begin()); + to_send_.resize(to_send_.size() - batch_size); + + record_ack_pending_ = true; + metrics_recorder_->Record(std::move(batch), [this]() { + record_ack_pending_ = false; + DrainBuffer(); + }); } void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) { - ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status) - << "MetricsRecorder connection lost."; + ZX_LOG(ERROR, status) << "MetricsRecorder connection lost."; // Stop recording & reporting user events. user_events_recorder_.reset(); timer_.AbandonAndStop(); } +void LegacyMetricsClient::OnCloseSoon() { + DVLOG(1) << __func__ << " called."; + + timer_.AbandonAndStop(); + + is_flushing_ = true; + if (notify_flush_callback_) { + // Defer reporting until the flush operation has finished. + std::move(notify_flush_callback_) + .Run(base::BindOnce(&LegacyMetricsClient::StartReport, + weak_factory_.GetWeakPtr())); + } else { + StartReport(); + } +} + } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/legacymetrics_client.h b/chromium/fuchsia/base/legacymetrics_client.h index 98576a1f410..b9ec76a2a1f 100644 --- a/chromium/fuchsia/base/legacymetrics_client.h +++ b/chromium/fuchsia/base/legacymetrics_client.h @@ -31,6 +31,8 @@ class LegacyMetricsClient { using ReportAdditionalMetricsCallback = base::RepeatingCallback<void( base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>)>; + using NotifyFlushCallback = + base::OnceCallback<void(base::OnceClosure completion_cb)>; LegacyMetricsClient(); ~LegacyMetricsClient(); @@ -50,18 +52,27 @@ class LegacyMetricsClient { void SetReportAdditionalMetricsCallback( ReportAdditionalMetricsCallback callback); + // Sets a |callback| which is invoked to warn that the connection to the + // remote MetricsRecorder will be terminated. The completion closure passed to + // |callback| should be invoked to signal flush completion. + void SetNotifyFlushCallback(NotifyFlushCallback callback); + private: void ScheduleNextReport(); void StartReport(); void Report(std::vector<fuchsia::legacymetrics::Event> additional_metrics); void OnMetricsRecorderDisconnected(zx_status_t status); + void OnCloseSoon(); - // Incrementally sends the contents of |buffer| to |metrics_recorder|, and - // invokes |done_cb| when finished. - void DrainBuffer(std::vector<fuchsia::legacymetrics::Event> buffer); + // Incrementally sends the contents of |to_send_| to |metrics_recorder_|. + void DrainBuffer(); base::TimeDelta report_interval_; ReportAdditionalMetricsCallback report_additional_callback_; + NotifyFlushCallback notify_flush_callback_; + bool is_flushing_ = false; + bool record_ack_pending_ = false; + std::vector<fuchsia::legacymetrics::Event> to_send_; std::unique_ptr<LegacyMetricsUserActionRecorder> user_events_recorder_; fuchsia::legacymetrics::MetricsRecorderPtr metrics_recorder_; diff --git a/chromium/fuchsia/base/legacymetrics_client_unittest.cc b/chromium/fuchsia/base/legacymetrics_client_unittest.cc index 8c735bbbffe..1b48827418a 100644 --- a/chromium/fuchsia/base/legacymetrics_client_unittest.cc +++ b/chromium/fuchsia/base/legacymetrics_client_unittest.cc @@ -14,6 +14,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "fuchsia/base/legacymetrics_client.h" #include "fuchsia/base/legacymetrics_histogram_flattener.h" +#include "fuchsia/base/result_receiver.h" #include "testing/gtest/include/gtest/gtest.h" namespace cr_fuchsia { @@ -45,10 +46,21 @@ class TestMetricsRecorder ack_callback_ = base::nullopt; } + void set_expect_ack_dropped(bool expect_dropped) { + expect_ack_dropped_ = expect_dropped; + } + // fuchsia::legacymetrics::MetricsRecorder implementation. void Record(std::vector<fuchsia::legacymetrics::Event> events, RecordCallback callback) override { - recorded_events_ = std::move(events); + std::move(events.begin(), events.end(), + std::back_inserter(recorded_events_)); + + // Received a call to Record() before the previous one was acknowledged, + // which can happen in some cases (e.g. flushing). + if (ack_callback_) + EXPECT_TRUE(expect_ack_dropped_); + ack_callback_ = std::move(callback); if (on_record_cb_) @@ -61,6 +73,7 @@ class TestMetricsRecorder std::vector<fuchsia::legacymetrics::Event> recorded_events_; base::OnceClosure on_record_cb_; base::Optional<RecordCallback> ack_callback_; + bool expect_ack_dropped_ = false; }; class LegacyMetricsClientTest : public testing::Test { @@ -71,9 +84,10 @@ class LegacyMetricsClientTest : public testing::Test { ~LegacyMetricsClientTest() override = default; void SetUp() override { - service_binding_ = std::make_unique<base::fuchsia::ScopedServiceBinding< - fuchsia::legacymetrics::MetricsRecorder>>( - test_context_.additional_services(), &test_recorder_); + service_binding_ = + std::make_unique<base::fuchsia::ScopedSingleClientServiceBinding< + fuchsia::legacymetrics::MetricsRecorder>>( + test_context_.additional_services(), &test_recorder_); base::SetRecordActionTaskRunner(base::ThreadTaskRunnerHandle::Get()); // Flush any dirty histograms from previous test runs in this process. @@ -84,7 +98,7 @@ class LegacyMetricsClientTest : public testing::Test { base::test::TaskEnvironment task_environment_; base::TestComponentContextForProcess test_context_; TestMetricsRecorder test_recorder_; - std::unique_ptr<base::fuchsia::ScopedServiceBinding< + std::unique_ptr<base::fuchsia::ScopedSingleClientServiceBinding< fuchsia::legacymetrics::MetricsRecorder>> service_binding_; LegacyMetricsClient client_; @@ -216,5 +230,92 @@ TEST_F(LegacyMetricsClientTest, Batching) { test_recorder_.SendAck(); } +TEST_F(LegacyMetricsClientTest, FlushWithPending) { + client_.Start(kReportInterval); + base::RunLoop().RunUntilIdle(); + + UMA_HISTOGRAM_COUNTS_1M("foo", 20); + + EXPECT_FALSE(test_recorder_.IsRecordInFlight()); + service_binding_->events().OnCloseSoon(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + // The service should be unbound once all data is drained. + EXPECT_TRUE(service_binding_->has_clients()); + auto events = test_recorder_.WaitForEvents(); + test_recorder_.SendAck(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ("foo", events[0].histogram().name()); + EXPECT_FALSE(service_binding_->has_clients()); +} + +TEST_F(LegacyMetricsClientTest, FlushNoData) { + client_.Start(kReportInterval); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(service_binding_->has_clients()); + EXPECT_FALSE(test_recorder_.IsRecordInFlight()); + service_binding_->events().OnCloseSoon(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(service_binding_->has_clients()); +} + +TEST_F(LegacyMetricsClientTest, FlushWithOutstandingAck) { + client_.Start(kReportInterval); + base::RunLoop().RunUntilIdle(); + + // Send "foo", but don't ack. + UMA_HISTOGRAM_COUNTS_1M("foo", 20); + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + // Allow the flush operation to call Record() without waiting for a prior ack. + test_recorder_.set_expect_ack_dropped(true); + + // Buffer another event and trigger a flush. + UMA_HISTOGRAM_COUNTS_1M("bar", 20); + EXPECT_TRUE(service_binding_->has_clients()); + service_binding_->events().OnCloseSoon(); + + // Simulate an asynchronous ack from the recorder, which be delivered around + // the same time as the flush's Record() call. The ack should be gracefully + // ignored by the client. + test_recorder_.SendAck(); + + base::RunLoop().RunUntilIdle(); + + auto events = test_recorder_.WaitForEvents(); + test_recorder_.SendAck(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ("foo", events[0].histogram().name()); + EXPECT_EQ("bar", events[1].histogram().name()); + EXPECT_FALSE(service_binding_->has_clients()); +} + +TEST_F(LegacyMetricsClientTest, ExternalFlushSignal) { + ResultReceiver<base::OnceClosure> flush_receiver; + client_.SetNotifyFlushCallback(flush_receiver.GetReceiveCallback()); + client_.Start(kReportInterval); + base::RunLoop().RunUntilIdle(); + + UMA_HISTOGRAM_COUNTS_1M("foo", 20); + + // Verify that reporting does not start until the flush completion callback is + // run. + EXPECT_FALSE(test_recorder_.IsRecordInFlight()); + service_binding_->events().OnCloseSoon(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(test_recorder_.IsRecordInFlight()); + + // Verify that invoking the completion callback unblocks reporting. + EXPECT_TRUE(flush_receiver.has_value()); + std::move(*flush_receiver).Run(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); +} + } // namespace } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/legacymetrics_histogram_flattener.cc b/chromium/fuchsia/base/legacymetrics_histogram_flattener.cc index e05d87ced9a..b6d728143c1 100644 --- a/chromium/fuchsia/base/legacymetrics_histogram_flattener.cc +++ b/chromium/fuchsia/base/legacymetrics_histogram_flattener.cc @@ -8,6 +8,7 @@ #include <utility> #include <vector> +#include "base/logging.h" #include "base/metrics/statistics_recorder.h" namespace cr_fuchsia { diff --git a/chromium/fuchsia/cast_streaming/BUILD.gn b/chromium/fuchsia/cast_streaming/BUILD.gn index c691165e93d..94d567cc610 100644 --- a/chromium/fuchsia/cast_streaming/BUILD.gn +++ b/chromium/fuchsia/cast_streaming/BUILD.gn @@ -11,16 +11,23 @@ source_set("cast_streaming") { "//components/openscreen_platform:openscreen_platform_network_service", "//fuchsia/base", "//media", + "//media/mojo/common", + "//media/mojo/mojom", + "//mojo/public/cpp/system", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web", "//third_party/openscreen/src/cast/streaming:receiver", "//third_party/openscreen/src/platform:api", "//third_party/openscreen/src/util", ] visibility = [ "//fuchsia/engine/*" ] - public = [ "public/cast_streaming_session.h" ] + public = [ + "public/cast_streaming.h", + "public/cast_streaming_session.h", + ] sources = [ "cast_message_port_impl.cc", "cast_message_port_impl.h", + "cast_streaming.cc", "cast_streaming_session.cc", "stream_consumer.cc", "stream_consumer.h", diff --git a/chromium/fuchsia/cast_streaming/DEPS b/chromium/fuchsia/cast_streaming/DEPS index 13d521158c3..5208c523d56 100644 --- a/chromium/fuchsia/cast_streaming/DEPS +++ b/chromium/fuchsia/cast_streaming/DEPS @@ -1,5 +1,7 @@ include_rules = [ "+components/openscreen_platform", "+media/base", + "+media/mojo", + "+mojo/public", "+third_party/openscreen/src", ] diff --git a/chromium/fuchsia/cast_streaming/cast_streaming.cc b/chromium/fuchsia/cast_streaming/cast_streaming.cc new file mode 100644 index 00000000000..2044900b05b --- /dev/null +++ b/chromium/fuchsia/cast_streaming/cast_streaming.cc @@ -0,0 +1,24 @@ +// Copyright 2020 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/cast_streaming/public/cast_streaming.h" + +#include "base/strings/string_piece.h" + +namespace cast_streaming { + +bool IsCastStreamingAppOrigin(base::StringPiece origin) { + constexpr char kCastStreamingMessagePortOrigin[] = "cast-streaming:receiver"; + return origin == kCastStreamingMessagePortOrigin; +} + +bool IsValidCastStreamingMessage(const fuchsia::web::WebMessage& message) { + // |message| should contain exactly one OutgoingTransferrable, with a single + // MessagePort. + return message.has_outgoing_transfer() && + message.outgoing_transfer().size() == 1u && + message.outgoing_transfer()[0].is_message_port(); +} + +} // namespace cast_streaming diff --git a/chromium/fuchsia/cast_streaming/cast_streaming_session.cc b/chromium/fuchsia/cast_streaming/cast_streaming_session.cc index 71f931737bc..565df0defae 100644 --- a/chromium/fuchsia/cast_streaming/cast_streaming_session.cc +++ b/chromium/fuchsia/cast_streaming/cast_streaming_session.cc @@ -8,11 +8,14 @@ #include "base/bind.h" #include "base/notreached.h" +#include "components/openscreen_platform/network_context.h" #include "components/openscreen_platform/network_util.h" #include "components/openscreen_platform/task_runner.h" #include "fuchsia/cast_streaming/cast_message_port_impl.h" #include "fuchsia/cast_streaming/stream_consumer.h" #include "media/base/media_util.h" +#include "media/mojo/common/mojo_decoder_buffer_converter.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "third_party/openscreen/src/cast/streaming/receiver.h" #include "third_party/openscreen/src/cast/streaming/receiver_session.h" @@ -27,6 +30,12 @@ constexpr char kVideoCodecVp8[] = "vp8"; namespace cast_streaming { +// static +void CastStreamingSession::SetNetworkContextGetter( + NetworkContextGetter getter) { + openscreen_platform::SetNetworkContextGetter(std::move(getter)); +} + // Owns the Open Screen ReceiverSession. The Cast Streaming Session is tied to // the lifespan of this object. class CastStreamingSession::Internal @@ -37,12 +46,9 @@ class CastStreamingSession::Internal fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request, scoped_refptr<base::SequencedTaskRunner> task_runner) : task_runner_(task_runner), - environment_(&openscreen::Clock::now, - &task_runner_, - openscreen::IPEndpoint{ - openscreen::IPAddress(0, 0, 0, 0, 0, 0, 0, 0), 0}), + environment_(&openscreen::Clock::now, &task_runner_), cast_message_port_impl_(std::move(message_port_request)), - // TODO(crbug.com/1042501): Add streaming session Constraints and + // TODO(crbug.com/1087520): Add streaming session Constraints and // DisplayDescription. receiver_session_( this, @@ -73,14 +79,31 @@ class CastStreamingSession::Internal DVLOG(1) << __func__; DCHECK_EQ(session, &receiver_session_); - base::Optional<media::AudioDecoderConfig> audio_decoder_config; + base::Optional<AudioStreamInfo> audio_stream_info; if (receivers.audio) { + // Creare the audio data pipe. + const MojoCreateDataPipeOptions data_pipe_options{ + sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, + 1u /* element_num_bytes */, + media::GetDefaultDecoderBufferConverterCapacity( + media::DemuxerStream::Type::AUDIO)}; + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + MojoResult result = mojo::CreateDataPipe( + &data_pipe_options, &data_pipe_producer, &data_pipe_consumer); + if (result != MOJO_RESULT_OK) { + client_->OnInitializationFailure(); + return; + } + + // Initialize the audio consumer. audio_consumer_ = std::make_unique<StreamConsumer>( - receivers.audio->receiver, + receivers.audio->receiver, std::move(data_pipe_producer), base::BindRepeating( - &CastStreamingSession::Client::OnAudioFrameReceived, + &CastStreamingSession::Client::OnAudioBufferReceived, base::Unretained(client_))); + // Gather data for the audio decoder config. media::ChannelLayout channel_layout = media::GuessChannelLayout(receivers.audio->receiver_config.channels); const std::string& audio_codec = @@ -89,22 +112,42 @@ class CastStreamingSession::Internal media::StringToAudioCodec(audio_codec); int samples_per_second = receivers.audio->receiver_config.rtp_timebase; - audio_decoder_config.emplace(media::AudioDecoderConfig( - media_audio_codec, media::SampleFormat::kSampleFormatF32, - channel_layout, samples_per_second, media::EmptyExtraData(), - media::EncryptionScheme::kUnencrypted)); + audio_stream_info.emplace(AudioStreamInfo{ + media::AudioDecoderConfig( + media_audio_codec, media::SampleFormat::kSampleFormatF32, + channel_layout, samples_per_second, media::EmptyExtraData(), + media::EncryptionScheme::kUnencrypted), + std::move(data_pipe_consumer)}); - DVLOG(1) << "Initialized audio stream using " << audio_codec << " codec."; + DVLOG(1) << "Initialized audio stream. " + << audio_stream_info->decoder_config.AsHumanReadableString(); } - base::Optional<media::VideoDecoderConfig> video_decoder_config; + base::Optional<VideoStreamInfo> video_stream_info; if (receivers.video) { + // Creare the video data pipe. + const MojoCreateDataPipeOptions data_pipe_options{ + sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, + 1u /* element_num_bytes */, + media::GetDefaultDecoderBufferConverterCapacity( + media::DemuxerStream::Type::VIDEO)}; + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + MojoResult result = mojo::CreateDataPipe( + &data_pipe_options, &data_pipe_producer, &data_pipe_consumer); + if (result != MOJO_RESULT_OK) { + client_->OnInitializationFailure(); + return; + } + + // Initialize the video consumer. video_consumer_ = std::make_unique<StreamConsumer>( - receivers.video->receiver, + receivers.video->receiver, std::move(data_pipe_producer), base::BindRepeating( - &CastStreamingSession::Client::OnVideoFrameReceived, + &CastStreamingSession::Client::OnVideoBufferReceived, base::Unretained(client_))); + // Gather data for the video decoder config. const std::string& video_codec = receivers.video->selected_stream.stream.codec_name; uint32_t video_width = @@ -114,36 +157,39 @@ class CastStreamingSession::Internal gfx::Size video_size(video_width, video_height); gfx::Rect video_rect(video_width, video_height); + media::VideoCodec media_video_codec = + media::VideoCodec::kUnknownVideoCodec; + media::VideoCodecProfile video_codec_profile = + media::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN; + if (video_codec == kVideoCodecH264) { - video_decoder_config.emplace(media::VideoDecoderConfig( - media::VideoCodec::kCodecH264, - media::VideoCodecProfile::H264PROFILE_BASELINE, - media::VideoDecoderConfig::AlphaMode::kIsOpaque, - media::VideoColorSpace(), media::VideoTransformation(), video_size, - video_rect, video_size, media::EmptyExtraData(), - media::EncryptionScheme::kUnencrypted)); + media_video_codec = media::VideoCodec::kCodecH264; + video_codec_profile = media::VideoCodecProfile::H264PROFILE_BASELINE; } else if (video_codec == kVideoCodecVp8) { - video_decoder_config.emplace(media::VideoDecoderConfig( - media::VideoCodec::kCodecVP8, - media::VideoCodecProfile::VP8PROFILE_MIN, - media::VideoDecoderConfig::AlphaMode::kIsOpaque, - media::VideoColorSpace(), media::VideoTransformation(), video_size, - video_rect, video_size, media::EmptyExtraData(), - media::EncryptionScheme::kUnencrypted)); + media_video_codec = media::VideoCodec::kCodecVP8; + video_codec_profile = media::VideoCodecProfile::VP8PROFILE_MIN; } else { NOTREACHED(); } - DVLOG(1) << "Initialized video stream of " << video_width << "x" - << video_height << " resolution using " << video_codec - << " codec."; + video_stream_info.emplace(VideoStreamInfo{ + media::VideoDecoderConfig( + media_video_codec, video_codec_profile, + media::VideoDecoderConfig::AlphaMode::kIsOpaque, + media::VideoColorSpace(), media::VideoTransformation(), + video_size, video_rect, video_size, media::EmptyExtraData(), + media::EncryptionScheme::kUnencrypted), + std::move(data_pipe_consumer)}); + + DVLOG(1) << "Initialized video stream. " + << video_stream_info->decoder_config.AsHumanReadableString(); } - if (!video_decoder_config && !audio_decoder_config) { + if (!audio_stream_info && !video_stream_info) { client_->OnInitializationFailure(); } else { - client_->OnInitializationSuccess(std::move(audio_decoder_config), - std::move(video_decoder_config)); + client_->OnInitializationSuccess(std::move(audio_stream_info), + std::move(video_stream_info)); } initialized_called_ = true; } @@ -192,4 +238,9 @@ void CastStreamingSession::Start( client, std::move(message_port_request), task_runner); } +void CastStreamingSession::Stop() { + DCHECK(internal_); + internal_.reset(); +} + } // namespace cast_streaming diff --git a/chromium/fuchsia/cast_streaming/public/cast_streaming.h b/chromium/fuchsia/cast_streaming/public/cast_streaming.h new file mode 100644 index 00000000000..0838317b6ac --- /dev/null +++ b/chromium/fuchsia/cast_streaming/public/cast_streaming.h @@ -0,0 +1,25 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_CAST_STREAMING_PUBLIC_CAST_STREAMING_H_ +#define FUCHSIA_CAST_STREAMING_PUBLIC_CAST_STREAMING_H_ + +#include <fuchsia/web/cpp/fidl.h> + +#include "base/strings/string_piece_forward.h" + +namespace cast_streaming { + +// TODO(crbug.com/1082821): Remove this file once the Cast Streaming Receiver is +// implemented as a separate component from WebEngine. + +// Returns true if |origin| is the Cast Streaming MessagePort origin. +bool IsCastStreamingAppOrigin(base::StringPiece origin); + +// Returns true if |message| contains a valid Cast Streaming Message. +bool IsValidCastStreamingMessage(const fuchsia::web::WebMessage& message); + +} // namespace cast_streaming + +#endif // FUCHSIA_CAST_STREAMING_PUBLIC_CAST_STREAMING_H_ diff --git a/chromium/fuchsia/cast_streaming/public/cast_streaming_session.h b/chromium/fuchsia/cast_streaming/public/cast_streaming_session.h index 6769ba1b49f..bb6703989c7 100644 --- a/chromium/fuchsia/cast_streaming/public/cast_streaming_session.h +++ b/chromium/fuchsia/cast_streaming/public/cast_streaming_session.h @@ -9,11 +9,19 @@ #include <fuchsia/web/cpp/fidl.h> +#include "base/callback.h" #include "base/optional.h" #include "base/sequenced_task_runner.h" #include "media/base/audio_decoder_config.h" -#include "media/base/decoder_buffer.h" #include "media/base/video_decoder_config.h" +#include "media/mojo/mojom/media_types.mojom.h" +#include "mojo/public/cpp/system/data_pipe.h" + +namespace network { +namespace mojom { +class NetworkContext; +} // namespace mojom +} // namespace network namespace cast_streaming { @@ -21,25 +29,44 @@ namespace cast_streaming { // Cast Streaming Session for a provided FIDL MessagePort request. class CastStreamingSession { public: + using NetworkContextGetter = + base::RepeatingCallback<network::mojom::NetworkContext*()>; + + // Sets the NetworkContextGetter. This must be called before any call to + // Start() and must only be called once. If the NetworkContext crashes, any + // existing Cast Streaming Session will eventually terminate and call + // OnReceiverSessionEnded(). + static void SetNetworkContextGetter(NetworkContextGetter getter); + + template <class T> + struct StreamInfo { + T decoder_config; + mojo::ScopedDataPipeConsumerHandle data_pipe; + }; + using AudioStreamInfo = StreamInfo<media::AudioDecoderConfig>; + using VideoStreamInfo = StreamInfo<media::VideoDecoderConfig>; + class Client { public: // Called when the Cast Streaming Session has been successfully initialized. - // It is guaranteed that at least one of |audio_decoder_config| or - // |video_decoder_config| will be set. + // It is guaranteed that at least one of |audio_stream_info| or + // |video_stream_info| will be set. virtual void OnInitializationSuccess( - base::Optional<media::AudioDecoderConfig> audio_decoder_config, - base::Optional<media::VideoDecoderConfig> video_decoder_config) = 0; + base::Optional<AudioStreamInfo> audio_stream_info, + base::Optional<VideoStreamInfo> video_stream_info) = 0; // Called when the Cast Stream Session failed to initialize. virtual void OnInitializationFailure() = 0; - // Called on every new audio frame after OnInitializationSuccess(). - virtual void OnAudioFrameReceived( - scoped_refptr<media::DecoderBuffer> buffer) = 0; + // Called on every new audio buffer after OnInitializationSuccess(). The + // frame data must be accessed via the |data_pipe| property in StreamInfo. + virtual void OnAudioBufferReceived( + media::mojom::DecoderBufferPtr buffer) = 0; - // Called on every new video frame after OnInitializationSuccess(). - virtual void OnVideoFrameReceived( - scoped_refptr<media::DecoderBuffer> buffer) = 0; + // Called on every new video buffer after OnInitializationSuccess(). The + // frame data must be accessed via the |data_pipe| property in StreamInfo. + virtual void OnVideoBufferReceived( + media::mojom::DecoderBufferPtr buffer) = 0; // Called when the Cast Streaming Session has ended. virtual void OnReceiverSessionEnded() = 0; @@ -66,6 +93,10 @@ class CastStreamingSession { fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request, scoped_refptr<base::SequencedTaskRunner> task_runner); + // Stops the Cast Streaming Session. This can only be called once during the + // lifespan of this object and only after a call to Start(). + void Stop(); + private: class Internal; std::unique_ptr<Internal> internal_; diff --git a/chromium/fuchsia/cast_streaming/stream_consumer.cc b/chromium/fuchsia/cast_streaming/stream_consumer.cc index bcfb8a4d6b1..353ac52119a 100644 --- a/chromium/fuchsia/cast_streaming/stream_consumer.cc +++ b/chromium/fuchsia/cast_streaming/stream_consumer.cc @@ -5,27 +5,132 @@ #include "fuchsia/cast_streaming/stream_consumer.h" #include "base/logging.h" +#include "base/time/time.h" +#include "media/base/media_util.h" namespace cast_streaming { StreamConsumer::StreamConsumer(openscreen::cast::Receiver* receiver, + mojo::ScopedDataPipeProducerHandle data_pipe, FrameReceivedCB frame_received_cb) - : receiver_(receiver), frame_received_cb_(std::move(frame_received_cb)) { + : receiver_(receiver), + data_pipe_(std::move(data_pipe)), + frame_received_cb_(std::move(frame_received_cb)), + pipe_watcher_(FROM_HERE, + mojo::SimpleWatcher::ArmingPolicy::MANUAL, + base::SequencedTaskRunnerHandle::Get()) { DCHECK(receiver_); receiver_->SetConsumer(this); + MojoResult result = + pipe_watcher_.Watch(data_pipe_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, + base::BindRepeating(&StreamConsumer::OnPipeWritable, + base::Unretained(this))); + if (result != MOJO_RESULT_OK) { + CloseDataPipeOnError(); + } } StreamConsumer::~StreamConsumer() { receiver_->SetConsumer(nullptr); } +void StreamConsumer::CloseDataPipeOnError() { + DLOG(WARNING) << "[ssrc:" << receiver_->ssrc() << "] Data pipe closed."; + receiver_->SetConsumer(nullptr); + pipe_watcher_.Cancel(); + data_pipe_.reset(); +} + +void StreamConsumer::OnPipeWritable(MojoResult result) { + DCHECK(data_pipe_); + + if (result != MOJO_RESULT_OK) { + CloseDataPipeOnError(); + return; + } + + uint32_t bytes_written = pending_buffer_remaining_bytes_; + result = data_pipe_->WriteData(pending_buffer_ + pending_buffer_offset_, + &bytes_written, MOJO_WRITE_DATA_FLAG_NONE); + if (result != MOJO_RESULT_OK) { + CloseDataPipeOnError(); + return; + } + + pending_buffer_offset_ += bytes_written; + pending_buffer_remaining_bytes_ -= bytes_written; + if (pending_buffer_remaining_bytes_ != 0) { + pipe_watcher_.ArmOrNotify(); + return; + } + + // Advance to the next frame if a new one is ready. + int next_frame_buffer_size = receiver_->AdvanceToNextFrame(); + if (next_frame_buffer_size != openscreen::cast::Receiver::kNoFramesReady) + OnFramesReady(next_frame_buffer_size); +} + void StreamConsumer::OnFramesReady(int next_frame_buffer_size) { - // TODO(crbug.com/1042501): Do not allocate a buffer for every new frame, use - // a buffer pool. - std::unique_ptr<uint8_t[]> buffer = - std::make_unique<uint8_t[]>(next_frame_buffer_size); - openscreen::cast::EncodedFrame encoded_frame = receiver_->ConsumeNextFrame( - absl::Span<uint8_t>(buffer.get(), next_frame_buffer_size)); + DCHECK(data_pipe_); + + if (pending_buffer_remaining_bytes_ != 0) { + // There already is a pending frame. Ignore this one for now. + return; + } + + void* buffer = nullptr; + uint32_t buffer_size = next_frame_buffer_size; + uint32_t mojo_buffer_size = next_frame_buffer_size; + + if (buffer_size > kMaxFrameSize) { + LOG(ERROR) << "[ssrc:" << receiver_->ssrc() << "] " + << "Frame size too big: " << buffer_size; + CloseDataPipeOnError(); + return; + } + + MojoResult result = data_pipe_->BeginWriteData( + &buffer, &mojo_buffer_size, MOJO_BEGIN_WRITE_DATA_FLAG_NONE); + + if (result == MOJO_RESULT_SHOULD_WAIT) { + pipe_watcher_.ArmOrNotify(); + return; + } + + if (result != MOJO_RESULT_OK) { + CloseDataPipeOnError(); + return; + } + + openscreen::cast::EncodedFrame encoded_frame; + size_t bytes_written = 0; + + if (mojo_buffer_size < buffer_size) { + DVLOG(2) << "[ssrc:" << receiver_->ssrc() << "] " + << "Mojo data pipe full"; + + // The |data_pipe_| buffer cannot take the full frame, write to + // |pending_buffer_| instead. + encoded_frame = receiver_->ConsumeNextFrame( + absl::Span<uint8_t>(pending_buffer_, buffer_size)); + + // Write as much as we can to the |data_pipe_| buffer. + memcpy(buffer, pending_buffer_, mojo_buffer_size); + pending_buffer_offset_ = mojo_buffer_size; + pending_buffer_remaining_bytes_ = buffer_size - mojo_buffer_size; + bytes_written = mojo_buffer_size; + } else { + // Write directly to the |data_pipe_| buffer. + encoded_frame = receiver_->ConsumeNextFrame( + absl::Span<uint8_t>(static_cast<uint8_t*>(buffer), buffer_size)); + bytes_written = buffer_size; + } + + result = data_pipe_->EndWriteData(bytes_written); + if (result != MOJO_RESULT_OK) { + CloseDataPipeOnError(); + return; + } const bool is_key_frame = encoded_frame.dependency == @@ -37,26 +142,21 @@ void StreamConsumer::OnFramesReady(int next_frame_buffer_size) { receiver_->rtp_timebase()) .count()); - DVLOG(3) << "[ssrc:" << receiver_->ssrc() - << "] Received new frame. Timestamp: " << playout_time + DVLOG(3) << "[ssrc:" << receiver_->ssrc() << "] " + << "Received new frame. Timestamp: " << playout_time << ", is_key_frame: " << is_key_frame; - if (playout_time <= last_playout_time_) { - // TODO(b/156129097): We sometimes receive identical playout time for two - // frames in a row. - DVLOG(2) << "[ssrc:" << receiver_->ssrc() - << "] Droped frame due to identical playout time."; - return; - } - last_playout_time_ = playout_time; + frame_received_cb_.Run(media::mojom::DecoderBuffer::New( + playout_time /* timestamp */, base::TimeDelta() /* duration */, + false /* is_end_of_stream */, buffer_size, is_key_frame, + media::EmptyExtraData(), media::mojom::DecryptConfigPtr(), + base::TimeDelta() /* front_discard */, + base::TimeDelta() /* back_discard */ + )); - scoped_refptr<media::DecoderBuffer> decoder_buffer = - media::DecoderBuffer::FromArray(std::move(buffer), - next_frame_buffer_size); - decoder_buffer->set_is_key_frame(is_key_frame); - decoder_buffer->set_timestamp(playout_time); - - frame_received_cb_.Run(decoder_buffer); + if (pending_buffer_remaining_bytes_ != 0) { + pipe_watcher_.ArmOrNotify(); + } } } // namespace cast_streaming diff --git a/chromium/fuchsia/cast_streaming/stream_consumer.h b/chromium/fuchsia/cast_streaming/stream_consumer.h index 4605d158bde..eef5f14b1a7 100644 --- a/chromium/fuchsia/cast_streaming/stream_consumer.h +++ b/chromium/fuchsia/cast_streaming/stream_consumer.h @@ -8,23 +8,33 @@ #include <fuchsia/media/cpp/fidl.h> #include "base/callback.h" -#include "base/time/time.h" -#include "media/base/decoder_buffer.h" +#include "media/mojo/mojom/media_types.mojom.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" #include "third_party/openscreen/src/cast/streaming/receiver.h" #include "third_party/openscreen/src/cast/streaming/receiver_session.h" namespace cast_streaming { -// Attaches to an Open Screen Receiver to receive frames and invokes -// |frame_received_cb_| with each received buffer of encoded data. +// Attaches to an Open Screen Receiver to receive buffers of encoded data and +// invokes |frame_received_cb_| with each buffer. +// +// Internally, this class writes buffers of encoded data directly to +// |data_pipe_| rather than using a helper class like MojoDecoderBufferWriter. +// This allows us to use |data_pipe_| as an end-to-end buffer to cap memory +// usage. Receiving new buffers is delayed until the pipe has free memory again. +// The Open Screen library takes care of discarding buffers that are too old and +// requesting new key frames as needed. class StreamConsumer : public openscreen::cast::Receiver::Consumer { public: using FrameReceivedCB = - base::RepeatingCallback<void(scoped_refptr<media::DecoderBuffer>)>; + base::RepeatingCallback<void(media::mojom::DecoderBufferPtr)>; // |receiver| sends frames to this object. It must outlive this object. - // |frame_received_cb| is called on every new frame. + // |frame_received_cb| is called on every new frame, after a new frame has + // been written to |data_pipe|. On error, |data_pipe| will be closed. StreamConsumer(openscreen::cast::Receiver* receiver, + mojo::ScopedDataPipeProducerHandle data_pipe, FrameReceivedCB frame_received_cb); ~StreamConsumer() final; @@ -32,12 +42,34 @@ class StreamConsumer : public openscreen::cast::Receiver::Consumer { StreamConsumer& operator=(const StreamConsumer&) = delete; private: + // Maximum frame size that OnFramesReady() can accept. + static constexpr uint32_t kMaxFrameSize = 512 * 1024; + + // Closes |data_pipe_| and resets the Consumer in |receiver_|. No frames will + // be received after this call. + void CloseDataPipeOnError(); + + // Callback when |data_pipe_| can be written to again after it was full. + void OnPipeWritable(MojoResult result); + // openscreen::cast::Receiver::Consumer implementation. void OnFramesReady(int next_frame_buffer_size) final; openscreen::cast::Receiver* const receiver_; + mojo::ScopedDataPipeProducerHandle data_pipe_; const FrameReceivedCB frame_received_cb_; - base::TimeDelta last_playout_time_; + + // Provides notifications about |data_pipe_| readiness. + mojo::SimpleWatcher pipe_watcher_; + + // Buffer used when |data_pipe_| is too full to accept the next frame size. + uint8_t pending_buffer_[kMaxFrameSize]; + + // Current offset for data |pending_buffer_| to be written to |data_pipe_|. + size_t pending_buffer_offset_ = 0; + + // Remaining bytes to write from |pending_buffer_| to |data_pipe_|. + size_t pending_buffer_remaining_bytes_ = 0; }; } // namespace cast_streaming diff --git a/chromium/fuchsia/cipd/BUILD.gn b/chromium/fuchsia/cipd/BUILD.gn index 22d726631ad..a59fd83d727 100644 --- a/chromium/fuchsia/cipd/BUILD.gn +++ b/chromium/fuchsia/cipd/BUILD.gn @@ -124,6 +124,17 @@ cipd_archive("http") { sources = [ "${root_gen_dir}/fuchsia/http/http/http.far" ] } +cipd_archive("web_engine_shell") { + package_definition_yaml = "web_engine_shell.yaml" + package = "chromium/fuchsia/web_engine_shell-\${targetarch}" + description = "Simple command-line embedder for WebEngine." + + deps = [ "//fuchsia/engine:web_engine_shell_pkg" ] + + sources = + [ "${root_gen_dir}/fuchsia/engine/web_engine_shell/web_engine_shell.far" ] +} + _stripped_chromedriver_file = "${root_out_dir}/clang_x64/stripped/chromedriver" action("strip_chromedriver_binary") { @@ -249,6 +260,7 @@ group("cipd") { ":debug_symbols", ":http", ":tests", + ":web_engine_shell", ":webrunner", ] } diff --git a/chromium/fuchsia/engine/BUILD.gn b/chromium/fuchsia/engine/BUILD.gn index 59e33cedeac..fe80aee12ce 100644 --- a/chromium/fuchsia/engine/BUILD.gn +++ b/chromium/fuchsia/engine/BUILD.gn @@ -6,7 +6,6 @@ assert(is_fuchsia) import("//build/config/fuchsia/generate_runner_scripts.gni") import("//build/config/fuchsia/symbol_archive.gni") -import("//fuchsia/release_channel.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") import("//tools/grit/repack.gni") @@ -28,10 +27,12 @@ declare_args() { mojom("mojom") { sources = [ + "cast_streaming_session.mojom", "on_load_script_injector.mojom", "url_request_rewrite.mojom", ] public_deps = [ + "//media/mojo/mojom", "//mojo/public/mojom/base", "//services/network/public/mojom", ] @@ -106,6 +107,7 @@ component("web_engine_core") { "//media", "//media/fuchsia/cdm/service", "//media/fuchsia/mojom", + "//media/mojo/common", "//media/mojo/services", "//mojo/public/cpp/bindings", "//services/media_session/public/mojom", @@ -145,6 +147,8 @@ component("web_engine_core") { "browser/accessibility_bridge.h", "browser/ax_tree_converter.cc", "browser/ax_tree_converter.h", + "browser/cast_streaming_session_client.cc", + "browser/cast_streaming_session_client.h", "browser/content_directory_loader_factory.cc", "browser/content_directory_loader_factory.h", "browser/context_impl.cc", @@ -185,6 +189,8 @@ component("web_engine_core") { "browser/web_engine_net_log_observer.h", "browser/web_engine_permission_delegate.cc", "browser/web_engine_permission_delegate.h", + "common/cast_streaming.cc", + "common/cast_streaming.h", "common/cors_exempt_headers.cc", "common/cors_exempt_headers.h", "common/web_engine_content_client.cc", @@ -195,12 +201,18 @@ component("web_engine_core") { "context_provider_impl.h", "context_provider_main.cc", "context_provider_main.h", + "renderer/cast_streaming_demuxer.cc", + "renderer/cast_streaming_demuxer.h", + "renderer/cast_streaming_receiver.cc", + "renderer/cast_streaming_receiver.h", "renderer/on_load_script_injector.cc", "renderer/on_load_script_injector.h", "renderer/url_request_rules_receiver.cc", "renderer/url_request_rules_receiver.h", "renderer/web_engine_content_renderer_client.cc", "renderer/web_engine_content_renderer_client.h", + "renderer/web_engine_render_frame_observer.cc", + "renderer/web_engine_render_frame_observer.h", "renderer/web_engine_url_loader_throttle_provider.cc", "renderer/web_engine_url_loader_throttle_provider.h", "url_request_rewrite_type_converters.cc", @@ -234,8 +246,6 @@ cr_fuchsia_package("web_engine") { binary = ":web_engine_exe" manifest = "context_provider.cmx" component_name_override = "context_provider" - package_name_override = "web_engine${release_channel_suffix}" - archive_name_override = "web_engine" excluded_files = [ "lib/libswiftshader_libEGL.so", @@ -272,6 +282,7 @@ source_set("browsertest_core") { test("web_engine_browsertests") { sources = [ "browser/accessibility_bridge_browsertest.cc", + "browser/cast_streaming_browsertest.cc", "browser/content_directory_browsertest.cc", "browser/context_impl_browsertest.cc", "browser/frame_impl_browsertest.cc", @@ -381,7 +392,6 @@ executable("web_engine_shell_exec") { deps = [ "//base", "//fuchsia/base", - "//fuchsia/base:release_channel_buildflags", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.policy", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web", "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", diff --git a/chromium/fuchsia/engine/DEPS b/chromium/fuchsia/engine/DEPS index b074327e28e..048342eb9f5 100644 --- a/chromium/fuchsia/engine/DEPS +++ b/chromium/fuchsia/engine/DEPS @@ -10,6 +10,7 @@ include_rules = [ "+media/fuchsia", "+mojo/public", "+services/service_manager", + "+third_party/blink/public/common/switches.h", "+third_party/widevine/cdm/widevine_cdm_common.h", "+ui/base", "+ui/gfx", diff --git a/chromium/fuchsia/engine/OWNERS b/chromium/fuchsia/engine/OWNERS index 7b7d0cfbd65..b9be02c3f28 100644 --- a/chromium/fuchsia/engine/OWNERS +++ b/chromium/fuchsia/engine/OWNERS @@ -10,3 +10,5 @@ per-file web_engine_browser_interface_binders.*=file://ipc/SECURITY_OWNERS # For sandbox security review. per-file context_provider_impl.*=set noparent per-file context_provider_impl.*=file://fuchsia/SECURITY_OWNERS +per-file *.cmx=set noparent +per-file *.cmx=file://fuchsia/SECURITY_OWNERS diff --git a/chromium/fuchsia/engine/browser/DEPS b/chromium/fuchsia/engine/browser/DEPS index 79f6dcab122..6ffa902e765 100644 --- a/chromium/fuchsia/engine/browser/DEPS +++ b/chromium/fuchsia/engine/browser/DEPS @@ -6,6 +6,8 @@ include_rules = [ "+media/capabilities", "+media/fuchsia/cdm/service", "+media/fuchsia/mojom", + "+media/mojo/common", + "+media/mojo/mojom", "+media/mojo/services", "+mojo/public/cpp/bindings", "+mojo/public/cpp/system", diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc b/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc index 0d97daf24e1..6476e8a0f28 100644 --- a/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc +++ b/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc @@ -4,13 +4,11 @@ #include <fuchsia/accessibility/semantics/cpp/fidl.h> #include <fuchsia/accessibility/semantics/cpp/fidl_test_base.h> -#include <lib/sys/cpp/component_context.h> #include <lib/ui/scenic/cpp/view_ref_pair.h> #include <zircon/types.h> #include "base/auto_reset.h" #include "base/check.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/notreached.h" #include "base/test/bind_test_util.h" diff --git a/chromium/fuchsia/engine/browser/cast_streaming_browsertest.cc b/chromium/fuchsia/engine/browser/cast_streaming_browsertest.cc new file mode 100644 index 00000000000..cc94207a341 --- /dev/null +++ b/chromium/fuchsia/engine/browser/cast_streaming_browsertest.cc @@ -0,0 +1,129 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/test/browser_test.h" +#include "fuchsia/base/fit_adapter.h" +#include "fuchsia/base/frame_test_util.h" +#include "fuchsia/base/mem_buffer_util.h" +#include "fuchsia/base/result_receiver.h" +#include "fuchsia/base/test_navigation_listener.h" +#include "fuchsia/engine/browser/frame_impl.h" +#include "fuchsia/engine/switches.h" +#include "fuchsia/engine/test/test_data.h" +#include "fuchsia/engine/test/web_engine_browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kCastStreamingReceiverPath[] = "/cast_streaming_receiver.html"; + +} // namespace + +// Base test fixture for Cast Streaming tests. +class CastStreamingBaseTest : public cr_fuchsia::WebEngineBrowserTest { + public: + CastStreamingBaseTest() { + set_test_server_root(base::FilePath(cr_fuchsia::kTestServerRoot)); + } + ~CastStreamingBaseTest() override = default; + + CastStreamingBaseTest(const CastStreamingBaseTest&) = delete; + CastStreamingBaseTest& operator=(const CastStreamingBaseTest&) = delete; + + protected: + // Creates a Frame with |navigation_listener_| attached. + fuchsia::web::FramePtr CreateFrame() { + return WebEngineBrowserTest::CreateFrame(&navigation_listener_); + } + + cr_fuchsia::TestNavigationListener navigation_listener_; +}; + +// Test fixture for Cast Streaming tests with the Cast Streaming Receiver flag +// disabled. +class CastStreamingDisabledTest : public CastStreamingBaseTest { + public: + CastStreamingDisabledTest() = default; + ~CastStreamingDisabledTest() override = default; + CastStreamingDisabledTest(const CastStreamingDisabledTest&) = delete; + CastStreamingDisabledTest& operator=(const CastStreamingDisabledTest&) = + delete; + + protected: + void SetUpCommandLine(base::CommandLine* command_line) override { + content::BrowserTestBase::SetUpCommandLine(command_line); + command_line->RemoveSwitch(switches::kEnableCastStreamingReceiver); + } +}; + +// Test fixture for Cast Streaming tests with the Cast Streaming Receiver flag +// enabled. +class CastStreamingTest : public CastStreamingBaseTest { + public: + CastStreamingTest() = default; + ~CastStreamingTest() override = default; + CastStreamingTest(const CastStreamingTest&) = delete; + CastStreamingTest& operator=(const CastStreamingTest&) = delete; + + protected: + void SetUpCommandLine(base::CommandLine* command_line) override { + content::BrowserTestBase::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kEnableCastStreamingReceiver); + } +}; + +// Check that attempting to load the cast streaming media source URL when the +// command line switch is not set fails as expected. +IN_PROC_BROWSER_TEST_F(CastStreamingDisabledTest, LoadFailure) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL page_url(embedded_test_server()->GetURL(kCastStreamingReceiverPath)); + + fuchsia::web::FramePtr frame = CreateFrame(); + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), page_url.spec())); + navigation_listener_.RunUntilTitleEquals("error"); +} + +// Check that the Cast Streaming MessagePort gets properly set on the Frame. +IN_PROC_BROWSER_TEST_F(CastStreamingTest, FrameMessagePort) { + fuchsia::web::FramePtr frame = CreateFrame(); + + FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame); + ASSERT_TRUE(frame_impl); + EXPECT_FALSE(frame_impl->cast_streaming_session_client_for_test()); + + fuchsia::web::MessagePortPtr cast_streaming_message_port; + + base::RunLoop run_loop; + cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> + post_result(run_loop.QuitClosure()); + frame->PostMessage( + "cast-streaming:receiver", + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + cast_streaming_message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("hi", "test")), + cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); + run_loop.Run(); + ASSERT_TRUE(post_result->is_response()); + + EXPECT_TRUE(frame_impl->cast_streaming_session_client_for_test()); +} + +// Check that attempting to load the cast streaming media source URL when the +// command line switch is set properly succeeds. +// TODO(crbug.com/1087537): Re-enable when we have a test implementation for a +// Cast Streaming Sender. +IN_PROC_BROWSER_TEST_F(CastStreamingTest, DISABLED_LoadSuccess) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL page_url(embedded_test_server()->GetURL(kCastStreamingReceiverPath)); + + fuchsia::web::FramePtr frame = CreateFrame(); + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), page_url.spec())); + navigation_listener_.RunUntilTitleEquals("canplay"); +} diff --git a/chromium/fuchsia/engine/browser/cast_streaming_session_client.cc b/chromium/fuchsia/engine/browser/cast_streaming_session_client.cc new file mode 100644 index 00000000000..5b022f927eb --- /dev/null +++ b/chromium/fuchsia/engine/browser/cast_streaming_session_client.cc @@ -0,0 +1,111 @@ +// Copyright 2020 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/browser/cast_streaming_session_client.h" + +#include "base/threading/sequenced_task_runner_handle.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/video_decoder_config.h" +#include "media/mojo/mojom/media_types.mojom.h" + +CastStreamingSessionClient::CastStreamingSessionClient( + fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request) + : message_port_request_(std::move(message_port_request)) {} + +CastStreamingSessionClient::~CastStreamingSessionClient() = default; + +void CastStreamingSessionClient::StartMojoConnection( + mojo::AssociatedRemote<mojom::CastStreamingReceiver> + cast_streaming_receiver) { + DVLOG(1) << __func__; + cast_streaming_receiver_ = std::move(cast_streaming_receiver); + + // It is fine to use an unretained pointer to |this| here as the + // AssociatedRemote, is owned by |this| and will be torn-down at the same time + // as |this|. + cast_streaming_receiver_->EnableReceiver(base::BindOnce( + &CastStreamingSessionClient::OnReceiverEnabled, base::Unretained(this))); + cast_streaming_receiver_.set_disconnect_handler(base::BindOnce( + &CastStreamingSessionClient::OnMojoDisconnect, base::Unretained(this))); +} + +void CastStreamingSessionClient::OnReceiverEnabled() { + DVLOG(1) << __func__; + DCHECK(message_port_request_); + cast_streaming_session_.Start(this, std::move(message_port_request_), + base::SequencedTaskRunnerHandle::Get()); +} + +void CastStreamingSessionClient::OnInitializationSuccess( + base::Optional<cast_streaming::CastStreamingSession::AudioStreamInfo> + audio_stream_info, + base::Optional<cast_streaming::CastStreamingSession::VideoStreamInfo> + video_stream_info) { + DVLOG(1) << __func__; + DCHECK(audio_stream_info || video_stream_info); + + mojom::AudioStreamInfoPtr mojo_audio_stream_info; + if (audio_stream_info) { + mojo_audio_stream_info = + mojom::AudioStreamInfo::New(audio_stream_info->decoder_config, + audio_remote_.BindNewPipeAndPassReceiver(), + std::move(audio_stream_info->data_pipe)); + } + + mojom::VideoStreamInfoPtr mojo_video_stream_info; + if (video_stream_info) { + mojo_video_stream_info = + mojom::VideoStreamInfo::New(video_stream_info->decoder_config, + video_remote_.BindNewPipeAndPassReceiver(), + std::move(video_stream_info->data_pipe)); + } + + cast_streaming_receiver_->OnStreamsInitialized( + std::move(mojo_audio_stream_info), std::move(mojo_video_stream_info)); +} + +void CastStreamingSessionClient::OnInitializationFailure() { + DVLOG(1) << __func__; + cast_streaming_receiver_.reset(); +} + +void CastStreamingSessionClient::OnAudioBufferReceived( + media::mojom::DecoderBufferPtr buffer) { + DVLOG(3) << __func__; + DCHECK(audio_remote_); + audio_remote_->ProvideBuffer(std::move(buffer)); +} + +void CastStreamingSessionClient::OnVideoBufferReceived( + media::mojom::DecoderBufferPtr buffer) { + DVLOG(3) << __func__; + DCHECK(video_remote_); + video_remote_->ProvideBuffer(std::move(buffer)); +} + +void CastStreamingSessionClient::OnReceiverSessionEnded() { + DVLOG(1) << __func__; + + // Tear down the Mojo connection. + cast_streaming_receiver_.reset(); +} + +void CastStreamingSessionClient::OnMojoDisconnect() { + DVLOG(1) << __func__; + + if (message_port_request_) { + // Close the MessagePort if the Cast Streaming Session was never started. + message_port_request_.Close(ZX_ERR_PEER_CLOSED); + cast_streaming_receiver_.reset(); + return; + } + + // Close the Cast Streaming Session. This will eventually call + // OnReceiverSessionEnded(), which will tear down the Mojo connection. + cast_streaming_session_.Stop(); + + // Tear down all remaining Mojo objects. + audio_remote_.reset(); + video_remote_.reset(); +} diff --git a/chromium/fuchsia/engine/browser/cast_streaming_session_client.h b/chromium/fuchsia/engine/browser/cast_streaming_session_client.h new file mode 100644 index 00000000000..f502a77e9b6 --- /dev/null +++ b/chromium/fuchsia/engine/browser/cast_streaming_session_client.h @@ -0,0 +1,57 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_ENGINE_BROWSER_CAST_STREAMING_SESSION_CLIENT_H_ +#define FUCHSIA_ENGINE_BROWSER_CAST_STREAMING_SESSION_CLIENT_H_ + +#include <fuchsia/web/cpp/fidl.h> + +#include "fuchsia/cast_streaming/public/cast_streaming_session.h" +#include "fuchsia/engine/cast_streaming_session.mojom.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "mojo/public/cpp/bindings/remote.h" + +// Owns the CastStreamingSession and sends buffers to the renderer process via +// a Mojo service. +class CastStreamingSessionClient + : public cast_streaming::CastStreamingSession::Client { + public: + explicit CastStreamingSessionClient( + fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request); + ~CastStreamingSessionClient() final; + + CastStreamingSessionClient(const CastStreamingSessionClient&) = delete; + CastStreamingSessionClient& operator=(const CastStreamingSessionClient&) = + delete; + + void StartMojoConnection(mojo::AssociatedRemote<mojom::CastStreamingReceiver> + cast_streaming_receiver); + + private: + // Handler for |cast_streaming_receiver_| disconnect. + void OnMojoDisconnect(); + + // Callback for mojom::CastStreamingReceiver::EnableReceiver() + void OnReceiverEnabled(); + + // cast_streaming::CastStreamingSession::Client implementation. + void OnInitializationSuccess( + base::Optional<cast_streaming::CastStreamingSession::AudioStreamInfo> + audio_stream_info, + base::Optional<cast_streaming::CastStreamingSession::VideoStreamInfo> + video_stream_info) final; + void OnInitializationFailure() final; + void OnAudioBufferReceived(media::mojom::DecoderBufferPtr buffer) final; + void OnVideoBufferReceived(media::mojom::DecoderBufferPtr buffer) final; + void OnReceiverSessionEnded() final; + + fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request_; + mojo::AssociatedRemote<mojom::CastStreamingReceiver> cast_streaming_receiver_; + cast_streaming::CastStreamingSession cast_streaming_session_; + + mojo::Remote<mojom::CastStreamingBufferReceiver> audio_remote_; + mojo::Remote<mojom::CastStreamingBufferReceiver> video_remote_; +}; + +#endif // FUCHSIA_ENGINE_BROWSER_CAST_STREAMING_SESSION_CLIENT_H_ diff --git a/chromium/fuchsia/engine/browser/context_impl.cc b/chromium/fuchsia/engine/browser/context_impl.cc index 6c8d4641fb9..d7a12f55e7c 100644 --- a/chromium/fuchsia/engine/browser/context_impl.cc +++ b/chromium/fuchsia/engine/browser/context_impl.cc @@ -11,8 +11,10 @@ #include "base/bind.h" #include "base/fuchsia/fuchsia_logging.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" +#include "fuchsia/cast_streaming/public/cast_streaming_session.h" #include "fuchsia/engine/browser/frame_impl.h" #include "fuchsia/engine/browser/web_engine_devtools_controller.h" @@ -27,6 +29,10 @@ ContextImpl::ContextImpl(content::BrowserContext* browser_context, DCHECK(browser_context_); DCHECK(devtools_controller_); devtools_controller_->OnContextCreated(); + + cast_streaming::CastStreamingSession::SetNetworkContextGetter( + base::BindRepeating(&ContextImpl::GetNetworkContext, + base::Unretained(this))); } ContextImpl::~ContextImpl() { @@ -130,3 +136,9 @@ FrameImpl* ContextImpl::GetFrameImplForTest( return nullptr; } + +network::mojom::NetworkContext* ContextImpl::GetNetworkContext() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return content::BrowserContext::GetDefaultStoragePartition(browser_context_) + ->GetNetworkContext(); +} diff --git a/chromium/fuchsia/engine/browser/context_impl.h b/chromium/fuchsia/engine/browser/context_impl.h index 48ee4a49b9a..53b4218a0b8 100644 --- a/chromium/fuchsia/engine/browser/context_impl.h +++ b/chromium/fuchsia/engine/browser/context_impl.h @@ -20,6 +20,12 @@ class BrowserContext; class WebContents; } // namespace content +namespace network { +namespace mojom { +class NetworkContext; +} // namespace mojom +} // namespace network + class FrameImpl; class WebEngineDevToolsController; @@ -68,6 +74,9 @@ class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { } private: + // Returns the NetworkContext from the default StoragePartition. + network::mojom::NetworkContext* GetNetworkContext(); + // Reference to the browser implementation for this Context. content::BrowserContext* const browser_context_; diff --git a/chromium/fuchsia/engine/browser/cookie_manager_impl.cc b/chromium/fuchsia/engine/browser/cookie_manager_impl.cc index e7362ae5618..d5c05f4e74b 100644 --- a/chromium/fuchsia/engine/browser/cookie_manager_impl.cc +++ b/chromium/fuchsia/engine/browser/cookie_manager_impl.cc @@ -67,12 +67,13 @@ class CookiesIteratorImpl : public fuchsia::web::CookiesIterator, } // Same as above except it takes CookieStatusList instead of just CookieList. CookiesIteratorImpl( - const std::vector<net::CookieWithStatus>& cookies_with_statuses, + const std::vector<net::CookieWithAccessResult>& + cookies_with_access_results, fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator) : CookiesIteratorImpl(std::move(iterator)) { - for (const auto& cookie_with_status : cookies_with_statuses) { - queued_cookies_[cookie_with_status.cookie.UniqueKey()] = - ConvertCanonicalCookie(cookie_with_status.cookie, + for (const auto& cookie_with_access_result : cookies_with_access_results) { + queued_cookies_[cookie_with_access_result.cookie.UniqueKey()] = + ConvertCanonicalCookie(cookie_with_access_result.cookie, net::CookieChangeCause::INSERTED); } } @@ -159,12 +160,12 @@ void OnAllCookiesReceived( void OnCookiesAndExcludedReceived( fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator, - const std::vector<net::CookieWithStatus>& cookies_with_statuses, - const std::vector<net::CookieWithStatus>& excluded_cookies) { + const std::vector<net::CookieWithAccessResult>& cookies_with_access_results, + const std::vector<net::CookieWithAccessResult>& excluded_cookies) { // Since CookieOptions::set_return_excluded_cookies() is not used when calling // the Mojo GetCookieList() API, |excluded_cookies| should be empty. DCHECK(excluded_cookies.empty()); - new CookiesIteratorImpl(cookies_with_statuses, std::move(iterator)); + new CookiesIteratorImpl(cookies_with_access_results, std::move(iterator)); } } // namespace diff --git a/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc b/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc index 93f28165f04..d756548fe92 100644 --- a/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc +++ b/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc @@ -17,6 +17,7 @@ #include "fuchsia/base/result_receiver.h" #include "fuchsia/engine/browser/cookie_manager_impl.h" #include "mojo/public/cpp/bindings/remote.h" +#include "net/cookies/cookie_inclusion_status.h" #include "services/network/network_service.h" #include "services/network/public/mojom/cookie_manager.mojom.h" #include "services/network/public/mojom/network_context.mojom.h" @@ -74,7 +75,7 @@ class CookieManagerImplTest : public testing::Test { net::CookieOptions options; mojo_cookie_manager_->SetCanonicalCookie( *CreateCookie(name, value), GURL(kTestCookieUrl), options, - base::BindOnce([](net::CanonicalCookie::CookieInclusionStatus status) { + base::BindOnce([](net::CookieInclusionStatus status) { EXPECT_TRUE(status.IsInclude()); })); } diff --git a/chromium/fuchsia/engine/browser/event_filter.cc b/chromium/fuchsia/engine/browser/event_filter.cc index 2a9351273bf..7ad83ac534e 100644 --- a/chromium/fuchsia/engine/browser/event_filter.cc +++ b/chromium/fuchsia/engine/browser/event_filter.cc @@ -6,6 +6,7 @@ #include <limits> +#include "base/notreached.h" #include "ui/events/event.h" namespace { diff --git a/chromium/fuchsia/engine/browser/frame_impl.cc b/chromium/fuchsia/engine/browser/frame_impl.cc index 6f9df2f15e3..317166e3c60 100644 --- a/chromium/fuchsia/engine/browser/frame_impl.cc +++ b/chromium/fuchsia/engine/browser/frame_impl.cc @@ -6,16 +6,16 @@ #include <fuchsia/ui/gfx/cpp/fidl.h> #include <lib/sys/cpp/component_context.h> +#include <lib/ui/scenic/cpp/view_ref_pair.h> #include <limits> #include "base/bind_helpers.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" #include "base/json/json_writer.h" #include "base/metrics/user_metrics.h" #include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" @@ -34,13 +34,16 @@ #include "content/public/common/was_activated_option.mojom.h" #include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/message_port.h" +#include "fuchsia/cast_streaming/public/cast_streaming.h" #include "fuchsia/engine/browser/accessibility_bridge.h" +#include "fuchsia/engine/browser/cast_streaming_session_client.h" #include "fuchsia/engine/browser/context_impl.h" #include "fuchsia/engine/browser/event_filter.h" #include "fuchsia/engine/browser/frame_layout_manager.h" #include "fuchsia/engine/browser/frame_window_tree_host.h" #include "fuchsia/engine/browser/media_player_impl.h" #include "fuchsia/engine/browser/web_engine_devtools_controller.h" +#include "fuchsia/engine/common/cast_streaming.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/system/platform_handle.h" #include "net/base/net_errors.h" @@ -475,8 +478,8 @@ void FrameImpl::DestroyWindowTreeHost() { // Allows posted focus events to process before the FocusController is torn // down. - base::DeleteSoon(FROM_HERE, {content::BrowserThread::UI}, - std::move(focus_controller_)); + content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, + std::move(focus_controller_)); } void FrameImpl::CloseAndDestroyFrame(zx_status_t error) { @@ -495,7 +498,81 @@ void FrameImpl::OnMediaPlayerDisconnect() { media_player_ = nullptr; } +bool FrameImpl::MaybeHandleCastStreamingMessage( + std::string* origin, + fuchsia::web::WebMessage* message, + PostMessageCallback* callback) { + if (!IsCastStreamingEnabled()) + return false; + + if (!cast_streaming::IsCastStreamingAppOrigin(*origin)) + return false; + + fuchsia::web::Frame_PostMessage_Result result; + if (cast_streaming_session_client_ || + !cast_streaming::IsValidCastStreamingMessage(*message)) { + // The Cast Streaming MessagePort should only be set once and |message| + // should be a valid Cast Streaming Message. + result.set_err(fuchsia::web::FrameError::INVALID_ORIGIN); + (*callback)(std::move(result)); + return true; + } + + cast_streaming_session_client_ = std::make_unique<CastStreamingSessionClient>( + std::move((*message->mutable_outgoing_transfer())[0].message_port())); + result.set_response(fuchsia::web::Frame_PostMessage_Response()); + (*callback)(std::move(result)); + return true; +} + +void FrameImpl::MaybeInjectBeforeLoadScripts( + content::NavigationHandle* navigation_handle) { + if (before_load_scripts_.empty()) + return; + + mojo::AssociatedRemote<mojom::OnLoadScriptInjector> + before_load_script_injector; + navigation_handle->GetRenderFrameHost() + ->GetRemoteAssociatedInterfaces() + ->GetInterface(&before_load_script_injector); + + // Provision the renderer's ScriptInjector with the scripts scoped to this + // page's origin. + before_load_script_injector->ClearOnLoadScripts(); + for (uint64_t script_id : before_load_scripts_order_) { + const OriginScopedScript& script = before_load_scripts_[script_id]; + if (IsOriginWhitelisted(navigation_handle->GetURL(), script.origins())) { + // TODO(crbug.com/1060846): Stop using handle<shared_buffer>. + before_load_script_injector->AddOnLoadScript( + mojo::WrapReadOnlySharedMemoryRegion(script.script().Duplicate())); + } + } +} + +void FrameImpl::MaybeStartCastStreaming( + content::NavigationHandle* navigation_handle) { + if (!IsCastStreamingEnabled() || !cast_streaming_session_client_) + return; + + mojo::AssociatedRemote<mojom::CastStreamingReceiver> cast_streaming_receiver; + navigation_handle->GetRenderFrameHost() + ->GetRemoteAssociatedInterfaces() + ->GetInterface(&cast_streaming_receiver); + cast_streaming_session_client_->StartMojoConnection( + std::move(cast_streaming_receiver)); +} + void FrameImpl::CreateView(fuchsia::ui::views::ViewToken view_token) { + scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New(); + CreateViewWithViewRef(std::move(view_token), + std::move(view_ref_pair.control_ref), + std::move(view_ref_pair.view_ref)); +} + +void FrameImpl::CreateViewWithViewRef( + fuchsia::ui::views::ViewToken view_token, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) { if (IsHeadless()) { LOG(WARNING) << "CreateView() called on a HEADLESS Context."; CloseAndDestroyFrame(ZX_ERR_INVALID_ARGS); @@ -505,10 +582,13 @@ void FrameImpl::CreateView(fuchsia::ui::views::ViewToken view_token) { // If a View to this Frame is already active then disconnect it. DestroyWindowTreeHost(); - InitWindowTreeHost(std::move(view_token)); + scenic::ViewRefPair view_ref_pair; + view_ref_pair.control_ref = std::move(control_ref); + view_ref_pair.view_ref = std::move(view_ref); + InitWindowTreeHost(std::move(view_token), std::move(view_ref_pair)); fuchsia::accessibility::semantics::SemanticsManagerPtr semantics_manager = - base::fuchsia::ComponentContextForCurrentProcess() + base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::accessibility::semantics::SemanticsManager>(); accessibility_bridge_ = std::make_unique<AccessibilityBridge>( @@ -619,6 +699,9 @@ void FrameImpl::RemoveBeforeLoadJavaScript(uint64_t id) { void FrameImpl::PostMessage(std::string origin, fuchsia::web::WebMessage message, PostMessageCallback callback) { + if (MaybeHandleCastStreamingMessage(&origin, &message, &callback)) + return; + constexpr char kWildcardOrigin[] = "*"; fuchsia::web::Frame_PostMessage_Result result; @@ -718,7 +801,8 @@ void FrameImpl::EnableHeadlessRendering() { return; } - InitWindowTreeHost(fuchsia::ui::views::ViewToken()); + scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New(); + InitWindowTreeHost(fuchsia::ui::views::ViewToken(), std::move(view_ref_pair)); gfx::Rect bounds(kHeadlessWindowSize); if (semantics_manager_for_test_) { @@ -744,11 +828,12 @@ void FrameImpl::DisableHeadlessRendering() { DestroyWindowTreeHost(); } -void FrameImpl::InitWindowTreeHost(fuchsia::ui::views::ViewToken view_token) { +void FrameImpl::InitWindowTreeHost(fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair) { DCHECK(!window_tree_host_); window_tree_host_ = std::make_unique<FrameWindowTreeHost>( - std::move(view_token), web_contents_.get()); + std::move(view_token), std::move(view_ref_pair), web_contents_.get()); window_tree_host_->InitHost(); root_window()->AddPreTargetHandler(&event_filter_); @@ -863,7 +948,7 @@ void FrameImpl::SetPermissionState( void FrameImpl::CloseContents(content::WebContents* source) { DCHECK_EQ(source, web_contents_.get()); - context_->DestroyFrame(this); + CloseAndDestroyFrame(ZX_OK); } void FrameImpl::SetBlockMediaLoading(bool blocked) { @@ -980,30 +1065,13 @@ bool FrameImpl::CheckMediaAccessPermission( void FrameImpl::ReadyToCommitNavigation( content::NavigationHandle* navigation_handle) { - if (before_load_scripts_.empty()) - return; - if (!navigation_handle->IsInMainFrame() || navigation_handle->IsSameDocument() || navigation_handle->IsErrorPage()) { return; } - mojo::AssociatedRemote<mojom::OnLoadScriptInjector> - before_load_script_injector; - navigation_handle->GetRenderFrameHost() - ->GetRemoteAssociatedInterfaces() - ->GetInterface(&before_load_script_injector); - - // Provision the renderer's ScriptInjector with the scripts scoped to this - // page's origin. - before_load_script_injector->ClearOnLoadScripts(); - for (uint64_t script_id : before_load_scripts_order_) { - const OriginScopedScript& script = before_load_scripts_[script_id]; - if (IsOriginWhitelisted(navigation_handle->GetURL(), script.origins())) { - before_load_script_injector->AddOnLoadScript( - mojo::WrapReadOnlySharedMemoryRegion(script.script().Duplicate())); - } - } + MaybeInjectBeforeLoadScripts(navigation_handle); + MaybeStartCastStreaming(navigation_handle); } void FrameImpl::DidFinishLoad(content::RenderFrameHost* render_frame_host, diff --git a/chromium/fuchsia/engine/browser/frame_impl.h b/chromium/fuchsia/engine/browser/frame_impl.h index 1a852488172..98574ac9b82 100644 --- a/chromium/fuchsia/engine/browser/frame_impl.h +++ b/chromium/fuchsia/engine/browser/frame_impl.h @@ -7,6 +7,7 @@ #include <fuchsia/web/cpp/fidl.h> #include <lib/fidl/cpp/binding_set.h> +#include <lib/ui/scenic/cpp/view_ref_pair.h> #include <lib/zx/channel.h> #include <list> @@ -35,6 +36,7 @@ namespace content { class FromRenderFrameHost; } // namespace content +class CastStreamingSessionClient; class ContextImpl; class FrameWindowTreeHost; class FrameLayoutManager; @@ -84,6 +86,9 @@ class FrameImpl : public fuchsia::web::Frame, void set_handle_actions_for_test(bool handle) { accessibility_bridge_->set_handle_actions_for_test(handle); } + CastStreamingSessionClient* cast_streaming_session_client_for_test() { + return cast_streaming_session_client_.get(); + } private: FRIEND_TEST_ALL_PREFIXES(FrameImplTest, DelayedNavigationEventAck); @@ -130,7 +135,8 @@ class FrameImpl : public fuchsia::web::Frame, // Initializes WindowTreeHost for the view with the specified |view_token|. // |view_token| may be uninitialized in headless mode. - void InitWindowTreeHost(fuchsia::ui::views::ViewToken view_token); + void InitWindowTreeHost(fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair); // Destroys the WindowTreeHost along with its view or other associated // resources. @@ -139,8 +145,25 @@ class FrameImpl : public fuchsia::web::Frame, // Destroys |this| and sends the FIDL |error| to the client. void CloseAndDestroyFrame(zx_status_t error); + // Determines whether |message| is a Cast Streaming message and if so, handles + // it. Returns whether it handled the message, regardless of whether that was + // successful. If true is returned, |callback| has been called. Returns false + // immediately if Cast Streaming support is not enabled. Called by + // PostMessage(). + bool MaybeHandleCastStreamingMessage(std::string* origin, + fuchsia::web::WebMessage* message, + PostMessageCallback* callback); + + void MaybeInjectBeforeLoadScripts( + content::NavigationHandle* navigation_handle); + + void MaybeStartCastStreaming(content::NavigationHandle* navigation_handle); + // fuchsia::web::Frame implementation. void CreateView(fuchsia::ui::views::ViewToken view_token) override; + void CreateViewWithViewRef(fuchsia::ui::views::ViewToken view_token, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) override; void GetMediaPlayer(fidl::InterfaceRequest<fuchsia::media::sessions2::Player> player) override; void GetNavigationController( @@ -272,6 +295,7 @@ class FrameImpl : public fuchsia::web::Frame, gfx::Size render_size_override_; std::unique_ptr<MediaPlayerImpl> media_player_; + std::unique_ptr<CastStreamingSessionClient> cast_streaming_session_client_; fidl::Binding<fuchsia::web::Frame> binding_; media_control::MediaBlocker media_blocker_; diff --git a/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc b/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc index bccb5200db3..5d19e3757d5 100644 --- a/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc +++ b/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc @@ -24,6 +24,7 @@ #include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/base/url_request_rewrite_test_util.h" #include "fuchsia/engine/browser/frame_impl.h" +#include "fuchsia/engine/switches.h" #include "fuchsia/engine/test/test_data.h" #include "fuchsia/engine/test/web_engine_browser_test.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -237,9 +238,9 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, ContextDeletedBeforeFrameWithView) { fuchsia::web::FramePtr frame = CreateFrame(); EXPECT_TRUE(frame); - auto view_tokens = scenic::NewViewTokenPair(); + auto view_tokens = scenic::ViewTokenPair::New(); - frame->CreateView(std::move(view_tokens.first)); + frame->CreateView(std::move(view_tokens.view_token)); base::RunLoop().RunUntilIdle(); base::RunLoop run_loop; @@ -1191,18 +1192,14 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessagePassMessagePort) { "messageport"); fuchsia::web::MessagePortPtr message_port; - fuchsia::web::WebMessage msg; { - fuchsia::web::OutgoingTransferable outgoing; - outgoing.set_message_port(message_port.NewRequest()); - std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; - outgoing_vector.push_back(std::move(outgoing)); - msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( - post_message_url.GetOrigin().spec(), std::move(msg), + post_message_url.GetOrigin().spec(), + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("hi", "test")), cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); base::RunLoop run_loop; @@ -1216,6 +1213,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessagePassMessagePort) { } { + fuchsia::web::WebMessage msg; msg.set_data(cr_fuchsia::MemBufferFromString("ping", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> post_result; @@ -1254,18 +1252,14 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageMessagePortDisconnected) { "messageport"); fuchsia::web::MessagePortPtr message_port; - fuchsia::web::WebMessage msg; { - fuchsia::web::OutgoingTransferable outgoing; - outgoing.set_message_port(message_port.NewRequest()); - std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; - outgoing_vector.push_back(std::move(outgoing)); - msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( - post_message_url.GetOrigin().spec(), std::move(msg), + post_message_url.GetOrigin().spec(), + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("hi", "test")), cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); base::RunLoop run_loop; @@ -1312,19 +1306,15 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) { "messageport"); fuchsia::web::MessagePortPtr incoming_message_port; - fuchsia::web::WebMessage msg; { fuchsia::web::MessagePortPtr message_port; - fuchsia::web::OutgoingTransferable outgoing; - outgoing.set_message_port(message_port.NewRequest()); - std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; - outgoing_vector.push_back(std::move(outgoing)); - msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( - "*", std::move(msg), + "*", + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("hi", "test")), cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); base::RunLoop run_loop; @@ -1348,6 +1338,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) { base::RunLoop run_loop; cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> post_result(run_loop.QuitClosure()); + fuchsia::web::WebMessage msg; msg.set_data(cr_fuchsia::MemBufferFromString("ping", "test")); incoming_message_port->PostMessage( std::move(msg), @@ -1360,20 +1351,16 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) { // that all the "ack pings" are ready to be consumed. { fuchsia::web::MessagePortPtr ack_message_port; - fuchsia::web::WebMessage msg; - fuchsia::web::OutgoingTransferable outgoing; - outgoing.set_message_port(ack_message_port.NewRequest()); - std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; - outgoing_vector.push_back(std::move(outgoing)); - msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); // Quit the runloop only after we've received a WebMessage AND a PostMessage // result. cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( - "*", std::move(msg), + "*", + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + ack_message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("hi", "test")), cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); base::RunLoop run_loop; cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver( @@ -1422,18 +1409,15 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageBadOriginDropped) { // PostMessage() to invalid origins should be ignored. We pass in a // MessagePort but nothing should happen to it. fuchsia::web::MessagePortPtr unused_message_port; - fuchsia::web::OutgoingTransferable unused_outgoing; - unused_outgoing.set_message_port(unused_message_port.NewRequest()); - std::vector<fuchsia::web::OutgoingTransferable> unused_outgoing_vector; - unused_outgoing_vector.push_back(std::move(unused_outgoing)); - msg.set_outgoing_transfer(std::move(unused_outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("bad origin, bad!", "test")); - cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> unused_post_result; - frame->PostMessage("https://example.com", std::move(msg), - cr_fuchsia::CallbackToFitFunction( - unused_post_result.GetReceiveCallback())); + frame->PostMessage( + "https://example.com", + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + unused_message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("bad origin, bad!", "test")), + cr_fuchsia::CallbackToFitFunction( + unused_post_result.GetReceiveCallback())); cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> unused_message_read; bad_origin_incoming_message_port->ReceiveMessage( cr_fuchsia::CallbackToFitFunction( @@ -1446,17 +1430,13 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageBadOriginDropped) { // discarded. fuchsia::web::MessagePortPtr incoming_message_port; fuchsia::web::MessagePortPtr message_port; - fuchsia::web::OutgoingTransferable outgoing; - outgoing.set_message_port(message_port.NewRequest()); - std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; - outgoing_vector.push_back(std::move(outgoing)); - msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("good origin", "test")); - cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( - "*", std::move(msg), + "*", + cr_fuchsia::CreateWebMessageWithMessagePortRequest( + message_port.NewRequest(), + cr_fuchsia::MemBufferFromString("good origin", "test")), cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); base::RunLoop run_loop; cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver( @@ -1696,6 +1676,11 @@ class RequestMonitoringFrameImplBrowserTest : public FrameImplTest { embedded_test_server()->StartAndReturnHandle()); } + void SetUpCommandLine(base::CommandLine* command_line) override { + // Needed for UrlRequestRewriteAddHeaders. + command_line->AppendSwitchNative(switches::kCorsExemptHeaders, "Test"); + } + std::map<GURL, net::test_server::HttpRequest> accumulated_requests_; private: diff --git a/chromium/fuchsia/engine/browser/frame_window_tree_host.cc b/chromium/fuchsia/engine/browser/frame_window_tree_host.cc index 5fc20eb54cd..4ad2732c543 100644 --- a/chromium/fuchsia/engine/browser/frame_window_tree_host.cc +++ b/chromium/fuchsia/engine/browser/frame_window_tree_host.cc @@ -51,13 +51,14 @@ class FrameWindowTreeHost::WindowParentingClientImpl FrameWindowTreeHost::FrameWindowTreeHost( fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair, content::WebContents* web_contents) : web_contents_(web_contents) { CreateCompositor(); ui::PlatformWindowInitProperties properties; properties.view_token = std::move(view_token); - properties.view_ref_pair = scenic::ViewRefPair::New(); + properties.view_ref_pair = std::move(view_ref_pair); view_ref_ = DupViewRef(properties.view_ref_pair.view_ref); CreateAndSetPlatformWindow(std::move(properties)); diff --git a/chromium/fuchsia/engine/browser/frame_window_tree_host.h b/chromium/fuchsia/engine/browser/frame_window_tree_host.h index 17fe15f5c5a..0e517bcf5d1 100644 --- a/chromium/fuchsia/engine/browser/frame_window_tree_host.h +++ b/chromium/fuchsia/engine/browser/frame_window_tree_host.h @@ -19,6 +19,7 @@ class FrameWindowTreeHost : public aura::WindowTreeHostPlatform { public: // |web_contents| must to outlive |FrameWindowTreeHost|. FrameWindowTreeHost(fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair, content::WebContents* web_contents); ~FrameWindowTreeHost() final; diff --git a/chromium/fuchsia/engine/browser/media_resource_provider_service.cc b/chromium/fuchsia/engine/browser/media_resource_provider_service.cc index c3e36e13202..154a96304cc 100644 --- a/chromium/fuchsia/engine/browser/media_resource_provider_service.cc +++ b/chromium/fuchsia/engine/browser/media_resource_provider_service.cc @@ -8,7 +8,7 @@ #include <lib/sys/cpp/component_context.h> #include "base/bind.h" -#include "base/fuchsia/default_context.h" +#include "base/fuchsia/process_context.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/frame_service_base.h" #include "content/public/browser/provision_fetcher_factory.h" @@ -79,7 +79,7 @@ void MediaResourceProviderImpl::CreateCdm( void MediaResourceProviderImpl::CreateAudioConsumer( fidl::InterfaceRequest<fuchsia::media::AudioConsumer> request) { - auto factory = base::fuchsia::ComponentContextForCurrentProcess() + auto factory = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::media::SessionAudioConsumerFactory>(); factory->CreateAudioConsumer( @@ -100,7 +100,7 @@ void MediaResourceProviderImpl::CreateAudioCapturer( return; } - auto factory = base::fuchsia::ComponentContextForCurrentProcess() + auto factory = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::media::Audio>(); factory->CreateAudioCapturer(std::move(request), /*loopback=*/false); @@ -114,7 +114,7 @@ class WidevineHandler : public media::FuchsiaCdmManager::KeySystemHandler { void CreateCdm( fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule> request) override { - auto widevine = base::fuchsia::ComponentContextForCurrentProcess() + auto widevine = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::media::drm::Widevine>(); widevine->CreateContentDecryptionModule(std::move(request)); @@ -123,7 +123,7 @@ class WidevineHandler : public media::FuchsiaCdmManager::KeySystemHandler { fuchsia::media::drm::ProvisionerPtr CreateProvisioner() override { fuchsia::media::drm::ProvisionerPtr provisioner; - auto widevine = base::fuchsia::ComponentContextForCurrentProcess() + auto widevine = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::media::drm::Widevine>(); widevine->CreateProvisioner(provisioner.NewRequest()); @@ -140,7 +140,7 @@ class PlayreadyHandler : public media::FuchsiaCdmManager::KeySystemHandler { void CreateCdm( fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule> request) override { - auto playready = base::fuchsia::ComponentContextForCurrentProcess() + auto playready = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::media::drm::PlayReady>(); playready->CreateContentDecryptionModule(std::move(request)); diff --git a/chromium/fuchsia/engine/browser/navigation_controller_impl.cc b/chromium/fuchsia/engine/browser/navigation_controller_impl.cc index 4c95d83979f..79879065f9d 100644 --- a/chromium/fuchsia/engine/browser/navigation_controller_impl.cc +++ b/chromium/fuchsia/engine/browser/navigation_controller_impl.cc @@ -15,36 +15,6 @@ #include "net/http/http_util.h" #include "ui/base/page_transition_types.h" -namespace { - -void UpdateNavigationStateFromNavigationEntry( - content::NavigationEntry* entry, - content::WebContents* web_contents, - fuchsia::web::NavigationState* navigation_state) { - DCHECK(entry); - DCHECK(web_contents); - DCHECK(navigation_state); - - navigation_state->set_title(base::UTF16ToUTF8(entry->GetTitleForDisplay())); - navigation_state->set_url(entry->GetURL().spec()); - - switch (entry->GetPageType()) { - case content::PageType::PAGE_TYPE_NORMAL: - case content::PageType::PAGE_TYPE_INTERSTITIAL: - navigation_state->set_page_type(fuchsia::web::PageType::NORMAL); - break; - case content::PageType::PAGE_TYPE_ERROR: - navigation_state->set_page_type(fuchsia::web::PageType::ERROR); - break; - } - - navigation_state->set_can_go_back(web_contents->GetController().CanGoBack()); - navigation_state->set_can_go_forward( - web_contents->GetController().CanGoForward()); -} - -} // namespace - NavigationControllerImpl::NavigationControllerImpl( content::WebContents* web_contents) : web_contents_(web_contents), weak_factory_(this) { @@ -88,19 +58,48 @@ void NavigationControllerImpl::SetEventListener( } } -void NavigationControllerImpl::OnNavigationEntryChanged() { - fuchsia::web::NavigationState new_state; - new_state.set_is_main_document_loaded(is_main_document_loaded_); - UpdateNavigationStateFromNavigationEntry( - web_contents_->GetController().GetVisibleEntry(), web_contents_, - &new_state); - if (new_state.page_type() != fuchsia::web::PageType::ERROR && - uncommitted_load_error_) { +fuchsia::web::NavigationState +NavigationControllerImpl::GetVisibleNavigationState() const { + content::NavigationEntry* const entry = + web_contents_->GetController().GetVisibleEntry(); + if (!entry) + return fuchsia::web::NavigationState(); + + fuchsia::web::NavigationState state; + + // Populate some fields directly from the NavigationEntry. + state.set_title(base::UTF16ToUTF8(entry->GetTitleForDisplay())); + state.set_url(entry->GetURL().spec()); + + if (web_contents_->IsCrashed()) { + // TODO(https:://crbug.com/1092506): Add an explicit crashed indicator to + // NavigationState, separate from PageType::ERROR. + state.set_page_type(fuchsia::web::PageType::ERROR); + } else if (uncommitted_load_error_) { // If there was a loading error which prevented the navigation entry from - // being committed, then reflect the error in |new_state|. - new_state.set_page_type(fuchsia::web::PageType::ERROR); + // being committed, then report PageType::ERROR. + state.set_page_type(fuchsia::web::PageType::ERROR); + } else { + switch (entry->GetPageType()) { + case content::PageType::PAGE_TYPE_NORMAL: + case content::PageType::PAGE_TYPE_INTERSTITIAL: + state.set_page_type(fuchsia::web::PageType::NORMAL); + break; + case content::PageType::PAGE_TYPE_ERROR: + state.set_page_type(fuchsia::web::PageType::ERROR); + break; + } } + state.set_is_main_document_loaded(is_main_document_loaded_); + state.set_can_go_back(web_contents_->GetController().CanGoBack()); + state.set_can_go_forward(web_contents_->GetController().CanGoForward()); + + return state; +} + +void NavigationControllerImpl::OnNavigationEntryChanged() { + fuchsia::web::NavigationState new_state = GetVisibleNavigationState(); DiffNavigationEntries(previous_navigation_state_, new_state, &pending_navigation_event_); previous_navigation_state_ = std::move(new_state); @@ -209,17 +208,7 @@ void NavigationControllerImpl::Reload(fuchsia::web::ReloadType type) { void NavigationControllerImpl::GetVisibleEntry( fuchsia::web::NavigationController::GetVisibleEntryCallback callback) { - content::NavigationEntry* entry = - web_contents_->GetController().GetVisibleEntry(); - if (!entry) { - callback({}); - return; - } - - fuchsia::web::NavigationState state; - state.set_is_main_document_loaded(is_main_document_loaded_); - UpdateNavigationStateFromNavigationEntry(entry, web_contents_, &state); - callback(std::move(state)); + callback(GetVisibleNavigationState()); } void NavigationControllerImpl::TitleWasSet(content::NavigationEntry* entry) { @@ -242,6 +231,14 @@ void NavigationControllerImpl::DidFinishLoad( OnNavigationEntryChanged(); } +void NavigationControllerImpl::RenderProcessGone( + base::TerminationStatus status) { + // If the current RenderProcess terminates then trigger a NavigationState + // change to let the caller know that something is wrong. + LOG(WARNING) << "RenderProcess gone, TerminationStatus=" << status; + OnNavigationEntryChanged(); +} + void NavigationControllerImpl::DidStartNavigation( content::NavigationHandle* navigation_handle) { uncommitted_load_error_ = false; diff --git a/chromium/fuchsia/engine/browser/navigation_controller_impl.h b/chromium/fuchsia/engine/browser/navigation_controller_impl.h index 1d48ad27976..9a34c7f3d0a 100644 --- a/chromium/fuchsia/engine/browser/navigation_controller_impl.h +++ b/chromium/fuchsia/engine/browser/navigation_controller_impl.h @@ -33,6 +33,11 @@ class NavigationControllerImpl : public fuchsia::web::NavigationController, fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener); private: + // Returns a NavigationState reflecting the current state of |web_contents_|'s + // visible navigation entry, taking into account |is_main_document_loaded_| + // and |uncommitted_load_error_| states. + fuchsia::web::NavigationState GetVisibleNavigationState() const; + // Processes the most recent changes to the browser's navigation state and // triggers the publishing of change events. void OnNavigationEntryChanged(); @@ -56,6 +61,7 @@ class NavigationControllerImpl : public fuchsia::web::NavigationController, void DocumentAvailableInMainFrame() final; void DidFinishLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url) final; + void RenderProcessGone(base::TerminationStatus status) final; void DidStartNavigation(content::NavigationHandle* navigation_handle) final; void DidFinishNavigation(content::NavigationHandle* navigation_handle) final; diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_context.cc b/chromium/fuchsia/engine/browser/web_engine_browser_context.cc index de9ce2c7b40..03a6e9fa3a7 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_context.cc +++ b/chromium/fuchsia/engine/browser/web_engine_browser_context.cc @@ -12,7 +12,6 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/path_service.h" -#include "base/task/post_task.h" #include "components/keyed_service/core/simple_key_map.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -67,8 +66,8 @@ WebEngineBrowserContext::~WebEngineBrowserContext() { NotifyWillBeDestroyed(this); if (resource_context_) { - base::DeleteSoon(FROM_HERE, {content::BrowserThread::IO}, - std::move(resource_context_)); + content::GetIOThreadTaskRunner({})->DeleteSoon( + FROM_HERE, std::move(resource_context_)); } ShutdownStoragePartitions(); diff --git a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc index 6d77b2eb8bf..0fb644e3883 100644 --- a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc +++ b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc @@ -11,9 +11,9 @@ #include "base/stl_util.h" #include "base/strings/string_split.h" #include "components/version_info/version_info.h" -#include "content/public/browser/cors_exempt_headers.h" #include "content/public/browser/devtools_manager_delegate.h" #include "content/public/browser/network_service_instance.h" +#include "content/public/common/content_switches.h" #include "content/public/common/user_agent.h" #include "content/public/common/web_preferences.h" #include "fuchsia/base/fuchsia_dir_scheme.h" @@ -153,13 +153,16 @@ void WebEngineContentBrowserClient:: void WebEngineContentBrowserClient::AppendExtraCommandLineSwitches( base::CommandLine* command_line, int child_process_id) { + // TODO(https://crbug.com/1083520): Pass based on process type. constexpr char const* kSwitchesToCopy[] = { switches::kContentDirectories, switches::kCorsExemptHeaders, switches::kDisableSoftwareVideoDecoders, + switches::kEnableCastStreamingReceiver, switches::kEnableProtectedVideoBuffers, switches::kEnableWidevine, switches::kForceProtectedVideoOutputBuffers, + switches::kMaxDecodedImageSizeMb, switches::kPlayreadyKeySystem, }; @@ -206,8 +209,4 @@ void WebEngineContentBrowserClient::ConfigureNetworkContextParams( // starting with the headers passed in via // |CreateContextParams.cors_exempt_headers|. network_context_params->cors_exempt_header_list = cors_exempt_headers_; - - // Exempt the minimal headers needed for CORS preflight checks (Purpose, - // X-Requested-With). - content::UpdateCorsExemptHeader(network_context_params); } diff --git a/chromium/fuchsia/engine/browser/web_engine_net_log_observer.cc b/chromium/fuchsia/engine/browser/web_engine_net_log_observer.cc index 66f454574e2..c24b7215ab9 100644 --- a/chromium/fuchsia/engine/browser/web_engine_net_log_observer.cc +++ b/chromium/fuchsia/engine/browser/web_engine_net_log_observer.cc @@ -20,7 +20,8 @@ namespace { std::unique_ptr<base::DictionaryValue> GetWebEngineConstants() { std::unique_ptr<base::DictionaryValue> constants_dict = - net::GetNetConstants(); + base::DictionaryValue::From( + base::Value::ToUniquePtrValue(net::GetNetConstants())); base::DictionaryValue dict; dict.SetKey("name", base::Value("WebEngine")); diff --git a/chromium/fuchsia/engine/cast_streaming_session.mojom b/chromium/fuchsia/engine/cast_streaming_session.mojom new file mode 100644 index 00000000000..33af21c3e95 --- /dev/null +++ b/chromium/fuchsia/engine/cast_streaming_session.mojom @@ -0,0 +1,59 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojom; + +import "media/mojo/mojom/media_types.mojom"; + +// Renderer-implemented interface that is used to receive metadata for buffers. +interface CastStreamingBufferReceiver { + // Provide metadata for a buffer to the Cast Streaming Receiver in the + // renderer process. The buffer data itself is on the associated |data_pipe|, + // from AudioStreamInfo or VideoStreamInfo. + // This is called for every new buffer pushed in the associated |data_pipe|. + ProvideBuffer(media.mojom.DecoderBuffer buffer); +}; + +// Information about a Cast Streaming audio stream. +struct AudioStreamInfo { + // Audio decoder configuration. + media.mojom.AudioDecoderConfig decoder_config; + + // Audio buffer metadata receiver. + pending_receiver<CastStreamingBufferReceiver> buffer_receiver; + + // Mojo data pipe over which audio buffer data is sent. + handle<data_pipe_consumer> data_pipe; +}; + +// Information about a Cast Streaming video stream. +struct VideoStreamInfo { + // Video decoder configuration. + media.mojom.VideoDecoderConfig decoder_config; + + // Video buffer metadata receiver. + pending_receiver<CastStreamingBufferReceiver> buffer_receiver; + + // Mojo data pipe over which video buffer data is sent. + handle<data_pipe_consumer> data_pipe; +}; + +// Implemented by the renderer, used to start the Cast Streaming Session. +// Closure of the Mojo channel will trigger the end of the Cast Streaming +// Session. +interface CastStreamingReceiver { + // Used for synchronization between the browser and the renderer. The browser + // should invoke this after binding the interface, and wait for the reply + // callback to know when the renderer is ready to receive and render frames. + EnableReceiver() => (); + + // Called when the streams have been successfully initialized. At least one of + // |audio_stream_info| or |video_stream_info| must be set. This will only be + // called once per the lifetime of CastStreamingReceiver. After this call, + // CastStreamingBufferReceiver.ProvideBuffer() will be called on every new + // frame. + OnStreamsInitialized( + AudioStreamInfo? audio_stream_info, + VideoStreamInfo? video_stream_info); +}; diff --git a/chromium/fuchsia/engine/common/cast_streaming.cc b/chromium/fuchsia/engine/common/cast_streaming.cc new file mode 100644 index 00000000000..a224697fce3 --- /dev/null +++ b/chromium/fuchsia/engine/common/cast_streaming.cc @@ -0,0 +1,24 @@ +// Copyright 2020 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/common/cast_streaming.h" + +#include "base/command_line.h" +#include "fuchsia/engine/switches.h" +#include "url/gurl.h" + +namespace { +constexpr char kCastStreamingReceiverUrl[] = "data:cast_streaming_receiver"; +} // namespace + +bool IsCastStreamingEnabled() { + static bool is_cast_streaming_enabled = + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableCastStreamingReceiver); + return is_cast_streaming_enabled; +} + +bool IsCastStreamingMediaSourceUrl(const GURL& url) { + return url == kCastStreamingReceiverUrl; +} diff --git a/chromium/fuchsia/engine/common/cast_streaming.h b/chromium/fuchsia/engine/common/cast_streaming.h new file mode 100644 index 00000000000..660c4b538ea --- /dev/null +++ b/chromium/fuchsia/engine/common/cast_streaming.h @@ -0,0 +1,16 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_ENGINE_COMMON_CAST_STREAMING_H_ +#define FUCHSIA_ENGINE_COMMON_CAST_STREAMING_H_ + +class GURL; + +// Returns true if Cast Streaming is enabled for this process. +bool IsCastStreamingEnabled(); + +// Returns true if |url| is the Cast Streaming media source URL. +bool IsCastStreamingMediaSourceUrl(const GURL& url); + +#endif // FUCHSIA_ENGINE_COMMON_CAST_STREAMING_H_ diff --git a/chromium/fuchsia/engine/common/web_engine_content_client.cc b/chromium/fuchsia/engine/common/web_engine_content_client.cc index 24c9a741950..30d007dfe4c 100644 --- a/chromium/fuchsia/engine/common/web_engine_content_client.cc +++ b/chromium/fuchsia/engine/common/web_engine_content_client.cc @@ -5,6 +5,7 @@ #include "fuchsia/engine/common/web_engine_content_client.h" #include "base/command_line.h" +#include "base/notreached.h" #include "fuchsia/base/fuchsia_dir_scheme.h" #include "fuchsia/engine/switches.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/fuchsia/engine/context_provider.cmx b/chromium/fuchsia/engine/context_provider.cmx index 4018804f3da..a36b72bc4f8 100644 --- a/chromium/fuchsia/engine/context_provider.cmx +++ b/chromium/fuchsia/engine/context_provider.cmx @@ -8,6 +8,7 @@ ], "services": [ "fuchsia.feedback.ComponentDataRegister", + "fuchsia.feedback.CrashReportingProductRegister", "fuchsia.logger.LogSink", "fuchsia.process.Launcher" ] diff --git a/chromium/fuchsia/engine/context_provider_impl.cc b/chromium/fuchsia/engine/context_provider_impl.cc index ce95251599c..e6054330e3c 100644 --- a/chromium/fuchsia/engine/context_provider_impl.cc +++ b/chromium/fuchsia/engine/context_provider_impl.cc @@ -54,6 +54,7 @@ #include "net/http/http_util.h" #include "services/network/public/cpp/features.h" #include "services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h" +#include "third_party/blink/public/common/switches.h" #include "third_party/widevine/cdm/widevine_cdm_common.h" #include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" @@ -61,6 +62,13 @@ namespace { +// Use a constexpr instead of the existing base::Feature, because of the +// additional dependencies required. +constexpr char kMixedContentAutoupgradeFeatureName[] = + "AutoupgradeMixedContent"; +constexpr char kDisableMixedContentAutoupgradeOrigin[] = + "disable-mixed-content-autoupgrade"; + // Returns the underlying channel if |directory| is a client endpoint for a // |fuchsia::io::Directory| protocol. Otherwise, returns an empty channel. zx::channel ValidateDirectoryAndTakeChannel( @@ -154,25 +162,30 @@ bool MaybeAddCommandLineArgsFromConfig(const base::Value& config, return true; static const base::StringPiece kAllowedArgs[] = { + blink::switches::kGpuRasterizationMSAASampleCount, + blink::switches::kMinHeightForGpuRasterTile, cc::switches::kEnableGpuBenchmarking, - switches::kAcceleratedCanvas2dMSAASampleCount, switches::kDisableFeatures, switches::kDisableGpuWatchdog, + // TODO(crbug.com/1082821): Remove this switch from the allow-list. + switches::kEnableCastStreamingReceiver, switches::kEnableFeatures, switches::kEnableLowEndDeviceMode, switches::kForceGpuMemAvailableMb, switches::kForceGpuMemDiscardableLimitMb, switches::kForceMaxTextureSize, - switches::kGpuRasterizationMSAASampleCount, - switches::kMinHeightForGpuRasterTile, + switches::kMaxDecodedImageSizeMb, switches::kRendererProcessLimit, + switches::kWebglAntialiasingMode, + switches::kWebglMSAASampleCount, }; for (const auto& arg : args->DictItems()) { if (!base::Contains(kAllowedArgs, arg.first)) { - LOG(ERROR) << "Unknown command-line arg: " << arg.first; - // TODO(https://crbug.com/1032439): Return false here once we are done - // experimenting with memory-related command-line options. + // TODO(https://crbug.com/1032439): Increase severity and return false + // once we have a mechanism for soft transitions of supported arguments. + LOG(WARNING) << "Unknown command-line arg: '" << arg.first + << "'. Config file and WebEngine version may not match."; continue; } @@ -191,7 +204,7 @@ bool MaybeAddCommandLineArgsFromConfig(const base::Value& config, // which // we don't yet support. if (arg.first == switches::kEnableLowEndDeviceMode) - command_line->AppendSwitch(switches::kDisableRGBA4444Textures); + command_line->AppendSwitch(blink::switches::kDisableRGBA4444Textures); } return true; @@ -211,12 +224,6 @@ bool IsFuchsiaCdmSupported() { #endif } -// Use the most significant bit to enable cast streaming receiver features. -// TODO(crbug.com/1078919): Remove this when we have a better way of enabling -// this feature. -constexpr auto kCastStreamingFeatureFlag = - static_cast<fuchsia::web::ContextFeatureFlags>(1ULL << 63); - } // namespace const uint32_t ContextProviderImpl::kContextRequestHandleId = @@ -405,18 +412,8 @@ void ContextProviderImpl::Create( // SkiaRenderer requires out-of-process rasterization be enabled. launch_command.AppendSwitch(switches::kEnableOopRasterization); - if (!enable_protected_graphics) { - launch_command.AppendSwitchASCII(switches::kUseGL, - gl::kGLImplementationANGLEName); - } else { - DLOG(WARNING) << "ANGLE is not compatible with " - << switches::kEnforceVulkanProtectedMemory - << ", disabling GL"; - // TODO(crbug.com/1059010): Fix this; probably don't protect canvas - // resources. - launch_command.AppendSwitchASCII(switches::kUseGL, - gl::kGLImplementationStubName); - } + launch_command.AppendSwitchASCII(switches::kUseGL, + gl::kGLImplementationANGLEName); } else { VLOG(1) << "Disabling GPU acceleration."; // Disable use of Vulkan GPU, and use of the software-GL rasterizer. The @@ -460,11 +457,6 @@ void ContextProviderImpl::Create( launch_command.AppendSwitch(switches::kDisableSoftwareVideoDecoders); } - const bool enable_cast_streaming_receiver = - (features & kCastStreamingFeatureFlag) == kCastStreamingFeatureFlag; - if (enable_cast_streaming_receiver) - launch_command.AppendSwitch(switches::kEnableCastStreamingReceiver); - // Validate embedder-supplied product, and optional version, and pass it to // the Context to include in the UserAgent. if (params.has_user_agent_product()) { @@ -502,10 +494,13 @@ void ContextProviderImpl::Create( if (params.has_unsafely_treat_insecure_origins_as_secure()) { const std::vector<std::string>& insecure_origins = params.unsafely_treat_insecure_origins_as_secure(); - if (std::find(insecure_origins.begin(), insecure_origins.end(), - switches::kAllowRunningInsecureContent) != - insecure_origins.end()) { - launch_command.AppendSwitch(switches::kAllowRunningInsecureContent); + for (auto origin : insecure_origins) { + if (origin == switches::kAllowRunningInsecureContent) + launch_command.AppendSwitch(switches::kAllowRunningInsecureContent); + if (origin == kDisableMixedContentAutoupgradeOrigin) { + AppendFeature(switches::kDisableFeatures, + kMixedContentAutoupgradeFeatureName, &launch_command); + } } // TODO(crbug.com/1023510): Pass the rest of the list to the Context // process. @@ -522,10 +517,19 @@ void ContextProviderImpl::Create( base::JoinString(cors_exempt_headers, ",")); } - if (launch_for_test_) - launch_for_test_.Run(launch_command, launch_options); - else - base::LaunchProcess(launch_command, launch_options); + base::Process context_process; + if (launch_for_test_) { + context_process = launch_for_test_.Run(launch_command, launch_options); + } else { + context_process = base::LaunchProcess(launch_command, launch_options); + } + + if (context_process.IsValid()) { + // Set |context_process| termination to teardown its job and sub-processes. + zx_status_t result = zx_job_set_critical(launch_options.job_handle, 0, + context_process.Handle()); + ZX_CHECK(ZX_OK == result, result) << "zx_job_set_critical"; + } // |context_request| and any DevTools channels were transferred (not copied) // to the Context process. diff --git a/chromium/fuchsia/engine/context_provider_impl_unittest.cc b/chromium/fuchsia/engine/context_provider_impl_unittest.cc index e6aae9929c8..22eeaa655c3 100644 --- a/chromium/fuchsia/engine/context_provider_impl_unittest.cc +++ b/chromium/fuchsia/engine/context_provider_impl_unittest.cc @@ -506,35 +506,3 @@ TEST(ContextProviderImplConfigTest, WithConfigWithWronglyTypedCommandLineArgs) { loop.Run(); } - -// Tests setting the custom Cast Streaming Receiver feature flag properly adds -// the Cast Streaming Receiver switch. -TEST(ContextProviderImplReceiverTest, CastStreamingReceiverFeatureFlag) { - const base::test::SingleThreadTaskEnvironment task_environment_{ - base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; - - constexpr auto kCastStreamingFeatureFlag = - static_cast<fuchsia::web::ContextFeatureFlags>(1ULL << 63); - - base::RunLoop loop; - ContextProviderImpl context_provider; - context_provider.SetLaunchCallbackForTest( - base::BindLambdaForTesting([&](const base::CommandLine& command, - const base::LaunchOptions& options) { - EXPECT_TRUE(command.HasSwitch(switches::kEnableCastStreamingReceiver)); - loop.Quit(); - return base::Process(); - })); - - fuchsia::web::ContextPtr context; - context.set_error_handler([&loop](zx_status_t status) { - ADD_FAILURE(); - loop.Quit(); - }); - - fuchsia::web::CreateContextParams params = BuildCreateContextParams(); - params.set_features(kCastStreamingFeatureFlag); - context_provider.Create(std::move(params), context.NewRequest()); - - loop.Run(); -} diff --git a/chromium/fuchsia/engine/context_provider_main.cc b/chromium/fuchsia/engine/context_provider_main.cc index 50195913faf..87cf253076e 100644 --- a/chromium/fuchsia/engine/context_provider_main.cc +++ b/chromium/fuchsia/engine/context_provider_main.cc @@ -9,19 +9,27 @@ #include <lib/sys/cpp/outgoing_directory.h> #include "base/command_line.h" -#include "base/fuchsia/default_context.h" +#include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/logging.h" #include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" #include "base/task/single_thread_task_executor.h" #include "components/version_info/version_info.h" +#include "fuchsia/base/feedback_registration.h" #include "fuchsia/base/init_logging.h" +#include "fuchsia/base/inspect.h" #include "fuchsia/base/lifecycle_impl.h" #include "fuchsia/engine/context_provider_impl.h" namespace { +constexpr char kFeedbackAnnotationsNamespace[] = "web-engine"; +constexpr char kCrashProductName[] = "FuchsiaWebEngine"; +// TODO(https://fxbug.dev/51490): Use a programmatic mechanism to obtain this. +constexpr char kComponentUrl[] = + "fuchsia-pkg://fuchsia.com/web_engine#meta/context_provider.cmx"; + std::string GetVersionString() { std::string version_string = version_info::GetVersionNumber(); #if !defined(OFFICIAL_BUILD) @@ -30,14 +38,16 @@ std::string GetVersionString() { return version_string; } +// TODO(ddorwin): Move to feedback_registration.h. // TODO(https://crbug.com/1010222): Add annotations at Context startup, once // Contexts are moved out to run in their own components. void RegisterFeedbackAnnotations() { fuchsia::feedback::ComponentData component_data; - component_data.set_namespace_("web-engine"); + component_data.set_namespace_(kFeedbackAnnotationsNamespace); + // TODO(https://crbug.com/1077428): Add release channel to the annotations. component_data.mutable_annotations()->push_back( {"version", version_info::GetVersionNumber()}); - base::fuchsia::ComponentContextForCurrentProcess() + base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::feedback::ComponentDataRegister>() ->Upsert(std::move(component_data), []() {}); @@ -48,6 +58,8 @@ void RegisterFeedbackAnnotations() { int ContextProviderMain() { base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI); + cr_fuchsia::RegisterCrashReportingFields(kComponentUrl, kCrashProductName); + if (!cr_fuchsia::InitLoggingFromCommandLine( *base::CommandLine::ForCurrentProcess())) { return 1; @@ -62,12 +74,15 @@ int ContextProviderMain() { // Publish the ContextProvider and Debug services. sys::OutgoingDirectory* const directory = - base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(); + base::ComponentContextForProcess()->outgoing().get(); base::fuchsia::ScopedServiceBinding<fuchsia::web::ContextProvider> binding( directory, &context_provider); base::fuchsia::ScopedServiceBinding<fuchsia::web::Debug> debug_binding( directory->debug_dir(), &context_provider); + // Publish version information for this component to Inspect. + cr_fuchsia::PublishVersionInfoToInspect(base::ComponentInspectorForProcess()); + // Publish the Lifecycle service, used by the framework to request that the // service terminate. base::RunLoop run_loop; diff --git a/chromium/fuchsia/engine/on_load_script_injector.mojom b/chromium/fuchsia/engine/on_load_script_injector.mojom index a641eff9392..1cac32b2c3d 100644 --- a/chromium/fuchsia/engine/on_load_script_injector.mojom +++ b/chromium/fuchsia/engine/on_load_script_injector.mojom @@ -8,6 +8,7 @@ module mojom; // injection tasks the frame. Does not enforce script injection policies, // which must be implemented at a higher level. interface OnLoadScriptInjector { + // TODO(crbug.com/1060846): Stop using handle<shared_buffer>. AddOnLoadScript(handle<shared_buffer> script); ClearOnLoadScripts(); diff --git a/chromium/fuchsia/engine/renderer/cast_streaming_demuxer.cc b/chromium/fuchsia/engine/renderer/cast_streaming_demuxer.cc new file mode 100644 index 00000000000..92937f33b35 --- /dev/null +++ b/chromium/fuchsia/engine/renderer/cast_streaming_demuxer.cc @@ -0,0 +1,352 @@ +// Copyright 2020 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/renderer/cast_streaming_demuxer.h" + +#include "base/bind.h" +#include "base/sequence_checker.h" +#include "base/single_thread_task_runner.h" +#include "fuchsia/engine/renderer/cast_streaming_receiver.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/decoder_buffer.h" +#include "media/base/timestamp_constants.h" +#include "media/base/video_decoder_config.h" +#include "media/mojo/common/mojo_decoder_buffer_converter.h" + +namespace { + +// media::DemuxerStream shared audio/video implementation for Cast Streaming. +// Receives buffer metadata over a Mojo service and reads the buffers over a +// Mojo data pipe from the browser process. +class CastStreamingDemuxerStream : public media::DemuxerStream, + public mojom::CastStreamingBufferReceiver { + public: + CastStreamingDemuxerStream( + mojo::PendingReceiver<mojom::CastStreamingBufferReceiver> + pending_receiver, + mojo::ScopedDataPipeConsumerHandle consumer) + : receiver_(this, std::move(pending_receiver)), + decoder_buffer_reader_(std::move(consumer)) { + DVLOG(1) << __func__; + + // Mojo service disconnection means the Cast Streaming Session ended and no + // further buffer will be received. kAborted will be returned to the media + // pipeline for every subsequent DemuxerStream::Read() attempt. + receiver_.set_disconnect_handler(base::BindOnce( + &CastStreamingDemuxerStream::OnMojoDisconnect, base::Unretained(this))); + } + ~CastStreamingDemuxerStream() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + } + + // mojom::CastStreamingBufferReceiver implementation. + void ProvideBuffer(media::mojom::DecoderBufferPtr buffer) final { + DVLOG(3) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + pending_buffer_metadata_.push_back(std::move(buffer)); + GetNextBuffer(); + } + + void AbortPendingRead() { + DVLOG(3) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (pending_read_cb_) + std::move(pending_read_cb_).Run(Status::kAborted, nullptr); + } + + private: + void CompletePendingRead() { + DVLOG(3) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!pending_read_cb_ || !current_buffer_) + return; + + if (current_buffer_->end_of_stream()) { + std::move(pending_read_cb_).Run(Status::kAborted, nullptr); + return; + } + + std::move(pending_read_cb_).Run(Status::kOk, std::move(current_buffer_)); + GetNextBuffer(); + } + + void GetNextBuffer() { + DVLOG(3) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (current_buffer_ || pending_buffer_metadata_.empty()) + return; + + media::mojom::DecoderBufferPtr buffer = + std::move(pending_buffer_metadata_.front()); + pending_buffer_metadata_.pop_front(); + decoder_buffer_reader_.ReadDecoderBuffer( + std::move(buffer), + base::BindOnce(&CastStreamingDemuxerStream::OnBufferRead, + base::Unretained(this))); + } + + void OnBufferRead(scoped_refptr<media::DecoderBuffer> buffer) { + DVLOG(3) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Stop processing the pending buffer. OnMojoDisconnect() will trigger + // sending kAborted on subsequent Read() calls. This can happen if this + // object was in the process of reading a buffer off the data pipe when the + // Mojo connection ended. + if (!receiver_.is_bound()) + return; + + DCHECK(!current_buffer_); + current_buffer_ = buffer; + CompletePendingRead(); + } + + void OnMojoDisconnect() { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + receiver_.reset(); + pending_buffer_metadata_.clear(); + current_buffer_ = media::DecoderBuffer::CreateEOSBuffer(); + CompletePendingRead(); + } + + // DemuxerStream implementation. + void Read(ReadCB read_cb) final { + DVLOG(3) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(pending_read_cb_.is_null()); + pending_read_cb_ = std::move(read_cb); + CompletePendingRead(); + } + bool IsReadPending() const final { return !pending_read_cb_.is_null(); } + Liveness liveness() const final { return Liveness::LIVENESS_LIVE; } + bool SupportsConfigChanges() final { return false; } + + mojo::Receiver<CastStreamingBufferReceiver> receiver_; + media::MojoDecoderBufferReader decoder_buffer_reader_; + + ReadCB pending_read_cb_; + base::circular_deque<media::mojom::DecoderBufferPtr> pending_buffer_metadata_; + scoped_refptr<media::DecoderBuffer> current_buffer_; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +} // namespace + +class CastStreamingAudioDemuxerStream : public CastStreamingDemuxerStream { + public: + explicit CastStreamingAudioDemuxerStream( + mojom::AudioStreamInfoPtr audio_stream_info) + : CastStreamingDemuxerStream( + std::move(audio_stream_info->buffer_receiver), + std::move(audio_stream_info->data_pipe)), + config_(audio_stream_info->decoder_config) { + DVLOG(1) << __func__ + << ": config info: " << config_.AsHumanReadableString(); + } + ~CastStreamingAudioDemuxerStream() final = default; + + private: + // DemuxerStream implementation. + media::AudioDecoderConfig audio_decoder_config() final { return config_; } + media::VideoDecoderConfig video_decoder_config() final { + NOTREACHED(); + return media::VideoDecoderConfig(); + } + Type type() const final { return Type::AUDIO; } + + media::AudioDecoderConfig config_; +}; + +class CastStreamingVideoDemuxerStream : public CastStreamingDemuxerStream { + public: + explicit CastStreamingVideoDemuxerStream( + mojom::VideoStreamInfoPtr video_stream_info) + : CastStreamingDemuxerStream( + std::move(video_stream_info->buffer_receiver), + std::move(video_stream_info->data_pipe)), + config_(video_stream_info->decoder_config) { + DVLOG(1) << __func__ + << ": config info: " << config_.AsHumanReadableString(); + } + ~CastStreamingVideoDemuxerStream() final = default; + + private: + // DemuxerStream implementation. + media::AudioDecoderConfig audio_decoder_config() final { + NOTREACHED(); + return media::AudioDecoderConfig(); + } + media::VideoDecoderConfig video_decoder_config() final { return config_; } + Type type() const final { return Type::VIDEO; } + + media::VideoDecoderConfig config_; +}; + +CastStreamingDemuxer::CastStreamingDemuxer( + CastStreamingReceiver* receiver, + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner) + : media_task_runner_(media_task_runner), + original_task_runner_(base::SequencedTaskRunnerHandle::Get()), + receiver_(receiver) { + DVLOG(1) << __func__; + DCHECK(receiver_); +} + +CastStreamingDemuxer::~CastStreamingDemuxer() { + DVLOG(1) << __func__; + DCHECK(media_task_runner_->BelongsToCurrentThread()); + + if (was_initialization_successful_) { + original_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&CastStreamingReceiver::OnDemuxerDestroyed, + base::Unretained(receiver_))); + } +} + +void CastStreamingDemuxer::OnStreamsInitialized( + mojom::AudioStreamInfoPtr audio_stream_info, + mojom::VideoStreamInfoPtr video_stream_info) { + DVLOG(1) << __func__; + DCHECK(!media_task_runner_->BelongsToCurrentThread()); + + media_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&CastStreamingDemuxer::OnStreamsInitializedOnMediaThread, + base::Unretained(this), std::move(audio_stream_info), + std::move(video_stream_info))); +} + +void CastStreamingDemuxer::OnStreamsInitializedOnMediaThread( + mojom::AudioStreamInfoPtr audio_stream_info, + mojom::VideoStreamInfoPtr video_stream_info) { + DVLOG(1) << __func__; + DCHECK(media_task_runner_->BelongsToCurrentThread()); + DCHECK(initialized_cb_); + + if (!audio_stream_info && !video_stream_info) { + std::move(initialized_cb_) + .Run(media::PipelineStatus::DEMUXER_ERROR_COULD_NOT_OPEN); + return; + } + + if (audio_stream_info) { + audio_stream_ = std::make_unique<CastStreamingAudioDemuxerStream>( + std::move(audio_stream_info)); + } + if (video_stream_info) { + video_stream_ = std::make_unique<CastStreamingVideoDemuxerStream>( + std::move(video_stream_info)); + } + was_initialization_successful_ = true; + + std::move(initialized_cb_).Run(media::PipelineStatus::PIPELINE_OK); +} + +std::vector<media::DemuxerStream*> CastStreamingDemuxer::GetAllStreams() { + DVLOG(1) << __func__; + DCHECK(media_task_runner_->BelongsToCurrentThread()); + + std::vector<media::DemuxerStream*> streams; + if (video_stream_) + streams.push_back(video_stream_.get()); + if (audio_stream_) + streams.push_back(audio_stream_.get()); + return streams; +} + +std::string CastStreamingDemuxer::GetDisplayName() const { + return "CastStreamingDemuxer"; +} + +void CastStreamingDemuxer::Initialize(media::DemuxerHost* host, + media::PipelineStatusCallback status_cb) { + DVLOG(1) << __func__; + DCHECK(media_task_runner_->BelongsToCurrentThread()); + host_ = host; + + // Live streams have infinite duration. + host_->SetDuration(media::kInfiniteDuration); + initialized_cb_ = std::move(status_cb); + + original_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&CastStreamingReceiver::SetDemuxer, + base::Unretained(receiver_), base::Unretained(this))); +} + +void CastStreamingDemuxer::AbortPendingReads() { + DVLOG(2) << __func__; + DCHECK(media_task_runner_->BelongsToCurrentThread()); + + if (audio_stream_) + audio_stream_->AbortPendingRead(); + if (video_stream_) + video_stream_->AbortPendingRead(); +} + +// Not supported. +void CastStreamingDemuxer::StartWaitingForSeek(base::TimeDelta seek_time) {} + +// Not supported. +void CastStreamingDemuxer::CancelPendingSeek(base::TimeDelta seek_time) {} + +// Not supported. +void CastStreamingDemuxer::Seek(base::TimeDelta time, + media::PipelineStatusCallback status_cb) { + std::move(status_cb).Run(media::PipelineStatus::PIPELINE_OK); +} + +void CastStreamingDemuxer::Stop() { + DVLOG(1) << __func__; + DCHECK(media_task_runner_->BelongsToCurrentThread()); + + if (audio_stream_) + audio_stream_.reset(); + if (video_stream_) + video_stream_.reset(); +} + +base::TimeDelta CastStreamingDemuxer::GetStartTime() const { + return base::TimeDelta(); +} + +// Not supported. +base::Time CastStreamingDemuxer::GetTimelineOffset() const { + return base::Time(); +} + +// Not supported. +int64_t CastStreamingDemuxer::GetMemoryUsage() const { + return 0; +} + +base::Optional<media::container_names::MediaContainerName> +CastStreamingDemuxer::GetContainerForMetrics() const { + // Cast Streaming frames have no container. + return base::nullopt; +} + +// Not supported. +void CastStreamingDemuxer::OnEnabledAudioTracksChanged( + const std::vector<media::MediaTrack::Id>& track_ids, + base::TimeDelta curr_time, + TrackChangeCB change_completed_cb) { + DLOG(WARNING) << "Track changes are not supported."; + std::vector<media::DemuxerStream*> streams; + std::move(change_completed_cb).Run(media::DemuxerStream::AUDIO, streams); +} + +// Not supported. +void CastStreamingDemuxer::OnSelectedVideoTrackChanged( + const std::vector<media::MediaTrack::Id>& track_ids, + base::TimeDelta curr_time, + TrackChangeCB change_completed_cb) { + DLOG(WARNING) << "Track changes are not supported."; + std::vector<media::DemuxerStream*> streams; + std::move(change_completed_cb).Run(media::DemuxerStream::VIDEO, streams); +} diff --git a/chromium/fuchsia/engine/renderer/cast_streaming_demuxer.h b/chromium/fuchsia/engine/renderer/cast_streaming_demuxer.h new file mode 100644 index 00000000000..849320655bd --- /dev/null +++ b/chromium/fuchsia/engine/renderer/cast_streaming_demuxer.h @@ -0,0 +1,84 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_ENGINE_RENDERER_CAST_STREAMING_DEMUXER_H_ +#define FUCHSIA_ENGINE_RENDERER_CAST_STREAMING_DEMUXER_H_ + +#include "fuchsia/engine/cast_streaming_session.mojom.h" +#include "media/base/demuxer.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" + +namespace base { +class SingleThreadTaskRunner; +} + +class CastStreamingReceiver; +class CastStreamingAudioDemuxerStream; +class CastStreamingVideoDemuxerStream; + +// media::Demuxer implementation for a Cast Streaming Receiver. +// This object is instantiated on the main thread, whose task runner is stored +// as |original_task_runner_|. OnStreamsInitialized() is the only method called +// on the main thread. Every other method is called on the media thread, whose +// task runner is |media_task_runner_|. +// |original_task_runner_| is used to post method calls to |receiver_|, which is +// guaranteed to outlive this object. +// TODO(crbug.com/1082821): Simplify the CastStreamingDemuxer initialization +// sequence when the CastStreamingReceiver Component has been implemented. +class CastStreamingDemuxer : public media::Demuxer { + public: + CastStreamingDemuxer( + CastStreamingReceiver* receiver, + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner); + ~CastStreamingDemuxer() final; + + CastStreamingDemuxer(const CastStreamingDemuxer&) = delete; + CastStreamingDemuxer& operator=(const CastStreamingDemuxer&) = delete; + + void OnStreamsInitialized(mojom::AudioStreamInfoPtr audio_stream_info, + mojom::VideoStreamInfoPtr video_stream_info); + + private: + void OnStreamsInitializedOnMediaThread( + mojom::AudioStreamInfoPtr audio_stream_info, + mojom::VideoStreamInfoPtr video_stream_info); + + // media::Demuxer implementation. + std::vector<media::DemuxerStream*> GetAllStreams() final; + std::string GetDisplayName() const final; + void Initialize(media::DemuxerHost* host, + media::PipelineStatusCallback status_cb) final; + void AbortPendingReads() final; + void StartWaitingForSeek(base::TimeDelta seek_time) final; + void CancelPendingSeek(base::TimeDelta seek_time) final; + void Seek(base::TimeDelta time, + media::PipelineStatusCallback status_cb) final; + void Stop() final; + base::TimeDelta GetStartTime() const final; + base::Time GetTimelineOffset() const final; + int64_t GetMemoryUsage() const final; + base::Optional<media::container_names::MediaContainerName> + GetContainerForMetrics() const final; + void OnEnabledAudioTracksChanged( + const std::vector<media::MediaTrack::Id>& track_ids, + base::TimeDelta curr_time, + TrackChangeCB change_completed_cb) final; + void OnSelectedVideoTrackChanged( + const std::vector<media::MediaTrack::Id>& track_ids, + base::TimeDelta curr_time, + TrackChangeCB change_completed_cb) final; + + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; + scoped_refptr<base::SequencedTaskRunner> original_task_runner_; + media::DemuxerHost* host_ = nullptr; + std::unique_ptr<CastStreamingAudioDemuxerStream> audio_stream_; + std::unique_ptr<CastStreamingVideoDemuxerStream> video_stream_; + + // Set to true if the Demuxer was successfully initialized. + bool was_initialization_successful_ = false; + media::PipelineStatusCallback initialized_cb_; + CastStreamingReceiver* const receiver_; +}; + +#endif // FUCHSIA_ENGINE_RENDERER_LIBCAST_STREAMING_DEMUXER_H_ diff --git a/chromium/fuchsia/engine/renderer/cast_streaming_receiver.cc b/chromium/fuchsia/engine/renderer/cast_streaming_receiver.cc new file mode 100644 index 00000000000..98654200d90 --- /dev/null +++ b/chromium/fuchsia/engine/renderer/cast_streaming_receiver.cc @@ -0,0 +1,128 @@ +// Copyright 2020 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/renderer/cast_streaming_receiver.h" + +#include "content/public/renderer/render_frame.h" +#include "fuchsia/engine/renderer/cast_streaming_demuxer.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" + +CastStreamingReceiver::CastStreamingReceiver( + content::RenderFrame* render_frame) { + DVLOG(1) << __func__; + DCHECK(render_frame); + + // It is fine to use an unretained pointer to |this| here as the + // AssociatedInterfaceRegistry, owned by |render_frame| will be torn-down at + // the same time as |this|. + render_frame->GetAssociatedInterfaceRegistry()->AddInterface( + base::BindRepeating(&CastStreamingReceiver::BindToReceiver, + base::Unretained(this))); +} + +CastStreamingReceiver::~CastStreamingReceiver() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void CastStreamingReceiver::SetDemuxer(CastStreamingDemuxer* demuxer) { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(demuxer); + + if (demuxer_) { + // We do not support more than one active CastStreamingDemuxer in the same + // RenderFrame. Return early here. + demuxer->OnStreamsInitialized(mojom::AudioStreamInfoPtr(), + mojom::VideoStreamInfoPtr()); + return; + } + + DCHECK(!is_demuxer_initialized_); + + if (IsBound()) { + demuxer_ = demuxer; + MaybeCallEnableReceiverCallback(); + } else { + // The Cast Streaming Sender disconnected after |demuxer| was instantiated + // but before |demuxer| was initialized on the media thread. + demuxer->OnStreamsInitialized(mojom::AudioStreamInfoPtr(), + mojom::VideoStreamInfoPtr()); + } +} + +void CastStreamingReceiver::OnDemuxerDestroyed() { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(demuxer_); + + demuxer_ = nullptr; + is_demuxer_initialized_ = false; + cast_streaming_receiver_receiver_.reset(); +} + +void CastStreamingReceiver::BindToReceiver( + mojo::PendingAssociatedReceiver<mojom::CastStreamingReceiver> receiver) { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!cast_streaming_receiver_receiver_.is_bound()); + + cast_streaming_receiver_receiver_.Bind(std::move(receiver)); + + // Mojo service disconnection means the Cast Streaming Session ended or the + // Cast Streaming Sender disconnected. + cast_streaming_receiver_receiver_.set_disconnect_handler(base::BindOnce( + &CastStreamingReceiver::OnReceiverDisconnected, base::Unretained(this))); +} + +bool CastStreamingReceiver::IsBound() const { + DVLOG(2) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + return cast_streaming_receiver_receiver_.is_bound(); +} + +void CastStreamingReceiver::MaybeCallEnableReceiverCallback() { + DVLOG(2) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (enable_receiver_callback_ && demuxer_) + std::move(enable_receiver_callback_).Run(); +} + +void CastStreamingReceiver::OnReceiverDisconnected() { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + cast_streaming_receiver_receiver_.reset(); + enable_receiver_callback_.Reset(); + + if (demuxer_ && !is_demuxer_initialized_) { + OnStreamsInitialized(mojom::AudioStreamInfoPtr(), + mojom::VideoStreamInfoPtr()); + } +} + +void CastStreamingReceiver::EnableReceiver(EnableReceiverCallback callback) { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!enable_receiver_callback_); + DCHECK(callback); + + enable_receiver_callback_ = std::move(callback); + MaybeCallEnableReceiverCallback(); +} + +void CastStreamingReceiver::OnStreamsInitialized( + mojom::AudioStreamInfoPtr audio_stream_info, + mojom::VideoStreamInfoPtr video_stream_info) { + DVLOG(1) << __func__; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!is_demuxer_initialized_); + DCHECK(demuxer_); + + is_demuxer_initialized_ = true; + demuxer_->OnStreamsInitialized(std::move(audio_stream_info), + std::move(video_stream_info)); +} diff --git a/chromium/fuchsia/engine/renderer/cast_streaming_receiver.h b/chromium/fuchsia/engine/renderer/cast_streaming_receiver.h new file mode 100644 index 00000000000..a95a4aededf --- /dev/null +++ b/chromium/fuchsia/engine/renderer/cast_streaming_receiver.h @@ -0,0 +1,63 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_ENGINE_RENDERER_CAST_STREAMING_RECEIVER_H_ +#define FUCHSIA_ENGINE_RENDERER_CAST_STREAMING_RECEIVER_H_ + +#include "base/sequence_checker.h" +#include "fuchsia/engine/cast_streaming_session.mojom.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" + +namespace content { +class RenderFrame; +} // namespace content + +class CastStreamingDemuxer; + +// Handles the Cast Streaming Session lifetime in the renderer process. +// Owned by WebEngineRenderFrameObserver, this object will be destroyed on +// RenderFrame destruction. This is guaranteed to outlive the +// CastStreamingDemuxer that uses it as the RenderFrame destruction will have +// triggered the destruction of the media pipeline and the CastStreamingDemuxer +// before the call to content::RenderFrameObserver::OnDestruct(), which triggers +// this object destruction. +class CastStreamingReceiver : public mojom::CastStreamingReceiver { + public: + explicit CastStreamingReceiver(content::RenderFrame* frame); + ~CastStreamingReceiver() final; + + CastStreamingReceiver(const CastStreamingReceiver&) = delete; + CastStreamingReceiver& operator=(const CastStreamingReceiver&) = delete; + + void SetDemuxer(CastStreamingDemuxer* demuxer); + void OnDemuxerDestroyed(); + + // Returns true if a Mojo connection is active. + bool IsBound() const; + + private: + void BindToReceiver( + mojo::PendingAssociatedReceiver<mojom::CastStreamingReceiver> receiver); + + void MaybeCallEnableReceiverCallback(); + + void OnReceiverDisconnected(); + + // mojom::CastStreamingReceiver implementation. + void EnableReceiver(EnableReceiverCallback callback) final; + void OnStreamsInitialized(mojom::AudioStreamInfoPtr audio_stream_info, + mojom::VideoStreamInfoPtr video_stream_info) final; + + mojo::AssociatedReceiver<mojom::CastStreamingReceiver> + cast_streaming_receiver_receiver_{this}; + + EnableReceiverCallback enable_receiver_callback_; + CastStreamingDemuxer* demuxer_ = nullptr; + bool is_demuxer_initialized_ = false; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +#endif // FUCHSIA_ENGINE_RENDERER_CAST_STREAMING_RECEIVER_H_ diff --git a/chromium/fuchsia/engine/renderer/on_load_script_injector.cc b/chromium/fuchsia/engine/renderer/on_load_script_injector.cc index b966fbacc1e..6fd0a6db314 100644 --- a/chromium/fuchsia/engine/renderer/on_load_script_injector.cc +++ b/chromium/fuchsia/engine/renderer/on_load_script_injector.cc @@ -28,12 +28,7 @@ void OnLoadScriptInjector::BindToReceiver( } void OnLoadScriptInjector::DidCommitProvisionalLoad( - bool is_same_document_navigation, ui::PageTransition transition) { - // Ignore pushState or document fragment navigation. - if (is_same_document_navigation) - return; - // Don't inject anything for subframes. if (!render_frame()->IsMainFrame()) return; diff --git a/chromium/fuchsia/engine/renderer/on_load_script_injector.h b/chromium/fuchsia/engine/renderer/on_load_script_injector.h index 80bd6b98d76..3e5364e8948 100644 --- a/chromium/fuchsia/engine/renderer/on_load_script_injector.h +++ b/chromium/fuchsia/engine/renderer/on_load_script_injector.h @@ -30,8 +30,7 @@ class OnLoadScriptInjector : public content::RenderFrameObserver, // RenderFrameObserver override: void OnDestruct() override; - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override; + void DidCommitProvisionalLoad(ui::PageTransition transition) override; private: // Called by OnDestruct(), when the RenderFrame is destroyed. diff --git a/chromium/fuchsia/engine/renderer/url_request_rules_receiver.cc b/chromium/fuchsia/engine/renderer/url_request_rules_receiver.cc index 0b0874954d0..c1da8c7bb99 100644 --- a/chromium/fuchsia/engine/renderer/url_request_rules_receiver.cc +++ b/chromium/fuchsia/engine/renderer/url_request_rules_receiver.cc @@ -11,13 +11,8 @@ #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" UrlRequestRulesReceiver::UrlRequestRulesReceiver( - content::RenderFrame* render_frame, - base::OnceCallback<void(int)> on_render_frame_deleted_callback) - : content::RenderFrameObserver(render_frame), - on_render_frame_deleted_callback_( - std::move(on_render_frame_deleted_callback)) { + content::RenderFrame* render_frame) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(on_render_frame_deleted_callback_); DCHECK(render_frame); // It is fine to use an unretained pointer to |this| here as the @@ -54,12 +49,3 @@ UrlRequestRulesReceiver::GetCachedRules() { base::AutoLock auto_lock(lock_); return cached_rules_; } - -void UrlRequestRulesReceiver::OnDestruct() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // The RenderFrame corresponding to this object was destroyed, which means - // the AssociatedInterfaceRegsitry is also gone. It is expected that - // |on_render_frame_deleted_callback_| will delete |this|. - std::move(on_render_frame_deleted_callback_).Run(routing_id()); -} diff --git a/chromium/fuchsia/engine/renderer/url_request_rules_receiver.h b/chromium/fuchsia/engine/renderer/url_request_rules_receiver.h index 8b2db7b10c1..98125825f38 100644 --- a/chromium/fuchsia/engine/renderer/url_request_rules_receiver.h +++ b/chromium/fuchsia/engine/renderer/url_request_rules_receiver.h @@ -19,22 +19,18 @@ namespace content { class RenderFrame; } // namespace content -// Provides rewriting rules for network requests. UrlRequestRulesReceiver -// objects are owned by their respective WebEngineContentRendererClient and they -// will be destroyed on RenderFrame destruction. This is guaranteed to outlive -// any WebEngineURLLoaderThrottle that uses it as the RenderFrame destruction -// will have triggered the destruction of all pending -// WebEngineURLLoaderThrottles. +// Provides rewriting rules for network requests. Owned by +// WebEngineRenderFrameObserver, this object will be destroyed on RenderFrame +// destruction. This is guaranteed to outlive any WebEngineURLLoaderThrottle +// that uses it as the RenderFrame destruction will have triggered the +// destruction of all pending WebEngineURLLoaderThrottles. // This class should only be used on the IO thread, with the exception of the // GetCachedRules() implementation, which can be called from any sequence. class UrlRequestRulesReceiver : public mojom::UrlRequestRulesReceiver, - public WebEngineURLLoaderThrottle::CachedRulesProvider, - public content::RenderFrameObserver { + public WebEngineURLLoaderThrottle::CachedRulesProvider { public: - UrlRequestRulesReceiver( - content::RenderFrame* render_frame, - base::OnceCallback<void(int)> on_render_frame_deleted_callback); + UrlRequestRulesReceiver(content::RenderFrame* render_frame); ~UrlRequestRulesReceiver() override; private: @@ -48,9 +44,6 @@ class UrlRequestRulesReceiver scoped_refptr<WebEngineURLLoaderThrottle::UrlRequestRewriteRules> GetCachedRules() override; - // content::RenderFrameObserver implementation. - void OnDestruct() override; - base::Lock lock_; // This is accessed by WebEngineURLLoaderThrottles, which can be off-sequence @@ -61,7 +54,6 @@ class UrlRequestRulesReceiver mojo::AssociatedReceiver<mojom::UrlRequestRulesReceiver> url_request_rules_receiver_{this}; - base::OnceCallback<void(int)> on_render_frame_deleted_callback_; SEQUENCE_CHECKER(sequence_checker_); DISALLOW_COPY_AND_ASSIGN(UrlRequestRulesReceiver); diff --git a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc index 2fc77c0d567..35d3f37a2fd 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc +++ b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc @@ -9,6 +9,8 @@ #include "components/cdm/renderer/widevine_key_system_properties.h" #include "components/media_control/renderer/media_playback_options.h" #include "content/public/renderer/render_frame.h" +#include "fuchsia/engine/common/cast_streaming.h" +#include "fuchsia/engine/renderer/cast_streaming_demuxer.h" #include "fuchsia/engine/renderer/on_load_script_injector.h" #include "fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.h" #include "fuchsia/engine/switches.h" @@ -110,16 +112,16 @@ WebEngineContentRendererClient::WebEngineContentRendererClient() = default; WebEngineContentRendererClient::~WebEngineContentRendererClient() = default; -UrlRequestRulesReceiver* -WebEngineContentRendererClient::GetUrlRequestRulesReceiverForRenderFrameId( +WebEngineRenderFrameObserver* +WebEngineContentRendererClient::GetWebEngineRenderFrameObserverForRenderFrameId( int render_frame_id) const { - auto iter = url_request_receivers_by_id_.find(render_frame_id); - DCHECK(iter != url_request_receivers_by_id_.end()); + auto iter = render_frame_id_to_observer_map_.find(render_frame_id); + DCHECK(iter != render_frame_id_to_observer_map_.end()); return iter->second.get(); } void WebEngineContentRendererClient::OnRenderFrameDeleted(int render_frame_id) { - size_t count = url_request_receivers_by_id_.erase(render_frame_id); + size_t count = render_frame_id_to_observer_map_.erase(render_frame_id); DCHECK_EQ(count, 1u); } @@ -130,13 +132,14 @@ void WebEngineContentRendererClient::RenderFrameCreated( new OnLoadScriptInjector(render_frame); int render_frame_id = render_frame->GetRoutingID(); - auto rules_receiver = std::make_unique<UrlRequestRulesReceiver>( - content::RenderFrame::FromRoutingID(render_frame_id), + + auto render_frame_observer = std::make_unique<WebEngineRenderFrameObserver>( + render_frame, base::BindOnce(&WebEngineContentRendererClient::OnRenderFrameDeleted, base::Unretained(this))); - auto iter = url_request_receivers_by_id_.emplace(render_frame_id, - std::move(rules_receiver)); - DCHECK(iter.second); + auto render_frame_observer_iter = render_frame_id_to_observer_map_.emplace( + render_frame_id, std::move(render_frame_observer)); + DCHECK(render_frame_observer_iter.second); // Lifetime is tied to |render_frame| via content::RenderFrameObserver. new media_control::MediaPlaybackOptions(render_frame); @@ -235,6 +238,30 @@ bool WebEngineContentRendererClient::DeferMediaLoad( return RunClosureWhenInForeground(render_frame, std::move(closure)); } +std::unique_ptr<media::Demuxer> +WebEngineContentRendererClient::OverrideDemuxerForUrl( + content::RenderFrame* render_frame, + const GURL& url, + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) { + if (IsCastStreamingEnabled() && IsCastStreamingMediaSourceUrl(url)) { + auto iter = + render_frame_id_to_observer_map_.find(render_frame->GetRoutingID()); + DCHECK(iter != render_frame_id_to_observer_map_.end()); + // Do not create a CastStreamingDemuxer if the Cast Streaming MessagePort + // was not set in the browser process. This will manifest as an unbound + // CastStreamingReceiver object in the renderer process. + // TODO(crbug.com/1082821): Simplify the instantiation conditions for the + // CastStreamingDemuxer once the CastStreamingReceiver Component has been + // implemented. + if (iter->second->cast_streaming_receiver()->IsBound()) { + return std::make_unique<CastStreamingDemuxer>( + iter->second->cast_streaming_receiver(), media_task_runner); + } + } + + return nullptr; +} + bool WebEngineContentRendererClient::RunClosureWhenInForeground( content::RenderFrame* render_frame, base::OnceClosure closure) { diff --git a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h index 93b3a110a9e..8c16a116bef 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h +++ b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h @@ -7,20 +7,21 @@ #include "base/macros.h" #include "content/public/renderer/content_renderer_client.h" -#include "fuchsia/engine/renderer/url_request_rules_receiver.h" +#include "fuchsia/engine/renderer/web_engine_render_frame_observer.h" class WebEngineContentRendererClient : public content::ContentRendererClient { public: WebEngineContentRendererClient(); ~WebEngineContentRendererClient() override; - // Returns the UrlRequestRulesReceiver corresponding to |render_frame_id|. - UrlRequestRulesReceiver* GetUrlRequestRulesReceiverForRenderFrameId( + // Returns the WebEngineRenderFrameObserver corresponding to + // |render_frame_id|. + WebEngineRenderFrameObserver* GetWebEngineRenderFrameObserverForRenderFrameId( int render_frame_id) const; private: - // Called by UrlRequestRulesReceivers when their corresponding RenderFrame is - // in the process of being deleted. + // Called by WebEngineRenderFrameObserver when its corresponding RenderFrame + // is in the process of being deleted. void OnRenderFrameDeleted(int render_frame_id); // content::ContentRendererClient overrides. @@ -35,13 +36,17 @@ class WebEngineContentRendererClient : public content::ContentRendererClient { bool DeferMediaLoad(content::RenderFrame* render_frame, bool has_played_media_before, base::OnceClosure closure) override; + std::unique_ptr<media::Demuxer> OverrideDemuxerForUrl( + content::RenderFrame* render_frame, + const GURL& url, + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) override; bool RunClosureWhenInForeground(content::RenderFrame* render_frame, base::OnceClosure closure); - // Map of rules receivers per RenderFrame ID. - std::map<int, std::unique_ptr<UrlRequestRulesReceiver>> - url_request_receivers_by_id_; + // Map of RenderFrame ID to WebEngineRenderFrameObserver. + std::map<int, std::unique_ptr<WebEngineRenderFrameObserver>> + render_frame_id_to_observer_map_; DISALLOW_COPY_AND_ASSIGN(WebEngineContentRendererClient); }; diff --git a/chromium/fuchsia/engine/renderer/web_engine_render_frame_observer.cc b/chromium/fuchsia/engine/renderer/web_engine_render_frame_observer.cc new file mode 100644 index 00000000000..8120227565b --- /dev/null +++ b/chromium/fuchsia/engine/renderer/web_engine_render_frame_observer.cc @@ -0,0 +1,25 @@ +// Copyright 2020 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/renderer/web_engine_render_frame_observer.h" + +#include "content/public/renderer/render_frame.h" + +WebEngineRenderFrameObserver::WebEngineRenderFrameObserver( + content::RenderFrame* render_frame, + base::OnceCallback<void(int)> on_render_frame_deleted_callback) + : content::RenderFrameObserver(render_frame), + url_request_rules_receiver_(render_frame), + cast_streaming_receiver_(render_frame), + on_render_frame_deleted_callback_( + std::move(on_render_frame_deleted_callback)) { + DCHECK(render_frame); + DCHECK(on_render_frame_deleted_callback_); +} + +WebEngineRenderFrameObserver::~WebEngineRenderFrameObserver() = default; + +void WebEngineRenderFrameObserver::OnDestruct() { + std::move(on_render_frame_deleted_callback_).Run(routing_id()); +} diff --git a/chromium/fuchsia/engine/renderer/web_engine_render_frame_observer.h b/chromium/fuchsia/engine/renderer/web_engine_render_frame_observer.h new file mode 100644 index 00000000000..5e3f5c5c8fe --- /dev/null +++ b/chromium/fuchsia/engine/renderer/web_engine_render_frame_observer.h @@ -0,0 +1,50 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_ENGINE_RENDERER_WEB_ENGINE_RENDER_FRAME_OBSERVER_H_ +#define FUCHSIA_ENGINE_RENDERER_WEB_ENGINE_RENDER_FRAME_OBSERVER_H_ + +#include "base/callback.h" +#include "content/public/renderer/render_frame_observer.h" +#include "fuchsia/engine/renderer/cast_streaming_receiver.h" +#include "fuchsia/engine/renderer/url_request_rules_receiver.h" + +namespace content { +class RenderFrame; +} // namespace content + +// This class owns WebEngine-specific objects whose lifespan is tied to a +// RenderFrame. Owned by WebEngineContentRendererClient, this object will be +// destroyed on RenderFrame destruction, triggering the destruction of all of +// the objects it exposes. +class WebEngineRenderFrameObserver : public content::RenderFrameObserver { + public: + // |on_render_frame_deleted_callback| must delete |this|. + WebEngineRenderFrameObserver( + content::RenderFrame* render_frame, + base::OnceCallback<void(int)> on_render_frame_deleted_callback); + ~WebEngineRenderFrameObserver() final; + + WebEngineRenderFrameObserver(const WebEngineRenderFrameObserver&) = delete; + WebEngineRenderFrameObserver& operator=(const WebEngineRenderFrameObserver&) = + delete; + + UrlRequestRulesReceiver* url_request_rules_receiver() { + return &url_request_rules_receiver_; + } + CastStreamingReceiver* cast_streaming_receiver() { + return &cast_streaming_receiver_; + } + + private: + // content::RenderFrameObserver implementation. + void OnDestruct() final; + + UrlRequestRulesReceiver url_request_rules_receiver_; + CastStreamingReceiver cast_streaming_receiver_; + + base::OnceCallback<void(int)> on_render_frame_deleted_callback_; +}; + +#endif // FUCHSIA_ENGINE_RENDERER_WEB_ENGINE_RENDER_FRAME_OBSERVER_H_ diff --git a/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc b/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc index 5b24b461406..95e8e64d510 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc +++ b/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc @@ -33,8 +33,9 @@ WebEngineURLLoaderThrottleProvider::CreateThrottles( std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles; throttles.emplace_back(std::make_unique<WebEngineURLLoaderThrottle>( - content_renderer_client_->GetUrlRequestRulesReceiverForRenderFrameId( - render_frame_id))); + content_renderer_client_ + ->GetWebEngineRenderFrameObserverForRenderFrameId(render_frame_id) + ->url_request_rules_receiver())); return throttles; } diff --git a/chromium/fuchsia/engine/web_engine_debug_integration_test.cc b/chromium/fuchsia/engine/web_engine_debug_integration_test.cc index 3dd1fe35fad..185ac678c6f 100644 --- a/chromium/fuchsia/engine/web_engine_debug_integration_test.cc +++ b/chromium/fuchsia/engine/web_engine_debug_integration_test.cc @@ -10,7 +10,6 @@ #include "base/files/file_enumerator.h" #include "base/files/file_util.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/file_utils.h" #include "base/macros.h" #include "base/test/task_environment.h" @@ -65,8 +64,7 @@ class WebEngineDebugIntegrationTest : public testing::Test { base::FileEnumerator file_enum( base::FilePath("/hub/c/context_provider.cmx"), false, base::FileEnumerator::DIRECTORIES); - base::FilePath web_engine_path = file_enum.Next(); - ASSERT_FALSE(web_engine_path.empty()); + base::FilePath web_engine_path; for (auto dir = file_enum.Next(); !dir.empty(); dir = file_enum.Next()) { std::string args; diff --git a/chromium/fuchsia/engine/web_engine_integration_test.cc b/chromium/fuchsia/engine/web_engine_integration_test.cc index 685698c3e4c..3b250cf5d4d 100644 --- a/chromium/fuchsia/engine/web_engine_integration_test.cc +++ b/chromium/fuchsia/engine/web_engine_integration_test.cc @@ -11,10 +11,10 @@ #include "base/command_line.h" #include "base/files/file_enumerator.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/file_utils.h" #include "base/fuchsia/filtered_service_directory.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/macros.h" #include "base/path_service.h" @@ -105,7 +105,7 @@ class WebEngineIntegrationTest : public testing::Test { ContextParamsWithFilteredServiceDirectory() { filtered_service_directory_ = std::make_unique<base::fuchsia::FilteredServiceDirectory>( - base::fuchsia::ComponentContextForCurrentProcess()->svc().get()); + base::ComponentContextForProcess()->svc().get()); fidl::InterfaceHandle<fuchsia::io::Directory> svc_dir; filtered_service_directory_->ConnectClient(svc_dir.NewRequest()); @@ -121,6 +121,31 @@ class WebEngineIntegrationTest : public testing::Test { return create_params; } + // Returns CreateContextParams that has AUDIO feature enabled with an injected + // FakeAudioConsumerService. + fuchsia::web::CreateContextParams ContextParamsWithAudio() { + // Use a FilteredServiceDirectory in order to inject a fake AudioConsumer + // service. + fuchsia::web::CreateContextParams create_params = + ContextParamsWithFilteredServiceDirectory(); + + fake_audio_consumer_service_ = + std::make_unique<media::FakeAudioConsumerService>( + filtered_service_directory_->outgoing_directory() + ->GetOrCreateDirectory("svc")); + + create_params.set_features(fuchsia::web::ContextFeatureFlags::AUDIO); + + return create_params; + } + + fuchsia::web::CreateContextParams ContextParamsWithAudioAndTestData() { + fuchsia::web::CreateContextParams create_params = ContextParamsWithAudio(); + create_params.mutable_content_directories()->push_back( + CreateTestDataDirectoryProvider()); + return create_params; + } + void CreateNavigationListener() { // Attach a navigation listener, to monitor the state of the Frame. navigation_listener_ = @@ -170,6 +195,12 @@ class WebEngineIntegrationTest : public testing::Test { navigation_listener_->RunUntilUrlEquals(url); } + void LoadUrlWithUserActivation(base::StringPiece url) { + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + navigation_controller_.get(), + cr_fuchsia::CreateLoadUrlParamsWithUserActivation(), url)); + } + void GrantPermission(fuchsia::web::PermissionType type, const std::string& origin) { fuchsia::web::PermissionDescriptor permission; @@ -218,6 +249,8 @@ class WebEngineIntegrationTest : public testing::Test { std::unique_ptr<base::fuchsia::FilteredServiceDirectory> filtered_service_directory_; + std::unique_ptr<media::FakeAudioConsumerService> fake_audio_consumer_service_; + DISALLOW_COPY_AND_ASSIGN(WebEngineIntegrationTest); }; @@ -470,41 +503,37 @@ TEST_F(WebEngineIntegrationTest, ContentDirectoryProvider) { TEST_F(WebEngineIntegrationTest, PlayAudio) { StartWebEngine(); - - // Use a FilteredServiceDirectory in order to inject a fake AudioConsumer - // service. - fuchsia::web::CreateContextParams create_params = - ContextParamsWithFilteredServiceDirectory(); - - media::FakeAudioConsumerService fake_audio_consumer_service( - filtered_service_directory_->outgoing_directory()->GetOrCreateDirectory( - "svc")); - - create_params.mutable_content_directories()->push_back( - CreateTestDataDirectoryProvider()); - create_params.set_features(fuchsia::web::ContextFeatureFlags::AUDIO); - CreateContextAndFrame(std::move(create_params)); + CreateContextAndFrame(ContextParamsWithAudioAndTestData()); static uint16_t kTestMediaSessionId = 43; frame_->SetMediaSessionId(kTestMediaSessionId); - EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( - navigation_controller_.get(), - cr_fuchsia::CreateLoadUrlParamsWithUserActivation(), - "fuchsia-dir://testdata/play_audio.html")); + LoadUrlWithUserActivation("fuchsia-dir://testdata/play_audio.html"); navigation_listener_->RunUntilTitleEquals("ended"); - ASSERT_EQ(fake_audio_consumer_service.num_instances(), 1U); + ASSERT_EQ(fake_audio_consumer_service_->num_instances(), 1U); - auto pos = fake_audio_consumer_service.instance(0)->GetMediaPosition(); + auto pos = fake_audio_consumer_service_->instance(0)->GetMediaPosition(); EXPECT_GT(pos, base::TimeDelta::FromSecondsD(2.0)); EXPECT_LT(pos, base::TimeDelta::FromSecondsD(2.5)); - EXPECT_EQ(fake_audio_consumer_service.instance(0)->session_id(), + 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()); + EXPECT_EQ(fake_audio_consumer_service_->instance(0)->volume(), 1.0); + EXPECT_FALSE(fake_audio_consumer_service_->instance(0)->is_muted()); +} + +// TODO(crbug.com/1090159): Reenable when cadence estimator DCHECK is fixed. +TEST_F(WebEngineIntegrationTest, DISABLED_PlayVideo) { + StartWebEngine(); + CreateContextAndFrame(ContextParamsWithAudioAndTestData()); + + frame_->SetBlockMediaLoading(false); + + LoadUrlWithUserActivation("fuchsia-dir://testdata/play_video.html?autoplay"); + + navigation_listener_->RunUntilTitleEquals("ended"); } void WebEngineIntegrationTest::RunPermissionTest(bool grant) { @@ -536,10 +565,7 @@ TEST_F(WebEngineIntegrationTest, PermissionGranted) { TEST_F(WebEngineIntegrationTest, MicrophoneAccess_WithPermission) { StartWebEngine(); - - fuchsia::web::CreateContextParams create_params = DefaultContextParams(); - create_params.set_features(fuchsia::web::ContextFeatureFlags::AUDIO); - CreateContextAndFrame(std::move(create_params)); + CreateContextAndFrame(ContextParamsWithAudio()); GrantPermission(fuchsia::web::PermissionType::MICROPHONE, embedded_test_server_.GetURL("/").GetOrigin().spec()); @@ -553,10 +579,7 @@ TEST_F(WebEngineIntegrationTest, MicrophoneAccess_WithPermission) { TEST_F(WebEngineIntegrationTest, MicrophoneAccess_WithoutPermission) { StartWebEngine(); - - fuchsia::web::CreateContextParams create_params = DefaultContextParams(); - create_params.set_features(fuchsia::web::ContextFeatureFlags::AUDIO); - CreateContextAndFrame(std::move(create_params)); + CreateContextAndFrame(ContextParamsWithAudio()); EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( navigation_controller_.get(), fuchsia::web::LoadUrlParams(), @@ -565,21 +588,14 @@ TEST_F(WebEngineIntegrationTest, MicrophoneAccess_WithoutPermission) { navigation_listener_->RunUntilTitleEquals("ended"); } -TEST_F(WebEngineIntegrationTest, SetBlockMediaLoading_Blocked) { +// TODO(crbug.com/1090159): Reenable when cadence estimator DCHECK is fixed. +TEST_F(WebEngineIntegrationTest, DISABLED_SetBlockMediaLoading_Blocked) { StartWebEngine(); - - fuchsia::web::CreateContextParams create_params = - DefaultContextParamsWithTestData(); - auto features = fuchsia::web::ContextFeatureFlags::AUDIO; - create_params.set_features(features); - CreateContextAndFrame(std::move(create_params)); + CreateContextAndFrame(ContextParamsWithAudioAndTestData()); frame_->SetBlockMediaLoading(true); - EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( - navigation_controller_.get(), - cr_fuchsia::CreateLoadUrlParamsWithUserActivation(), - "fuchsia-dir://testdata/play_vp8.html?autoplay")); + LoadUrlWithUserActivation("fuchsia-dir://testdata/play_video.html?autoplay"); // Check different indicators that media has not loaded and is not playing. navigation_listener_->RunUntilTitleEquals("stalled"); @@ -591,21 +607,14 @@ TEST_F(WebEngineIntegrationTest, SetBlockMediaLoading_Blocked) { // Initially, set media blocking to be true. When media is unblocked, check that // it begins playing, since autoplay=true. -TEST_F(WebEngineIntegrationTest, SetBlockMediaLoading_AfterUnblock) { +// TODO(crbug.com/1090159): Reenable when cadence estimator DCHECK is fixed. +TEST_F(WebEngineIntegrationTest, DISABLED_SetBlockMediaLoading_AfterUnblock) { StartWebEngine(); - - fuchsia::web::CreateContextParams create_params = - DefaultContextParamsWithTestData(); - auto features = fuchsia::web::ContextFeatureFlags::AUDIO; - create_params.set_features(features); - CreateContextAndFrame(std::move(create_params)); + CreateContextAndFrame(ContextParamsWithAudioAndTestData()); frame_->SetBlockMediaLoading(true); - EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( - navigation_controller_.get(), - cr_fuchsia::CreateLoadUrlParamsWithUserActivation(), - "fuchsia-dir://testdata/play_vp8.html?autoplay")); + LoadUrlWithUserActivation("fuchsia-dir://testdata/play_video.html?autoplay"); // Check that media loading has been blocked. navigation_listener_->RunUntilTitleEquals("stalled"); @@ -619,19 +628,13 @@ TEST_F(WebEngineIntegrationTest, SetBlockMediaLoading_AfterUnblock) { // 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(WebEngineIntegrationTest, SetBlockMediaLoading_SetBlockedAfterLoading) { +// TODO(crbug.com/1090159): Reenable when cadence estimator DCHECK is fixed. +TEST_F(WebEngineIntegrationTest, + DISABLED_SetBlockMediaLoading_SetBlockedAfterLoading) { StartWebEngine(); + CreateContextAndFrame(ContextParamsWithAudioAndTestData()); - fuchsia::web::CreateContextParams create_params = - DefaultContextParamsWithTestData(); - auto features = fuchsia::web::ContextFeatureFlags::AUDIO; - create_params.set_features(features); - CreateContextAndFrame(std::move(create_params)); - - EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( - navigation_controller_.get(), - cr_fuchsia::CreateLoadUrlParamsWithUserActivation(), - "fuchsia-dir://testdata/play_vp8.html")); + LoadUrlWithUserActivation("fuchsia-dir://testdata/play_video.html"); navigation_listener_->RunUntilTitleEquals("loaded"); frame_->SetBlockMediaLoading(true); diff --git a/chromium/fuchsia/engine/web_engine_integration_tests.cmx b/chromium/fuchsia/engine/web_engine_integration_tests.cmx index 13c1bca70be..7ab18a4b3c2 100644 --- a/chromium/fuchsia/engine/web_engine_integration_tests.cmx +++ b/chromium/fuchsia/engine/web_engine_integration_tests.cmx @@ -13,6 +13,7 @@ "fuchsia.logger.LogSink", "fuchsia.media.Audio", "fuchsia.media.SessionAudioConsumerFactory", + "fuchsia.memorypressure.Provider", "fuchsia.net.NameLookup", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", diff --git a/chromium/fuchsia/fidl/cast/application_context.fidl b/chromium/fuchsia/fidl/cast/application_context.fidl index 3b8b284dc85..4a1027a6092 100644 --- a/chromium/fuchsia/fidl/cast/application_context.fidl +++ b/chromium/fuchsia/fidl/cast/application_context.fidl @@ -12,4 +12,11 @@ protocol ApplicationContext { /// Used by the Agent to receive a controller from the Cast Runner. /// Can only be called at most one time for the lifetime of the Component. SetApplicationController(ApplicationController controller); + + /// Called if the application component exits gracefully, with the same + /// `exit_code` as would be reported via `ComponentController.OnTerminated()`. + /// Values less than or equal to zero are `zx.status` codes. + /// May be called at most once, immediately prior to termination. + [Transitional] + OnApplicationExit(int64 exit_code); }; diff --git a/chromium/fuchsia/http/http_service_main.cc b/chromium/fuchsia/http/http_service_main.cc index 359a15bb84e..6df26c0b1f0 100644 --- a/chromium/fuchsia/http/http_service_main.cc +++ b/chromium/fuchsia/http/http_service_main.cc @@ -7,7 +7,7 @@ #include "base/at_exit.h" #include "base/bind.h" #include "base/command_line.h" -#include "base/fuchsia/default_context.h" +#include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" @@ -25,7 +25,7 @@ int main(int argc, char** argv) { // Bind the parent-supplied OutgoingDirectory-request to a directory and // publish the HTTP service into it. sys::OutgoingDirectory* outgoing_directory = - base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(); + base::ComponentContextForProcess()->outgoing().get(); HttpServiceImpl http_service; base::fuchsia::ScopedServiceBinding<::fuchsia::net::oldhttp::HttpService> binding(outgoing_directory, &http_service); @@ -36,7 +36,7 @@ int main(int argc, char** argv) { // connected to this service. The system service manager will restart the // service on demand as needed. binding.SetOnLastClientCallback( - base::BindOnce(&base::RunLoop::Quit, base::Unretained(&run_loop))); + base::BindRepeating(&base::RunLoop::Quit, base::Unretained(&run_loop))); run_loop.Run(); return 0; diff --git a/chromium/fuchsia/mojom/BUILD.gn b/chromium/fuchsia/mojom/BUILD.gn index d06caabf100..ed7c1eb4f85 100644 --- a/chromium/fuchsia/mojom/BUILD.gn +++ b/chromium/fuchsia/mojom/BUILD.gn @@ -10,6 +10,26 @@ import("//testing/test.gni") mojom("example_interfaces") { testonly = true sources = [ "example.mojom" ] + cpp_typemaps = [ + { + types = [ + { + mojom = "fuchsia.test.mojom.TestInterfaceRequest" + cpp = "::fidl::InterfaceRequest<::base::fuchsia::testfidl::TestInterface>" + move_only = true + }, + ] + + traits_headers = [ + "//base/fuchsia/testfidl/cpp/fidl.h", + "//fuchsia/mojom/test_interface_request_mojom_traits.h", + ] + traits_public_deps = [ + ":traits", + "//base:testfidl", + ] + }, + ] } source_set("traits") { diff --git a/chromium/fuchsia/mojom/OWNERS b/chromium/fuchsia/mojom/OWNERS index ae29a36aac8..1feb5149750 100644 --- a/chromium/fuchsia/mojom/OWNERS +++ b/chromium/fuchsia/mojom/OWNERS @@ -2,5 +2,3 @@ per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/fuchsia/mojom/example.typemap b/chromium/fuchsia/mojom/example.typemap deleted file mode 100644 index 7e2aaac3df0..00000000000 --- a/chromium/fuchsia/mojom/example.typemap +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2019 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. - -mojom = "//fuchsia/mojom/example.mojom" -os_whitelist = [ "fuchsia" ] -public_headers = [ "base/fuchsia/testfidl/cpp/fidl.h" ] -traits_headers = [ "//fuchsia/mojom/test_interface_request_mojom_traits.h" ] -sources = [ - "//fuchsia/mojom/test_interface_request_mojom_traits.h", -] -public_deps = [ - "//base:testfidl", - "//fuchsia/mojom:traits", -] -type_mappings = [ "fuchsia.test.mojom.TestInterfaceRequest=::fidl::InterfaceRequest<::base::fuchsia::testfidl::TestInterface>[move_only]" ] diff --git a/chromium/fuchsia/mojom/test_typemaps.gni b/chromium/fuchsia/mojom/test_typemaps.gni deleted file mode 100644 index 521c8d4f8fb..00000000000 --- a/chromium/fuchsia/mojom/test_typemaps.gni +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2019 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. - -typemaps = [ "//fuchsia/mojom/example.typemap" ] diff --git a/chromium/fuchsia/release_channel.gni b/chromium/fuchsia/release_channel.gni deleted file mode 100644 index 1bab7421f82..00000000000 --- a/chromium/fuchsia/release_channel.gni +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2020 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. - -declare_args() { - # Specifies the name of the release channel for specific packages - # (web_engine, web_runner, cast_runner) when buildng on a release bot. - fuchsia_release_channel = "" -} - -if (fuchsia_release_channel != "") { - release_channel_suffix = "-${fuchsia_release_channel}" -} else { - release_channel_suffix = "" -} diff --git a/chromium/fuchsia/runners/BUILD.gn b/chromium/fuchsia/runners/BUILD.gn index 72d04361911..4b1a7c74646 100644 --- a/chromium/fuchsia/runners/BUILD.gn +++ b/chromium/fuchsia/runners/BUILD.gn @@ -7,7 +7,6 @@ assert(is_fuchsia) import("//build/buildflag_header.gni") import("//build/config/fuchsia/generate_runner_scripts.gni") import("//build/config/fuchsia/symbol_archive.gni") -import("//fuchsia/release_channel.gni") import("//testing/test.gni") declare_args() { @@ -15,9 +14,6 @@ declare_args() { web_runner_remote_debugging_port = 0 } -_cast_runner_package_name = "cast_runner${release_channel_suffix}" -_web_runner_package_name = "web_runner${release_channel_suffix}" - buildflag_header("buildflags") { header = "buildflags.h" flags = @@ -38,6 +34,7 @@ source_set("common") { "//base", "//fuchsia/base", "//fuchsia/base:modular", + "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//ui/gfx/geometry", "//url", ] @@ -63,17 +60,21 @@ source_set("cast_runner_core") { "cast/cast_component.h", "cast/cast_runner.cc", "cast/cast_runner.h", + "cast/cast_streaming.cc", + "cast/cast_streaming.h", "cast/named_message_port_connector.cc", "cast/named_message_port_connector.h", "cast/pending_cast_component.cc", "cast/pending_cast_component.h", ] data_deps = [ "//chromecast/bindings:named_message_port_connector_resources" ] + data = [ "cast/data" ] deps = [ "//base", "//fuchsia/base", "//fuchsia/base:modular", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.modular", + "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", "//url", ] @@ -100,19 +101,17 @@ executable("cast_runner_exe") { cr_fuchsia_package("cast_runner_pkg") { binary = ":cast_runner_exe" - package_name_override = _cast_runner_package_name - archive_name_override = "cast_runner" - component_name_override = "cast_runner" + package_name_override = "cast_runner" manifest = "cast/cast_runner.cmx" } fuchsia_package_runner("cast_runner") { package = ":cast_runner_pkg" - archive_name_override = "cast_runner" + package_name_override = "cast_runner" install_only = true package_deps = [ [ "//fuchsia/engine:web_engine", - "web_engine${release_channel_suffix}", + "web_engine", ] ] } @@ -208,19 +207,17 @@ executable("web_runner_exe") { cr_fuchsia_package("web_runner_pkg") { binary = ":web_runner_exe" - package_name_override = _web_runner_package_name - component_name_override = "web_runner" - archive_name_override = "web_runner" + package_name_override = "web_runner" manifest = "web/web_runner.cmx" } fuchsia_package_runner("web_runner") { package = ":web_runner_pkg" - archive_name_override = "web_runner" + package_name_override = "web_runner" install_only = true package_deps = [ [ "//fuchsia/engine:web_engine", - "web_engine${release_channel_suffix}", + "web_engine", ] ] } diff --git a/chromium/fuchsia/runners/cast/OWNERS b/chromium/fuchsia/runners/cast/OWNERS new file mode 100644 index 00000000000..2f43650c9db --- /dev/null +++ b/chromium/fuchsia/runners/cast/OWNERS @@ -0,0 +1,2 @@ +per-file *.cmx=set noparent +per-file *.cmx=file://fuchsia/SECURITY_OWNERS diff --git a/chromium/fuchsia/runners/cast/api_bindings_client.cc b/chromium/fuchsia/runners/cast/api_bindings_client.cc index 153c76b10f1..9cd338c6d42 100644 --- a/chromium/fuchsia/runners/cast/api_bindings_client.cc +++ b/chromium/fuchsia/runners/cast/api_bindings_client.cc @@ -33,11 +33,14 @@ ApiBindingsClient::ApiBindingsClient( }); } -void ApiBindingsClient::OnBindingsReceived( - std::vector<chromium::cast::ApiBinding> bindings) { - bindings_ = std::move(bindings); - bindings_service_.set_error_handler(nullptr); - std::move(on_initialization_complete_).Run(); +ApiBindingsClient::~ApiBindingsClient() { + if (connector_ && frame_) { + connector_->Register({}); + + // Remove all injected scripts using their automatically enumerated IDs. + for (uint64_t i = 0; i < bindings_->size(); ++i) + frame_->RemoveBeforeLoadJavaScript(kBindingsIdStart + i); + } } void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame, @@ -74,14 +77,14 @@ void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame, } } -ApiBindingsClient::~ApiBindingsClient() { - if (connector_ && frame_) { - connector_->Register({}); +void ApiBindingsClient::DetachFromFrame(fuchsia::web::Frame* frame) { + DCHECK_EQ(frame, frame_); + frame_ = nullptr; + bindings_service_.set_error_handler(nullptr); +} - // Remove all injected scripts using their automatically enumerated IDs. - for (uint64_t i = 0; i < bindings_->size(); ++i) - frame_->RemoveBeforeLoadJavaScript(kBindingsIdStart + i); - } +bool ApiBindingsClient::HasBindings() const { + return bindings_.has_value(); } void ApiBindingsClient::OnPortConnected( @@ -91,6 +94,9 @@ void ApiBindingsClient::OnPortConnected( bindings_service_->Connect(port_name.as_string(), std::move(port)); } -bool ApiBindingsClient::HasBindings() const { - return bindings_.has_value(); +void ApiBindingsClient::OnBindingsReceived( + std::vector<chromium::cast::ApiBinding> bindings) { + bindings_ = std::move(bindings); + bindings_service_.set_error_handler(nullptr); + std::move(on_initialization_complete_).Run(); } diff --git a/chromium/fuchsia/runners/cast/api_bindings_client.h b/chromium/fuchsia/runners/cast/api_bindings_client.h index 8cb11926837..219e419e312 100644 --- a/chromium/fuchsia/runners/cast/api_bindings_client.h +++ b/chromium/fuchsia/runners/cast/api_bindings_client.h @@ -27,24 +27,31 @@ class ApiBindingsClient { ~ApiBindingsClient(); // Injects APIs and handles channel connections on |frame|. - // |on_error_closure|: Invoked in the event of an unrecoverable error (e.g. - // lost connection to the Agent). The callback must - // remain valid for the entire lifetime of |this|. + // |on_error_callback| is invoked in the event of an unrecoverable error (e.g. + // lost connection to the Agent). The callback must remain valid for the + // entire lifetime of |this|. void AttachToFrame(fuchsia::web::Frame* frame, NamedMessagePortConnector* connector, base::OnceClosure on_error_callback); + // Indicates that the Frame is no longer live, preventing the API bindings + // client from attempting to remove injected bindings from it. + void DetachFromFrame(fuchsia::web::Frame* frame); + // Indicates that bindings were successfully received from // |bindings_service_|. bool HasBindings() const; + // TODO(crbug.com/1082821): Move this method back to private once the Cast + // Streaming Receiver component has been implemented. + // Called when |connector_| has connected a port. + void OnPortConnected(base::StringPiece port_name, + fidl::InterfaceHandle<fuchsia::web::MessagePort> port); + private: // Called when ApiBindings::GetAll() has responded. void OnBindingsReceived(std::vector<chromium::cast::ApiBinding> bindings); - // Called when |connector_| has connected a port. - void OnPortConnected(base::StringPiece port_name, - fidl::InterfaceHandle<fuchsia::web::MessagePort> port); base::Optional<std::vector<chromium::cast::ApiBinding>> bindings_; fuchsia::web::Frame* frame_ = nullptr; diff --git a/chromium/fuchsia/runners/cast/application_controller_impl.cc b/chromium/fuchsia/runners/cast/application_controller_impl.cc index 48d0327db6a..642eacabee2 100644 --- a/chromium/fuchsia/runners/cast/application_controller_impl.cc +++ b/chromium/fuchsia/runners/cast/application_controller_impl.cc @@ -6,17 +6,17 @@ #include <utility> +#include "base/check.h" #include "base/fuchsia/fuchsia_logging.h" -#include "base/logging.h" ApplicationControllerImpl::ApplicationControllerImpl( fuchsia::web::Frame* frame, - fidl::InterfaceHandle<chromium::cast::ApplicationContext> context) + chromium::cast::ApplicationContext* context) : binding_(this), frame_(frame) { DCHECK(context); DCHECK(frame_); - context.Bind()->SetApplicationController(binding_.NewBinding()); + context->SetApplicationController(binding_.NewBinding()); binding_.set_error_handler([](zx_status_t status) { if (status != ZX_ERR_PEER_CLOSED && status != ZX_ERR_CANCELED) { diff --git a/chromium/fuchsia/runners/cast/application_controller_impl.h b/chromium/fuchsia/runners/cast/application_controller_impl.h index ae77a875022..193c1e80e45 100644 --- a/chromium/fuchsia/runners/cast/application_controller_impl.h +++ b/chromium/fuchsia/runners/cast/application_controller_impl.h @@ -15,9 +15,8 @@ class ApplicationControllerImpl : public chromium::cast::ApplicationController { public: - ApplicationControllerImpl( - fuchsia::web::Frame* frame, - fidl::InterfaceHandle<chromium::cast::ApplicationContext> context); + ApplicationControllerImpl(fuchsia::web::Frame* frame, + chromium::cast::ApplicationContext* context); ~ApplicationControllerImpl() final; protected: diff --git a/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc b/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc index 46d85770919..624787c3fe4 100644 --- a/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc +++ b/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc @@ -41,8 +41,9 @@ class ApplicationControllerImplTest : public chromium::cast::ApplicationContext, public testing::Test { public: ApplicationControllerImplTest() - : application_context_(this), - application_(&frame_, application_context_.NewBinding()) { + : application_context_binding_(this), + application_context_(application_context_binding_.NewBinding().Bind()), + application_(&frame_, application_context_.get()) { base::RunLoop run_loop; wait_for_controller_callback_ = run_loop.QuitClosure(); run_loop.Run(); @@ -51,7 +52,7 @@ class ApplicationControllerImplTest : public chromium::cast::ApplicationContext, ~ApplicationControllerImplTest() override = default; protected: - // chromium::cast::ApplicationReceiver implementation. + // chromium::cast::ApplicationContext implementation. void GetMediaSessionId(GetMediaSessionIdCallback callback) final { NOTREACHED(); } @@ -68,10 +69,12 @@ class ApplicationControllerImplTest : public chromium::cast::ApplicationContext, base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; MockFrame frame_; - fidl::Binding<chromium::cast::ApplicationContext> application_context_; + fidl::Binding<chromium::cast::ApplicationContext> + application_context_binding_; + chromium::cast::ApplicationContextPtr application_context_; + ApplicationControllerImpl application_; chromium::cast::ApplicationControllerPtr application_ptr_; - ApplicationControllerImpl application_; base::OnceClosure wait_for_controller_callback_; private: diff --git a/chromium/fuchsia/runners/cast/cast_component.cc b/chromium/fuchsia/runners/cast/cast_component.cc index 8a3c24c90b5..863de881129 100644 --- a/chromium/fuchsia/runners/cast/cast_component.cc +++ b/chromium/fuchsia/runners/cast/cast_component.cc @@ -5,6 +5,7 @@ #include "fuchsia/runners/cast/cast_component.h" #include <lib/fidl/cpp/binding.h> +#include <lib/ui/scenic/cpp/view_ref_pair.h> #include <algorithm> #include <utility> @@ -18,6 +19,7 @@ #include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/fidl/chromium/cast/cpp/fidl.h" #include "fuchsia/runners/cast/cast_runner.h" +#include "fuchsia/runners/cast/cast_streaming.h" #include "fuchsia/runners/common/web_component.h" namespace { @@ -56,9 +58,9 @@ CastComponent::CastComponent(WebContentRunner* runner, initial_url_rewrite_rules_( std::move(params.initial_url_rewrite_rules.value())), api_bindings_client_(std::move(params.api_bindings_client)), + application_context_(params.application_context.Bind()), media_session_id_(params.media_session_id.value()), - headless_disconnect_watch_(FROM_HERE), - navigation_listener_binding_(this) { + headless_disconnect_watch_(FROM_HERE) { base::AutoReset<bool> constructor_active_reset(&constructor_active_, true); } @@ -89,8 +91,34 @@ void CastComponent::StartComponent() { frame()->SetMediaSessionId(media_session_id_); frame()->ConfigureInputTypes(fuchsia::web::InputTypes::ALL, fuchsia::web::AllowInputState::DENY); - frame()->SetNavigationEventListener( - navigation_listener_binding_.NewBinding()); + frame()->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::WARN); + + if (IsAppConfigForCastStreaming(application_config_)) { + // TODO(crbug.com/1082821): Remove this once the Cast Streaming Receiver + // component has been implemented. + + // Register the MessagePort for the Cast Streaming Receiver. + fidl::InterfaceHandle<fuchsia::web::MessagePort> message_port; + fuchsia::web::WebMessage message; + message.set_data(cr_fuchsia::MemBufferFromString("", "empty_message")); + fuchsia::web::OutgoingTransferable outgoing_transferable; + outgoing_transferable.set_message_port(message_port.NewRequest()); + std::vector<fuchsia::web::OutgoingTransferable> outgoing_transferables; + outgoing_transferables.push_back(std::move(outgoing_transferable)); + message.set_outgoing_transfer(std::move(outgoing_transferables)); + + frame()->PostMessage( + kCastStreamingMessagePortOrigin, std::move(message), + [this](fuchsia::web::Frame_PostMessage_Result result) { + if (result.is_err()) { + DestroyComponent(kBindingsFailureExitCode, + fuchsia::sys::TerminationReason::INTERNAL_ERROR); + } + }); + api_bindings_client_->OnPortConnected(kCastStreamingMessagePortName, + std::move(message_port)); + } + api_bindings_client_->AttachToFrame( frame(), connector_.get(), base::BindOnce(&CastComponent::DestroyComponent, base::Unretained(this), @@ -107,13 +135,11 @@ void CastComponent::StartComponent() { } application_controller_ = std::make_unique<ApplicationControllerImpl>( - frame(), - agent_manager_->ConnectToAgentService<chromium::cast::ApplicationContext>( - application_config_.agent_url())); + frame(), application_context_.get()); // Pass application permissions to the frame. - std::string origin = GURL(application_config_.web_url()).GetOrigin().spec(); if (application_config_.has_permissions()) { + std::string origin = GURL(application_config_.web_url()).GetOrigin().spec(); for (auto& permission : application_config_.permissions()) { fuchsia::web::PermissionDescriptor permission_clone; zx_status_t status = permission.Clone(&permission_clone); @@ -124,13 +150,24 @@ void CastComponent::StartComponent() { } } -void CastComponent::DestroyComponent(int termination_exit_code, +void CastComponent::DestroyComponent(int64_t exit_code, fuchsia::sys::TerminationReason reason) { DCHECK(!constructor_active_); std::move(on_destroyed_).Run(); - WebComponent::DestroyComponent(termination_exit_code, reason); + // If the component EXITED then pass the |exit_code| to the Agent, to allow it + // to distinguish graceful termination from crashes. + if (reason == fuchsia::sys::TerminationReason::EXITED && + application_controller_) { + application_context_->OnApplicationExit(exit_code); + } + + // frame() is about to be destroyed, so there is no need to perform cleanup + // such as removing before-load JavaScripts. + api_bindings_client_->DetachFromFrame(frame()); + + WebComponent::DestroyComponent(exit_code, reason); } void CastComponent::OnRewriteRulesReceived( @@ -146,13 +183,24 @@ void CastComponent::OnNavigationStateChanged( OnNavigationStateChangedCallback callback) { if (change.has_is_main_document_loaded() && change.is_main_document_loaded()) connector_->OnPageLoad(); - callback(); + WebComponent::OnNavigationStateChanged(std::move(change), + std::move(callback)); } void CastComponent::CreateView( zx::eventpair view_token, fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services, fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) { + scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New(); + CreateViewWithViewRef(std::move(view_token), + std::move(view_ref_pair.control_ref), + std::move(view_ref_pair.view_ref)); +} + +void CastComponent::CreateViewWithViewRef( + zx::eventpair view_token, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) { if (is_headless_) { // For headless CastComponents, |view_token| does not actually connect to a // Scenic View. It is merely used as a conduit for propagating termination @@ -166,8 +214,8 @@ void CastComponent::CreateView( return; } - WebComponent::CreateView(std::move(view_token), std::move(incoming_services), - std::move(outgoing_services)); + WebComponent::CreateViewWithViewRef( + std::move(view_token), std::move(control_ref), std::move(view_ref)); } void CastComponent::OnZxHandleSignalled(zx_handle_t handle, diff --git a/chromium/fuchsia/runners/cast/cast_component.h b/chromium/fuchsia/runners/cast/cast_component.h index 2d0516deeca..b1589330797 100644 --- a/chromium/fuchsia/runners/cast/cast_component.h +++ b/chromium/fuchsia/runners/cast/cast_component.h @@ -14,6 +14,7 @@ #include "base/message_loop/message_pump_for_io.h" #include "base/message_loop/message_pump_fuchsia.h" #include "base/optional.h" +#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" #include "fuchsia/runners/cast/api_bindings_client.h" #include "fuchsia/runners/cast/application_controller_impl.h" #include "fuchsia/runners/cast/named_message_port_connector.h" @@ -27,7 +28,6 @@ FORWARD_DECLARE_TEST(HeadlessCastRunnerIntegrationTest, Headless); // A specialization of WebComponent which adds Cast-specific services. class CastComponent : public WebComponent, - public fuchsia::web::NavigationEventListener, public base::MessagePumpFuchsia::ZxHandleWatcher { public: struct Params { @@ -52,6 +52,8 @@ class CastComponent : public WebComponent, // Parameters asynchronously initialized by PendingCastComponent. std::unique_ptr<ApiBindingsClient> api_bindings_client; chromium::cast::ApplicationConfig application_config; + fidl::InterfaceHandle<chromium::cast::ApplicationContext> + application_context; base::Optional<std::vector<fuchsia::web::UrlRequestRewriteRule>> initial_url_rewrite_rules; base::Optional<uint64_t> media_session_id; @@ -64,7 +66,7 @@ class CastComponent : public WebComponent, // WebComponent overrides. void StartComponent() final; - void DestroyComponent(int termination_exit_code, + void DestroyComponent(int64_t termination_exit_code, fuchsia::sys::TerminationReason reason) final; const chromium::cast::ApplicationConfig& application_config() { @@ -89,6 +91,9 @@ class CastComponent : public WebComponent, fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services, fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) final; + void CreateViewWithViewRef(zx::eventpair view_token, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) final; // base::MessagePumpFuchsia::ZxHandleWatcher implementation. // Called when the headless "view" token is disconnected. @@ -106,13 +111,11 @@ class CastComponent : public WebComponent, std::unique_ptr<NamedMessagePortConnector> connector_; std::unique_ptr<ApiBindingsClient> api_bindings_client_; std::unique_ptr<ApplicationControllerImpl> application_controller_; + chromium::cast::ApplicationContextPtr application_context_; uint64_t media_session_id_ = 0; zx::eventpair headless_view_token_; base::MessagePumpForIO::ZxHandleWatchController headless_disconnect_watch_; - fidl::Binding<fuchsia::web::NavigationEventListener> - navigation_listener_binding_; - DISALLOW_COPY_AND_ASSIGN(CastComponent); }; diff --git a/chromium/fuchsia/runners/cast/cast_runner.cc b/chromium/fuchsia/runners/cast/cast_runner.cc index 26f30af3870..a35875b941c 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.cc +++ b/chromium/fuchsia/runners/cast/cast_runner.cc @@ -15,8 +15,10 @@ #include "base/fuchsia/file_utils.h" #include "base/fuchsia/filtered_service_directory.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" #include "base/logging.h" #include "fuchsia/base/agent_manager.h" +#include "fuchsia/runners/cast/cast_streaming.h" #include "fuchsia/runners/cast/pending_cast_component.h" #include "fuchsia/runners/common/web_content_runner.h" #include "url/gurl.h" @@ -69,15 +71,13 @@ bool IsPermissionGrantedInAppConfig( CastRunner::CastRunner(bool is_headless) : is_headless_(is_headless), main_services_(std::make_unique<base::fuchsia::FilteredServiceDirectory>( - base::fuchsia::ComponentContextForCurrentProcess()->svc().get())), + base::ComponentContextForProcess()->svc().get())), main_context_(std::make_unique<WebContentRunner>( base::BindRepeating(&CastRunner::GetMainContextParams, base::Unretained(this)))), isolated_services_( std::make_unique<base::fuchsia::FilteredServiceDirectory>( - base::fuchsia::ComponentContextForCurrentProcess() - ->svc() - .get())) { + base::ComponentContextForProcess()->svc().get())) { // Specify the services to connect via the Runner process' service directory. for (const char* name : kServices) { main_services_->AddService(name); @@ -132,40 +132,38 @@ void CastRunner::SetOnMainContextLostCallbackForTest( void CastRunner::LaunchPendingComponent(PendingCastComponent* pending_component, CastComponent::Params params) { - WebContentRunner* component_owner = main_context_.get(); - // Save the list of CORS exemptions so that they can be used in Context // creation parameters. cors_exempt_headers_ = pending_component->TakeCorsExemptHeaders(); - const bool is_isolated = - params.application_config - .has_content_directories_for_isolated_application(); - if (is_isolated) { - // Create an isolated context which will own the CastComponent. - auto context = - std::make_unique<WebContentRunner>(GetIsolatedContextParams(std::move( - *params.application_config - .mutable_content_directories_for_isolated_application()))); - context->SetOnEmptyCallback(base::BindOnce( - &CastRunner::OnIsolatedContextEmpty, base::Unretained(this), - base::Unretained(context.get()))); - component_owner = context.get(); - isolated_contexts_.insert(std::move(context)); + // TODO(crbug.com/1082821): Remove |web_content_url| once the Cast Streaming + // Receiver component has been implemented. + GURL web_content_url(params.application_config.web_url()); + if (IsAppConfigForCastStreaming(params.application_config)) + web_content_url = GURL(kCastStreamingWebUrl); + + base::Optional<fuchsia::web::CreateContextParams> create_context_params = + GetContextParamsForAppConfig(¶ms.application_config); + + WebContentRunner* component_owner = main_context_.get(); + if (create_context_params) { + component_owner = CreateIsolatedContextForParams( + std::move(create_context_params.value())); } - // Launch the URL specified in the component |params|. - GURL app_url = GURL(params.application_config.web_url()); auto cast_component = std::make_unique<CastComponent>( component_owner, std::move(params), is_headless_); + + // Start the component, which creates and configures the web.Frame, and load + // the specified web content into it. cast_component->SetOnDestroyedCallback( base::BindOnce(&CastRunner::OnComponentDestroyed, base::Unretained(this), base::Unretained(cast_component.get()))); cast_component->StartComponent(); - cast_component->LoadUrl(std::move(app_url), + cast_component->LoadUrl(std::move(web_content_url), std::vector<fuchsia::net::http::Header>()); - if (!is_isolated) { + if (component_owner == main_context_.get()) { // If this component has the microphone permission then use it to route // Audio service requests through. if (IsPermissionGrantedInAppConfig( @@ -205,6 +203,7 @@ fuchsia::web::CreateContextParams CastRunner::GetCommonContextParams() { LOG(WARNING) << "Running in headless mode."; *params.mutable_features() |= fuchsia::web::ContextFeatureFlags::HEADLESS; } else { + // TODO(crbug.com/1078227): Remove HARDWARE_VIDEO_DECODER_ONLY. *params.mutable_features() |= fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER | fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY | @@ -247,12 +246,13 @@ fuchsia::web::CreateContextParams CastRunner::GetMainContextParams() { // TODO(crbug.com/1023514): Remove this switch when it is no longer // necessary. params.set_unsafely_treat_insecure_origins_as_secure( - {"allow-running-insecure-content"}); + {"allow-running-insecure-content", "disable-mixed-content-autoupgrade"}); return params; } -fuchsia::web::CreateContextParams CastRunner::GetIsolatedContextParams( +fuchsia::web::CreateContextParams +CastRunner::GetIsolatedContextParamsWithFuchsiaDirs( std::vector<fuchsia::web::ContentDirectoryProvider> content_directories) { fuchsia::web::CreateContextParams params = GetCommonContextParams(); params.set_content_directories(std::move(content_directories)); @@ -261,6 +261,54 @@ fuchsia::web::CreateContextParams CastRunner::GetIsolatedContextParams( return params; } +fuchsia::web::CreateContextParams +CastRunner::GetIsolatedContextParamsForCastStreaming() { + fuchsia::web::CreateContextParams params = GetCommonContextParams(); + ApplyCastStreamingContextParams(¶ms); + // TODO(crbug.com/1069746): Use a different FilteredServiceDirectory for Cast + // Streaming Contexts. + main_services_->ConnectClient( + params.mutable_service_directory()->NewRequest()); + return params; +} + +base::Optional<fuchsia::web::CreateContextParams> +CastRunner::GetContextParamsForAppConfig( + chromium::cast::ApplicationConfig* app_config) { + base::Optional<fuchsia::web::CreateContextParams> params; + + if (IsAppConfigForCastStreaming(*app_config)) { + // TODO(crbug.com/1082821): Remove this once the CastStreamingReceiver + // Component has been implemented. + return base::make_optional(GetIsolatedContextParamsForCastStreaming()); + } + + const bool is_isolated_app = + app_config->has_content_directories_for_isolated_application(); + if (is_isolated_app) { + return base::make_optional( + GetIsolatedContextParamsWithFuchsiaDirs(std::move( + *app_config + ->mutable_content_directories_for_isolated_application()))); + } + + // No need to create an isolated context in other cases. + return base::nullopt; +} + +WebContentRunner* CastRunner::CreateIsolatedContextForParams( + fuchsia::web::CreateContextParams create_context_params) { + // Create an isolated context which will own the CastComponent. + auto context = + std::make_unique<WebContentRunner>(std::move(create_context_params)); + context->SetOnEmptyCallback( + base::BindOnce(&CastRunner::OnIsolatedContextEmpty, + base::Unretained(this), base::Unretained(context.get()))); + WebContentRunner* raw_context = context.get(); + isolated_contexts_.insert(std::move(context)); + return raw_context; +} + void CastRunner::OnIsolatedContextEmpty(WebContentRunner* context) { auto it = isolated_contexts_.find(context); DCHECK(it != isolated_contexts_.end()); @@ -279,10 +327,9 @@ void CastRunner::OnAudioServiceRequest( } // Otherwise use the Runner's fuchsia.media.Audio service. fuchsia.media.Audio - // may be used by frames without MICRIPHONE permission to create AudioRenderer + // may be used by frames without MICROPHONE permission to create AudioRenderer // instance. - base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect( - std::move(request)); + base::ComponentContextForProcess()->svc()->Connect(std::move(request)); } void CastRunner::OnCameraServiceRequest( @@ -309,6 +356,5 @@ void CastRunner::OnMetricsRecorderServiceRequest( reinterpret_cast<CastComponent*>(main_context_->GetAnyComponent()); DCHECK(component); - component->agent_manager()->ConnectToAgentService( - component->application_config().agent_url(), std::move(request)); + component->startup_context()->svc()->Connect(std::move(request)); } diff --git a/chromium/fuchsia/runners/cast/cast_runner.cmx b/chromium/fuchsia/runners/cast/cast_runner.cmx index e3d34e48097..495cf2f9841 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.cmx +++ b/chromium/fuchsia/runners/cast/cast_runner.cmx @@ -8,6 +8,7 @@ "fuchsia.accessibility.semantics.SemanticsManager", "fuchsia.camera3.DeviceWatcher", "fuchsia.device.NameProvider", + "fuchsia.feedback.CrashReportingProductRegister", "fuchsia.fonts.Provider", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", diff --git a/chromium/fuchsia/runners/cast/cast_runner.h b/chromium/fuchsia/runners/cast/cast_runner.h index dfd77efa5de..d1b5d5ec043 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.h +++ b/chromium/fuchsia/runners/cast/cast_runner.h @@ -70,13 +70,21 @@ class CastRunner : public fuchsia::sys::Runner, // Handlers used to provide parameters for main & isolated Contexts. fuchsia::web::CreateContextParams GetCommonContextParams(); fuchsia::web::CreateContextParams GetMainContextParams(); - fuchsia::web::CreateContextParams GetIsolatedContextParams( + fuchsia::web::CreateContextParams GetIsolatedContextParamsWithFuchsiaDirs( std::vector<fuchsia::web::ContentDirectoryProvider> content_directories); + // TODO(crbug.com/1082821): Remove this once the CastStreamingReceiver + // Component has been implemented. + fuchsia::web::CreateContextParams GetIsolatedContextParamsForCastStreaming(); - // Creates a CastRunner configured to serve data from content directories in - // |component_params|. + // Returns CreateContextParams for |app_config|. Returns nullopt if there is + // no need to create an isolated context. + base::Optional<fuchsia::web::CreateContextParams> + GetContextParamsForAppConfig(chromium::cast::ApplicationConfig* app_config); + + // Launches an isolated Context with the given |create_context_params| and + // returns the newly created WebContentRunner. WebContentRunner* CreateIsolatedContextForParams( - CastComponent::Params* component_params); + fuchsia::web::CreateContextParams create_context_params); // Called when an isolated component terminates, to allow the Context hosting // it to be torn down. @@ -99,9 +107,10 @@ class CastRunner : public fuchsia::sys::Runner, const std::unique_ptr<base::fuchsia::FilteredServiceDirectory> main_services_; const std::unique_ptr<WebContentRunner> main_context_; - // Holds fuchsia.web.Contexts used to host isolated components. const std::unique_ptr<base::fuchsia::FilteredServiceDirectory> isolated_services_; + + // Holds fuchsia.web.Contexts used to host isolated components. base::flat_set<std::unique_ptr<WebContentRunner>, base::UniquePtrComparator> isolated_contexts_; diff --git a/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc b/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc index bcbda931614..17962e75d80 100644 --- a/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc +++ b/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc @@ -35,7 +35,6 @@ #include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/fuchsia_dir_scheme.h" #include "fuchsia/base/mem_buffer_util.h" -#include "fuchsia/base/release_channel.h" #include "fuchsia/base/result_receiver.h" #include "fuchsia/base/string_util.h" #include "fuchsia/base/test_devtools_list_fetcher.h" @@ -122,6 +121,13 @@ class FakeApplicationContext : public chromium::cast::ApplicationContext { return controller_.get(); } + base::Optional<int64_t> WaitForApplicationTerminated() { + base::RunLoop loop; + on_application_terminated_ = loop.QuitClosure(); + loop.Run(); + return application_exit_code_; + } + private: // chromium::cast::ApplicationContext implementation. void GetMediaSessionId(GetMediaSessionIdCallback callback) final { @@ -132,8 +138,16 @@ class FakeApplicationContext : public chromium::cast::ApplicationContext { final { controller_ = controller.Bind(); } + void OnApplicationExit(int64_t exit_code) final { + application_exit_code_ = exit_code; + if (on_application_terminated_) + std::move(on_application_terminated_).Run(); + } chromium::cast::ApplicationControllerPtr controller_; + + base::Optional<int64_t> application_exit_code_; + base::OnceClosure on_application_terminated_; }; class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { @@ -549,8 +563,7 @@ TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) { CreateComponentContext(kIncorrectComponentUrl); StartCastComponent(kIncorrectComponentUrl); - // Run the loop until the ComponentController is dropped, or a WebComponent is - // created. + // Run the loop until the ComponentController is dropped. base::RunLoop run_loop; component_controller_.set_error_handler([&run_loop](zx_status_t status) { EXPECT_EQ(status, ZX_ERR_PEER_CLOSED); @@ -865,26 +878,106 @@ TEST_F(CastRunnerIntegrationTest, LegacyMetricsRedirect) { CreateComponentContext(component_url); EXPECT_NE(component_context_, nullptr); + base::RunLoop run_loop; + + // Add MetricsRecorder the the component's incoming_services. + component_services_.AddPublicService( + std::make_unique<vfs::Service>( + [&run_loop](zx::channel request, async_dispatcher_t* dispatcher) { + run_loop.Quit(); + }), + fuchsia::legacymetrics::MetricsRecorder::Name_); + StartCastComponent(component_url); - // Wait until we see the CastRunner connect to the LegacyMetrics service. - base::RunLoop run_loop; - component_state_created_callback_ = base::BindOnce( - [](FakeComponentState** component_state, - base::RepeatingClosure quit_closure) { - (*component_state) - ->outgoing_directory() - ->AddPublicService( - std::make_unique<vfs::Service>( - [quit_closure](zx::channel, async_dispatcher_t*) { - quit_closure.Run(); - }), - fuchsia::legacymetrics::MetricsRecorder::Name_); - }, - base::Unretained(&component_state_), run_loop.QuitClosure()); + // Wait until we see the CastRunner connect to the MetricsRecorder service. run_loop.Run(); } +// Verifies that the ApplicationContext::OnApplicationTerminated() is notified +// with the component exit code if the web content closes itself. +TEST_F(CastRunnerIntegrationTest, OnApplicationTerminated_WindowClose) { + const GURL url = test_server_.GetURL(kBlankAppUrl); + app_config_manager_.AddApp(kTestAppId, url); + + CreateComponentContextAndStartComponent(); + + // It is possible to observe the ComponentController close before + // OnApplicationTerminated() is received, so ignore that. + component_controller_.set_error_handler([](zx_status_t) {}); + + // Have the web content close itself, and wait for OnApplicationTerminated(). + EXPECT_EQ(ExecuteJavaScript("window.close()"), "undefined"); + base::Optional<zx_status_t> exit_code = + component_state_->application_context()->WaitForApplicationTerminated(); + ASSERT_TRUE(exit_code); + EXPECT_EQ(exit_code.value(), ZX_OK); +} + +// Verifies that the ComponentController reports TerminationReason::EXITED and +// exit code ZX_OK if the web content terminates itself. +// TODO(https://crbug.com/1066833): Make this a WebRunner test. +TEST_F(CastRunnerIntegrationTest, OnTerminated_WindowClose) { + const GURL url = test_server_.GetURL(kBlankAppUrl); + app_config_manager_.AddApp(kTestAppId, url); + + CreateComponentContextAndStartComponent(); + + // Register an handler on the ComponentController channel, for the + // OnTerminated event. + base::RunLoop exit_code_loop; + component_controller_.set_error_handler( + [quit_loop = exit_code_loop.QuitClosure()](zx_status_t) { + quit_loop.Run(); + ADD_FAILURE(); + }); + component_controller_.events().OnTerminated = + [quit_loop = exit_code_loop.QuitClosure()]( + int64_t exit_code, fuchsia::sys::TerminationReason reason) { + quit_loop.Run(); + EXPECT_EQ(reason, fuchsia::sys::TerminationReason::EXITED); + EXPECT_EQ(exit_code, ZX_OK); + }; + + // Have the web content close itself, and wait for OnTerminated(). + EXPECT_EQ(ExecuteJavaScript("window.close()"), "undefined"); + exit_code_loop.Run(); + + component_controller_.Unbind(); +} + +// Verifies that the ComponentController reports TerminationReason::EXITED and +// exit code ZX_OK if Kill() is used. +// TODO(https://crbug.com/1066833): Make this a WebRunner test. +TEST_F(CastRunnerIntegrationTest, OnTerminated_ComponentKill) { + const GURL url = test_server_.GetURL(kBlankAppUrl); + app_config_manager_.AddApp(kTestAppId, url); + + CreateComponentContextAndStartComponent(); + + // Register an handler on the ComponentController channel, for the + // OnTerminated event. + base::RunLoop exit_code_loop; + component_controller_.set_error_handler( + [quit_loop = exit_code_loop.QuitClosure()](zx_status_t) { + quit_loop.Run(); + ADD_FAILURE(); + }); + component_controller_.events().OnTerminated = + [quit_loop = exit_code_loop.QuitClosure()]( + int64_t exit_code, fuchsia::sys::TerminationReason reason) { + quit_loop.Run(); + EXPECT_EQ(reason, fuchsia::sys::TerminationReason::EXITED); + EXPECT_EQ(exit_code, ZX_OK); + }; + + // Kill() the component and wait for OnTerminated(). + component_controller_->Kill(); + exit_code_loop.Run(); + + component_controller_.Unbind(); +} + TEST_F(CastRunnerIntegrationTest, WebGLContextAbsentWithoutVulkanFeature) { const char kTestPath[] = "/webgl_presence.html"; const GURL test_url = test_server_.GetURL(kTestPath); diff --git a/chromium/fuchsia/runners/cast/cast_streaming.cc b/chromium/fuchsia/runners/cast/cast_streaming.cc new file mode 100644 index 00000000000..88754ededc6 --- /dev/null +++ b/chromium/fuchsia/runners/cast/cast_streaming.cc @@ -0,0 +1,66 @@ +// Copyright 2020 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/runners/cast/cast_streaming.h" + +#include "base/fuchsia/file_utils.h" +#include "base/path_service.h" + +namespace { + +constexpr char kCastStreamingAppUrl[] = "cast-streaming:receiver"; +constexpr char kCastDataDirectory[] = "fuchsia/runners/cast/data"; +constexpr char kCastStreamingContentDirectoryName[] = "cast-streaming"; + +// Returns the content directories for the Cast Streaming application. +std::vector<fuchsia::web::ContentDirectoryProvider> +GetCastStreamingContentDirectories() { + base::FilePath pkg_path; + bool success = base::PathService::Get(base::DIR_ASSETS, &pkg_path); + DCHECK(success); + + fuchsia::web::ContentDirectoryProvider content_directory; + content_directory.set_directory( + base::fuchsia::OpenDirectory(pkg_path.AppendASCII(kCastDataDirectory))); + content_directory.set_name(kCastStreamingContentDirectoryName); + std::vector<fuchsia::web::ContentDirectoryProvider> content_directories; + content_directories.emplace_back(std::move(content_directory)); + + return content_directories; +} + +} // namespace + +const char kCastStreamingWebUrl[] = + "fuchsia-dir://cast-streaming/receiver.html"; + +const char kCastStreamingMessagePortOrigin[] = "cast-streaming:receiver"; + +const char kCastStreamingMessagePortName[] = "cast.__platform__.cast_transport"; + +bool IsAppConfigForCastStreaming( + const chromium::cast::ApplicationConfig& application_config) { + return application_config.web_url() == kCastStreamingAppUrl; +} + +void ApplyCastStreamingContextParams( + fuchsia::web::CreateContextParams* params) { + // Disable the HARDWARE_VIDEO_DECODER_ONLY tag. + // TODO(crbug.com/1078227): Remove HARDWARE_VIDEO_DECODER_ONLY once it is + // no longer set in CastRunner::CommonContextParams(). For now, this is + // required to enable software decoders. + *params->mutable_features() &= + ~fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY; + + // Disable the WIDEVINE_CDM tag. + // TODO(crbug.com/1069746): Remove this once WIDEVINE_CDM is no longer set in + // CastRunner::CommonContextParams(). + *params->mutable_features() &= + ~fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM; + + *params->mutable_features() |= fuchsia::web::ContextFeatureFlags::NETWORK; + + // Set the content directory with the streaming app. + params->set_content_directories(GetCastStreamingContentDirectories()); +} diff --git a/chromium/fuchsia/runners/cast/cast_streaming.h b/chromium/fuchsia/runners/cast/cast_streaming.h new file mode 100644 index 00000000000..0bc7f650ea4 --- /dev/null +++ b/chromium/fuchsia/runners/cast/cast_streaming.h @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FUCHSIA_RUNNERS_CAST_CAST_STREAMING_H_ +#define FUCHSIA_RUNNERS_CAST_CAST_STREAMING_H_ + +#include <fuchsia/web/cpp/fidl.h> + +#include "base/callback.h" +#include "base/strings/string_piece.h" +#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" + +// TODO(crbug.com/1082821): Remove unused methods here once the +// Cast Streaming Receiver component has been implemented. + +// URL for the Cast Streaming application. +extern const char kCastStreamingWebUrl[]; + +// Origin value for the Cast Streaming MessagePort. +extern const char kCastStreamingMessagePortOrigin[]; + +// Name of the Cast Streaming MessagePort. +extern const char kCastStreamingMessagePortName[]; + +// Returns true if |application_config| is a cast streaming application. +bool IsAppConfigForCastStreaming( + const chromium::cast::ApplicationConfig& application_config); + +// Modifies |params| to apply Cast Streaming-specific Context Params. +void ApplyCastStreamingContextParams(fuchsia::web::CreateContextParams* params); + +#endif // FUCHSIA_RUNNERS_CAST_CAST_STREAMING_H_ diff --git a/chromium/fuchsia/runners/cast/data/receiver.html b/chromium/fuchsia/runners/cast/data/receiver.html new file mode 100644 index 00000000000..29a914e1ae3 --- /dev/null +++ b/chromium/fuchsia/runners/cast/data/receiver.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<!-- +Copyright 2020 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. +--> +<html> +<head> + <meta charset="utf-8"> + <style> + video { + background-color: black; + max-width: 100%; + height: auto; + } + </style> +</head> + +<body> + <video src="data:cast_streaming_receiver" autoplay> + + <script> + // The Cast Streaming session must stop when the stream is no longer visible. crbug.com/1111886 + document.addEventListener('visibilitychange', function(e) { + if (document.hidden) { + window.close(); + } + }); + + // TODO(crbug.com/1087528): This should not be necessary. Figure out why + // autoplay is not enough here. + var video = document.querySelector('video'); + video.play(); + </script> +</body> +</html> diff --git a/chromium/fuchsia/runners/cast/main.cc b/chromium/fuchsia/runners/cast/main.cc index e6e0f47d04a..8ec770080bc 100644 --- a/chromium/fuchsia/runners/cast/main.cc +++ b/chromium/fuchsia/runners/cast/main.cc @@ -5,7 +5,7 @@ #include <lib/sys/cpp/component_context.h> #include "base/command_line.h" -#include "base/fuchsia/default_context.h" +#include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/message_loop/message_pump_type.h" #include "base/optional.h" @@ -13,12 +13,19 @@ #include "base/task/single_thread_task_executor.h" #include "base/values.h" #include "fuchsia/base/config_reader.h" +#include "fuchsia/base/feedback_registration.h" #include "fuchsia/base/fuchsia_dir_scheme.h" #include "fuchsia/base/init_logging.h" +#include "fuchsia/base/inspect.h" #include "fuchsia/runners/cast/cast_runner.h" namespace { +constexpr char kCrashProductName[] = "FuchsiaCastRunner"; +// TODO(https://fxbug.dev/51490): Use a programmatic mechanism to obtain this. +constexpr char kComponentUrl[] = + "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx"; + bool IsHeadless() { constexpr char kHeadlessConfigKey[] = "headless"; @@ -37,6 +44,8 @@ int main(int argc, char** argv) { base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO); base::RunLoop run_loop; + cr_fuchsia::RegisterCrashReportingFields(kComponentUrl, kCrashProductName); + base::CommandLine::Init(argc, argv); CHECK(cr_fuchsia::InitLoggingFromCommandLine( *base::CommandLine::ForCurrentProcess())) @@ -46,12 +55,12 @@ int main(int argc, char** argv) { CastRunner runner(IsHeadless()); base::fuchsia::ScopedServiceBinding<fuchsia::sys::Runner> binding( - base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(), - &runner); + base::ComponentContextForProcess()->outgoing().get(), &runner); + + base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo(); - base::fuchsia::ComponentContextForCurrentProcess() - ->outgoing() - ->ServeFromStartupInfo(); + // Publish version information for this component to Inspect. + cr_fuchsia::PublishVersionInfoToInspect(base::ComponentInspectorForProcess()); // Run until there are no Components, or the last service client channel is // closed. diff --git a/chromium/fuchsia/runners/cast/named_message_port_connector.cc b/chromium/fuchsia/runners/cast/named_message_port_connector.cc index 515403889b9..76b80b347c2 100644 --- a/chromium/fuchsia/runners/cast/named_message_port_connector.cc +++ b/chromium/fuchsia/runners/cast/named_message_port_connector.cc @@ -11,6 +11,7 @@ #include "base/callback.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/logging.h" #include "base/macros.h" #include "base/path_service.h" #include "base/strings/string_piece.h" diff --git a/chromium/fuchsia/runners/cast/pending_cast_component.cc b/chromium/fuchsia/runners/cast/pending_cast_component.cc index 16dce5b11b2..b6c353763a8 100644 --- a/chromium/fuchsia/runners/cast/pending_cast_component.cc +++ b/chromium/fuchsia/runners/cast/pending_cast_component.cc @@ -163,5 +163,7 @@ void PendingCastComponent::MaybeLaunchComponent() { // user-after-free of |this|. params_.url_rewrite_rules_provider.set_error_handler(nullptr); + params_.application_context = application_context_.Unbind(); + delegate_->LaunchPendingComponent(this, std::move(params_)); } diff --git a/chromium/fuchsia/runners/common/web_component.cc b/chromium/fuchsia/runners/common/web_component.cc index 53ef3d1e641..edaf54562da 100644 --- a/chromium/fuchsia/runners/common/web_component.cc +++ b/chromium/fuchsia/runners/common/web_component.cc @@ -9,10 +9,10 @@ #include <lib/fidl/cpp/binding_set.h> #include <lib/fit/function.h> #include <lib/sys/cpp/component_context.h> +#include <lib/ui/scenic/cpp/view_ref_pair.h> #include <utility> #include "base/bind.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/logging.h" #include "fuchsia/runners/common/web_content_runner.h" @@ -25,9 +25,9 @@ WebComponent::WebComponent( : runner_(runner), startup_context_(std::move(context)), controller_binding_(this), - module_context_(startup_context() - ->svc() - ->Connect<fuchsia::modular::ModuleContext>()) { + module_context_( + startup_context()->svc()->Connect<fuchsia::modular::ModuleContext>()), + navigation_listener_binding_(this) { DCHECK(runner); // If the ComponentController request is valid then bind it, and configure it @@ -67,13 +67,19 @@ void WebComponent::StartComponent() { create_params.set_enable_remote_debugging(enable_remote_debugging_); frame_ = runner_->CreateFrame(std::move(create_params)); - // If the Frame unexpectedly disconnect us then tear-down this Component. + // If the Frame unexpectedly disconnects then tear-down this Component. + // ZX_OK indicates intentional termination (e.g. via window.close()). + // ZX_ERR_PEER_CLOSED will usually indicate a crash, reported elsewhere. + // Therefore only log other, more unusual, |status| codes. frame_.set_error_handler([this](zx_status_t status) { - ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status) - << " Frame disconnected"; - DestroyComponent(0, fuchsia::sys::TerminationReason::EXITED); + if (status != ZX_OK && status != ZX_ERR_PEER_CLOSED) + ZX_LOG(ERROR, status) << " Frame disconnected"; + DestroyComponent(status, fuchsia::sys::TerminationReason::EXITED); }); + // Observe the Frame for failures, via navigation state change events. + frame_->SetNavigationEventListener(navigation_listener_binding_.NewBinding()); + if (startup_context()->has_outgoing_directory_request()) { // Publish outgoing services and start serving component's outgoing // directory. @@ -110,8 +116,8 @@ void WebComponent::LoadUrl( } void WebComponent::Kill() { - // Signal abnormal process termination. - DestroyComponent(1, fuchsia::sys::TerminationReason::RUNNER_TERMINATED); + // Signal normal termination, since the caller requested it. + DestroyComponent(ZX_OK, fuchsia::sys::TerminationReason::EXITED); } void WebComponent::Detach() { @@ -122,19 +128,53 @@ void WebComponent::CreateView( zx::eventpair view_token_value, fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services, fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) { + scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New(); + CreateViewWithViewRef(std::move(view_token_value), + std::move(view_ref_pair.control_ref), + std::move(view_ref_pair.view_ref)); +} + +void WebComponent::CreateViewWithViewRef( + zx::eventpair view_token_value, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) { DCHECK(frame_); - DCHECK(!view_is_bound_); + if (view_is_bound_) { + LOG(ERROR) << "CreateView() called more than once."; + DestroyComponent(ZX_ERR_BAD_STATE, fuchsia::sys::TerminationReason::EXITED); + return; + } fuchsia::ui::views::ViewToken view_token; view_token.value = std::move(view_token_value); - frame_->CreateView(std::move(view_token)); + frame_->CreateViewWithViewRef(std::move(view_token), std::move(control_ref), + std::move(view_ref)); view_is_bound_ = true; } -void WebComponent::DestroyComponent(int termination_exit_code, +void WebComponent::OnNavigationStateChanged( + fuchsia::web::NavigationState change, + OnNavigationStateChangedCallback callback) { + if (change.has_page_type()) { + switch (change.page_type()) { + case fuchsia::web::PageType::ERROR: + DestroyComponent(ZX_ERR_INTERNAL, + fuchsia::sys::TerminationReason::EXITED); + break; + case fuchsia::web::PageType::NORMAL: + break; + } + } + // Do not touch |this|, which may have been deleted by DestroyComponent(). + + // |callback| is safe to run, since it is on the stack. + callback(); +} + +void WebComponent::DestroyComponent(int64_t exit_code, fuchsia::sys::TerminationReason reason) { termination_reason_ = reason; - termination_exit_code_ = termination_exit_code; + termination_exit_code_ = exit_code; runner_->DestroyComponent(this); } diff --git a/chromium/fuchsia/runners/common/web_component.h b/chromium/fuchsia/runners/common/web_component.h index 4653f465058..0ed8cebbc42 100644 --- a/chromium/fuchsia/runners/common/web_component.h +++ b/chromium/fuchsia/runners/common/web_component.h @@ -18,7 +18,6 @@ #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/startup_context.h" -#include "base/logging.h" #include "fuchsia/base/lifecycle_impl.h" #include "url/gurl.h" @@ -30,7 +29,8 @@ class WebContentRunner; // (e.g. Cast applications) can extend this class to configure the Frame to // their needs, publish additional APIs, etc. class WebComponent : public fuchsia::sys::ComponentController, - public fuchsia::ui::app::ViewProvider { + public fuchsia::ui::app::ViewProvider, + public fuchsia::web::NavigationEventListener { public: // Creates a WebComponent encapsulating a web.Frame. A ViewProvider service // will be published to the service-directory specified by |startup_context|, @@ -60,6 +60,12 @@ class WebComponent : public fuchsia::sys::ComponentController, WebContentRunner* runner() const { return runner_; } + // Returns the component's startup context (e.g. incoming services, public + // service directory, etc). + base::fuchsia::StartupContext* startup_context() const { + return startup_context_.get(); + } + protected: // fuchsia::sys::ComponentController implementation. void Kill() override; @@ -71,18 +77,24 @@ class WebComponent : public fuchsia::sys::ComponentController, fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services, fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) override; + void CreateViewWithViewRef(zx::eventpair view_token, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) override; + + // fuchsia::web::NavigationEventListener implementation. + // Used to detect when the Frame enters an error state (e.g. the top-level + // content's Renderer process crashes). + void OnNavigationStateChanged( + fuchsia::web::NavigationState change, + OnNavigationStateChangedCallback callback) override; // Reports the supplied exit-code and reason to the |controller_binding_| and - // requests that the |runner_| delete this component. - virtual void DestroyComponent(int termination_exit_code, + // requests that the |runner_| delete this component. The EXITED |reason| is + // used to indicate Frame disconnection, in which case the |exit_code| is set + // to the status reported by the FramePtr's error handler. + virtual void DestroyComponent(int64_t exit_code, fuchsia::sys::TerminationReason reason); - // Returns the component's startup context (e.g. incoming services, public - // service directory, etc). - base::fuchsia::StartupContext* startup_context() const { - return startup_context_.get(); - } - private: WebContentRunner* const runner_ = nullptr; const std::unique_ptr<base::fuchsia::StartupContext> startup_context_; @@ -105,13 +117,18 @@ class WebComponent : public fuchsia::sys::ComponentController, // sys::ComponentController::OnTerminated event. fuchsia::sys::TerminationReason termination_reason_ = fuchsia::sys::TerminationReason::UNKNOWN; - int termination_exit_code_ = 0; + int64_t termination_exit_code_ = 0; bool view_is_bound_ = false; bool component_started_ = false; bool enable_remote_debugging_ = false; + // Used to watch for failures of the Frame's web content, including Renderer + // process crashes. + fidl::Binding<fuchsia::web::NavigationEventListener> + navigation_listener_binding_; + DISALLOW_COPY_AND_ASSIGN(WebComponent); }; diff --git a/chromium/fuchsia/runners/common/web_content_runner.cc b/chromium/fuchsia/runners/common/web_content_runner.cc index 22ea1651c35..34140e73d25 100644 --- a/chromium/fuchsia/runners/common/web_content_runner.cc +++ b/chromium/fuchsia/runners/common/web_content_runner.cc @@ -12,9 +12,9 @@ #include "base/bind.h" #include "base/files/file.h" #include "base/files/file_util.h" -#include "base/fuchsia/default_context.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/startup_context.h" #include "base/logging.h" @@ -26,7 +26,7 @@ namespace { fuchsia::web::ContextPtr CreateWebContext( fuchsia::web::CreateContextParams context_params) { - auto context_provider = base::fuchsia::ComponentContextForCurrentProcess() + auto context_provider = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::web::ContextProvider>(); fuchsia::web::ContextPtr web_context; diff --git a/chromium/fuchsia/runners/web/OWNERS b/chromium/fuchsia/runners/web/OWNERS new file mode 100644 index 00000000000..2f43650c9db --- /dev/null +++ b/chromium/fuchsia/runners/web/OWNERS @@ -0,0 +1,2 @@ +per-file *.cmx=set noparent +per-file *.cmx=file://fuchsia/SECURITY_OWNERS diff --git a/chromium/fuchsia/runners/web/main.cc b/chromium/fuchsia/runners/web/main.cc index 719419d8350..267fe44de47 100644 --- a/chromium/fuchsia/runners/web/main.cc +++ b/chromium/fuchsia/runners/web/main.cc @@ -5,14 +5,15 @@ #include <lib/sys/cpp/component_context.h> #include "base/command_line.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/file_utils.h" +#include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" #include "base/task/single_thread_task_executor.h" #include "fuchsia/base/fuchsia_dir_scheme.h" #include "fuchsia/base/init_logging.h" +#include "fuchsia/base/inspect.h" #include "fuchsia/runners/buildflags.h" #include "fuchsia/runners/common/web_content_runner.h" @@ -60,12 +61,12 @@ int main(int argc, char** argv) { WebContentRunner runner(std::move(get_context_params_callback)); base::fuchsia::ScopedServiceBinding<fuchsia::sys::Runner> binding( - base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(), - &runner); + base::ComponentContextForProcess()->outgoing().get(), &runner); - base::fuchsia::ComponentContextForCurrentProcess() - ->outgoing() - ->ServeFromStartupInfo(); + base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo(); + + // Publish version information for this component to Inspect. + cr_fuchsia::PublishVersionInfoToInspect(base::ComponentInspectorForProcess()); // Run until there are no Components, or the last service client channel is // closed. diff --git a/chromium/fuchsia/runners/web/web_runner_smoke_test.cc b/chromium/fuchsia/runners/web/web_runner_smoke_test.cc index 91ba0d00ddb..bd38e2ca004 100644 --- a/chromium/fuchsia/runners/web/web_runner_smoke_test.cc +++ b/chromium/fuchsia/runners/web/web_runner_smoke_test.cc @@ -8,7 +8,7 @@ #include <lib/sys/cpp/component_context.h> #include "base/bind.h" -#include "base/fuchsia/default_context.h" +#include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/service_provider_impl.h" #include "base/test/task_environment.h" @@ -107,7 +107,7 @@ TEST_F(WebRunnerSmokeTest, MAYBE_RequestHtmlAndImage) { fuchsia::sys::LaunchInfo launch_info = LaunchInfoWithServices(); launch_info.url = test_server_.GetURL("/test.html").spec(); - auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + auto launcher = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::sys::Launcher>(); @@ -128,7 +128,7 @@ TEST_F(WebRunnerSmokeTest, LifecycleTerminate) { launch_info.url = test_server_.GetURL("/test.html").spec(); launch_info.directory_request = directory.NewRequest().TakeChannel(); - auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + auto launcher = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::sys::Launcher>(); @@ -157,7 +157,7 @@ TEST_F(WebRunnerSmokeTest, ComponentExitOnFrameClose) { fuchsia::sys::LaunchInfo launch_info = LaunchInfoWithServices(); launch_info.url = test_server_.GetURL("/window_close.html").spec(); - auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + auto launcher = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::sys::Launcher>(); @@ -204,7 +204,7 @@ TEST_F(WebRunnerSmokeTest, RemoveSelfFromStoryOnFrameClose) { launch_info.additional_services->names.emplace_back( fuchsia::modular::ModuleContext::Name_); - auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + auto launcher = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::sys::Launcher>(); |