diff options
Diffstat (limited to 'chromium/fuchsia/runners/cast')
18 files changed, 473 insertions, 109 deletions
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_)); } |