blob: 363430c75a637bbcd15180be8354b8cd60826ae3 [file] [log] [blame]
// Copyright 2022 The Fuchsia 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/app/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <string>
#include "src/testing/system-validation/web/apps/web_view_config_lib.h"
namespace {
fuchsia::mem::Buffer LoadFileToBuffer(const std::string& filePath) {
fuchsia::mem::Buffer buffer;
zx::vmo vmo;
FILE* fp = fopen(filePath.c_str(), "rb");
FX_CHECK(fp) << "failed to open file " << filePath;
fseek(fp, 0, SEEK_END);
uint64_t num_bytes = ftell(fp);
rewind(fp);
zx_status_t status = zx::vmo::create(num_bytes, 0u, &vmo);
FX_CHECK(status >= 0);
std::string contents;
contents.resize(num_bytes);
fread(contents.data(), 1, contents.size(), fp);
status = vmo.write(contents.data(), 0, num_bytes);
FX_CHECK(status >= 0);
buffer.vmo = std::move(vmo);
buffer.size = num_bytes;
fclose(fp);
return buffer;
}
class NavListener : public fuchsia::web::NavigationEventListener {
public:
// |fuchsia::web::NavigationEventListener|
void OnNavigationStateChanged(fuchsia::web::NavigationState nav_state,
OnNavigationStateChangedCallback send_ack) override {
if (nav_state.has_url()) {
FX_VLOGS(1) << "nav_state.url = " << nav_state.url();
}
if (nav_state.has_page_type()) {
FX_VLOGS(1) << "nav_state.page_type = " << static_cast<size_t>(nav_state.page_type());
}
if (nav_state.has_is_main_document_loaded()) {
FX_LOGS(INFO) << "nav_state.is_main_document_loaded = "
<< nav_state.is_main_document_loaded();
}
send_ack();
}
};
// Implements a simple web app, which enabled keyboard events.
class WebApp : public fuchsia::ui::app::ViewProvider {
public:
explicit WebApp(sys::ComponentContext* component_context)
: context_(component_context), view_provider_binding_(this) {
FX_LOGS(INFO) << "Starting web client";
SetupWebEngine();
SetupViewProvider();
}
void Run() {
// Set up navigation affordances.
FX_LOGS(INFO) << "Loading web app";
fuchsia::web::NavigationControllerPtr navigation_controller;
NavListener navigation_event_listener;
fidl::Binding<fuchsia::web::NavigationEventListener> navigation_event_listener_binding(
&navigation_event_listener);
web_frame_->SetNavigationEventListener(navigation_event_listener_binding.NewBinding());
web_frame_->GetNavigationController(navigation_controller.NewRequest());
// Load the web page.
FX_LOGS(INFO) << "Loading web page";
navigation_controller->LoadUrl("about:blank", fuchsia::web::LoadUrlParams(), [](auto result) {
if (result.is_err()) {
FX_LOGS(FATAL) << "Error while loading URL: " << static_cast<uint32_t>(result.err());
} else {
FX_LOGS(INFO) << "Loaded about:blank";
}
});
auto web_view_config = web_view_config_lib::Config::TakeFromStartupHandle();
FX_CHECK(!web_view_config.javascript_file().empty());
FX_LOGS(INFO) << "Running javascript file: " << web_view_config.javascript_file();
web_frame_->ExecuteJavaScript({"*"}, LoadFileToBuffer(web_view_config.javascript_file()),
[](auto result) {
if (result.is_err()) {
FX_LOGS(FATAL) << "Error while executing JavaScript: "
<< static_cast<uint32_t>(result.err());
}
});
}
private:
void SetupWebEngine() {
auto web_context_provider = context_->svc()->Connect<fuchsia::web::ContextProvider>();
auto incoming_service_clone = context_->svc()->CloneChannel();
web_context_provider.set_error_handler([](zx_status_t status) {
FX_LOGS(WARNING) << "web_context_provider: " << zx_status_get_string(status);
});
FX_CHECK(incoming_service_clone.is_valid());
fuchsia::web::CreateContextParams params;
params.set_service_directory(std::move(incoming_service_clone));
params.set_features(
fuchsia::web::ContextFeatureFlags::VULKAN | fuchsia::web::ContextFeatureFlags::NETWORK |
fuchsia::web::ContextFeatureFlags::AUDIO | fuchsia::web::ContextFeatureFlags::KEYBOARD);
web_context_provider->Create(std::move(params), web_context_.NewRequest());
web_context_.set_error_handler([](zx_status_t status) {
FX_LOGS(WARNING) << "web_context_: " << zx_status_get_string(status);
});
web_context_->CreateFrame(web_frame_.NewRequest());
fuchsia::web::ContentAreaSettings settings;
settings.set_autoplay_policy(fuchsia::web::AutoplayPolicy::ALLOW);
web_frame_->SetContentAreaSettings(std::move(settings));
web_frame_.set_error_handler([](zx_status_t status) {
FX_LOGS(WARNING) << "web_frame_: " << zx_status_get_string(status);
});
web_frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::ERROR);
}
void SetupViewProvider() {
fidl::InterfaceRequestHandler<fuchsia::ui::app::ViewProvider> handler =
[&](fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider> request) {
if (view_provider_binding_.is_bound()) {
request.Close(ZX_ERR_ALREADY_BOUND);
return;
}
view_provider_binding_.Bind(std::move(request));
};
context_->outgoing()->AddPublicService(std::move(handler));
}
// |fuchsia::ui::app::ViewProvider|
// Only Flatland
void CreateView2(fuchsia::ui::app::CreateView2Args args) override {
fuchsia::web::CreateView2Args args2;
fuchsia::ui::views::ViewCreationToken token;
args2.set_view_creation_token(std::move(*args.mutable_view_creation_token()));
web_frame_->CreateView2(std::move(args2));
}
std::unique_ptr<sys::ComponentContext> context_;
fidl::Binding<fuchsia::ui::app::ViewProvider> view_provider_binding_;
fuchsia::web::ContextPtr web_context_;
fuchsia::web::FramePtr web_frame_;
};
} // namespace
int main(int argc, const char** argv) {
auto context = sys::ComponentContext::Create();
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto web_app = std::make_unique<WebApp>(context.get());
web_app->Run();
context->outgoing()->ServeFromStartupInfo();
loop.Run();
return 0;
}