| // 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. |
| |
| #include <fuchsia/ui/policy/cpp/fidl.h> |
| #include <fuchsia/ui/views/cpp/fidl.h> |
| #include <fuchsia/web/cpp/fidl.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/ui/scenic/cpp/view_token_pair.h> |
| #include <iostream> |
| |
| #include "base/base_paths_fuchsia.h" |
| #include "base/command_line.h" |
| #include "base/fuchsia/default_context.h" |
| #include "base/fuchsia/file_utils.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_pump_type.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/task/single_thread_task_executor.h" |
| #include "fuchsia/base/init_logging.h" |
| #include "url/gurl.h" |
| |
| constexpr char kRemoteDebuggingPortSwitch[] = "remote-debugging-port"; |
| constexpr char kEnableLoggingSwitch[] = "enable-logging"; |
| constexpr char kHeadlessSwitch[] = "headless"; |
| |
| void PrintUsage() { |
| std::cerr << "Usage: " |
| << base::CommandLine::ForCurrentProcess()->GetProgram().BaseName() |
| << " [--" << kRemoteDebuggingPortSwitch << "] [--" |
| << kHeadlessSwitch << "] URL." << std::endl |
| << "Setting " << kRemoteDebuggingPortSwitch << " to 0 will " |
| << "automatically choose an available port." << std::endl |
| << "Setting " << kHeadlessSwitch << " will prevent creation of " |
| << "a view." << std::endl; |
| } |
| |
| int main(int argc, char** argv) { |
| base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO); |
| |
| // Parse the command line arguments and set up logging. |
| CHECK(base::CommandLine::Init(argc, argv)); |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| command_line->AppendSwitchNative(kEnableLoggingSwitch, "stderr"); |
| |
| CHECK(cr_fuchsia::InitLoggingFromCommandLine(*command_line)); |
| base::CommandLine::StringVector args = |
| base::CommandLine::ForCurrentProcess()->GetArgs(); |
| if (args.empty()) { |
| LOG(ERROR) << "No URL provided."; |
| PrintUsage(); |
| return 1; |
| } |
| if (args.size() > 1) { |
| LOG(ERROR) << "Too many arguments provided."; |
| PrintUsage(); |
| return 1; |
| } |
| GURL url(args.front()); |
| if (!url.is_valid()) { |
| LOG(ERROR) << "URL is not valid: " << url.spec(); |
| PrintUsage(); |
| return 1; |
| } |
| base::Optional<uint16_t> remote_debugging_port; |
| if (command_line->HasSwitch(kRemoteDebuggingPortSwitch)) { |
| std::string port_str = |
| command_line->GetSwitchValueNative(kRemoteDebuggingPortSwitch); |
| int port_parsed; |
| if (!base::StringToInt(port_str, &port_parsed) || port_parsed < 0 || |
| port_parsed > 65535) { |
| LOG(ERROR) << "Invalid value for --remote-debugging-port (must be in the " |
| "range 0-65535)."; |
| PrintUsage(); |
| return 1; |
| } |
| remote_debugging_port = base::checked_cast<uint16_t>(port_parsed); |
| } |
| |
| auto web_context_provider = base::fuchsia::ComponentContextForCurrentProcess() |
| ->svc() |
| ->Connect<fuchsia::web::ContextProvider>(); |
| |
| bool is_headless = command_line->HasSwitch(kHeadlessSwitch); |
| |
| // Set up the content directory fuchsia-pkg://shell-data/, which will host |
| // the files stored under //fuchsia/engine/test/shell_data. |
| fuchsia::web::CreateContextParams create_context_params; |
| fuchsia::web::ContentDirectoryProvider content_directory; |
| base::FilePath pkg_path; |
| base::PathService::Get(base::DIR_ASSETS, &pkg_path); |
| content_directory.set_directory(base::fuchsia::OpenDirectory( |
| pkg_path.AppendASCII("fuchsia/engine/test/shell_data"))); |
| content_directory.set_name("shell-data"); |
| std::vector<fuchsia::web::ContentDirectoryProvider> content_directories; |
| content_directories.emplace_back(std::move(content_directory)); |
| create_context_params.set_content_directories( |
| {std::move(content_directories)}); |
| |
| // WebEngine Contexts can only make use of the services provided by the |
| // embedder application. By passing a handle to this process' service |
| // directory to the ContextProvider, we are allowing the Context access to the |
| // same set of services available to this application. |
| create_context_params.set_service_directory(base::fuchsia::OpenDirectory( |
| base::FilePath(base::fuchsia::kServiceDirectoryPath))); |
| |
| // Enable other WebEngine features. |
| fuchsia::web::ContextFeatureFlags features = |
| fuchsia::web::ContextFeatureFlags::AUDIO | |
| fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER | |
| fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM; |
| if (is_headless) |
| features |= fuchsia::web::ContextFeatureFlags::HEADLESS; |
| else |
| features |= fuchsia::web::ContextFeatureFlags::VULKAN; |
| |
| create_context_params.set_features(features); |
| if (remote_debugging_port) |
| create_context_params.set_remote_debugging_port(*remote_debugging_port); |
| |
| base::RunLoop run_loop; |
| |
| // Create the browser |context|. |
| fuchsia::web::ContextPtr context; |
| web_context_provider->Create(std::move(create_context_params), |
| context.NewRequest()); |
| context.set_error_handler( |
| [quit_run_loop = run_loop.QuitClosure()](zx_status_t status) { |
| ZX_LOG(ERROR, status) << "Context connection lost:"; |
| quit_run_loop.Run(); |
| }); |
| |
| // Create the browser |frame| which will contain the webpage. |
| fuchsia::web::CreateFrameParams frame_params; |
| if (remote_debugging_port) |
| frame_params.set_enable_remote_debugging(true); |
| |
| fuchsia::web::FramePtr frame; |
| context->CreateFrameWithParams(std::move(frame_params), frame.NewRequest()); |
| frame.set_error_handler( |
| [quit_run_loop = run_loop.QuitClosure()](zx_status_t status) { |
| ZX_LOG(ERROR, status) << "Frame connection lost:"; |
| quit_run_loop.Run(); |
| }); |
| |
| // Log the debugging port, if debugging is requested. |
| if (remote_debugging_port) { |
| context->GetRemoteDebuggingPort( |
| [](fuchsia::web::Context_GetRemoteDebuggingPort_Result result) { |
| if (result.is_err()) { |
| LOG(ERROR) << "Remote debugging service was not opened."; |
| return; |
| } |
| // Telemetry expects this exact format of log line output to retrieve |
| // the remote debugging port. |
| LOG(INFO) << "Remote debugging port: " << result.response().port; |
| }); |
| } |
| |
| // Navigate |frame| to |url|. |
| fuchsia::web::LoadUrlParams load_params; |
| load_params.set_type(fuchsia::web::LoadUrlReason::TYPED); |
| load_params.set_was_user_activated(true); |
| fuchsia::web::NavigationControllerPtr nav_controller; |
| frame->GetNavigationController(nav_controller.NewRequest()); |
| nav_controller->LoadUrl( |
| url.spec(), std::move(load_params), |
| [quit_run_loop = run_loop.QuitClosure()]( |
| fuchsia::web::NavigationController_LoadUrl_Result result) { |
| if (result.is_err()) { |
| LOG(ERROR) << "LoadUrl failed."; |
| quit_run_loop.Run(); |
| } |
| }); |
| |
| if (is_headless) |
| frame->EnableHeadlessRendering(); |
| else { |
| // Present a fullscreen view of |frame|. |
| fuchsia::ui::views::ViewToken view_token; |
| fuchsia::ui::views::ViewHolderToken view_holder_token; |
| std::tie(view_token, view_holder_token) = scenic::NewViewTokenPair(); |
| frame->CreateView(std::move(view_token)); |
| auto presenter = base::fuchsia::ComponentContextForCurrentProcess() |
| ->svc() |
| ->Connect<::fuchsia::ui::policy::Presenter>(); |
| presenter->PresentView(std::move(view_holder_token), nullptr); |
| } |
| |
| LOG(INFO) << "Launched browser at URL " << url.spec(); |
| |
| // Run until the process is killed with CTRL-C or the connections to Web |
| // Engine interfaces are dropped. |
| run_loop.Run(); |
| |
| return 0; |
| } |