1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176

fuchsia_web / webinstance_host / web_instance_host.h [blame]

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FUCHSIA_WEB_WEBINSTANCE_HOST_WEB_INSTANCE_HOST_H_
#define FUCHSIA_WEB_WEBINSTANCE_HOST_WEB_INSTANCE_HOST_H_

#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.h>
#include <lib/fidl/cpp/interface_ptr.h>
#include <lib/fidl/cpp/interface_request.h>
#include <zircon/types.h>

#include <string_view>
#include <utility>

#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/sequence_checker.h"
#include "base/uuid.h"
#include "fuchsia_web/webinstance_host/fuchsia_web_debug_proxy.h"

namespace fuchsia::io {
class Directory;
}

namespace sys {
class OutgoingDirectory;
}

// Helper class that allows web_instance Components to be launched based on
// caller-supplied |CreateContextParams|.
// Use one of the concrete subclasses when instantiating.
//
// Note that Components using this class must:
// 1. Include `web_instance_host.shard.cml` in their component manifest.
// 2. Have web_instance's config-data available to the calling Component as
//    the `config-data-for-web-instance` directory capability.
//
// To ensure proper product data registration, Components using the class must:
// * Have the same version and channel as WebEngine.
// * Instantiate the class on a thread with an async_dispatcher.
// TODO(crbug.com/42050393): Remove these requirements when platform supports
// it.
class WebInstanceHost {
 public:
  virtual ~WebInstanceHost();

  WebInstanceHost(const WebInstanceHost&) = delete;
  WebInstanceHost& operator=(const WebInstanceHost&) = delete;

  // Creates a new web_instance Component and connects |services_request| to it.
  // Returns ZX_OK if `params` were valid, and the Component was launched.
  // `extra_args` are included on the command line when launching the new
  // web_instance. Use `base::CommandLine(base::CommandLine::NO_PROGRAM)` for
  // empty args.
  virtual zx_status_t CreateInstanceForContextWithCopiedArgs(
      fuchsia::web::CreateContextParams params,
      fidl::InterfaceRequest<fuchsia::io::Directory> services_request,
      const base::CommandLine& extra_args) = 0;

  // Exposes a fuchsia.web.Debug protocol implementation that can be used
  // to receive notifications of DevTools debug ports for new web instances.
  fuchsia::web::Debug& debug_api() { return debug_proxy_; }

  // The next created WebInstance will have access to the given directory handle
  // for temporary directory reading and writing.
  // Ownership of the directory is passed to the next created instance.
  void set_tmp_dir(fidl::InterfaceHandle<fuchsia::io::Directory> tmp_dir) {
    tmp_dir_ = std::move(tmp_dir);
  }

 protected:
  // The host will offer capabilities to child instances via
  // `outgoing_directory`. WebInstanceHost owners must serve the directory
  // before creating web instances, and must ensure that the directory outlives
  // the WebInstanceHost instance.
  // TODO(crbug.com/40841277): Remove `outgoing_directory` if and when it is
  // possible for tests to serve a test-specific outgoing directory via
  // base::TestComponentContextForProcess on a separate thread.
  WebInstanceHost(sys::OutgoingDirectory& outgoing_directory,
                  bool is_web_instance_component_in_same_package);

  // Creates a new web_instance Component using `instance_component_url` and
  // connects |services_request| to it. Returns ZX_OK if `params` were valid,
  // and the Component was launched. `extra_args` are included on the command
  // line when launching the new web_instance. Use
  // `base::CommandLine(base::CommandLine::NO_PROGRAM)` for empty args.
  // `services_to_offer` must be non-empty if and only if
  // `params.service_directory` is present
  zx_status_t CreateInstanceForContextWithCopiedArgsAndUrl(
      fuchsia::web::CreateContextParams params,
      fidl::InterfaceRequest<fuchsia::io::Directory> services_request,
      base::CommandLine extra_args,
      std::string_view instance_component_url,
      std::vector<std::string> services_to_offer);

 private:
  bool is_initialized() const VALID_CONTEXT_REQUIRED(sequence_checker_) {
    return realm_.is_bound();
  }

  // Connects to the fuchsia.component/Realm protocol.
  void Initialize();

  // Destroys all child instances and associated resources and unbinds from the
  // fuchsia.component/Realm protocol.
  void Uninitialize();

  // Error handler for the channel to RealmBuilder.
  void OnRealmError(zx_status_t status);

  // Error handler for the channel to an instance's Binder.
  void OnComponentBinderClosed(const base::Uuid& id, zx_status_t status);

  // The directory via which directory capabilities are dynamically provided to
  // child instances.
  const raw_ref<sys::OutgoingDirectory> outgoing_directory_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // The framework-provided protocol used to manage child instances.
  fidl::InterfacePtr<fuchsia::component::Realm> realm_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // A mapping of child instance GUID to the child's Binder interface, by which
  // child instance shutdown is observed.
  base::flat_map<base::Uuid, fidl::InterfacePtr<fuchsia::component::Binder>>
      instances_ GUARDED_BY_CONTEXT(sequence_checker_);

  // Implements the fuchsia.web.Debug API across all instances.
  FuchsiaWebDebugProxy debug_proxy_;

  // If set, then the next created WebInstance will gain ownership of this
  // directory.
  fidl::InterfaceHandle<fuchsia::io::Directory> tmp_dir_;

  // Whether `web_instance.cm` is in the same Package as this host Component.
  // TODO(crbug.com/42050363): Determine this based on a static Structured
  // Configuration value once Structured Configuration is supported.
  const bool is_web_instance_component_in_same_package_;

  SEQUENCE_CHECKER(sequence_checker_);
};

// An instantiable WebInstanceHost where services from this Component are
// provided to each `WebInstance`.
class WebInstanceHostWithServicesFromThisComponent : public WebInstanceHost {
 public:
  WebInstanceHostWithServicesFromThisComponent(
      sys::OutgoingDirectory& outgoing_directory,
      bool is_web_instance_component_in_same_package);

  zx_status_t CreateInstanceForContextWithCopiedArgs(
      fuchsia::web::CreateContextParams params,
      fidl::InterfaceRequest<fuchsia::io::Directory> services_request,
      const base::CommandLine& extra_args) override;
};

// An instantiable WebInstanceHost where services must be provided for each
// `WebInstance` in `params.service_directory` in the call to
// `CreateInstanceForContextWithCopiedArgs()`.
class WebInstanceHostWithoutServices : public WebInstanceHost {
 public:
  WebInstanceHostWithoutServices(
      sys::OutgoingDirectory& outgoing_directory,
      bool is_web_instance_component_in_same_package);

  zx_status_t CreateInstanceForContextWithCopiedArgs(
      fuchsia::web::CreateContextParams params,
      fidl::InterfaceRequest<fuchsia::io::Directory> services_request,
      const base::CommandLine& extra_args) override;
};

#endif  // FUCHSIA_WEB_WEBINSTANCE_HOST_WEB_INSTANCE_HOST_H_