web_engine_shell.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // Copyright 2019 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/sys/cpp/fidl.h>
  5. #include <fuchsia/ui/policy/cpp/fidl.h>
  6. #include <fuchsia/web/cpp/fidl.h>
  7. #include <lib/fdio/directory.h>
  8. #include <lib/sys/cpp/component_context.h>
  9. #include <lib/ui/scenic/cpp/view_token_pair.h>
  10. #include <lib/vfs/cpp/pseudo_file.h>
  11. #include <iostream>
  12. #include <utility>
  13. #include "base/base_paths.h"
  14. #include "base/command_line.h"
  15. #include "base/files/file_util.h"
  16. #include "base/fuchsia/file_utils.h"
  17. #include "base/fuchsia/fuchsia_logging.h"
  18. #include "base/fuchsia/process_context.h"
  19. #include "base/json/json_writer.h"
  20. #include "base/logging.h"
  21. #include "base/message_loop/message_pump_type.h"
  22. #include "base/path_service.h"
  23. #include "base/run_loop.h"
  24. #include "base/strings/stringprintf.h"
  25. #include "base/task/single_thread_task_executor.h"
  26. #include "base/values.h"
  27. #include "build/build_config.h"
  28. #include "fuchsia_web/common/init_logging.h"
  29. #include "fuchsia_web/shell/remote_debugging_port.h"
  30. #include "fuchsia_web/webinstance_host/web_instance_host.h"
  31. #include "url/gurl.h"
  32. fuchsia::sys::ComponentControllerPtr component_controller_;
  33. namespace {
  34. constexpr char kHeadlessSwitch[] = "headless";
  35. constexpr char kEnableProtectedMediaIdentifier[] =
  36. "enable-protected-media-identifier";
  37. constexpr char kWebEnginePackageName[] = "web-engine-package-name";
  38. constexpr char kUseWebInstance[] = "use-web-instance";
  39. constexpr char kEnableWebInstanceTmp[] = "enable-web-instance-tmp";
  40. void PrintUsage() {
  41. std::cerr << "Usage: "
  42. << base::CommandLine::ForCurrentProcess()->GetProgram().BaseName()
  43. << " [--" << kRemoteDebuggingPortSwitch << "] [--"
  44. << kHeadlessSwitch << "] [--" << kWebEnginePackageName
  45. << "=name] URL. [--] [--{extra_flag1}] "
  46. << "[--{extra_flag2}]" << std::endl
  47. << "Setting " << kRemoteDebuggingPortSwitch << " to 0 will "
  48. << "automatically choose an available port." << std::endl
  49. << "Setting " << kHeadlessSwitch << " will prevent creation of "
  50. << "a view." << std::endl
  51. << "Extra flags will be passed to "
  52. << "WebEngine to be processed." << std::endl;
  53. }
  54. GURL GetUrlFromArgs(const base::CommandLine::StringVector& args) {
  55. if (args.empty()) {
  56. LOG(ERROR) << "No URL provided.";
  57. return GURL();
  58. }
  59. GURL url = GURL(args.front());
  60. if (!url.is_valid()) {
  61. LOG(ERROR) << "URL is not valid: " << url.spec();
  62. return GURL();
  63. }
  64. return url;
  65. }
  66. fuchsia::web::ContextProviderPtr ConnectToContextProvider(
  67. base::StringPiece web_engine_package_name_override,
  68. const base::CommandLine::StringVector& extra_command_line_arguments) {
  69. sys::ComponentContext* const component_context =
  70. base::ComponentContextForProcess();
  71. // If there are no additional command-line arguments then use the
  72. // system instance of the ContextProvider.
  73. if (extra_command_line_arguments.empty() &&
  74. web_engine_package_name_override.empty()) {
  75. return component_context->svc()->Connect<fuchsia::web::ContextProvider>();
  76. }
  77. base::StringPiece web_engine_package_name =
  78. web_engine_package_name_override.empty()
  79. ? "web_engine"
  80. : web_engine_package_name_override;
  81. // Launch a private ContextProvider instance, with the desired command-line
  82. // arguments.
  83. fuchsia::sys::LauncherPtr launcher;
  84. component_context->svc()->Connect(launcher.NewRequest());
  85. fuchsia::sys::LaunchInfo launch_info;
  86. launch_info.url = base::StringPrintf(
  87. "fuchsia-pkg://fuchsia.com/%s#meta/context_provider.cmx",
  88. web_engine_package_name.data());
  89. launch_info.arguments = extra_command_line_arguments;
  90. fidl::InterfaceHandle<fuchsia::io::Directory> service_directory;
  91. launch_info.directory_request = service_directory.NewRequest().TakeChannel();
  92. launcher->CreateComponent(std::move(launch_info),
  93. component_controller_.NewRequest());
  94. sys::ServiceDirectory web_engine_service_dir(std::move(service_directory));
  95. return web_engine_service_dir.Connect<fuchsia::web::ContextProvider>();
  96. }
  97. } // namespace
  98. int main(int argc, char** argv) {
  99. base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO);
  100. // Parse the command line arguments and set up logging.
  101. CHECK(base::CommandLine::Init(argc, argv));
  102. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  103. CHECK(InitLoggingFromCommandLineDefaultingToStderrForTest( // IN-TEST
  104. command_line));
  105. absl::optional<uint16_t> remote_debugging_port =
  106. GetRemoteDebuggingPort(*command_line);
  107. if (!remote_debugging_port) {
  108. PrintUsage();
  109. return 1;
  110. }
  111. const bool is_headless = command_line->HasSwitch(kHeadlessSwitch);
  112. const bool enable_protected_media_identifier_access =
  113. command_line->HasSwitch(kEnableProtectedMediaIdentifier);
  114. const bool use_context_provider = !command_line->HasSwitch(kUseWebInstance);
  115. const bool enable_web_instance_tmp =
  116. command_line->HasSwitch(kEnableWebInstanceTmp);
  117. base::CommandLine::StringVector additional_args = command_line->GetArgs();
  118. GURL url(GetUrlFromArgs(additional_args));
  119. if (!url.is_valid()) {
  120. PrintUsage();
  121. return 1;
  122. }
  123. if (enable_web_instance_tmp && use_context_provider) {
  124. LOG(ERROR) << "Cannot use --enable-web-instance-tmp without "
  125. << "--use-web-instance";
  126. return 1;
  127. }
  128. // Remove the url since we don't pass it into WebEngine
  129. additional_args.erase(additional_args.begin());
  130. // Set up the content directory fuchsia-pkg://shell-data/, which will host
  131. // the files stored under //fuchsia_web/shell/data.
  132. fuchsia::web::CreateContextParams create_context_params;
  133. fuchsia::web::ContentDirectoryProvider content_directory;
  134. base::FilePath pkg_path;
  135. base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &pkg_path);
  136. content_directory.set_directory(base::OpenDirectoryHandle(
  137. pkg_path.AppendASCII("fuchsia_web/shell/data")));
  138. content_directory.set_name("shell-data");
  139. std::vector<fuchsia::web::ContentDirectoryProvider> content_directories;
  140. content_directories.emplace_back(std::move(content_directory));
  141. create_context_params.set_content_directories(
  142. {std::move(content_directories)});
  143. // WebEngine Contexts can only make use of the services provided by the
  144. // embedder application. By passing a handle to this process' service
  145. // directory to the ContextProvider, we are allowing the Context access to the
  146. // same set of services available to this application.
  147. create_context_params.set_service_directory(
  148. base::OpenDirectoryHandle(base::FilePath(base::kServiceDirectoryPath)));
  149. // Enable other WebEngine features.
  150. fuchsia::web::ContextFeatureFlags features =
  151. fuchsia::web::ContextFeatureFlags::AUDIO |
  152. fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
  153. fuchsia::web::ContextFeatureFlags::KEYBOARD |
  154. fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD;
  155. #if defined(ARCH_CPU_ARM64)
  156. features |= fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM;
  157. #endif
  158. if (is_headless)
  159. features |= fuchsia::web::ContextFeatureFlags::HEADLESS;
  160. else
  161. features |= fuchsia::web::ContextFeatureFlags::VULKAN;
  162. create_context_params.set_features(features);
  163. create_context_params.set_remote_debugging_port(*remote_debugging_port);
  164. // DRM services require cdm_data_directory to be populated, so create a
  165. // directory under /data and use that as the cdm_data_directory.
  166. base::FilePath cdm_data_path =
  167. base::FilePath(base::kPersistedDataDirectoryPath).Append("cdm_data");
  168. base::File::Error error;
  169. CHECK(base::CreateDirectoryAndGetError(cdm_data_path, &error)) << error;
  170. create_context_params.set_cdm_data_directory(
  171. base::OpenDirectoryHandle(cdm_data_path));
  172. CHECK(create_context_params.cdm_data_directory());
  173. base::RunLoop run_loop;
  174. // Create the browser |context|.
  175. fuchsia::web::ContextPtr context;
  176. // Keep alive in run_loop scope.
  177. fuchsia::web::ContextProviderPtr web_context_provider;
  178. std::unique_ptr<WebInstanceHost> web_instance_host;
  179. fuchsia::io::DirectoryHandle tmp_directory;
  180. if (use_context_provider) {
  181. web_context_provider = ConnectToContextProvider(
  182. command_line->GetSwitchValueASCII(kWebEnginePackageName),
  183. additional_args);
  184. web_context_provider->Create(std::move(create_context_params),
  185. context.NewRequest());
  186. } else {
  187. web_instance_host = std::make_unique<WebInstanceHost>();
  188. if (enable_web_instance_tmp) {
  189. const zx_status_t status = fdio_open(
  190. "/tmp",
  191. static_cast<uint32_t>(fuchsia::io::OpenFlags::RIGHT_READABLE |
  192. fuchsia::io::OpenFlags::RIGHT_WRITABLE |
  193. fuchsia::io::OpenFlags::DIRECTORY),
  194. tmp_directory.NewRequest().TakeChannel().release());
  195. ZX_CHECK(status == ZX_OK, status) << "fdio_open(/tmp)";
  196. web_instance_host->set_tmp_dir(std::move(tmp_directory));
  197. }
  198. fidl::InterfaceRequest<fuchsia::io::Directory> services_request;
  199. auto services = sys::ServiceDirectory::CreateWithRequest(&services_request);
  200. zx_status_t result =
  201. web_instance_host->CreateInstanceForContextWithCopiedArgs(
  202. std::move(create_context_params), std::move(services_request),
  203. base::CommandLine(additional_args));
  204. if (result == ZX_OK) {
  205. services->Connect(context.NewRequest());
  206. } else {
  207. ZX_LOG(ERROR, result) << "CreateInstanceForContextWithCopiedArgs failed";
  208. return 2;
  209. }
  210. }
  211. context.set_error_handler(
  212. [quit_run_loop = run_loop.QuitClosure()](zx_status_t status) {
  213. ZX_LOG(ERROR, status) << "Context connection lost:";
  214. quit_run_loop.Run();
  215. });
  216. // Create the browser |frame| which will contain the webpage.
  217. fuchsia::web::CreateFrameParams frame_params;
  218. frame_params.set_enable_remote_debugging(true);
  219. fuchsia::web::FramePtr frame;
  220. context->CreateFrameWithParams(std::move(frame_params), frame.NewRequest());
  221. frame.set_error_handler(
  222. [quit_run_loop = run_loop.QuitClosure()](zx_status_t status) {
  223. ZX_LOG(ERROR, status) << "Frame connection lost:";
  224. quit_run_loop.Run();
  225. });
  226. fuchsia::web::ContentAreaSettings settings;
  227. settings.set_autoplay_policy(fuchsia::web::AutoplayPolicy::ALLOW);
  228. frame->SetContentAreaSettings(std::move(settings));
  229. // Log the debugging port.
  230. context->GetRemoteDebuggingPort(
  231. [](fuchsia::web::Context_GetRemoteDebuggingPort_Result result) {
  232. if (result.is_err()) {
  233. LOG(ERROR) << "Remote debugging service was not opened.";
  234. return;
  235. }
  236. // Telemetry expects this exact format of log line output to retrieve
  237. // the remote debugging port.
  238. LOG(INFO) << "Remote debugging port: " << result.response().port;
  239. });
  240. // Navigate |frame| to |url|.
  241. fuchsia::web::LoadUrlParams load_params;
  242. load_params.set_type(fuchsia::web::LoadUrlReason::TYPED);
  243. load_params.set_was_user_activated(true);
  244. fuchsia::web::NavigationControllerPtr nav_controller;
  245. frame->GetNavigationController(nav_controller.NewRequest());
  246. nav_controller->LoadUrl(
  247. url.spec(), std::move(load_params),
  248. [quit_run_loop = run_loop.QuitClosure()](
  249. fuchsia::web::NavigationController_LoadUrl_Result result) {
  250. if (result.is_err()) {
  251. LOG(ERROR) << "LoadUrl failed.";
  252. quit_run_loop.Run();
  253. }
  254. });
  255. // Since this is for development, enable all logging.
  256. frame->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::DEBUG);
  257. if (enable_protected_media_identifier_access) {
  258. fuchsia::web::PermissionDescriptor protected_media_permission;
  259. protected_media_permission.set_type(
  260. fuchsia::web::PermissionType::PROTECTED_MEDIA_IDENTIFIER);
  261. frame->SetPermissionState(std::move(protected_media_permission),
  262. url.DeprecatedGetOriginAsURL().spec(),
  263. fuchsia::web::PermissionState::GRANTED);
  264. }
  265. if (is_headless) {
  266. frame->EnableHeadlessRendering();
  267. } else {
  268. // Present a fullscreen view of |frame|.
  269. auto view_tokens = scenic::ViewTokenPair::New();
  270. frame->CreateView(std::move(view_tokens.view_token));
  271. auto presenter = base::ComponentContextForProcess()
  272. ->svc()
  273. ->Connect<fuchsia::ui::policy::Presenter>();
  274. presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
  275. nullptr);
  276. }
  277. LOG(INFO) << "Launched browser at URL " << url.spec();
  278. // Run until the process is killed with CTRL-C or the connections to Web
  279. // Engine interfaces are dropped.
  280. run_loop.Run();
  281. return 0;
  282. }