web_component.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "fuchsia_web/runners/common/web_component.h"
  5. #include <fuchsia/ui/views/cpp/fidl.h>
  6. #include <lib/fit/function.h>
  7. #include <lib/sys/cpp/component_context.h>
  8. #include <lib/ui/scenic/cpp/view_ref_pair.h>
  9. #include "base/bind.h"
  10. #include "base/fuchsia/fuchsia_logging.h"
  11. #include "base/logging.h"
  12. #include "base/strings/string_piece.h"
  13. #include "fuchsia_web/runners/common/web_content_runner.h"
  14. WebComponent::WebComponent(
  15. base::StringPiece debug_name,
  16. WebContentRunner* runner,
  17. std::unique_ptr<base::StartupContext> context,
  18. fidl::InterfaceRequest<fuchsia::sys::ComponentController>
  19. controller_request)
  20. : debug_name_(debug_name),
  21. runner_(runner),
  22. startup_context_(std::move(context)),
  23. controller_binding_(this),
  24. module_context_(
  25. startup_context()->svc()->Connect<fuchsia::modular::ModuleContext>()),
  26. navigation_listener_binding_(this) {
  27. DCHECK(!debug_name_.empty());
  28. DCHECK(runner);
  29. LOG(INFO) << "Creating component " << debug_name_;
  30. // If the ComponentController request is valid then bind it, and configure it
  31. // to destroy this component on error.
  32. if (controller_request.is_valid()) {
  33. controller_binding_.Bind(std::move(controller_request));
  34. controller_binding_.set_error_handler([this](zx_status_t status) {
  35. ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
  36. << " ComponentController disconnected for component " << debug_name_;
  37. // Teardown the component with dummy values, since ComponentController
  38. // channel isn't there to receive them.
  39. DestroyComponent(0, fuchsia::sys::TerminationReason::UNKNOWN);
  40. });
  41. }
  42. }
  43. WebComponent::~WebComponent() {
  44. // If Modular is available, request to be removed from the Story.
  45. if (module_context_)
  46. module_context_->RemoveSelfFromStory();
  47. if (controller_binding_.is_bound()) {
  48. // Send process termination details to the client.
  49. controller_binding_.events().OnTerminated(termination_exit_code_,
  50. termination_reason_);
  51. }
  52. }
  53. void WebComponent::EnableRemoteDebugging() {
  54. DCHECK(!component_started_);
  55. enable_remote_debugging_ = true;
  56. }
  57. void WebComponent::StartComponent() {
  58. DCHECK(!component_started_);
  59. // Create the underlying Frame and get its NavigationController.
  60. fuchsia::web::CreateFrameParams create_params;
  61. if (!debug_name_.empty())
  62. create_params.set_debug_name(debug_name_);
  63. create_params.set_enable_remote_debugging(enable_remote_debugging_);
  64. runner_->CreateFrameWithParams(std::move(create_params), frame_.NewRequest());
  65. // If the Frame unexpectedly disconnects then tear-down this Component.
  66. // ZX_OK indicates intentional termination (e.g. via window.close()).
  67. // ZX_ERR_PEER_CLOSED will usually indicate a crash, reported elsewhere.
  68. // Therefore only log other, more unusual, |status| codes.
  69. frame_.set_error_handler([this](zx_status_t status) {
  70. if (status != ZX_OK && status != ZX_ERR_PEER_CLOSED) {
  71. ZX_LOG(ERROR, status)
  72. << " component " << debug_name_ << ": Frame disconnected";
  73. }
  74. DestroyComponent(status, fuchsia::sys::TerminationReason::EXITED);
  75. });
  76. fuchsia::web::ContentAreaSettings settings;
  77. settings.set_autoplay_policy(
  78. fuchsia::web::AutoplayPolicy::REQUIRE_USER_ACTIVATION);
  79. frame_->SetContentAreaSettings(std::move(settings));
  80. // Observe the Frame for failures, via navigation state change events.
  81. frame_->SetNavigationEventListener2(navigation_listener_binding_.NewBinding(),
  82. /*flags=*/{});
  83. if (startup_context()->has_outgoing_directory_request()) {
  84. // Publish outgoing services and start serving component's outgoing
  85. // directory.
  86. view_provider_binding_ = std::make_unique<
  87. base::ScopedServiceBinding<fuchsia::ui::app::ViewProvider>>(
  88. startup_context()->component_context()->outgoing().get(), this);
  89. lifecycle_ = std::make_unique<cr_fuchsia::LifecycleImpl>(
  90. startup_context()->component_context()->outgoing().get(),
  91. base::BindOnce(&WebComponent::Kill, base::Unretained(this)));
  92. startup_context()->ServeOutgoingDirectory();
  93. }
  94. component_started_ = true;
  95. }
  96. void WebComponent::LoadUrl(
  97. const GURL& url,
  98. std::vector<fuchsia::net::http::Header> extra_headers) {
  99. DCHECK(url.is_valid());
  100. fuchsia::web::NavigationControllerPtr navigation_controller;
  101. frame()->GetNavigationController(navigation_controller.NewRequest());
  102. // Set the page activation flag on the initial load, so that features like
  103. // autoplay work as expected when a WebComponent first loads the specified
  104. // content.
  105. fuchsia::web::LoadUrlParams params;
  106. params.set_was_user_activated(true);
  107. if (!extra_headers.empty())
  108. params.set_headers(std::move(extra_headers));
  109. navigation_controller->LoadUrl(
  110. url.spec(), std::move(params),
  111. [](fuchsia::web::NavigationController_LoadUrl_Result) {});
  112. }
  113. void WebComponent::Kill() {
  114. // Signal normal termination, since the caller requested it.
  115. DestroyComponent(ZX_OK, fuchsia::sys::TerminationReason::EXITED);
  116. }
  117. void WebComponent::Detach() {
  118. controller_binding_.set_error_handler(nullptr);
  119. }
  120. void WebComponent::CreateView(
  121. zx::eventpair view_token_value,
  122. fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
  123. fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) {
  124. scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New();
  125. CreateViewWithViewRef(std::move(view_token_value),
  126. std::move(view_ref_pair.control_ref),
  127. std::move(view_ref_pair.view_ref));
  128. }
  129. void WebComponent::CreateViewWithViewRef(
  130. zx::eventpair view_token_value,
  131. fuchsia::ui::views::ViewRefControl control_ref,
  132. fuchsia::ui::views::ViewRef view_ref) {
  133. DCHECK(frame_);
  134. if (view_is_bound_) {
  135. LOG(ERROR) << "CreateView() called more than once.";
  136. DestroyComponent(ZX_ERR_BAD_STATE, fuchsia::sys::TerminationReason::EXITED);
  137. return;
  138. }
  139. fuchsia::ui::views::ViewToken view_token;
  140. view_token.value = std::move(view_token_value);
  141. frame_->CreateViewWithViewRef(std::move(view_token), std::move(control_ref),
  142. std::move(view_ref));
  143. view_is_bound_ = true;
  144. }
  145. void WebComponent::CreateView2(fuchsia::ui::app::CreateView2Args view_args) {
  146. DCHECK(frame_);
  147. if (view_is_bound_) {
  148. LOG(ERROR) << "CreateView() called more than once.";
  149. DestroyComponent(ZX_ERR_BAD_STATE, fuchsia::sys::TerminationReason::EXITED);
  150. return;
  151. }
  152. fuchsia::web::CreateView2Args web_view_args;
  153. web_view_args.set_view_creation_token(
  154. std::move(*view_args.mutable_view_creation_token()));
  155. frame_->CreateView2(std::move(web_view_args));
  156. view_is_bound_ = true;
  157. }
  158. void WebComponent::OnNavigationStateChanged(
  159. fuchsia::web::NavigationState change,
  160. OnNavigationStateChangedCallback callback) {
  161. if (change.has_page_type()) {
  162. switch (change.page_type()) {
  163. case fuchsia::web::PageType::ERROR:
  164. DestroyComponent(ZX_ERR_INTERNAL,
  165. fuchsia::sys::TerminationReason::EXITED);
  166. break;
  167. case fuchsia::web::PageType::NORMAL:
  168. break;
  169. }
  170. }
  171. // Do not touch |this|, which may have been deleted by DestroyComponent().
  172. // |callback| is safe to run, since it is on the stack.
  173. callback();
  174. }
  175. void WebComponent::DestroyComponent(int64_t exit_code,
  176. fuchsia::sys::TerminationReason reason) {
  177. LOG(INFO) << "Component " << debug_name_
  178. << " is shutting down. reason=" << static_cast<int>(reason)
  179. << " exit_code=" << exit_code;
  180. termination_reason_ = reason;
  181. termination_exit_code_ = exit_code;
  182. runner_->DestroyComponent(this);
  183. }