aboutsummaryrefslogtreecommitdiffstats
path: root/meta-agl-html5-demo/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-agl-html5-demo/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch')
-rw-r--r--meta-agl-html5-demo/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch3702
1 files changed, 3702 insertions, 0 deletions
diff --git a/meta-agl-html5-demo/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch b/meta-agl-html5-demo/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch
new file mode 100644
index 000000000..65c2e7c66
--- /dev/null
+++ b/meta-agl-html5-demo/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch
@@ -0,0 +1,3702 @@
+From 54da26083a7a08b73fe617b683c3f3c8c895c4a2 Mon Sep 17 00:00:00 2001
+From: Jose Dapena Paz <jdapena@igalia.com>
+Date: Fri, 9 Jun 2023 14:08:08 +0200
+Subject: [PATCH] [agl][cef] Snapshot with all patches needed to enable wam-cef
+
+---
+ CMakeLists.txt | 18 +-
+ src/CMakeLists.txt | 58 ++-
+ src/agl-cef/CMakeLists.txt | 54 +++
+ src/agl-cef/plugin/CMakeLists.txt | 59 +++
+ src/agl-cef/plugin/agl_cef_context.cc | 4 +
+ src/agl-cef/plugin/agl_cef_context.h | 42 ++
+ src/agl-cef/plugin/background_cef_app.cc | 33 ++
+ src/agl-cef/plugin/background_cef_app.h | 20 +
+ src/agl-cef/plugin/homescreen_cef_app.cc | 25 ++
+ src/agl-cef/plugin/homescreen_cef_app.h | 15 +
+ src/agl-cef/plugin/regular_cef_app.cc | 11 +
+ src/agl-cef/plugin/regular_cef_app.h | 12 +
+ src/agl-cef/plugin/web_app_factory_agl_cef.cc | 49 +++
+ src/agl-cef/plugin/web_app_factory_agl_cef.h | 23 ++
+ src/agl-cef/web_runtime_agl_cef.cc | 63 +++
+ src/agl-cef/web_runtime_agl_cef.h | 11 +
+ src/agl/web_runtime_agl.cc | 16 +-
+ src/agl/web_runtime_agl.h | 20 +-
+ src/cef/cli/CMakeLists.txt | 17 +
+ src/cef/cli/wam_cli.cc | 171 ++++++++
+ src/cef/device_info_cef.cc | 104 +++++
+ src/cef/device_info_cef.h | 38 ++
+ src/cef/handlers/wam_cef_browser_handler.cc | 31 ++
+ src/cef/handlers/wam_cef_browser_handler.h | 23 ++
+ src/cef/handlers/wam_cef_client.cc | 39 ++
+ src/cef/handlers/wam_cef_client.h | 31 ++
+ src/cef/handlers/wam_cef_render_handler.cc | 135 +++++++
+ src/cef/handlers/wam_cef_render_handler.h | 54 +++
+ src/cef/platform_module_factory_cef.cc | 31 ++
+ src/cef/platform_module_factory_cef.h | 27 ++
+ src/cef/plugin/web_app_cef.cc | 172 ++++++++
+ src/cef/plugin/web_app_cef.h | 95 +++++
+ src/cef/plugin/web_page_cef.cc | 48 +++
+ src/cef/plugin/web_page_cef.h | 69 ++++
+ src/cef/service/CMakeLists.txt | 64 +++
+ src/cef/service/applauncher.proto | 50 +++
+ src/cef/service/applauncher_client_grpc.cc | 58 +++
+ src/cef/service/applauncher_client_grpc.h | 24 ++
+ src/cef/service/wam_ipc.proto | 22 +
+ .../service/web_app_manager_client_grpc.cc | 42 ++
+ src/cef/service/web_app_manager_client_grpc.h | 23 ++
+ .../service/web_app_manager_service_grpc.cc | 382 ++++++++++++++++++
+ .../service/web_app_manager_service_grpc.h | 85 ++++
+ src/core/CMakeLists.txt | 4 +
+ src/core/application_description.cc | 37 ++
+ src/core/application_installation_handler.h | 12 +
+ .../application_installation_handler_stub.cc | 4 +
+ src/core/memory_pressure_level.h | 6 +
+ src/core/web_app_manager.cc | 29 +-
+ src/core/web_app_manager.h | 4 +-
+ src/core/web_app_manager_service.cc | 4 +-
+ src/core/web_app_manager_service.h | 3 +-
+ src/core/web_page_base.h | 6 +-
+ src/core/web_process_manager.h | 1 +
+ src/core/web_runtime.h | 3 +-
+ src/desktop/CMakeLists.txt | 100 +++++
+ src/desktop/README.md | 102 +++++
+ src/desktop/web_runtime_desktop.cc | 28 ++
+ src/desktop/web_runtime_desktop.h | 11 +
+ src/platform/CMakeLists.txt | 23 +-
+ src/platform/web_app_window.h | 6 +-
+ src/util/log_msg_id.h | 2 +
+ src/util/timer.h | 4 +-
+ src/wam_main.cc | 4 +-
+ src/webos/web_app_manager_service_luna.cc | 2 +
+ src/webos/web_runtime_webos.cc | 2 +-
+ src/webos/web_runtime_webos.h | 2 +-
+ 67 files changed, 2693 insertions(+), 74 deletions(-)
+ create mode 100644 src/agl-cef/CMakeLists.txt
+ create mode 100644 src/agl-cef/plugin/CMakeLists.txt
+ create mode 100644 src/agl-cef/plugin/agl_cef_context.cc
+ create mode 100644 src/agl-cef/plugin/agl_cef_context.h
+ create mode 100644 src/agl-cef/plugin/background_cef_app.cc
+ create mode 100644 src/agl-cef/plugin/background_cef_app.h
+ create mode 100644 src/agl-cef/plugin/homescreen_cef_app.cc
+ create mode 100644 src/agl-cef/plugin/homescreen_cef_app.h
+ create mode 100644 src/agl-cef/plugin/regular_cef_app.cc
+ create mode 100644 src/agl-cef/plugin/regular_cef_app.h
+ create mode 100644 src/agl-cef/plugin/web_app_factory_agl_cef.cc
+ create mode 100644 src/agl-cef/plugin/web_app_factory_agl_cef.h
+ create mode 100644 src/agl-cef/web_runtime_agl_cef.cc
+ create mode 100644 src/agl-cef/web_runtime_agl_cef.h
+ create mode 100644 src/cef/cli/CMakeLists.txt
+ create mode 100644 src/cef/cli/wam_cli.cc
+ create mode 100644 src/cef/device_info_cef.cc
+ create mode 100644 src/cef/device_info_cef.h
+ create mode 100644 src/cef/handlers/wam_cef_browser_handler.cc
+ create mode 100644 src/cef/handlers/wam_cef_browser_handler.h
+ create mode 100644 src/cef/handlers/wam_cef_client.cc
+ create mode 100644 src/cef/handlers/wam_cef_client.h
+ create mode 100644 src/cef/handlers/wam_cef_render_handler.cc
+ create mode 100644 src/cef/handlers/wam_cef_render_handler.h
+ create mode 100644 src/cef/platform_module_factory_cef.cc
+ create mode 100644 src/cef/platform_module_factory_cef.h
+ create mode 100644 src/cef/plugin/web_app_cef.cc
+ create mode 100644 src/cef/plugin/web_app_cef.h
+ create mode 100644 src/cef/plugin/web_page_cef.cc
+ create mode 100644 src/cef/plugin/web_page_cef.h
+ create mode 100644 src/cef/service/CMakeLists.txt
+ create mode 100644 src/cef/service/applauncher.proto
+ create mode 100644 src/cef/service/applauncher_client_grpc.cc
+ create mode 100644 src/cef/service/applauncher_client_grpc.h
+ create mode 100644 src/cef/service/wam_ipc.proto
+ create mode 100644 src/cef/service/web_app_manager_client_grpc.cc
+ create mode 100644 src/cef/service/web_app_manager_client_grpc.h
+ create mode 100644 src/cef/service/web_app_manager_service_grpc.cc
+ create mode 100644 src/cef/service/web_app_manager_service_grpc.h
+ create mode 100644 src/core/application_installation_handler.h
+ create mode 100644 src/core/application_installation_handler_stub.cc
+ create mode 100644 src/core/memory_pressure_level.h
+ create mode 100644 src/desktop/CMakeLists.txt
+ create mode 100644 src/desktop/README.md
+ create mode 100644 src/desktop/web_runtime_desktop.cc
+ create mode 100644 src/desktop/web_runtime_desktop.h
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 1e9f94f..073e326 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -18,22 +18,24 @@ cmake_minimum_required(VERSION 3.13.0)
+
+ project(WebAppMgr VERSION 1.0.0 LANGUAGES CXX)
+
+-set(CMAKE_CXX_STANDARD 14)
++set(CMAKE_CXX_STANDARD 20)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+-set(CMAKE_BUILD_TYPE Release)
+
+ include(FindPkgConfig)
+
+ set(OS_WEBOS FALSE)
+ set(OS_AGL FALSE)
+-set(PLATFORM_NAME "WebOS" CACHE STRING "Target platform name (WebOS, POKY_AGL)")
++set(OS_DESKTOP FALSE)
++set(WEBENGINE_CEF TRUE)
++set(WEBENGINE_CBE FALSE)
++set(PLATFORM_NAME "Desktop" CACHE STRING "Target platform name (WebOS, POKY_AGL, Desktop)")
+ string(TOLOWER ${PLATFORM_NAME} PLATFORM)
+ if(${PLATFORM} STREQUAL "webos")
+ set(OS_WEBOS TRUE)
+ elseif(${PLATFORM} MATCHES ".*agl")
+ set(OS_AGL TRUE)
+ else()
+- message(FATAL_ERROR "Unsupported platform: ${PLATFORM}")
++ set(OS_DESKTOP TRUE)
+ endif()
+
+ if(OS_WEBOS)
+@@ -45,4 +47,12 @@ if(OS_WEBOS)
+ webos_build_system_bus_files(${CMAKE_SOURCE_DIR}/files/sysbus)
+ endif()
+
++message(STATUS "WAM Configuration:")
++message(STATUS "OS_WEBOS: ${OS_WEBOS}")
++message(STATUS "OS_AGL: ${OS_AGL}")
++message(STATUS "OS_DESKTOP: ${OS_DESKTOP}")
++message(STATUS "WEBENGINE_CEF: ${WEBENGINE_CEF}")
++message(STATUS "WEBENGINE_CBE: ${WEBENGINE_CBE}")
++message(STATUS "PLATFORM_NAME: ${PLATFORM_NAME}")
++
+ add_subdirectory(src)
+diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
+index 3cca138..173dafc 100644
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -18,10 +18,12 @@ project(WebAppMgrExec VERSION 1.0.0 DESCRIPTION "WAM")
+
+ set(WAM_ROOT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+-if(NOT DEFINED CHROMIUM_SRC_DIR)
+- message(FATAL_ERROR "CHROMIUM_SRC_DIR was not set")
++if (WEBVIEW_CBE)
++ if(NOT DEFINED CHROMIUM_SRC_DIR)
++ message(FATAL_ERROR "CHROMIUM_SRC_DIR was not set")
++ endif()
++ set(CHROMIUM_LDFLAGS -lcbe)
+ endif()
+-set(CHROMIUM_LDFLAGS -lcbe)
+
+ find_package(PkgConfig REQUIRED)
+ find_package(Boost COMPONENTS filesystem REQUIRED)
+@@ -39,6 +41,8 @@ elseif(OS_AGL)
+ find_package(gRPC REQUIRED)
+ find_program(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)
+ find_package(Threads)
++elseif(OS_DESKTOP)
++ set(DISABLE_PMLOG(true))
+ endif()
+
+ if(DISABLE_PMLOG)
+@@ -58,7 +62,6 @@ set(WAM_COMPILER_FLAGS -fno-rtti
+ -Wall
+ -fpermissive
+ -funwind-tables
+- -std=c++14
+ -Wno-psabi
+ -Werror
+ )
+@@ -70,29 +73,68 @@ endif()
+ add_link_options(-Wl,--no-as-needed -Wno-psabi -rdynamic)
+
+ add_subdirectory(core)
+-add_subdirectory(platform)
+
+ set(WAM_INCLUDE_DIRS
+ ${GLIB_INCLUDE_DIRS}
+ ${CMAKE_CURRENT_SOURCE_DIR}/core
+ ${CMAKE_CURRENT_SOURCE_DIR}/util
+- ${CMAKE_CURRENT_SOURCE_DIR}/webos
+ )
+
+ set(WAM_LIBS
+ ${CHROMIUM_LDFLAGS}
+ ${PMLOGLIB_LDFLAGS}
+- WebAppMgr
+ WebAppMgrCore
+ )
+
++if(WEBENGINE_CBE)
++ add_subdirectory(platform)
++ LIST(APPEND WAM_INCLUDE_DIRS
++ ${CMAKE_CURRENT_SOURCE_DIR}/webos
++ )
++ LIST(APPEND WAM_LIBS
++ WebAppMgr
++ )
++endif()
++
++if(OS_DESKTOP OR WEBENGINE_CEF)
++ if(NOT DEFINED CEF_ROOT)
++ message(FATAL_ERROR "CEF_ROOT needs to point to the binary distribution directory")
++ endif()
++
++ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CEF_ROOT}/cmake")
++ find_package(CEF REQUIRED)
++ message(STATUS "CEF lib location: ${CEF_LIB_RELEASE}")
++ print_cef_config()
++
++ add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper)
++ add_library(libcef_lib SHARED IMPORTED)
++ set_target_properties(libcef_lib PROPERTIES IMPORTED_LOCATION ${CEF_LIB_RELEASE})
++ target_link_libraries(libcef_dll_wrapper libcef_lib)
++
++ LIST(APPEND WAM_LIBS libcef_lib libcef_dll_wrapper)
++endif()
++
++if(OS_DESKTOP)
++ add_subdirectory(desktop)
++ add_subdirectory(cef/service)
++ add_subdirectory(cef/cli)
++ LIST(APPEND WAM_LIBS WebAppMgrDesktop)
++endif()
++
+ if(OS_WEBOS)
+ add_subdirectory(plugin)
+ add_subdirectory(tests)
+ add_subdirectory(testplugin)
+ LIST(APPEND WAM_LIBS ${LIBLUNAPREFS_LDFLAGS})
+ elseif(OS_AGL)
+- add_subdirectory(agl/plugin)
++ if(WEBENGINE_CEF)
++ add_subdirectory(agl-cef)
++ add_subdirectory(cef/service)
++ add_subdirectory(cef/cli)
++ LIST(APPEND WAM_LIBS WebAppMgrAGLCEF)
++ else()
++ add_subdirectory(agl/plugin)
++ endif()
+ LIST(APPEND WAM_INCLUDE_DIRS
+ ${Protobuf_INCLUDE_DIRS}
+ ${gRPC_INCLUDE_DIRS}
+diff --git a/src/agl-cef/CMakeLists.txt b/src/agl-cef/CMakeLists.txt
+new file mode 100644
+index 0000000..8d29004
+--- /dev/null
++++ b/src/agl-cef/CMakeLists.txt
+@@ -0,0 +1,54 @@
++project(WebAppMgrAGLCEF VERSION 1.0.0 DESCRIPTION "Web Application Manager library")
++
++set(WAM_LIB_LIBS
++ ${JSONCPP_LDFLAGS}
++ WebAppMgrCore
++ WebAppMgrService
++ libcef_lib
++ libcef_dll_wrapper
++)
++
++set(SOURCES
++ web_runtime_agl_cef.cc
++)
++
++set(HEADERS
++ web_runtime_agl_cef.h
++)
++
++set(WAM_LIB_CEF_DIR ${WAM_ROOT_SOURCE_DIR}/cef)
++
++set(WAM_LIB_INCLUDE_DIRS
++ ${JSONCPP_INCLUDE_DIRS}
++ ${CEF_INCLUDE_PATH}
++)
++
++LIST(APPEND SOURCES
++ ${WAM_LIB_CEF_DIR}/device_info_cef.cc
++ ${WAM_LIB_CEF_DIR}/platform_module_factory_cef.cc
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_browser_handler.cc
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_client.cc
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_render_handler.cc
++)
++LIST(APPEND HEADERS
++ ${WAM_LIB_CEF_DIR}/device_info_cef.h
++ ${WAM_LIB_CEF_DIR}/platform_module_factory_cef.h
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_browser_handler.h
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_client.h
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_render_handler.cc
++)
++LIST(APPEND WAM_LIB_INCLUDE_DIRS
++ ${WAM_LIB_CEF_DIR}
++ ${WAM_LIB_CEF_DIR}/handlers
++)
++
++add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES})
++target_include_directories(${PROJECT_NAME} PUBLIC ${WAM_LIB_INCLUDE_DIRS})
++set_target_properties(${PROJECT_NAME} PROPERTIES VERSION 1.0.0 SOVERSION 1.0)
++
++install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/webappmanager)
++
++target_link_libraries(${PROJECT_NAME} PUBLIC ${WAM_LIB_LIBS})
++install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
++
++add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/plugin)
+diff --git a/src/agl-cef/plugin/CMakeLists.txt b/src/agl-cef/plugin/CMakeLists.txt
+new file mode 100644
+index 0000000..052c94e
+--- /dev/null
++++ b/src/agl-cef/plugin/CMakeLists.txt
+@@ -0,0 +1,59 @@
++project(webappmgr-default-plugin VERSION 1.0.0 DESCRIPTION "Default WAM plugin")
++
++set(CEF_PLUGINS_DIR ${WAM_ROOT_SOURCE_DIR}/cef/plugin)
++set(CEF_HANDLERS_DIR ${WAM_ROOT_SOURCE_DIR}/cef/handlers)
++
++set (SOURCES
++ ${CEF_HANDLERS_DIR}/wam_cef_client.cc
++ ${CEF_PLUGINS_DIR}/web_app_cef.cc
++ ${CEF_PLUGINS_DIR}/web_page_cef.cc
++ agl_cef_context.cc
++ web_app_factory_agl_cef.cc
++ background_cef_app.cc
++ homescreen_cef_app.cc
++ regular_cef_app.cc
++)
++set (HEADERS
++ ${CEF_HANDLERS_DIR}/wam_cef_client.h
++ ${CEF_PLUGINS_DIR}/web_app_cef.h
++ ${CEF_PLUGINS_DIR}/web_page_cef.h
++ agl_cef_context.h
++ web_app_factory_agl_cef.h
++ background_cef_app.h
++ homescreen_cef_app.h
++ regular_cef_app.h
++)
++set (WAM_PLUGIN_INCLUDE_DIRS
++ ${CEF_HANDLERS_DIR}
++ ${CEF_PLUGINS_DIR}
++ ${WAM_ROOT_SOURCE_DIR}
++ ${WAM_ROOT_SOURCE_DIR}/core
++ ${WAM_ROOT_SOURCE_DIR}/platform
++ ${WAM_ROOT_SOURCE_DIR}/platform/webengine
++ ${WAM_ROOT_SOURCE_DIR}/util
++ ${CEF_INCLUDE_PATH}
++)
++set(WAM_PLUGIN_LIBS
++ WebAppMgrService
++ WebAppMgrService-grpc
++ libcef_lib
++ libcef_dll_wrapper
++)
++
++if (OS_DESKTOP)
++ LIST(APPEND WAM_PLUGIN_LIBS
++ WebAppMgrDesktop
++ )
++else()
++ LIST(APPEND WAM_PLUGIN_LIBS
++ WebAppMgrAGLCEF
++ )
++endif()
++
++add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES})
++add_dependencies(${PROJECT_NAME} libcef_lib libcef_dll_wrapper)
++
++target_include_directories(${PROJECT_NAME} PRIVATE ${WAM_PLUGIN_INCLUDE_DIRS})
++target_link_libraries(${PROJECT_NAME} PRIVATE ${WAM_PLUGIN_LIBS})
++install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/webappmanager/plugins)
++install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webappmanager)
+diff --git a/src/agl-cef/plugin/agl_cef_context.cc b/src/agl-cef/plugin/agl_cef_context.cc
+new file mode 100644
+index 0000000..8c1a390
+--- /dev/null
++++ b/src/agl-cef/plugin/agl_cef_context.cc
+@@ -0,0 +1,4 @@
++#include "agl_cef_context.h"
++
++AglCefContext* AglCefContext::instance_ = nullptr;
++
+diff --git a/src/agl-cef/plugin/agl_cef_context.h b/src/agl-cef/plugin/agl_cef_context.h
+new file mode 100644
+index 0000000..a1e698f
+--- /dev/null
++++ b/src/agl-cef/plugin/agl_cef_context.h
+@@ -0,0 +1,42 @@
++#ifndef AGL_CEF_PLUGIN_AGL_CEF_CONTEXT_H
++#define AGL_CEF_PLUGIN_AGL_CEF_CONTEXT_H
++
++#include <memory>
++
++class AglCefContext {
++ public:
++ AglCefContext(const AglCefContext&) = delete;
++ AglCefContext& operator=(const AglCefContext&) = delete;
++
++ static AglCefContext* get() {
++ if (!instance_) {
++ instance_ = new AglCefContext();
++ }
++ return instance_;
++ }
++
++ uint32_t activation_area_width() const { return activation_area_width_; }
++ void set_activation_area_width(uint32_t activation_area_width) {
++ activation_area_width_ = activation_area_width;
++ }
++
++ uint32_t activation_area_height() const { return activation_area_height_; }
++ void set_activation_area_height(uint32_t activation_area_height) {
++ activation_area_height_ = activation_area_height;
++ }
++
++ uint32_t panel_offset() const { return panel_offset_; }
++ void set_panel_offset(uint32_t panel_offset) {
++ panel_offset_ = panel_offset;
++ }
++
++ private:
++ AglCefContext() = default;
++
++ static AglCefContext* instance_;
++ uint32_t activation_area_width_ = 0;
++ uint32_t activation_area_height_ = 0;
++ uint32_t panel_offset_ = 0;
++};
++
++#endif // AGL_CEF_PLUGIN_AGL_CEF_CONTEXT_H
+diff --git a/src/agl-cef/plugin/background_cef_app.cc b/src/agl-cef/plugin/background_cef_app.cc
+new file mode 100644
+index 0000000..c406641
+--- /dev/null
++++ b/src/agl-cef/plugin/background_cef_app.cc
+@@ -0,0 +1,33 @@
++#include "background_cef_app.h"
++
++BackgroundCefApp::BackgroundCefApp(std::shared_ptr<ApplicationDescription> app_desc)
++ : WebAppCEF(app_desc) {
++ CefRect display_bounds = GetDisplayBounds();
++ width_override_ = display_bounds.width;
++ height_override_ = display_bounds.height;
++}
++
++BackgroundCefApp::~BackgroundCefApp() {}
++
++void BackgroundCefApp::Init(int width,
++ int height,
++ AglShellSurfaceType surface_type,
++ AglShellPanelEdge panel_type) {
++ if (!IsReady()) {
++ return;
++ }
++
++ /*
++ // TODO: change when in portrait mode
++ window_->SetupActivationArea(AglCefContext::get()->panel_offset(),
++ 0,
++ AglCefContext::get()->activation_area_width(),
++ AglCefContext::get()->activation_area_height());
++ */
++
++ WebAppCEF::Init(width, height, surface_type, panel_type);
++}
++
++bool BackgroundCefApp::IsReady() const {
++ return window_ != nullptr;
++}
+diff --git a/src/agl-cef/plugin/background_cef_app.h b/src/agl-cef/plugin/background_cef_app.h
+new file mode 100644
+index 0000000..6681b5d
+--- /dev/null
++++ b/src/agl-cef/plugin/background_cef_app.h
+@@ -0,0 +1,20 @@
++#ifndef AGL_CEF_PLUGIN_BACKGROUND_CEF_APP_H
++#define AGL_CEF_PLUGIN_BACKGROUND_CEF_APP_H
++
++#include "agl_cef_context.h"
++#include "web_app_cef.h"
++
++class BackgroundCefApp : public WebAppCEF {
++ public:
++ BackgroundCefApp(std::shared_ptr<ApplicationDescription> app_desc);
++ ~BackgroundCefApp();
++
++ void Init(int width,
++ int height,
++ AglShellSurfaceType surface_type,
++ AglShellPanelEdge panel_type) override;
++
++ bool IsReady() const override;
++};
++
++#endif // AGL_CEF_PLUGIN_BACKGROUND_CEF_APP_H
+diff --git a/src/agl-cef/plugin/homescreen_cef_app.cc b/src/agl-cef/plugin/homescreen_cef_app.cc
+new file mode 100644
+index 0000000..cc6af64
+--- /dev/null
++++ b/src/agl-cef/plugin/homescreen_cef_app.cc
+@@ -0,0 +1,25 @@
++#include "homescreen_cef_app.h"
++
++#include "agl_cef_context.h"
++
++HomescreenCefApp::HomescreenCefApp(std::shared_ptr<ApplicationDescription> app_desc)
++ : WebAppCEF(app_desc) {
++ CefRect display_bounds = GetDisplayBounds();
++ uint32_t panel_offset = app_desc->WidthOverride(); // TODO: height when in portrait mode
++ uint32_t activation_width = display_bounds.width - panel_offset - 1;
++ uint32_t activation_height = display_bounds.height;
++
++ // TODO: change when in portrait mode
++ width_override_ = panel_offset;
++ height_override_ = activation_height;
++
++ AglCefContext::get()->set_panel_offset(panel_offset);
++ AglCefContext::get()->set_activation_area_width(activation_width);
++ AglCefContext::get()->set_activation_area_height(activation_height);
++}
++
++HomescreenCefApp::~HomescreenCefApp() {}
++
++bool HomescreenCefApp::IsReady() const {
++ return window_ && window_->IsSurfaceConfigured();
++}
+diff --git a/src/agl-cef/plugin/homescreen_cef_app.h b/src/agl-cef/plugin/homescreen_cef_app.h
+new file mode 100644
+index 0000000..c4f86ab
+--- /dev/null
++++ b/src/agl-cef/plugin/homescreen_cef_app.h
+@@ -0,0 +1,15 @@
++#ifndef AGL_CEF_PLUGIN_HOMESCREEN_CEF_APP_H
++#define AGL_CEF_PLUGIN_HOMESCREEN_CEF_APP_H
++
++#include "web_app_cef.h"
++
++class HomescreenCefApp : public WebAppCEF {
++ public:
++ HomescreenCefApp(std::shared_ptr<ApplicationDescription> app_desc);
++ ~HomescreenCefApp();
++
++ protected:
++ bool IsReady() const override;
++};
++
++#endif // AGL_CEF_PLUGIN_BACKGROUND_CEF_APP_H
+diff --git a/src/agl-cef/plugin/regular_cef_app.cc b/src/agl-cef/plugin/regular_cef_app.cc
+new file mode 100644
+index 0000000..a882314
+--- /dev/null
++++ b/src/agl-cef/plugin/regular_cef_app.cc
+@@ -0,0 +1,11 @@
++#include "regular_cef_app.h"
++
++#include "agl_cef_context.h"
++
++RegularCefApp::RegularCefApp(std::shared_ptr<ApplicationDescription> app_desc)
++ : WebAppCEF(app_desc) {
++ width_override_ = AglCefContext::get()->activation_area_width();
++ height_override_ = AglCefContext::get()->activation_area_height();
++}
++
++RegularCefApp::~RegularCefApp() {}
+diff --git a/src/agl-cef/plugin/regular_cef_app.h b/src/agl-cef/plugin/regular_cef_app.h
+new file mode 100644
+index 0000000..ed4b34d
+--- /dev/null
++++ b/src/agl-cef/plugin/regular_cef_app.h
+@@ -0,0 +1,12 @@
++#ifndef AGL_CEF_PLUGIN_REGULAR_CEF_APP_H
++#define AGL_CEF_PLUGIN_REGULAR_CEF_APP_H
++
++#include "web_app_cef.h"
++
++class RegularCefApp : public WebAppCEF {
++ public:
++ RegularCefApp(std::shared_ptr<ApplicationDescription> app_desc);
++ ~RegularCefApp();
++};
++
++#endif // AGL_CEF_PLUGIN_REGULAR_CEF_APP_H
+diff --git a/src/agl-cef/plugin/web_app_factory_agl_cef.cc b/src/agl-cef/plugin/web_app_factory_agl_cef.cc
+new file mode 100644
+index 0000000..8ca2ba8
+--- /dev/null
++++ b/src/agl-cef/plugin/web_app_factory_agl_cef.cc
+@@ -0,0 +1,49 @@
++#include "web_app_factory_agl_cef.h"
++
++#include "application_description.h"
++#include "plugin_interface.h"
++
++#include "background_cef_app.h"
++#include "homescreen_cef_app.h"
++#include "regular_cef_app.h"
++
++#include "web_page_cef.h"
++
++#include "wam_cef_client.h"
++
++const char* kPluginApplicationType = "default";
++
++WebAppFactoryInterface* CreateInstance() {
++ return new WebAppFactoryCEF();
++}
++
++void DeleteInstance(WebAppFactoryInterface* interface) {
++ delete interface;
++}
++
++WebAppBase* WebAppFactoryCEF::CreateWebApp(
++ const std::string&,
++ std::shared_ptr<ApplicationDescription> app_desc) {
++ std::string app_id = app_desc->Id();
++
++ if (app_id == "homescreen") {
++ return new HomescreenCefApp(app_desc);
++ } else if (app_id == "webapps-html5-background") {
++ return new BackgroundCefApp(app_desc);
++ }
++ return new RegularCefApp(app_desc);
++}
++
++WebAppBase* WebAppFactoryCEF::CreateWebApp(
++ const std::string& win_type,
++ WebPageBase*,
++ std::shared_ptr<ApplicationDescription> desc) {
++ return CreateWebApp(win_type, desc);
++}
++
++WebPageBase* WebAppFactoryCEF::CreateWebPage(
++ const wam::Url& url,
++ std::shared_ptr<ApplicationDescription> app_desc,
++ const std::string&) {
++ return new WebPageCEF(app_desc, url.ToString());
++}
+diff --git a/src/agl-cef/plugin/web_app_factory_agl_cef.h b/src/agl-cef/plugin/web_app_factory_agl_cef.h
+new file mode 100644
+index 0000000..eb4b7ba
+--- /dev/null
++++ b/src/agl-cef/plugin/web_app_factory_agl_cef.h
+@@ -0,0 +1,23 @@
++#ifndef AGL_CEF_PLUGIN_WEB_APP_FACTORY_CEF_H
++#define AGL_CEF_PLUGIN_WEB_APP_FACTORY_CEF_H
++
++#include <memory>
++#include <string>
++
++#include "web_app_factory_interface.h"
++
++class WebAppFactoryCEF : public WebAppFactoryInterface {
++ public:
++ WebAppBase* CreateWebApp(
++ const std::string& win_type,
++ std::shared_ptr<ApplicationDescription> desc = 0) override;
++ WebAppBase* CreateWebApp(
++ const std::string& win_type,
++ WebPageBase* page,
++ std::shared_ptr<ApplicationDescription> desc = 0) override;
++ WebPageBase* CreateWebPage(const wam::Url& url,
++ std::shared_ptr<ApplicationDescription> desc,
++ const std::string& launchParams = "") override;
++};
++
++#endif // AGL_CEF_PLUGIN_WEB_APP_FACTORY_CEF_H
+diff --git a/src/agl-cef/web_runtime_agl_cef.cc b/src/agl-cef/web_runtime_agl_cef.cc
+new file mode 100644
+index 0000000..9bc1bd6
+--- /dev/null
++++ b/src/agl-cef/web_runtime_agl_cef.cc
+@@ -0,0 +1,63 @@
++#include "web_runtime_agl_cef.h"
++
++#include "include/cef_base.h"
++#include "include/cef_origin_whitelist.h"
++
++#include "wam_cef_browser_handler.h"
++#include "wam_cef_render_handler.h"
++
++const char kProcessType[] = "type";
++const char kRendererProcess[] = "renderer";
++const char kZygoteProcess[] = "zygote";
++
++class WamCefUtilityHandler : public CefApp {
++ public:
++ WamCefUtilityHandler () {}
++
++ private:
++ IMPLEMENT_REFCOUNTING(WamCefUtilityHandler);
++ DISALLOW_COPY_AND_ASSIGN(WamCefUtilityHandler);
++};
++
++int WebRuntimeCEF::Run(int argc, char** argv) {
++ CefMainArgs main_args(argc, argv);
++
++ CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
++ command_line->InitFromArgv(main_args.argc, main_args.argv);
++
++ std::string app_id = command_line->GetSwitchValue("appid");
++ if (app_id.empty()) {
++ app_id = "WebAppMgr";
++ }
++
++ CefRefPtr<CefApp> app;
++ if (!command_line->HasSwitch(kProcessType)) {
++ app = new WamCefBrowserHandler();
++ } else {
++ const std::string& process_type = command_line->GetSwitchValue(kProcessType);
++ if (process_type == kRendererProcess || process_type == kZygoteProcess) {
++ app = new WamCefRenderHandler();
++ } else {
++ app = new WamCefUtilityHandler();
++ }
++ }
++
++ auto exit_code = CefExecuteProcess(main_args, app.get(), nullptr);
++ if (exit_code >= 0) {
++ return exit_code;
++ }
++
++ CefSettings settings;
++ settings.remote_debugging_port = 9998;
++ CefInitialize(main_args, settings, app.get(), nullptr);
++
++ CefRunMessageLoop();
++
++ CefShutdown();
++
++ return 0;
++}
++
++std::unique_ptr<WebRuntime> WebRuntime::Create() {
++ return std::make_unique<WebRuntimeCEF>();
++}
+diff --git a/src/agl-cef/web_runtime_agl_cef.h b/src/agl-cef/web_runtime_agl_cef.h
+new file mode 100644
+index 0000000..049e554
+--- /dev/null
++++ b/src/agl-cef/web_runtime_agl_cef.h
+@@ -0,0 +1,11 @@
++#ifndef AGL_CEF_WEB_RUNTIME_AGL_CEF_H_
++#define AGL_CEF_WEB_RUNTIME_AGL_CEF_H_
++
++#include "web_runtime.h"
++
++class WebRuntimeCEF : public WebRuntime {
++ public:
++ int Run(int argc, char** argv) override;
++};
++
++#endif // AGL_CEF_WEB_RUNTIME_CEF_H_
+diff --git a/src/agl/web_runtime_agl.cc b/src/agl/web_runtime_agl.cc
+index ce8525b..c791c70 100644
+--- a/src/agl/web_runtime_agl.cc
++++ b/src/agl/web_runtime_agl.cc
+@@ -128,7 +128,7 @@ void SingleBrowserProcessWebAppLauncher::Launch(
+ }
+
+ int SingleBrowserProcessWebAppLauncher::Loop(int argc,
+- const char** argv,
++ char** argv,
+ volatile sig_atomic_t& e_flag) {
+ AGLMainDelegateWAM delegate;
+ webos::WebOSMain webOSMain(&delegate);
+@@ -154,7 +154,7 @@ void SharedBrowserProcessWebAppLauncher::Launch(
+ }
+
+ int SharedBrowserProcessWebAppLauncher::Loop(int argc,
+- const char** argv,
++ char** argv,
+ volatile sig_atomic_t& e_flag) {
+ // TODO: wait for a pid
+ while (e_flag)
+@@ -173,7 +173,7 @@ static void AglShellActivateApp(const std::string& app_id) {
+ WebAppManagerServiceAGL::Instance()->SendEvent(kActivateEvent, app_id);
+ }
+
+-int WebAppLauncherRuntime::Run(int argc, const char** argv) {
++int WebAppLauncherRuntime::Run(int argc, char** argv) {
+ bool is_wait_host_service = IsWaitForHostService();
+ std::string app_id = IsActivateApp(Args::Instance());
+
+@@ -334,7 +334,7 @@ bool WebAppLauncherRuntime::ParseJsonConfig(const char* path_to_config) {
+ return true;
+ }
+
+-int SharedBrowserProcessRuntime::Run(int argc, const char** argv) {
++int SharedBrowserProcessRuntime::Run(int argc, char** argv) {
+ if (WebAppManagerServiceAGL::Instance()->InitializeAsHostService()) {
+ AGLMainDelegateWAM delegate;
+ webos::WebOSMain webOSMain(&delegate);
+@@ -347,7 +347,7 @@ int SharedBrowserProcessRuntime::Run(int argc, const char** argv) {
+ }
+ }
+
+-int RenderProcessRuntime::Run(int argc, const char** argv) {
++int RenderProcessRuntime::Run(int argc, char** argv) {
+ AGLMainDelegateWAM delegate;
+ webos::WebOSMain webOSMain(&delegate);
+ return webOSMain.Run(argc, argv);
+@@ -372,7 +372,7 @@ static void print_help(void) {
+ exit(EXIT_FAILURE);
+ }
+
+-void Args::parse_args(int argc, const char** argv) {
++void Args::parse_args(int argc, char** argv) {
+ int c;
+ int option_index;
+ opterr = 0;
+@@ -427,7 +427,7 @@ void Args::parse_args(int argc, const char** argv) {
+ }
+ }
+
+-void Args::copy_cmdline(int argc, const char** argv) {
++void Args::copy_cmdline(int argc, char** argv) {
+ new_argc = argc;
+ new_argv = static_cast<char**>(calloc(new_argc + 1, sizeof(*new_argv)));
+
+@@ -446,7 +446,7 @@ void Args::clear_cmdline(void) {
+ free(new_argv);
+ }
+
+-int WebRuntimeAGL::Run(int argc, const char** argv) {
++int WebRuntimeAGL::Run(int argc, char** argv) {
+ int ret;
+ Args::Instance()->parse_args(argc, argv);
+
+diff --git a/src/agl/web_runtime_agl.h b/src/agl/web_runtime_agl.h
+index ad045c2..14bc0ed 100644
+--- a/src/agl/web_runtime_agl.h
++++ b/src/agl/web_runtime_agl.h
+@@ -39,7 +39,7 @@ class Args {
+ static Args* args = new Args();
+ return args;
+ }
+- void parse_args(int argc, const char** argv);
++ void parse_args(int argc, char** argv);
+
+ inline void set_flag(unsigned int flag) { flags |= flag; }
+
+@@ -56,7 +56,7 @@ class Args {
+
+ private:
+ uint32_t flags = FLAG_NONE;
+- void copy_cmdline(int argc, const char** argv);
++ void copy_cmdline(int argc, char** argv);
+ char** new_argv;
+ int new_argc;
+ };
+@@ -69,9 +69,7 @@ class Launcher {
+ AglShellPanelEdge panel_edge,
+ int width,
+ int height) = 0;
+- virtual int Loop(int argc,
+- const char** argv,
+- volatile sig_atomic_t& e_flag) = 0;
++ virtual int Loop(int argc, char** argv, volatile sig_atomic_t& e_flag) = 0;
+ };
+
+ class SharedBrowserProcessWebAppLauncher : public Launcher {
+@@ -82,7 +80,7 @@ class SharedBrowserProcessWebAppLauncher : public Launcher {
+ AglShellPanelEdge panel_edge,
+ int width,
+ int height) override;
+- int Loop(int argc, const char** argv, volatile sig_atomic_t& e_flag) override;
++ int Loop(int argc, char** argv, volatile sig_atomic_t& e_flag) override;
+ };
+
+ class SingleBrowserProcessWebAppLauncher : public Launcher {
+@@ -93,12 +91,12 @@ class SingleBrowserProcessWebAppLauncher : public Launcher {
+ AglShellPanelEdge panel_edge,
+ int width,
+ int height) override;
+- int Loop(int argc, const char** argv, volatile sig_atomic_t& e_flag) override;
++ int Loop(int argc, char** argv, volatile sig_atomic_t& e_flag) override;
+ };
+
+ class WebAppLauncherRuntime : public WebRuntime {
+ public:
+- int Run(int argc, const char** argv) override;
++ int Run(int argc, char** argv) override;
+
+ private:
+ bool Init(Args* args);
+@@ -127,17 +125,17 @@ class WebAppLauncherRuntime : public WebRuntime {
+
+ class SharedBrowserProcessRuntime : public WebRuntime {
+ public:
+- int Run(int argc, const char** argv) override;
++ int Run(int argc, char** argv) override;
+ };
+
+ class RenderProcessRuntime : public WebRuntime {
+ public:
+- int Run(int argc, const char** argv) override;
++ int Run(int argc, char** argv) override;
+ };
+
+ class WebRuntimeAGL : public WebRuntime {
+ public:
+- int Run(int argc, const char** argv) override;
++ int Run(int argc, char** argv) override;
+
+ private:
+ WebRuntime* runtime_;
+diff --git a/src/cef/cli/CMakeLists.txt b/src/cef/cli/CMakeLists.txt
+new file mode 100644
+index 0000000..1b5f04a
+--- /dev/null
++++ b/src/cef/cli/CMakeLists.txt
+@@ -0,0 +1,17 @@
++project(WebAppMgrCli VERSION 1.0.0 DESCRIPTION "Web Application Manager cli helper")
++
++set(CLI_EXE ${PROJECT_NAME})
++set(CLI_EXE_INCLUDE_DIRS
++ ${WAM_ROOT_SOURCE_DIR}/util
++)
++
++set(CLI_EXE_LIBS
++ WebAppMgrCore
++ WebAppMgrService
++ WebAppMgrService-grpc
++)
++
++add_executable(${CLI_EXE} wam_cli.cc)
++target_include_directories(${CLI_EXE} PUBLIC ${CLI_EXE_INCLUDE_DIRS})
++target_link_libraries(${CLI_EXE} PUBLIC ${CLI_EXE_LIBS})
++install(TARGETS ${CLI_EXE} DESTINATION ${CMAKE_INSTALL_BINDIR})
+diff --git a/src/cef/cli/wam_cli.cc b/src/cef/cli/wam_cli.cc
+new file mode 100644
+index 0000000..4775ce4
+--- /dev/null
++++ b/src/cef/cli/wam_cli.cc
+@@ -0,0 +1,171 @@
++// Copyright (c) 2018-2022 LG Electronics, Inc.
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++//
++// SPDX-License-Identifier: Apache-2.0
++
++#include <getopt.h>
++#include <optional>
++
++#include "web_app_manager_client_grpc.h"
++
++#include "log_manager.h"
++
++class Args {
++ public:
++ enum flags {
++ FLAG_NONE = 0,
++ FLAG_APP_TYPE = 1 << 0,
++ FLAG_ACTIVATE_APP = 1 << 1,
++ FLAG_HTTP_LINK = 1 << 2,
++ FLAG_APP_ID = 1 << 3,
++ FLAG_APP_DIR = 1 << 4,
++ };
++
++ static Args* Instance() {
++ static Args* args = new Args();
++ return args;
++ }
++ void PrintHelp(void) {
++ fprintf(stderr, "WAM: Web Application Manager\n");
++ fprintf(stderr,
++ "\t[--activate_app=appid] -- activate application. Interrnal "
++ "usage.\n\tNot needing for starting applications.\n");
++ fprintf(stderr,
++ "\t[--appid=appid] name of an application id.\n\tRequired if "
++ "starting a "
++ "web application.\n");
++ fprintf(
++ stderr,
++ "\t[--app-install-dir=/path/to/root_index] installation path for web "
++ "application.\n\tRequired if starting a web application.\n");
++ fprintf(stderr, "\t-h -- this help message \n");
++ exit(EXIT_FAILURE);
++ }
++
++ void ParseArgs(int argc, char** argv) {
++ int c;
++ int option_index;
++ opterr = 0;
++
++ CopyCmdLine(argc, argv);
++
++ struct option long_opts[] = {{"help", no_argument, 0, 'h'},
++ {"activate-app", required_argument, 0, 'x'},
++ {"appid", required_argument, 0, 'a'},
++ {"app-install-dir", required_argument, 0, 'd'},
++ {0, 0, 0, 0}};
++
++ while ((c = getopt_long(new_argc, new_argv, "ht:a:i:d:", long_opts,
++ &option_index)) != -1) {
++ switch (c) {
++ case 'h':
++ PrintHelp();
++ break;
++ case 'x':
++ activate_app_id_ = optarg;
++ break;
++ case 'a':
++ app_id_ = optarg;
++ break;
++ case 'd':
++ app_dir_ = optarg;
++ break;
++ default:
++ break;
++ }
++ }
++
++ if (optind < new_argc) {
++ // check for 'http://'
++ int p = optind;
++ while (p < new_argc) {
++ if (!strcmp(new_argv[p], "http://")) {
++ http_link_ = new_argv[p];
++ break;
++ }
++ p++;
++ }
++ }
++ }
++
++ void ClearCmdLine() {
++ for (int i = 0; i < new_argc; i++)
++ free(new_argv[i]);
++ free(new_argv);
++ }
++
++ std::string GetAppId() const {
++ return app_id_.has_value() ? *app_id_ : std::string();
++ }
++
++ std::string GetAppUri() const {
++ if (http_link_.has_value())
++ return *http_link_;
++ else if (app_dir_.has_value())
++ return *app_dir_;
++ return std::string();
++ }
++
++ bool HasActivateAppId() const { return activate_app_id_.has_value(); }
++ std::string GetActivateAppId() const {
++ return activate_app_id_.has_value() ? *activate_app_id_ : std::string();
++ }
++
++ private:
++ void CopyCmdLine(int argc, char** argv) {
++ new_argc = argc;
++ new_argv = static_cast<char**>(calloc(new_argc + 1, sizeof(*new_argv)));
++
++ for (int i = 0; i < new_argc; i++) {
++ size_t len = strlen(argv[i]) + 1;
++ new_argv[i] = static_cast<char*>(calloc(len, sizeof(char)));
++ memcpy(new_argv[i], argv[i], len);
++ }
++
++ new_argv[argc] = nullptr;
++ }
++ char** new_argv;
++ int new_argc;
++
++ std::optional<std::string> activate_app_id_;
++ std::optional<std::string> http_link_;
++ std::optional<std::string> app_id_;
++ std::optional<std::string> app_dir_;
++};
++
++WebAppManagerClientGRPC* GetGrpcClient() {
++ static std::unique_ptr<WebAppManagerClientGRPC> grpc_client;
++ if (!grpc_client) {
++ grpc_client = std::make_unique<WebAppManagerClientGRPC>();
++ }
++ return grpc_client.get();
++}
++
++int main(int argc, char** argv) {
++ auto* args = Args::Instance();
++ args->ParseArgs(argc, argv);
++
++ // TODO: handle completed grpc calls
++ // and get the correct ok() result
++ if (args->HasActivateAppId()) {
++ GetGrpcClient()->Activate(args->GetActivateAppId());
++ } else {
++ WebAppManagerClientGRPC::LaunchParams params;
++ params.app_id = args->GetAppId();
++ params.uri = args->GetAppUri();
++ GetGrpcClient()->Launch(params);
++ }
++
++ return 0;
++}
+diff --git a/src/cef/device_info_cef.cc b/src/cef/device_info_cef.cc
+new file mode 100644
+index 0000000..f5fd681
+--- /dev/null
++++ b/src/cef/device_info_cef.cc
+@@ -0,0 +1,104 @@
++#include "device_info_cef.h"
++
++#include <string>
++
++#include <glib.h>
++#include <json/value.h>
++
++#include "log_manager.h"
++#include "utils.h"
++
++DeviceInfoCEF::DeviceInfoCEF() = default;
++
++void DeviceInfoCEF::Initialize() {
++ const std::string& json_string =
++ util::ReadFile("/var/luna/preferences/localeInfo");
++ if (json_string.empty()) {
++ return;
++ }
++
++ Json::Value locale_json = util::StringToJson(json_string);
++ if (!locale_json.isObject() || locale_json.empty() ||
++ !locale_json["localeInfo"].isObject() ||
++ !locale_json["localeInfo"]["locales"].isObject() ||
++ !locale_json["localeInfo"]["locales"]["UI"].isString() ||
++ !locale_json["country"].isString() ||
++ !locale_json["smartServiceCountryCode3"].isString()) {
++ LOG_ERROR(MSGID_LOCALEINFO_READ_FAIL, 1,
++ PMLOGKS("CONTENT", json_string.c_str()), "");
++ return;
++ }
++
++ Json::Value locale_info = locale_json["localeInfo"];
++
++ std::string language(locale_info["locales"]["UI"].asString());
++ std::string localcountry(locale_json["country"].asString());
++ std::string smartservicecountry(
++ locale_json["smartServiceCountryCode3"].asString());
++
++ SetSystemLanguage(language.c_str());
++ SetDeviceInfo("LocalCountry", localcountry.c_str());
++ SetDeviceInfo("SmartServiceCountry", smartservicecountry.c_str());
++}
++
++void DeviceInfoCEF::InitDisplayInfo() {
++ // Display information
++ // --------------------------------------------------------
++ float screen_density_ = 1.0f;
++ int hardware_screen_width = 0;
++ int hardware_screen_height = 0;
++
++ std::string hardware_screen_width_str;
++ std::string hardware_screen_height_str;
++ if (GetDeviceInfo("HardwareScreenWidth", hardware_screen_width_str) &&
++ GetDeviceInfo("HardwareScreenHeight", hardware_screen_height_str)) {
++ hardware_screen_width =
++ util::StrToIntWithDefault(hardware_screen_width_str, 0);
++ hardware_screen_height =
++ util::StrToIntWithDefault(hardware_screen_height_str, 0);
++ } else {
++ GetDisplayWidth(hardware_screen_width);
++ GetDisplayHeight(hardware_screen_height);
++ }
++
++ screen_width_ = static_cast<int>(hardware_screen_width / screen_density_);
++ screen_height_ = static_cast<int>(hardware_screen_height / screen_density_);
++}
++
++void DeviceInfoCEF::InitPlatformInfo() {
++ // normally like this info
++ /*
++ "modelName": "WEBOS1",
++ "platformVersion": "00.00.00",
++ "platformVersionDot": 00,
++ "platformVersionMajor_pos": 00,
++ "platformVersionMinor": 00,
++ */
++
++ std::string value;
++ if (GetDeviceInfo("ModelName", value))
++ model_name_ = value;
++ if (GetDeviceInfo("FirmwareVersion", value))
++ platform_version_ = value;
++
++ size_t major_pos = 0, minor_pos = 0;
++ major_pos = platform_version_.find_first_of('.');
++ if (major_pos != std::string::npos &&
++ major_pos <= platform_version_.size() - 1)
++ minor_pos = platform_version_.find_first_of('.', major_pos + 1);
++ if (major_pos == std::string::npos || minor_pos == std::string::npos) {
++ version_major_ = version_minor_ = version_dot_ = -1;
++ } else {
++ version_major_ =
++ util::StrToIntWithDefault(platform_version_.substr(0, major_pos), 0);
++ version_minor_ = util::StrToIntWithDefault(
++ platform_version_.substr(major_pos + 1, minor_pos), 0);
++ version_dot_ =
++ util::StrToIntWithDefault(platform_version_.substr(minor_pos + 1), 0);
++ }
++}
++
++void DeviceInfoCEF::GatherInfo() {
++ InitDisplayInfo();
++ InitPlatformInfo();
++}
+diff --git a/src/cef/device_info_cef.h b/src/cef/device_info_cef.h
+new file mode 100644
+index 0000000..e2b3712
+--- /dev/null
++++ b/src/cef/device_info_cef.h
+@@ -0,0 +1,38 @@
++#ifndef CEF_DEVICE_INFO_CEF_H_
++#define CEF_DEVICE_INFO_CEF_H_
++
++#include <string>
++
++#include "device_info.h"
++
++class DeviceInfoCEF : public DeviceInfo {
++ public:
++ DeviceInfoCEF();
++
++ void Initialize() override;
++
++ private:
++ int screen_width_ = 0;
++ int screen_height_ = 0;
++
++ float screen_density_ = 1.0f;
++
++ std::string model_name_ = "webOS.Open.CEF";
++ std::string platform_version_ = "00.00.00";
++
++ // platform versions are <major>.<minor>.<dot>
++ int version_major_ = 0;
++ int version_minor_ = 0;
++ int version_dot_ = 0;
++
++ bool support_3d_ = false;
++ std::string ota_id_;
++ std::string hardware_version_ = "0x00000001";
++ std::string firmware_version_ = "00.00.01";
++
++ void InitDisplayInfo();
++ void InitPlatformInfo();
++ void GatherInfo();
++};
++
++#endif // CEF_DEVICE_INFO_IMPL_H_
+diff --git a/src/cef/handlers/wam_cef_browser_handler.cc b/src/cef/handlers/wam_cef_browser_handler.cc
+new file mode 100644
+index 0000000..0708363
+--- /dev/null
++++ b/src/cef/handlers/wam_cef_browser_handler.cc
+@@ -0,0 +1,31 @@
++#include "wam_cef_browser_handler.h"
++
++#include "log_manager.h"
++#include "log_msg_id.h"
++#include "platform_module_factory_cef.h"
++#include "wam_cef_client.h"
++#include "web_app_manager_service_grpc.h"
++
++WamCefBrowserHandler::WamCefBrowserHandler() :
++ client_(new WamCefClient()) {
++ WebAppManager::Instance()->SetPlatformModules(
++ std::make_unique<PlatformModuleFactoryCEF>());
++}
++
++WamCefBrowserHandler::~WamCefBrowserHandler() {}
++
++CefRefPtr<CefBrowserProcessHandler> WamCefBrowserHandler::GetBrowserProcessHandler() {
++ return this;
++}
++
++CefRefPtr<CefClient> WamCefBrowserHandler::GetDefaultClient() {
++ return client_;
++}
++
++void WamCefBrowserHandler::OnContextInitialized() {
++ if (!WebAppManagerServiceGRPC::Instance()->InitializeAsHostService()) {
++ LOG_ERROR(MSGID_ERROR_CANNOT_LOCK_SERVICE, 0,
++ "Cannot lock WAM GRPC service IPC");
++ }
++ WebAppManagerServiceGRPC::Instance()->StartService();
++}
+diff --git a/src/cef/handlers/wam_cef_browser_handler.h b/src/cef/handlers/wam_cef_browser_handler.h
+new file mode 100644
+index 0000000..58c7f20
+--- /dev/null
++++ b/src/cef/handlers/wam_cef_browser_handler.h
+@@ -0,0 +1,23 @@
++#ifndef CEF_HANDLERS_WAM_CEF_BROWSER_HANDLER_H
++#define CEF_HANDLERS_WAM_CEF_BROWSER_HANDLER_H
++
++#include "include/cef_app.h"
++#include "include/cef_browser_process_handler.h"
++
++#include "wam_cef_client.h"
++
++class WamCefBrowserHandler : public CefApp, public CefBrowserProcessHandler {
++ public:
++ WamCefBrowserHandler();
++ virtual ~WamCefBrowserHandler();
++
++ CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override;
++ CefRefPtr<CefClient> GetDefaultClient() override;
++ void OnContextInitialized() override;
++
++ private:
++ IMPLEMENT_REFCOUNTING(WamCefBrowserHandler);
++ CefRefPtr<WamCefClient> client_;
++};
++
++#endif // CEF_HANDLERS_WAM_CEF_BROWSER_HANDLER_H
+diff --git a/src/cef/handlers/wam_cef_client.cc b/src/cef/handlers/wam_cef_client.cc
+new file mode 100644
+index 0000000..5ba6201
+--- /dev/null
++++ b/src/cef/handlers/wam_cef_client.cc
+@@ -0,0 +1,39 @@
++#include "wam_cef_client.h"
++
++#include "include/wrapper/cef_helpers.h"
++
++WamCefClient* g_instance = nullptr;
++
++WamCefClient::WamCefClient() {
++ DCHECK(!g_instance);
++ g_instance = this;
++}
++
++// static
++WamCefClient* WamCefClient::GetInstance() {
++ return g_instance;
++}
++
++bool WamCefClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefProcessId source_process,
++ CefRefPtr<CefProcessMessage> message) {
++ std::string message_name = message->GetName();
++ CefRefPtr<CefListValue> args = message->GetArgumentList();
++ if (message_name == "start") {
++ if (args->GetSize() != 1) {
++ return false;
++ }
++ std::string app_id = args->GetString(0);
++ applauncher_.Start(app_id);
++ return true;
++ } else if (message_name == "get_applications") {
++ if (args->GetSize() != 1) {
++ return false;
++ }
++ bool only_graphical = args->GetBool(0);
++ applauncher_.GetApplications(browser, only_graphical);
++ return true;
++ }
++ return false;
++}
+diff --git a/src/cef/handlers/wam_cef_client.h b/src/cef/handlers/wam_cef_client.h
+new file mode 100644
+index 0000000..3189c42
+--- /dev/null
++++ b/src/cef/handlers/wam_cef_client.h
+@@ -0,0 +1,31 @@
++#ifndef CEF_HANDLERS_WAM_CEF_CLIENT_H
++#define CEF_HANDLERS_WAM_CEF_CLIENT_H
++
++#include "include/cef_client.h"
++
++#include "applauncher_client_grpc.h"
++
++class WamCefClient : public CefClient,
++ public CefLifeSpanHandler {
++ public:
++ WamCefClient();
++
++ static WamCefClient* GetInstance();
++
++ CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
++ return this;
++ }
++
++ bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefProcessId source_process,
++ CefRefPtr<CefProcessMessage> message) override;
++
++ private:
++ AppLauncherClientGRPC applauncher_;
++
++ IMPLEMENT_REFCOUNTING(WamCefClient);
++ DISALLOW_COPY_AND_ASSIGN(WamCefClient);
++};
++
++#endif // CEF_HANDLERS_WAM_CEF_CLIENT_H
+diff --git a/src/cef/handlers/wam_cef_render_handler.cc b/src/cef/handlers/wam_cef_render_handler.cc
+new file mode 100644
+index 0000000..48c881a
+--- /dev/null
++++ b/src/cef/handlers/wam_cef_render_handler.cc
+@@ -0,0 +1,135 @@
++#include "wam_cef_render_handler.h"
++
++#include "include/cef_parser.h"
++#include "include/cef_process_message.h"
++
++WamCefRenderHandler::WamCefRenderHandler() {}
++
++void WamCefRenderHandler::OnContextCreated(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefRefPtr<CefV8Context> context) {
++
++ CefRefPtr<CefV8Value> app_service = CefV8Value::CreateObject(nullptr, nullptr);
++
++ CefRefPtr<CefV8Value> start = CefV8Value::CreateFunction("start", this);
++ app_service->SetValue("start", start, V8_PROPERTY_ATTRIBUTE_NONE);
++
++ CefRefPtr<CefV8Value> get_applications = CefV8Value::CreateFunction("getApplications", this);
++ app_service->SetValue("getApplications", get_applications, V8_PROPERTY_ATTRIBUTE_NONE);
++
++ CefRefPtr<CefV8Value> global = context->GetGlobal(); // window object
++ CefRefPtr<CefV8Value> navigator = global->GetValue("navigator");
++
++ navigator->SetValue("appService", app_service, V8_PROPERTY_ATTRIBUTE_NONE);
++}
++
++void WamCefRenderHandler::OnContextReleased(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefRefPtr<CefV8Context> context) {
++ if (callback_map_.empty()) {
++ return;
++ }
++
++ CallbackMap::iterator it = callback_map_.begin();
++ for (; it != callback_map_.end();) {
++ if (it->second.first->IsSame(context)) {
++ callback_map_.erase(it++);
++ } else {
++ ++it;
++ }
++ }
++}
++
++void WamCefRenderHandler::Start(const std::string &app_id) {
++ CefRefPtr<CefProcessMessage> message = CefProcessMessage::Create("start");
++ CefRefPtr<CefListValue> args = message->GetArgumentList();
++ args->SetString(0, app_id);
++ auto context = CefV8Context::GetCurrentContext();
++ context->GetFrame()->SendProcessMessage(PID_BROWSER, message);
++}
++
++void WamCefRenderHandler::GetApplications(bool only_graphical, CefRefPtr<CefV8Value> callback) {
++ std::string message_name = "get_applications";
++
++ // store the callback until we receive the browser's response
++ CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
++ int browser_id = context->GetBrowser()->GetIdentifier();
++ callback_map_.insert(
++ std::make_pair(std::make_pair(message_name, browser_id),
++ std::make_pair(context, callback)));
++
++ CefRefPtr<CefProcessMessage> message = CefProcessMessage::Create(message_name);
++ CefRefPtr<CefListValue> args = message->GetArgumentList();
++ args->SetBool(0, only_graphical);
++ context->GetFrame()->SendProcessMessage(PID_BROWSER, message);
++}
++
++bool WamCefRenderHandler::Execute(const CefString& name,
++ CefRefPtr<CefV8Value> object,
++ const CefV8ValueList& arguments,
++ CefRefPtr<CefV8Value>& retval,
++ CefString& exception) {
++ if (name == "start") {
++ if (arguments.size() != 1 || !arguments[0]->IsString()) {
++ return false;
++ }
++ std::string app_id = arguments[0]->GetStringValue();
++ Start(app_id);
++ return true;
++ } else if (name == "getApplications") {
++ if (arguments.size() != 2 ||
++ !arguments[0]->IsBool() ||
++ !arguments[1]->IsFunction()) {
++ return false;
++ }
++ GetApplications(arguments[0]->GetBoolValue(), arguments[1]);
++ return true;
++ }
++
++ return false;
++}
++
++bool WamCefRenderHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefProcessId source_process,
++ CefRefPtr<CefProcessMessage> message) {
++ std::string message_name = message->GetName();
++ CefRefPtr<CefListValue> args = message->GetArgumentList();
++ if (message_name == "get_applications") {
++ CallbackMap::const_iterator it = callback_map_.find(
++ std::make_pair(message_name,
++ browser->GetIdentifier()));
++ if (it == callback_map_.end()) {
++ return false;
++ }
++
++ CefRefPtr<CefV8Context> context = it->second.first;
++ CefRefPtr<CefV8Value> callback = it->second.second;
++
++ context->Enter();
++
++ CefRefPtr<CefValue> apps_list_value = CefParseJSON(args->GetString(0), JSON_PARSER_RFC);
++
++ CefRefPtr<CefListValue> list = apps_list_value->GetList();
++ int size = list->GetSize();
++ CefRefPtr<CefV8Value> apps_list = CefV8Value::CreateArray(size);
++ for (int i = 0; i < size; i++) {
++ CefRefPtr<CefDictionaryValue> app_info_dict = list->GetDictionary(i);
++ CefRefPtr<CefV8Value> dict = CefV8Value::CreateObject(nullptr, nullptr);
++ dict->SetValue("id", CefV8Value::CreateString(app_info_dict->GetString("id")), V8_PROPERTY_ATTRIBUTE_NONE);
++ dict->SetValue("name", CefV8Value::CreateString(app_info_dict->GetString("name")), V8_PROPERTY_ATTRIBUTE_NONE);
++ dict->SetValue("icon", CefV8Value::CreateString(app_info_dict->GetString("icon")), V8_PROPERTY_ATTRIBUTE_NONE);
++ apps_list->SetValue(i, dict);
++ }
++
++ CefV8ValueList arguments;
++ arguments.push_back(apps_list);
++ callback->ExecuteFunctionWithContext(context, nullptr, arguments);
++
++ context->Exit();
++
++ return true;
++ }
++
++ return false;
++}
+diff --git a/src/cef/handlers/wam_cef_render_handler.h b/src/cef/handlers/wam_cef_render_handler.h
+new file mode 100644
+index 0000000..4a05ed6
+--- /dev/null
++++ b/src/cef/handlers/wam_cef_render_handler.h
+@@ -0,0 +1,54 @@
++#ifndef CEF_HANDLERS_WAM_CEF_RENDER_HANDLER_H
++#define CEF_HANDLERS_WAM_CEF_RENDER_HANDLER_H
++
++#include <map>
++#include <string>
++
++#include "include/cef_app.h"
++#include "include/cef_render_process_handler.h"
++#include "include/cef_v8.h"
++
++class WamCefRenderHandler : public CefApp,
++ public CefRenderProcessHandler,
++ public CefV8Handler {
++ public:
++ typedef std::map<std::pair<std::string, int>,
++ std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value>>> CallbackMap;
++
++ WamCefRenderHandler();
++
++ CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override {
++ return this;
++ }
++
++ void OnContextCreated(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefRefPtr<CefV8Context> context) override;
++
++
++ void OnContextReleased(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefRefPtr<CefV8Context> context) override;
++
++ bool Execute(const CefString& name,
++ CefRefPtr<CefV8Value> object,
++ const CefV8ValueList& arguments,
++ CefRefPtr<CefV8Value>& retval,
++ CefString& exception) override;
++
++ bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
++ CefRefPtr<CefFrame> frame,
++ CefProcessId source_process,
++ CefRefPtr<CefProcessMessage> message) override;
++
++ void Start(const std::string &app_id);
++ void GetApplications(bool only_graphical, CefRefPtr<CefV8Value> callback);
++
++ private:
++ IMPLEMENT_REFCOUNTING(WamCefRenderHandler);
++ DISALLOW_COPY_AND_ASSIGN(WamCefRenderHandler);
++
++ CallbackMap callback_map_;
++};
++
++#endif // CEF_HANDLERS_WAM_CEF_RENDER_HANDLER_H
+diff --git a/src/cef/platform_module_factory_cef.cc b/src/cef/platform_module_factory_cef.cc
+new file mode 100644
+index 0000000..e225b9d
+--- /dev/null
++++ b/src/cef/platform_module_factory_cef.cc
+@@ -0,0 +1,31 @@
++#include "platform_module_factory_cef.h"
++
++#include "device_info_cef.h"
++#include "service_sender.h"
++#include "web_app_manager_config.h"
++#include "web_process_manager.h"
++
++PlatformModuleFactoryCEF::PlatformModuleFactoryCEF() {
++ PrepareRenderingContext();
++}
++
++std::unique_ptr<ServiceSender>
++PlatformModuleFactoryCEF::CreateServiceSender() {
++ return nullptr;
++}
++
++std::unique_ptr<WebProcessManager>
++PlatformModuleFactoryCEF::CreateWebProcessManager() {
++ return nullptr;
++}
++
++std::unique_ptr<DeviceInfo> PlatformModuleFactoryCEF::CreateDeviceInfo() {
++ return std::make_unique<DeviceInfoCEF>();
++}
++
++std::unique_ptr<WebAppManagerConfig>
++PlatformModuleFactoryCEF::CreateWebAppManagerConfig() {
++ return std::unique_ptr<WebAppManagerConfig>(new WebAppManagerConfig());
++}
++
++void PlatformModuleFactoryCEF::PrepareRenderingContext() {}
+diff --git a/src/cef/platform_module_factory_cef.h b/src/cef/platform_module_factory_cef.h
+new file mode 100644
+index 0000000..5015dda
+--- /dev/null
++++ b/src/cef/platform_module_factory_cef.h
+@@ -0,0 +1,27 @@
++#ifndef CEF_PLATFORM_MODULE_FACTORY_CEF_H_
++#define CEF_PLATFORM_MODULE_FACTORY_CEF_H_
++
++#include <memory>
++
++#include "platform_module_factory.h"
++
++class ServiceSender;
++class WebProcessManager;
++class DeviceInfo;
++class WebAppManagerConfig;
++
++class PlatformModuleFactoryCEF : public PlatformModuleFactory {
++ public:
++ PlatformModuleFactoryCEF();
++
++ protected:
++ std::unique_ptr<ServiceSender> CreateServiceSender() override;
++ std::unique_ptr<WebProcessManager> CreateWebProcessManager() override;
++ std::unique_ptr<DeviceInfo> CreateDeviceInfo() override;
++ std::unique_ptr<WebAppManagerConfig> CreateWebAppManagerConfig() override;
++
++ private:
++ void PrepareRenderingContext();
++};
++
++#endif // CEF_PLATFORM_MODULE_FACTORY_CEF_H_
+diff --git a/src/cef/plugin/web_app_cef.cc b/src/cef/plugin/web_app_cef.cc
+new file mode 100644
+index 0000000..f57212b
+--- /dev/null
++++ b/src/cef/plugin/web_app_cef.cc
+@@ -0,0 +1,172 @@
++#include "web_app_cef.h"
++
++#include "include/base/cef_callback.h"
++#include "include/views/cef_display.h"
++#include "include/wrapper/cef_closure_task.h"
++
++#include "web_page_cef.h"
++
++WebAppCEF::WebAppCEF(std::shared_ptr<ApplicationDescription> app_desc) {
++ SetAppDescription(app_desc);
++}
++
++WebAppCEF::~WebAppCEF() {}
++
++void WebAppCEF::Init(int width,
++ int height,
++ AglShellSurfaceType surface_type,
++ AglShellPanelEdge panel_type) {
++ ApplicationDescription* app_desc = GetAppDescription();
++ std::string app_id = app_desc->Id();
++
++ if (!IsReady()) {
++ return;
++ }
++
++ surface_type_ = surface_type;
++ panel_type_ = panel_type;
++
++ switch (surface_type_) {
++ case AglShellSurfaceType::kBackground:
++ window_->AglSetBackGroundApp();
++ window_->AglSetAppReady();
++ break;
++ case AglShellSurfaceType::kPanel:
++ window_->AglSetPanelApp(static_cast<int>(panel_type_));
++ break;
++ case AglShellSurfaceType::kNone:
++ surface_type_ = AglShellSurfaceType::kNone;
++ CefPostDelayedTask(
++ TID_UI,
++ base::BindOnce(&WebAppCEF::DelayedActivate, this),
++ 500);
++ break;
++ }
++}
++
++void WebAppCEF::TryInitialize() {
++ ApplicationDescription* app_desc = GetAppDescription();
++
++ if (!IsReady()) {
++ CefPostDelayedTask(
++ TID_UI,
++ base::BindOnce(&WebAppCEF::TryInitialize, this),
++ 500);
++ return;
++ // TODO: add a maximum number of retries
++ }
++
++ Init(app_desc->WidthOverride(),
++ app_desc->HeightOverride(),
++ app_desc->SurfaceType(),
++ app_desc->PanelType());
++}
++
++void WebAppCEF::Attach(WebPageBase* web_page) {
++ WebAppBase::Attach(web_page);
++
++ WebPageCEF* web_page_cef = static_cast<WebPageCEF*>(Page());
++ if (!web_page_cef) {
++ // TODO: handle error
++ return;
++ }
++ web_page_cef->SetWebApp(this);
++}
++
++void WebAppCEF::OnWindowCreated(CefRefPtr<CefWindow> window) {
++ if (!browser_view_) {
++ return;
++ }
++
++ window_ = window;
++ window_->AddChildView(browser_view_);
++ window_->Show();
++
++ TryInitialize();
++}
++
++CefRect WebAppCEF::GetDisplayBounds() const {
++ CefRefPtr<CefDisplay> display = CefDisplay::GetPrimaryDisplay();
++ CefRect display_bounds;
++ if (display) {
++ display_bounds = display->GetBounds();
++ }
++ return display_bounds;
++}
++
++CefSize WebAppCEF::GetPreferredSize(CefRefPtr<CefView> view) {
++ return CefSize(width_override_, height_override_);
++}
++
++CefRect WebAppCEF::GetInitialBounds(CefRefPtr<CefWindow> window) {
++ return CefRect(0, 0, width_override_, height_override_);
++}
++
++void WebAppCEF::SendAglReady() {
++ // Empty because it's called on Init()
++}
++
++void WebAppCEF::SetAglAppId(const char* app_id) {
++ if (!window_) {
++ return;
++ }
++ window_->SetTitle(app_id);
++ window_->AglSetAppId(app_id);
++}
++
++void WebAppCEF::SendAglActivate(const char* app_id) {
++ if (!window_) {
++ return;
++ }
++ window_->AglActivateApp(app_id);
++}
++
++void WebAppCEF::Resize(int width, int height) {
++ if (!window_) {
++ return;
++ }
++ window_->SetSize(CefSize(width, height));
++}
++
++bool WebAppCEF::IsReady() const {
++ return window_ != nullptr;
++}
++
++void WebAppCEF::Hide(bool forced_hide) {
++ if (!window_) {
++ return;
++ }
++ window_->Hide();
++}
++
++bool WebAppCEF::HideWindow() {
++ if (!window_) {
++ return false;
++ }
++ window_->Hide();
++ return true;
++}
++
++void WebAppCEF::Raise() {
++ if (!window_) {
++ return;
++ }
++ window_->BringToTop();
++}
++
++void WebAppCEF::DelayedActivate() {
++ ApplicationDescription* app_desc = GetAppDescription();
++ SendAglActivate(app_desc->Id().c_str());
++}
++
++void WebAppCEF::Relaunch(const std::string& args,
++ const std::string& launching_app_id) {
++ if (!window_) {
++ return;
++ }
++
++ ApplicationDescription* app_desc = GetAppDescription();
++ std::string app_id = app_desc->Id();
++
++ SendAglActivate(app_id.c_str());
++}
+diff --git a/src/cef/plugin/web_app_cef.h b/src/cef/plugin/web_app_cef.h
+new file mode 100644
+index 0000000..57145cf
+--- /dev/null
++++ b/src/cef/plugin/web_app_cef.h
+@@ -0,0 +1,95 @@
++#ifndef CEF_PLUGIN_WEB_APP_CEF_H
++#define CEF_PLUGIN_WEB_APP_CEF_H
++
++#include <cstdint>
++#include <memory>
++
++#include "web_app_base.h"
++
++#include "include/views/cef_window.h"
++#include "include/views/cef_browser_view.h"
++#include "include/views/cef_window_delegate.h"
++
++#include "application_description.h"
++
++class WebAppCEF : public WebAppBase, public CefWindowDelegate {
++ public:
++ WebAppCEF(std::shared_ptr<ApplicationDescription> app_desc);
++
++ ~WebAppCEF();
++
++ virtual void Init(int width,
++ int height,
++ AglShellSurfaceType surface_type,
++ AglShellPanelEdge panel_type) override;
++
++ void SuspendAppRendering() override {}
++ void ResumeAppRendering() override {}
++ bool IsFocused() const override { return false; }
++ void Resize(int width, int height) override;
++ bool IsActivated() const override { return false; }
++ bool IsMinimized() override { return false; }
++ bool IsNormal() override { return true; }
++ void OnStageActivated() override {}
++ void OnStageDeactivated() override {}
++ void DoAttach() override {}
++ void ConfigureWindow(const std::string& type) override {}
++ void SetWindowProperty(const std::string& name,
++ const std::string& value) override {}
++ void PlatformBack() override {}
++ void SetCursor(const std::string& cursor_arg,
++ int hotspot_x,
++ int hotspot_y) override {}
++ void SetInputRegion(const Json::Value& json_doc) override {}
++ void SetKeyMask(const Json::Value& json_doc) override {}
++ void Hide(bool forced_hide = false) override;
++ void Focus() override {}
++ void Unfocus() override {}
++ void SetOpacity(float opacity) override {}
++ void Raise() override;
++ void GoBackground() override {}
++ void DoPendingRelaunch() override {}
++ void DeleteSurfaceGroup() override {}
++ void DoClose() override {}
++ void SetUseVirtualKeyboard(const bool enable) override {}
++ bool HideWindow() override;
++
++ void SendAglReady() override;
++ void SendAglActivate(const char* app_id) override;
++ void SetAglAppId(const char* app_id) override;
++
++ void Attach(WebPageBase* web_page) override;
++
++ void Relaunch(const std::string& args,
++ const std::string& launching_app_id) override;
++
++ void SetBrowserView(CefRefPtr<CefBrowserView> browser_view) {
++ browser_view_ = browser_view;
++ }
++
++ virtual bool IsReady() const;
++
++ // CEF overrides
++ void OnWindowCreated(CefRefPtr<CefWindow> window) override;
++ CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
++ CefRect GetInitialBounds(CefRefPtr<CefWindow> window) override;
++ bool IsFrameless(CefRefPtr<CefWindow> window) override { return true; }
++
++ protected:
++ void TryInitialize();
++ void DelayedActivate();
++
++ CefRect GetDisplayBounds() const;
++
++ CefRefPtr<CefBrowserView> browser_view_;
++ CefRefPtr<CefWindow> window_;
++ AglShellSurfaceType surface_type_;
++ AglShellPanelEdge panel_type_;
++ uint32_t width_override_ = 0;
++ uint32_t height_override_ = 0;
++
++ private:
++ IMPLEMENT_REFCOUNTING(WebAppCEF);
++};
++
++#endif // CEF_PLUGIN_WEB_APP_CEF_H
+diff --git a/src/cef/plugin/web_page_cef.cc b/src/cef/plugin/web_page_cef.cc
+new file mode 100644
+index 0000000..32c6e89
+--- /dev/null
++++ b/src/cef/plugin/web_page_cef.cc
+@@ -0,0 +1,48 @@
++#include "web_page_cef.h"
++#include "application_description.h"
++
++#include "include/views/cef_window.h"
++
++#include "wam_cef_client.h"
++
++WebPageCEF::WebPageCEF(std::shared_ptr<ApplicationDescription> app_desc, const std::string& url)
++ : url_{url} {
++ SetApplicationDescription(app_desc);
++}
++
++WebPageCEF::~WebPageCEF() {}
++
++void WebPageCEF::LoadUrl(const std::string& url) {
++ CefBrowserSettings browser_settings;
++ browser_view_ = CefBrowserView::CreateBrowserView(
++ WamCefClient::GetInstance(), url, browser_settings, nullptr, nullptr, this);
++
++ web_app_->SetBrowserView(browser_view_);
++
++ ApplicationDescription* app_desc = GetAppDescription();
++ CefWindow::CreateTopLevelWindowWithId(web_app_, app_desc->Id());
++}
++
++void WebPageCEF::LoadDefaultUrl() {
++ LoadUrl(url_);
++}
++
++bool WebPageCEF::HasBeenShown() const {
++ if (!web_app_) {
++ return false;
++ }
++
++ return web_app_->IsReady();
++}
++
++
++void WebPageCEF::EvaluateJavaScript(const std::string& jsCode) {
++ /*if (!browser_view_) {
++ return;
++ }
++ CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
++ if (!browser) {
++ return;
++ }
++ browser->GetMainFrame()->ExecuteJavaScript(jsCode, url_, 0);*/
++}
+diff --git a/src/cef/plugin/web_page_cef.h b/src/cef/plugin/web_page_cef.h
+new file mode 100644
+index 0000000..f62c223
+--- /dev/null
++++ b/src/cef/plugin/web_page_cef.h
+@@ -0,0 +1,69 @@
++#ifndef CEF_PLUGIN_WEB_PAGE_CEF_H
++#define CEF_PLUGIN_WEB_PAGE_CEF_H
++
++#include <memory>
++
++#include "web_page_base.h"
++#include "web_app_cef.h"
++
++#include "include/views/cef_browser_view_delegate.h"
++
++class WebPageCEF : public WebPageBase,
++ public CefBrowserViewDelegate {
++ public:
++ WebPageCEF(std::shared_ptr<ApplicationDescription> app_desc, const std::string& url);
++ ~WebPageCEF() override;
++
++ void Init() override {}
++ void* GetWebContents() override { return nullptr; }
++
++ wam::Url Url() const override { return wam::Url(""); }
++ std::string FailedUrl() const override { return ""; }
++ void LoadUrl(const std::string& url) override;
++ int Progress() const override { return 0; }
++ bool HasBeenShown() const override;
++ void SetPageProperties() override {}
++ void SetPreferredLanguages(const std::string& language) override {}
++ void SetDefaultFont(const std::string& font) override {}
++ void ReloadDefaultPage() override {}
++ void Reload() override {}
++ void SetVisibilityState(WebPageVisibilityState visibility_state) override {}
++ void SetFocus(bool focus) override {}
++ std::string Title() override { return ""; }
++ bool CanGoBack() override { return false; }
++ void CloseVkb() override {}
++ void HandleDeviceInfoChanged(const std::string& device_info) override {}
++ void EvaluateJavaScript(const std::string& jsCode) override;
++ void EvaluateJavaScriptInAllFrames(const std::string& js_code,
++ const char* method = {}) override {}
++ uint32_t GetWebProcessProxyID() override { return 0; }
++ uint32_t GetWebProcessPID() const override { return 0; }
++ void CreatePalmSystem(WebAppBase* app) override {}
++
++ void SuspendWebPageAll() override {}
++ void ResumeWebPageAll() override {}
++ void SuspendWebPageMedia() override {}
++ void ResumeWebPageMedia() override {}
++ void ResumeWebPagePaintingAndJSExecution() override {}
++ void ForwardEvent(void* event) override {}
++
++ void SuspendWebPagePaintingAndJSExecution() override {}
++
++ void SetWebApp(CefRefPtr<WebAppCEF> web_app) { web_app_ = web_app; }
++
++ protected:
++ void LoadDefaultUrl() override;
++ void AddUserScript(const std::string& script) override {}
++ void AddUserScriptUrl(const wam::Url& url) override {}
++ void LoadErrorPage(int error_code) override {}
++ void RecreateWebView() override {}
++
++ private:
++ IMPLEMENT_REFCOUNTING(WebPageCEF);
++
++ CefRefPtr<CefBrowserView> browser_view_;
++ CefRefPtr<WebAppCEF> web_app_;
++ std::string url_;
++};
++
++#endif // CEF_PLUGIN_WEB_PAGE_CEF_H
+diff --git a/src/cef/service/CMakeLists.txt b/src/cef/service/CMakeLists.txt
+new file mode 100644
+index 0000000..763b527
+--- /dev/null
++++ b/src/cef/service/CMakeLists.txt
+@@ -0,0 +1,64 @@
++project(WebAppMgrService VERSION 1.0.0 DESCRIPTION "Web Application Manager cli helper")
++
++find_package(gRPC REQUIRED)
++find_program(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)
++find_package(Protobuf REQUIRED)
++find_package(Threads)
++
++set(WAM_GRPC_LIB_NAME ${PROJECT_NAME}-grpc)
++set(APPLAUNCHER_LIB_NAME WamAppLauncher-grpc)
++
++set(WAM_GRPC_LIBS
++ protobuf::libprotobuf
++ gRPC::grpc
++ gRPC::grpc++
++ gRPC::grpc++_reflection
++)
++set(WAM_SERVICE_LIBS
++ ${WAM_GRPC_LIBS}
++ ${WAM_GRPC_LIB_NAME}
++ ${APPLAUNCHER_LIB_NAME}
++ libcef_lib
++ libcef_dll_wrapper
++)
++set(WAM_GRPC_INCLUDE_DIRS
++ ${CMAKE_CURRENT_SOURCE_DIR}
++ ${CMAKE_CURRENT_BINARY_DIR}
++)
++set(WAM_SERVICE_INCLUDE_DIRS
++ ${CEF_INCLUDE_PATH}
++ ${WAM_ROOT_SOURCE_DIR}/core
++ ${WAM_ROOT_SOURCE_DIR}/util
++)
++set(SOURCES
++ applauncher_client_grpc.cc
++ web_app_manager_client_grpc.cc
++ web_app_manager_service_grpc.cc
++)
++set(HEADERS
++ applauncher_client_grpc.h
++ web_app_manager_client_grpc.h
++ web_app_manager_service_grpc.h
++)
++
++
++macro(add_wam_grpc_lib target proto)
++ add_library(${target} SHARED ${proto})
++ target_include_directories(${target} PUBLIC ${WAM_GRPC_INCLUDE_DIRS})
++ target_link_libraries(${target} PUBLIC ${WAM_GRPC_LIBS})
++ set_target_properties(${target} PROPERTIES VERSION 1.0.0 SOVERSION 1.0)
++ protobuf_generate(TARGET ${target} LANGUAGE cpp APPEND_PATH)
++ protobuf_generate(TARGET ${target} LANGUAGE grpc APPEND_PATH GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${GRPC_CPP_PLUGIN_EXECUTABLE}")
++ install(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_LIBDIR})
++endmacro(add_wam_grpc_lib)
++
++add_wam_grpc_lib(${WAM_GRPC_LIB_NAME} wam_ipc.proto)
++add_wam_grpc_lib(${APPLAUNCHER_LIB_NAME} applauncher.proto)
++
++add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES})
++set_target_properties(${PROJECT_NAME} PROPERTIES VERSION 1.0.0 SOVERSION 1.0)
++add_dependencies(${PROJECT_NAME} ${WAM_GRPC_LIB_NAME})
++target_include_directories(${PROJECT_NAME} PUBLIC ${WAM_SERVICE_INCLUDE_DIRS})
++target_link_libraries(${PROJECT_NAME} PUBLIC ${WAM_SERVICE_LIBS})
++install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
++install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+diff --git a/src/cef/service/applauncher.proto b/src/cef/service/applauncher.proto
+new file mode 100644
+index 0000000..0b8e0fc
+--- /dev/null
++++ b/src/cef/service/applauncher.proto
+@@ -0,0 +1,50 @@
++syntax = "proto3";
++
++package automotivegradelinux;
++
++service AppLauncher {
++ rpc StartApplication(StartRequest) returns (StartResponse) {}
++ rpc ListApplications(ListRequest) returns (ListResponse) {}
++ rpc GetStatusEvents(StatusRequest) returns (stream StatusResponse) {}
++}
++
++message StartRequest {
++ string id = 1;
++}
++
++message StartResponse {
++ bool status = 1;
++ string message = 2;
++}
++
++message ListRequest {
++}
++
++message ListResponse {
++ repeated AppInfo apps = 1;
++}
++
++message AppInfo {
++ string id = 1;
++ string name = 2;
++ string icon_path = 3;
++}
++
++message StatusRequest {
++}
++
++message AppStatus {
++ string id = 1;
++ string status = 2;
++}
++
++// Future-proofing for e.g. potentially signaling a list refresh
++message LauncherStatus {
++}
++
++message StatusResponse {
++ oneof status {
++ AppStatus app = 1;
++ LauncherStatus launcher = 2;
++ }
++}
+diff --git a/src/cef/service/applauncher_client_grpc.cc b/src/cef/service/applauncher_client_grpc.cc
+new file mode 100644
+index 0000000..f704742
+--- /dev/null
++++ b/src/cef/service/applauncher_client_grpc.cc
+@@ -0,0 +1,58 @@
++#include "applauncher_client_grpc.h"
++
++#include <grpcpp/ext/proto_server_reflection_plugin.h>
++#include <grpcpp/grpcpp.h>
++#include <grpcpp/health_check_service_interface.h>
++
++#include "include/cef_parser.h"
++
++AppLauncherClientGRPC::AppLauncherClientGRPC()
++ : stub_{MakeStub()} {
++}
++
++std::unique_ptr<automotivegradelinux::AppLauncher::Stub>AppLauncherClientGRPC::MakeStub() const {
++ return automotivegradelinux::AppLauncher::NewStub(grpc::CreateChannel("localhost:50052",
++ grpc::InsecureChannelCredentials()));
++}
++
++void AppLauncherClientGRPC::Start(const std::string& app_id) {
++ automotivegradelinux::StartRequest request;
++ request.set_id(app_id);
++
++ grpc::ClientContext context;
++ automotivegradelinux::StartResponse response;
++
++ grpc::Status status = stub_->StartApplication(&context, request, &response);
++}
++
++void AppLauncherClientGRPC::GetApplications(CefRefPtr<CefBrowser> browser, bool only_graphical) {
++ automotivegradelinux::ListRequest request;
++ automotivegradelinux::ListResponse response;
++ grpc::ClientContext context;
++
++ grpc::Status status = stub_->ListApplications(&context, request, &response);
++ if (!status.ok()) {
++ return;
++ }
++
++ CefRefPtr<CefListValue> apps_list = CefListValue::Create();
++ for (int i = 0; i < response.apps_size(); i++) {
++ automotivegradelinux::AppInfo app_info = response.apps(i);
++ CefRefPtr<CefDictionaryValue> app_info_dict = CefDictionaryValue::Create();
++ app_info_dict->SetString("id", app_info.id());
++ app_info_dict->SetString("name", app_info.name());
++ app_info_dict->SetString("icon", app_info.icon_path());
++ apps_list->SetDictionary(i, app_info_dict);
++ }
++
++ CefRefPtr<CefValue> apps_list_value = CefValue::Create();
++ apps_list_value->SetList(apps_list);
++ std::string response_string = CefWriteJSON(apps_list_value, JSON_WRITER_DEFAULT);
++
++ // send the response to renderer process
++ CefRefPtr<CefProcessMessage> message = CefProcessMessage::Create("get_applications");
++ CefRefPtr<CefListValue> args = message->GetArgumentList();
++ args->SetString(0, response_string);
++ browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, message);
++}
++
+diff --git a/src/cef/service/applauncher_client_grpc.h b/src/cef/service/applauncher_client_grpc.h
+new file mode 100644
+index 0000000..4a3f289
+--- /dev/null
++++ b/src/cef/service/applauncher_client_grpc.h
+@@ -0,0 +1,24 @@
++#ifndef CEF_SERVICE_APPLAUNCHER_CLIENT_GRPC_H
++#define CEF_SERVICE_APPLAUNCHER_CLIENT_GRPC_H
++
++#include <string>
++
++#include "applauncher.grpc.pb.h"
++
++#include "include/cef_browser.h"
++
++class AppLauncherClientGRPC {
++ public:
++
++ AppLauncherClientGRPC();
++
++ void Start(const std::string& app_id);
++ void GetApplications(CefRefPtr<CefBrowser> browser, bool only_graphical);
++
++ private:
++ std::unique_ptr<automotivegradelinux::AppLauncher::Stub> MakeStub() const;
++
++ std::unique_ptr<automotivegradelinux::AppLauncher::Stub> stub_;
++};
++
++#endif // CEF_SERVICE_APPLAUNCHER_CLIENT_GRPC_H
+diff --git a/src/cef/service/wam_ipc.proto b/src/cef/service/wam_ipc.proto
+new file mode 100644
+index 0000000..313de8f
+--- /dev/null
++++ b/src/cef/service/wam_ipc.proto
+@@ -0,0 +1,22 @@
++syntax = "proto3";
++import "google/protobuf/empty.proto";
++package wam_ipc;
++
++service WebAppManagerService {
++ rpc Launch(LaunchRequest) returns (google.protobuf.Empty) {}
++ rpc Activate(ActivateRequest) returns (google.protobuf.Empty) {}
++ rpc Kill(KillRequest) returns (google.protobuf.Empty) {}
++}
++
++message LaunchRequest {
++ string app_id = 1;
++ string uri = 2;
++}
++
++message ActivateRequest {
++ string app_id = 1;
++}
++
++message KillRequest {
++ string app_id = 1;
++}
+diff --git a/src/cef/service/web_app_manager_client_grpc.cc b/src/cef/service/web_app_manager_client_grpc.cc
+new file mode 100644
+index 0000000..8529868
+--- /dev/null
++++ b/src/cef/service/web_app_manager_client_grpc.cc
+@@ -0,0 +1,42 @@
++#include "web_app_manager_client_grpc.h"
++
++#include <grpcpp/ext/proto_server_reflection_plugin.h>
++#include <grpcpp/grpcpp.h>
++#include <grpcpp/health_check_service_interface.h>
++
++const char kDefaultGrpcServiceAddress[] = "127.0.0.1:15000";
++
++WebAppManagerClientGRPC::WebAppManagerClientGRPC() {
++ auto channel = grpc::CreateChannel(kDefaultGrpcServiceAddress,
++ grpc::InsecureChannelCredentials());
++ stub_ = wam_ipc::WebAppManagerService::NewStub(channel);
++}
++
++bool WebAppManagerClientGRPC::Launch(const LaunchParams& params) {
++ wam_ipc::LaunchRequest request;
++ request.set_app_id(params.app_id);
++ request.set_uri(params.uri);
++
++ grpc::ClientContext context;
++ google::protobuf::Empty reply;
++ grpc::Status status = stub_->Launch(&context, request, &reply);
++ return status.ok();
++}
++
++bool WebAppManagerClientGRPC::Activate(const std::string& app_id) {
++ grpc::ClientContext context;
++ google::protobuf::Empty reply;
++ wam_ipc::ActivateRequest request;
++ request.set_app_id(app_id);
++ grpc::Status status = stub_->Activate(&context, request, &reply);
++ return status.ok();
++}
++
++bool WebAppManagerClientGRPC::Kill(const std::string& app_id) {
++ grpc::ClientContext context;
++ google::protobuf::Empty reply;
++ wam_ipc::KillRequest request;
++ request.set_app_id(app_id);
++ grpc::Status status = stub_->Kill(&context, request, &reply);
++ return status.ok();
++}
+diff --git a/src/cef/service/web_app_manager_client_grpc.h b/src/cef/service/web_app_manager_client_grpc.h
+new file mode 100644
+index 0000000..9c4be70
+--- /dev/null
++++ b/src/cef/service/web_app_manager_client_grpc.h
+@@ -0,0 +1,23 @@
++#ifndef CEF_SERVICE_WEB_APP_MANAGER_CLIENT_GRPC_H
++#define CEF_SERVICE_WEB_APP_MANAGER_CLIENT_GRPC_H
++
++#include "wam_ipc.grpc.pb.h"
++
++class WebAppManagerClientGRPC {
++ public:
++ struct LaunchParams {
++ std::string app_id;
++ std::string uri;
++ };
++
++ WebAppManagerClientGRPC();
++ bool Launch(const LaunchParams& params);
++ bool Activate(const std::string& app_id);
++ bool Kill(const std::string& app_id);
++
++ private:
++ std::unique_ptr<wam_ipc::WebAppManagerService::Stub> stub_;
++};
++
++#endif // CEF_SERVICE_WEB_APP_MANAGER_CLIENT_GRPC_H
++
+diff --git a/src/cef/service/web_app_manager_service_grpc.cc b/src/cef/service/web_app_manager_service_grpc.cc
+new file mode 100644
+index 0000000..52de924
+--- /dev/null
++++ b/src/cef/service/web_app_manager_service_grpc.cc
+@@ -0,0 +1,382 @@
++// Copyright (c) 2018-2022 LG Electronics, Inc.
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++//
++// SPDX-License-Identifier: Apache-2.0
++
++#include "web_app_manager_service_grpc.h"
++
++#include <grpcpp/ext/proto_server_reflection_plugin.h>
++#include <grpcpp/grpcpp.h>
++#include <grpcpp/health_check_service_interface.h>
++#include <pthread.h>
++#include <sys/file.h>
++#include <sys/un.h>
++#include <unistd.h>
++#include <algorithm>
++#include <cassert>
++#include <climits>
++#include <cstdlib>
++#include <exception>
++#include <fstream>
++#include <iostream>
++#include <set>
++#include <sstream>
++
++#include <json/value.h>
++
++#include "log_manager.h"
++#include "utils.h"
++#include "wam_ipc.grpc.pb.h"
++#include "web_app_base.h"
++#include "web_app_manager.h"
++
++namespace {
++const char kDefaultGrpcServiceAddress[] = "127.0.0.1:15000";
++} // namespace
++
++class WamIPCLockFile {
++ public:
++ WamIPCLockFile() {
++ const char* runtime_dir;
++ if ((runtime_dir = getenv("XDG_RUNTIME_DIR")) == NULL) {
++ LOG_DEBUG("Failed to retrieve XDG_RUNTIME_DIR, falling back to /tmp");
++ runtime_dir = "/tmp";
++ }
++ lock_file_ = std::string(runtime_dir);
++ lock_file_.append("/wamipc.lock");
++ }
++
++ ~WamIPCLockFile() {
++ if (lock_fd_ != -1)
++ ReleaseLock(lock_fd_);
++ if (lock_fd_ != -1)
++ close(lock_fd_);
++ }
++
++ bool CreateAndLock() {
++ lock_fd_ = OpenLockFile();
++ if (!AcquireLock(lock_fd_)) {
++ LOG_DEBUG("Failed to lock file %d", lock_fd_);
++ return false;
++ }
++ return true;
++ }
++
++ bool OwnsLock() const { return lock_fd_ != -1; }
++
++ bool TryAcquireLock() {
++ int fd = OpenLockFile();
++ if (fd != -1) {
++ if (AcquireLock(fd)) {
++ ReleaseLock(fd);
++ return true;
++ }
++ }
++ return false;
++ }
++
++ private:
++ int OpenLockFile() {
++ int fd = open(lock_file_.c_str(), O_CREAT | O_TRUNC, S_IRWXU);
++ if (fd == -1) {
++ LOG_DEBUG("Failed to open lock file descriptor");
++ return fd;
++ }
++
++ int flags = fcntl(fd, F_GETFD);
++ if (flags == -1)
++ LOG_DEBUG("Could not get flags for lock file %d", fd);
++
++ flags |= FD_CLOEXEC;
++
++ if (fcntl(fd, F_SETFD, flags) == -1)
++ LOG_DEBUG("Could not set flags for lock file %d", fd);
++
++ return fd;
++ }
++
++ bool AcquireLock(int fd) {
++ if (flock(fd, LOCK_EX | LOCK_NB) != 0)
++ return false;
++ return true;
++ }
++
++ void ReleaseLock(int fd) { flock(fd, LOCK_UN); }
++
++ std::string lock_file_;
++ int lock_fd_ = -1;
++};
++
++class GrpcServiceImpl final
++ : public wam_ipc::WebAppManagerService::CallbackService {
++ grpc::ServerUnaryReactor* Launch(grpc::CallbackServerContext* context,
++ const ::wam_ipc::LaunchRequest* request,
++ google::protobuf::Empty* /*response*/) {
++ WebAppManagerServiceGRPC::LaunchParams launch_params;
++ launch_params.app_id = request->app_id();
++ launch_params.uri = request->uri();
++ launch_params.width = 0;
++ launch_params.height = 0;
++
++ WebAppManagerServiceGRPC::Instance()->LaunchOnIdle(launch_params);
++
++ grpc::ServerUnaryReactor* reactor = context->DefaultReactor();
++ reactor->Finish(grpc::Status::OK);
++ return reactor;
++ }
++ grpc::ServerUnaryReactor* Activate(grpc::CallbackServerContext* context,
++ const ::wam_ipc::ActivateRequest* request,
++ google::protobuf::Empty* /*response*/) {
++ WebAppManagerServiceGRPC::Instance()->SendEventOnIdle(kActivateEvent,
++ request->app_id());
++ grpc::ServerUnaryReactor* reactor = context->DefaultReactor();
++ reactor->Finish(grpc::Status::OK);
++ return reactor;
++ }
++ grpc::ServerUnaryReactor* Kill(grpc::CallbackServerContext* context,
++ const ::wam_ipc::KillRequest* request,
++ google::protobuf::Empty* /*response*/) {
++ WebAppManagerServiceGRPC::Instance()->SendEventOnIdle(kKilledApp,
++ request->app_id());
++ grpc::ServerUnaryReactor* reactor = context->DefaultReactor();
++ reactor->Finish(grpc::Status::OK);
++ return reactor;
++ }
++};
++
++WebAppManagerServiceGRPC::WebAppManagerServiceGRPC()
++ : lock_file_(std::make_unique<WamIPCLockFile>()) {}
++
++WebAppManagerServiceGRPC* WebAppManagerServiceGRPC::Instance() {
++ static WebAppManagerServiceGRPC* srv = new WebAppManagerServiceGRPC();
++ return srv;
++}
++
++bool WebAppManagerServiceGRPC::InitializeAsHostService() {
++ return lock_file_->CreateAndLock();
++}
++
++bool WebAppManagerServiceGRPC::IsHostServiceRunning() {
++ return !lock_file_->TryAcquireLock();
++}
++
++void* RunGrpcService(void*) {
++ std::string server_address(kDefaultGrpcServiceAddress);
++ GrpcServiceImpl service;
++
++ grpc::EnableDefaultHealthCheckService(true);
++ grpc::reflection::InitProtoReflectionServerBuilderPlugin();
++
++ grpc::ServerBuilder builder;
++ builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
++ builder.RegisterService(&service);
++
++ std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
++ std::cout << "Server listening on " << server_address << std::endl;
++ server->Wait();
++
++ return nullptr;
++}
++
++bool WebAppManagerServiceGRPC::StartService() {
++ if (lock_file_->OwnsLock()) {
++ pthread_t thread_id;
++ if (pthread_create(&thread_id, nullptr, RunGrpcService, nullptr) < 0) {
++ perror("Could not create thread");
++ LOG_DEBUG("Could not create thread...");
++ return false;
++ }
++ }
++
++ return true;
++}
++
++void WebAppManagerServiceGRPC::LaunchOnIdle(const LaunchParams& params) {
++ auto launch_params = std::make_unique<LaunchParams>(params);
++
++ auto* timer =
++ new OneShotTimerWithData<WebAppManagerServiceGRPC, LaunchParams>();
++ timer->Start(0, this, &WebAppManagerServiceGRPC::OnLaunchApp,
++ std::move(launch_params));
++}
++
++void WebAppManagerServiceGRPC::SendEventOnIdle(const std::string& event,
++ const std::string& app_id) {
++ auto event_data = std::make_unique<EventData>();
++ event_data->app_id = app_id;
++ auto* timer = new OneShotTimerWithData<WebAppManagerServiceGRPC, EventData>();
++ if (event == kActivateEvent)
++ timer->Start(0, this, &WebAppManagerServiceGRPC::OnActivateEvent,
++ std::move(event_data));
++ else if (event == kDeactivateEvent)
++ timer->Start(0, this, &WebAppManagerServiceGRPC::OnDeactivateEvent,
++ std::move(event_data));
++ else if (event == kKilledApp)
++ timer->Start(1000, this, &WebAppManagerServiceGRPC::OnKillEvent,
++ std::move(event_data));
++}
++
++void WebAppManagerServiceGRPC::OnLaunchApp(LaunchParams* params) {
++ LOG_DEBUG("Triggering app start: %s", params->uri.c_str());
++ if (!params->uri.empty()) {
++ if (params->uri.find("http://") == 0) {
++ LaunchStartupAppFromURL(params);
++ } else {
++ LaunchStartupAppFromJsonConfig(params);
++ }
++ }
++}
++
++void WebAppManagerServiceGRPC::LaunchStartupAppFromJsonConfig(
++ LaunchParams* params) {
++ std::string configfile;
++ configfile.append(params->uri);
++ configfile.append("/appinfo.json");
++
++ Json::Value root;
++ Json::CharReaderBuilder builder;
++ JSONCPP_STRING errs;
++
++ std::ifstream ifs;
++ ifs.open(configfile.c_str());
++
++ if (!parseFromStream(builder, ifs, &root, &errs)) {
++ LOG_DEBUG("Failed to parse %s configuration file", configfile.c_str());
++ }
++
++ root["folderPath"] = params->uri.c_str();
++
++ auto surface_obj = root["surface"];
++ auto surface_type = surface_obj["type"].asString();
++ if (surface_type == "background") {
++ root["surface_type"] = 1; // AglShellSurfaceType::kBackground;
++ } else if (surface_type == "panel") {
++ root["surface_type"] = 2; // AglShellSurfaceType::kPanel;
++ } else {
++ root["surface_type"] = 0; // AglShellSurfaceType::kNone;
++ }
++
++ std::string app_desc = util::JsonToString(root);
++ std::string empty_params = "{}";
++ std::string app_id = root["id"].asString();
++ int err_code = 0;
++ std::string err_msg;
++ WebAppManagerService::OnLaunch(app_desc, empty_params, app_id, err_code,
++ err_msg);
++}
++
++void WebAppManagerServiceGRPC::LaunchStartupAppFromURL(LaunchParams* params) {
++ LOG_DEBUG("WebAppManagerServiceGRPC::LaunchStartupAppFromURL");
++ LOG_DEBUG(" url: %s", params->uri.c_str());
++ Json::Value obj(Json::objectValue);
++ obj["id"] = params->app_id;
++ obj["version"] = "1.0";
++ obj["vendor"] = "some vendor";
++ obj["type"] = "web";
++ obj["main"] = params->uri;
++ obj["title"] = "webapp";
++ obj["uiRevision"] = "2";
++
++ obj["widthOverride"] = params->width;
++ obj["heightOverride"] = params->height;
++
++ std::string app_desc = util::JsonToString(obj);
++ std::string app_id = params->app_id;
++ int err_code = 0;
++ std::string empty_params = "{}";
++ std::string err_msg;
++
++ LOG_DEBUG("Launching with appDesc=[%s]", app_desc.c_str());
++
++ WebAppManagerService::OnLaunch(app_desc, empty_params, app_id, err_code,
++ err_msg);
++ LOG_DEBUG("onLaunch: Done.");
++}
++
++Json::Value WebAppManagerServiceGRPC::launchApp(const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::killApp(const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::pauseApp(const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::logControl(const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::setInspectorEnable(
++ const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::closeAllApps(const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::discardCodeCache(
++ const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::listRunningApps(
++ const Json::Value& request,
++ bool subscribed) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::getWebProcessSize(
++ const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::clearBrowsingData(
++ const Json::Value& request) {
++ return Json::Value(Json::objectValue);
++}
++
++Json::Value WebAppManagerServiceGRPC::webProcessCreated(
++ const Json::Value& request,
++ bool subscribed) {
++ return Json::Value(Json::objectValue);
++}
++
++void WebAppManagerServiceGRPC::OnActivateEvent(EventData* event_data) {
++ LOG_DEBUG("Activate app=%s", event_data->app_id.c_str());
++ WebAppBase* web_app =
++ WebAppManager::Instance()->FindAppById(event_data->app_id);
++ if (web_app) {
++ web_app->OnStageActivated();
++ web_app->SendAglActivate(event_data->app_id.c_str());
++ } else {
++ LOG_DEBUG("Not found app=%s running", event_data->app_id.c_str());
++ }
++}
++
++void WebAppManagerServiceGRPC::OnDeactivateEvent(EventData* event_data) {
++ LOG_DEBUG("Dectivate app=%s", event_data->app_id.c_str());
++ WebAppBase* web_app =
++ WebAppManager::Instance()->FindAppById(event_data->app_id);
++ if (web_app)
++ web_app->OnStageDeactivated();
++}
++
++void WebAppManagerServiceGRPC::OnKillEvent(EventData* event_data) {
++ LOG_DEBUG("Kill app=%s", event_data->app_id.c_str());
++ WebAppManager::Instance()->OnKillApp(event_data->app_id, event_data->app_id);
++}
+diff --git a/src/cef/service/web_app_manager_service_grpc.h b/src/cef/service/web_app_manager_service_grpc.h
+new file mode 100644
+index 0000000..69ea0ed
+--- /dev/null
++++ b/src/cef/service/web_app_manager_service_grpc.h
+@@ -0,0 +1,85 @@
++// Copyright (c) 2018-2022 LG Electronics, Inc.
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++//
++// SPDX-License-Identifier: Apache-2.0
++
++#ifndef CEF_SERVICE_WEB_APP_MANAGER_SERVICE_GRPC_H
++#define CEF_SERVICE_WEB_APP_MANAGER_SERVICE_GRPC_H
++
++#include <memory>
++
++#include "timer.h"
++#include "web_app_manager_service.h"
++
++constexpr char kStartApp[] = "start-app";
++constexpr char kKilledApp[] = "killed-app";
++constexpr char kActivateEvent[] = "activate-event";
++constexpr char kDeactivateEvent[] = "deactivate-event";
++
++class GrpcClient;
++class WamIPCLockFile;
++
++class WebAppManagerServiceGRPC : public WebAppManagerService,
++ public TimerReceiver {
++ public:
++ struct LaunchParams {
++ std::string app_id;
++ std::string uri;
++ int width = 0;
++ int height = 0;
++ };
++
++ static WebAppManagerServiceGRPC* Instance();
++
++ bool InitializeAsHostService();
++ bool IsHostServiceRunning();
++
++ void LaunchOnIdle(const LaunchParams& params);
++ void SendEventOnIdle(const std::string& event, const std::string& app_id);
++
++ // WebAppManagerService
++ bool StartService() override;
++ Json::Value launchApp(const Json::Value& request) override;
++ Json::Value killApp(const Json::Value& request) override;
++ Json::Value pauseApp(const Json::Value& request) override;
++ Json::Value logControl(const Json::Value& request) override;
++ Json::Value setInspectorEnable(const Json::Value& request) override;
++ Json::Value closeAllApps(const Json::Value& request) override;
++ Json::Value discardCodeCache(const Json::Value& request) override;
++ Json::Value listRunningApps(const Json::Value& request,
++ bool subscribed) override;
++ Json::Value getWebProcessSize(const Json::Value& request) override;
++ Json::Value clearBrowsingData(const Json::Value& request) override;
++ Json::Value webProcessCreated(const Json::Value& request,
++ bool subscribed) override;
++
++ void TriggerStartupApp();
++
++ private:
++ WebAppManagerServiceGRPC();
++
++ void OnLaunchApp(LaunchParams* launch_data);
++ void LaunchStartupAppFromJsonConfig(LaunchParams*);
++ void LaunchStartupAppFromURL(LaunchParams*);
++ struct EventData {
++ std::string app_id;
++ };
++ void OnActivateEvent(EventData* event_data);
++ void OnDeactivateEvent(EventData* event_data);
++ void OnKillEvent(EventData* event_data);
++
++ std::unique_ptr<WamIPCLockFile> lock_file_;
++};
++
++#endif // CEF_SERVICE_WEB_APP_MANAGER_SERVICE_GRPC_H
+diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
+index 182f96a..41871b9 100644
+--- a/src/core/CMakeLists.txt
++++ b/src/core/CMakeLists.txt
+@@ -18,6 +18,7 @@ project(WebAppMgrCore VERSION 1.0.0 DESCRIPTION "Core of the Web Application Man
+
+ set(SOURCES
+ application_description.cc
++ application_installation_handler_stub.cc
+ device_info.cc
+ palm_system_base.cc
+ plugin_service.cc
+@@ -44,7 +45,9 @@ set(SOURCES
+ set(HEADERS
+ agl_shell_types.h
+ application_description.h
++ application_installation_handler.h
+ device_info.h
++ memory_pressure_level.h
+ palm_system_base.h
+ platform_module_factory.h
+ plugin_service.h
+@@ -95,6 +98,7 @@ set(CORE_INCLUDE_DIRS
+ set(CORE_LIBS
+ ${CHROMIUM_LDFLAGS}
+ ${GLIB_LDFLAGS}
++ ${JSONCPP_LDFLAGS}
+ ${PMLOGLIB_LDFLAGS}
+ Boost::filesystem
+ dl
+diff --git a/src/core/application_description.cc b/src/core/application_description.cc
+index 9b494fc..adbb7d8 100644
+--- a/src/core/application_description.cc
++++ b/src/core/application_description.cc
+@@ -144,12 +144,49 @@ std::unique_ptr<ApplicationDescription> ApplicationDescription::FromJsonString(
+ auto app_desc =
+ std::unique_ptr<ApplicationDescription>(new ApplicationDescription());
+
++
+ app_desc->surface_type_ =
+ static_cast<AglShellSurfaceType>(json_obj["surface_type"].asInt());
+ app_desc->panel_type_ =
+ static_cast<AglShellPanelEdge>(json_obj["panel_type"].asInt());
+ app_desc->width_override_ = json_obj["widthOverride"].asInt();
+ app_desc->height_override_ = json_obj["heightOverride"].asInt();
++
++ // override previous values if the json config file contains
++ // a "surface" object with nested data
++ auto surface_obj = json_obj["surface"];
++ if (!surface_obj.empty()) {
++ std::string surface_type = surface_obj["type"].asString();
++ if (surface_type == "background") {
++ app_desc->surface_type_ = AglShellSurfaceType::kBackground;
++ } else if (surface_type == "panel") {
++ app_desc->surface_type_ = AglShellSurfaceType::kPanel;
++ } else {
++ app_desc->surface_type_ = AglShellSurfaceType::kNone;
++ }
++
++ std::string panel_edge = surface_obj["panel_edge"].asString();
++ if (panel_edge == "left") {
++ app_desc->panel_type_ = AglShellPanelEdge::kLeft;
++ } else if (panel_edge == "right") {
++ app_desc->panel_type_ = AglShellPanelEdge::kRight;
++ } else if (panel_edge == "top") {
++ app_desc->panel_type_ = AglShellPanelEdge::kTop;
++ } else if (panel_edge == "bottom") {
++ app_desc->panel_type_ = AglShellPanelEdge::kBottom;
++ }
++
++ int width = 0;
++ if (!surface_obj["width"].empty())
++ util::StrToInt(surface_obj["width"].asString(), width);
++ app_desc->width_override_ = width;
++
++ int height = 0;
++ if (!surface_obj["height"].empty())
++ util::StrToInt(surface_obj["height"].asString(), height);
++ app_desc->height_override_ = height;
++ }
++
+ app_desc->transparency_ = json_obj["transparent"].asBool();
+ auto vendor_extension =
+ json_obj.get("vendorExtension", Json::Value(Json::objectValue));
+diff --git a/src/core/application_installation_handler.h b/src/core/application_installation_handler.h
+new file mode 100644
+index 0000000..054bc9d
+--- /dev/null
++++ b/src/core/application_installation_handler.h
+@@ -0,0 +1,12 @@
++#ifndef CORE_APPLICATION_INSTALLATION_HANDLER_H_
++#define CORE_APPLICATION_INSTALLATION_HANDLER_H_
++
++#include <string>
++
++class ApplicationInstallationHandler {
++ public:
++ static void OnAppInstalled(const std::string& app_id);
++ static void OnAppRemoved(const std::string& app_id);
++};
++
++#endif // CORE_APPLICATION_INSTALLATION_HANDLER_H_
+\ No newline at end of file
+diff --git a/src/core/application_installation_handler_stub.cc b/src/core/application_installation_handler_stub.cc
+new file mode 100644
+index 0000000..016c7d2
+--- /dev/null
++++ b/src/core/application_installation_handler_stub.cc
+@@ -0,0 +1,4 @@
++#include "application_installation_handler.h"
++
++void ApplicationInstallationHandler::OnAppInstalled(const std::string&) {}
++void ApplicationInstallationHandler::OnAppRemoved(const std::string&) {}
+\ No newline at end of file
+diff --git a/src/core/memory_pressure_level.h b/src/core/memory_pressure_level.h
+new file mode 100644
+index 0000000..01c9316
+--- /dev/null
++++ b/src/core/memory_pressure_level.h
+@@ -0,0 +1,6 @@
++#ifndef CORE_MEMORY_PRESSURE_LEVEL_H_
++#define CORE_MEMORY_PRESSURE_LEVEL_H_
++
++enum class MemoryPressureLevel { kNone, kLow, kCritical };
++
++#endif // CORE_MEMORY_PRESSURE_LEVEL_H_
+\ No newline at end of file
+diff --git a/src/core/web_app_manager.cc b/src/core/web_app_manager.cc
+index ca64ef1..42e8be7 100644
+--- a/src/core/web_app_manager.cc
++++ b/src/core/web_app_manager.cc
+@@ -22,10 +22,9 @@
+ #include <string>
+
+ #include <json/value.h>
+-#include "webos/application_installation_handler.h"
+-#include "webos/public/runtime.h"
+
+ #include "application_description.h"
++#include "application_installation_handler.h"
+ #include "base_check.h"
+ #include "device_info.h"
+ #include "log_manager.h"
+@@ -63,8 +62,7 @@ WebAppManager::~WebAppManager() {
+ device_info_->Terminate();
+ }
+
+-void WebAppManager::NotifyMemoryPressure(
+- webos::WebViewBase::MemoryPressureLevel level) {
++void WebAppManager::NotifyMemoryPressure(MemoryPressureLevel level) {
+ std::list<const WebAppBase*> app_list = RunningApps();
+ for (auto it = app_list.begin(); it != app_list.end(); ++it) {
+ const WebAppBase* app = *it;
+@@ -72,15 +70,14 @@ void WebAppManager::NotifyMemoryPressure(
+ // critical (when system is on low or critical) because they will be killed
+ // anyway
+ if (app->IsActivated() &&
+- (!app->Page()->IsPreload() ||
+- level != webos::WebViewBase::MEMORY_PRESSURE_CRITICAL))
++ (!app->Page()->IsPreload() || level != MemoryPressureLevel::kCritical))
+ app->Page()->NotifyMemoryPressure(level);
+ else {
+ LOG_DEBUG(
+ "Skipping memory pressure handler for"
+ " instanceId(%s) appId(%s) isActivated(%d) isPreload(%d) Level(%d)",
+ app->InstanceId().c_str(), app->AppId().c_str(), app->IsActivated(),
+- app->Page()->IsPreload(), level);
++ app->Page()->IsPreload(), static_cast<int>(level));
+ }
+ }
+ }
+@@ -341,8 +338,10 @@ WebAppBase* WebAppManager::OnLaunchUrl(
+ WebPageAdded(page);
+
+ /* if the surface role is a background send ready to display them */
+- if (app_desc->SurfaceType() == AglShellSurfaceType::kBackground)
++ if (app_desc->SurfaceType() == AglShellSurfaceType::kBackground) {
++ LOG_DEBUG("Sending agl_ready from app %s", app_desc->Id().c_str());
+ app->SendAglReady();
++ }
+
+ app_list_.push_back(app);
+
+@@ -831,8 +830,10 @@ void WebAppManager::UpdateNetworkStatus(const Json::Value& object) {
+ NetworkStatus status;
+ status.FromJsonObject(object);
+
++#if defined(OS_WEBOS)
+ webos::Runtime::GetInstance()->SetNetworkConnected(
+ status.IsInternetConnectionAvailable());
++#endif
+ network_status_manager_->UpdateNetworkStatus(status);
+
+ if (status.IsInternetConnectionAvailable()) {
+@@ -867,16 +868,12 @@ int WebAppManager::MaskForBrowsingDataType(const char* type) {
+
+ void WebAppManager::AppInstalled(const std::string& app_id) {
+ LOG_INFO(MSGID_WAM_DEBUG, 0, "App installed; id=%s", app_id.c_str());
+- auto p = webos::ApplicationInstallationHandler::GetInstance();
+- if (p)
+- p->OnAppInstalled(app_id);
++ ApplicationInstallationHandler::OnAppInstalled(app_id);
+ }
+
+ void WebAppManager::AppRemoved(const std::string& app_id) {
+ LOG_INFO(MSGID_WAM_DEBUG, 0, "App removed; id=%s", app_id.c_str());
+- auto p = webos::ApplicationInstallationHandler::GetInstance();
+- if (p)
+- p->OnAppRemoved(app_id);
++ ApplicationInstallationHandler::OnAppRemoved(app_id);
+ }
+
+ std::string WebAppManager::IdentifierForSecurityOrigin(
+@@ -889,5 +886,9 @@ std::string WebAppManager::IdentifierForSecurityOrigin(
+ LOG_WARNING(MSGID_APPID_HAS_UPPERCASE, 0,
+ "Application id should not contain capital letters");
+ }
++#if defined(OS_WEBOS)
+ return (lowcase_identifier + webos::WebViewBase::kSecurityOriginPostfix);
++#else
++ return lowcase_identifier;
++#endif
+ }
+diff --git a/src/core/web_app_manager.h b/src/core/web_app_manager.h
+index b10f53d..0f30a85 100644
+--- a/src/core/web_app_manager.h
++++ b/src/core/web_app_manager.h
+@@ -24,7 +24,7 @@
+ #include <unordered_map>
+ #include <vector>
+
+-#include "webos/webview_base.h"
++#include "memory_pressure_level.h"
+
+ class ApplicationDescription;
+ class DeviceInfo;
+@@ -150,7 +150,7 @@ class WebAppManager {
+ const std::string& payload,
+ const std::string& app_id);
+ void UpdateNetworkStatus(const Json::Value& object);
+- void NotifyMemoryPressure(webos::WebViewBase::MemoryPressureLevel level);
++ void NotifyMemoryPressure(MemoryPressureLevel level);
+
+ bool IsEnyoApp(const std::string& app_id);
+
+diff --git a/src/core/web_app_manager_service.cc b/src/core/web_app_manager_service.cc
+index 1770d02..90b880f 100644
+--- a/src/core/web_app_manager_service.cc
++++ b/src/core/web_app_manager_service.cc
+@@ -19,6 +19,7 @@
+ #include <json/value.h>
+
+ #include "log_manager.h"
++#include "memory_pressure_level.h"
+ #include "web_app_base.h"
+ #include "web_app_manager_tracer.h"
+
+@@ -158,8 +159,7 @@ void WebAppManagerService::UpdateNetworkStatus(const Json::Value& object) {
+ WebAppManager::Instance()->UpdateNetworkStatus(object);
+ }
+
+-void WebAppManagerService::NotifyMemoryPressure(
+- webos::WebViewBase::MemoryPressureLevel level) {
++void WebAppManagerService::NotifyMemoryPressure(MemoryPressureLevel level) {
+ WebAppManager::Instance()->NotifyMemoryPressure(level);
+ }
+
+diff --git a/src/core/web_app_manager_service.h b/src/core/web_app_manager_service.h
+index 7ead117..c294e50 100644
+--- a/src/core/web_app_manager_service.h
++++ b/src/core/web_app_manager_service.h
+@@ -22,7 +22,6 @@
+ #include <vector>
+
+ #include "web_app_manager.h"
+-#include "webos/webview_base.h"
+
+ namespace Json {
+ class Value;
+@@ -114,7 +113,7 @@ class WebAppManagerService {
+ void KillCustomPluginProcess(const std::string& app_base_path);
+ void RequestKillWebProcess(uint32_t pid);
+ void UpdateNetworkStatus(const Json::Value& object);
+- void NotifyMemoryPressure(webos::WebViewBase::MemoryPressureLevel level);
++ void NotifyMemoryPressure(MemoryPressureLevel level);
+ void SetAccessibilityEnabled(bool enable);
+ uint32_t GetWebProcessId(const std::string& app_id,
+ const std::string& instance_id);
+diff --git a/src/core/web_page_base.h b/src/core/web_page_base.h
+index 7bbca84..8a689a7 100644
+--- a/src/core/web_page_base.h
++++ b/src/core/web_page_base.h
+@@ -20,8 +20,7 @@
+ #include <memory>
+ #include <string>
+
+-#include "webos/webview_base.h"
+-
++#include "memory_pressure_level.h"
+ #include "observer_list.h"
+ #include "util/url.h"
+
+@@ -58,8 +57,7 @@ class WebPageBase {
+ virtual void Init() = 0;
+ virtual void* GetWebContents() = 0;
+ virtual void SetLaunchParams(const std::string& params);
+- virtual void NotifyMemoryPressure(
+- webos::WebViewBase::MemoryPressureLevel level) {}
++ virtual void NotifyMemoryPressure(MemoryPressureLevel level) {}
+
+ virtual std::string GetIdentifier() const;
+ virtual wam::Url Url() const = 0;
+diff --git a/src/core/web_process_manager.h b/src/core/web_process_manager.h
+index b63d270..c7ffde1 100644
+--- a/src/core/web_process_manager.h
++++ b/src/core/web_process_manager.h
+@@ -17,6 +17,7 @@
+ #ifndef CORE_WEB_PROCESS_MANAGER_H_
+ #define CORE_WEB_PROCESS_MANAGER_H_
+
++#include <cstdint>
+ #include <list>
+ #include <string>
+ #include <unordered_map>
+diff --git a/src/core/web_runtime.h b/src/core/web_runtime.h
+index 69bc204..1ae6ca9 100644
+--- a/src/core/web_runtime.h
++++ b/src/core/web_runtime.h
+@@ -21,8 +21,9 @@
+
+ class WebRuntime {
+ public:
++ virtual ~WebRuntime() = default;
+ static std::unique_ptr<WebRuntime> Create();
+- virtual int Run(int argc, const char** argv) = 0;
++ virtual int Run(int argc, char** argv) = 0;
+ };
+
+ #endif // CORE_WEB_RUNTIME_H_
+diff --git a/src/desktop/CMakeLists.txt b/src/desktop/CMakeLists.txt
+new file mode 100644
+index 0000000..06078da
+--- /dev/null
++++ b/src/desktop/CMakeLists.txt
+@@ -0,0 +1,100 @@
++project(WebAppMgrDesktop VERSION 1.0.0 DESCRIPTION "Web Application Manager library")
++
++find_package(gRPC REQUIRED)
++find_program(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)
++find_package(Protobuf REQUIRED)
++find_package(Threads)
++
++set(WAM_IPC_LIB_NAME ${PROJECT_NAME}IPC)
++set(WAM_IPC_PROTO_FILES ${WAM_ROOT_SOURCE_DIR}/cef/ipc/wam_ipc.proto)
++set(WAM_IPC_LIBS
++ protobuf::libprotobuf
++ gRPC::grpc
++ gRPC::grpc++
++ gRPC::grpc++_reflection
++)
++set(WAM_IPC_INCLUDE_DIRS
++ ${CMAKE_CURRENT_BINARY_DIR}
++)
++
++set(WAM_LIB_LIBS
++ ${JSONCPP_LDFLAGS}
++ WebAppMgrCore
++ ${WAM_IPC_LIB_NAME}
++ libcef_lib
++ libcef_dll_wrapper
++)
++
++set(SOURCES
++ web_runtime_desktop.cc
++)
++
++set(HEADERS
++ web_runtime_desktop.h
++)
++
++set(WAM_LIB_CEF_DIR ${WAM_ROOT_SOURCE_DIR}/cef)
++
++
++set(WAM_LIB_INCLUDE_DIRS
++ ${JSONCPP_INCLUDE_DIRS}
++ ${CEF_INCLUDE_PATH}
++)
++
++add_library(${WAM_IPC_LIB_NAME} SHARED ${WAM_IPC_PROTO_FILES})
++target_include_directories(${WAM_IPC_LIB_NAME} PUBLIC ${WAM_IPC_INCLUDE_DIRS})
++target_link_libraries(${WAM_IPC_LIB_NAME} PUBLIC ${WAM_IPC_LIBS})
++set_target_properties(${WAM_IPC_LIB_NAME} PROPERTIES VERSION 1.0.0 SOVERSION 1.0)
++protobuf_generate(TARGET ${WAM_IPC_LIB_NAME} LANGUAGE cpp APPEND_PATH)
++protobuf_generate(TARGET ${WAM_IPC_LIB_NAME} LANGUAGE grpc APPEND_PATH GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${GRPC_CPP_PLUGIN_EXECUTABLE}")
++install(TARGETS ${WAM_IPC_LIB_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
++
++
++LIST(APPEND SOURCES
++ ${WAM_LIB_CEF_DIR}/device_info_cef.cc
++ ${WAM_LIB_CEF_DIR}/platform_module_factory_cef.cc
++ ${WAM_LIB_CEF_DIR}/ipc/web_app_manager_service_grpc.cc
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_browser_handler.cc
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_client.cc
++)
++LIST(APPEND HEADERS
++ ${WAM_LIB_CEF_DIR}/device_info_cef.h
++ ${WAM_LIB_CEF_DIR}/platform_module_factory_cef.h
++ ${WAM_LIB_CEF_DIR}/ipc/web_app_manager_service_grpc.h
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_browser_handler.h
++ ${WAM_LIB_CEF_DIR}/handlers/wam_cef_client.h
++)
++LIST(APPEND WAM_LIB_INCLUDE_DIRS
++ ${WAM_LIB_CEF_DIR}
++ ${WAM_LIB_CEF_DIR}/ipc
++ ${WAM_LIB_CEF_DIR}/webapp
++)
++
++add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES})
++target_include_directories(${PROJECT_NAME} PUBLIC ${WAM_LIB_INCLUDE_DIRS})
++set_target_properties(${PROJECT_NAME} PROPERTIES VERSION 1.0.0 SOVERSION 1.0)
++
++install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/webappmanager)
++
++macro(INSTALL_CEF_FILES file_list source_dir target_dir)
++ foreach(FILENAME ${file_list})
++ set(source_file ${source_dir}/${FILENAME})
++
++ # Remove the target file path component.
++ get_filename_component(target_name ${FILENAME} NAME)
++ set(target_file ${target_dir}/${target_name})
++
++ if (IS_DIRECTORY ${source_file})
++ install(DIRECTORY ${source_file} DESTINATION ${target_dir})
++ else()
++ install(FILES ${source_file} DESTINATION ${target_dir})
++ endif()
++ endforeach()
++endmacro()
++
++# Copy CEF dependencies
++install_cef_files("${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CMAKE_INSTALL_PREFIX}")
++install_cef_files("${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CMAKE_INSTALL_PREFIX}")
++
++target_link_libraries(${PROJECT_NAME} PUBLIC ${WAM_LIB_LIBS})
++install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
+diff --git a/src/desktop/README.md b/src/desktop/README.md
+new file mode 100644
+index 0000000..2d32b39
+--- /dev/null
++++ b/src/desktop/README.md
+@@ -0,0 +1,102 @@
++# CEF backend
++
++This is an experimental CEF backend for WAM.
++
++## Compilation
++
++### Preparations
++
++First prepare a workspace:
++```
++mkdir wam-cef
++cd wam-cef
++```
++
++Now clone WAM repository:
++```
++git clone ssh://git@gitlab.igalia.com:4429/dape/wam.git
++```
++
++Fetch latest stable CEF binary distribution (standard) for your system, from [CEF binary download](https://cef-builds.spotifycdn.com/index.html). Then uncompress the tarball:
++```
++tar xvf ...path...to...binary/cef_binary...tar.bz2
++```
++
++### Compilation of CEF DLL wrapper
++
++Create a folder to compile the CEF DLL wrapper `.a` file:
++```
++mkdir build-cef-dll
++cd build-cef-dll
++```
++
++Prepare compilation scripts:
++```
++cmake ../cef_binary_...
++```
++
++Then compile the DLL wrapper:
++```
++make libcef_dll_wrapper
++```
++
++Finally go back to the top directory:
++```
++cd ..
++```
++
++After this, you can see the wrapper at `build-cef-dll/libcef_dll_wrapper/libcef_dll_wrapper.a`.
++
++### Test applications
++
++You can just use webOS `test-apps` repository:
++```
++git clone https://github.com/webosose/test-apps.git
++```
++
++### Compilation of WAM
++
++Prepare build folder:
++
++```
++mkdir build-wam
++cd build-wam
++mkdir wam-install
++```
++
++Then call *CMake* to generate the compilation scripts. You will need to pass several variables:
++* `CEF_ROOT`: full path to the CEF dist directory.
++* `CMAKE_INSTALL_PREFIX`: base install directory.
++* `CMAKE_INSTALL_BINDIR`: where executables will go.
++* `CMAKE_INSTALL_LIBDIR`: libraries.
++* `CMAKE_INSTALL_INCLUDEDIR`: path for includes.
++
++You can also use `CMAKE_BUILD_TYPE` to set `Debug` or `Release` builds.
++
++An example of the *CMake* invokation:
++```
++cmake -DCMAKE_INSTALL_PREFIX=$PWD/wam-install -DCMAKE_INSTALL_BINDIR=$PWD/wam-install/bin -DCMAKE_INSTALL_LIBDIR=$PWD/wam-install/lib -DCMAKE_INSTALL_INCLUDEDIR=$PWD/wam-install/include -DCEF_ROOT=$WAM_BASE_PATH/cef_binary_114.2.10+g398e3c3+chromium-114.0.5735.110_linux64/Debug/ -DCMAKE_BUILD_TYPE=Debug
++```
++
++And finally compilation of WAM:
++```
++make
++```
++
++And installation:
++```
++make install
++```
++
++## Running
++
++To run the daemon, you can do:
++```
++cd wam-install/bin
++WEBAPPFACTORY_PLUGIN_PATH=../lib/webappmanager/plugins/ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib:$WAM_BASE_PATH/cef_binary_114.2.10+g398e3c3+chromium-114.0.5735.110_linux64/Release/ ./WebAppMgr &
++```
++
++Then, you can launch an application:
++```
++LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ../WebAppMgrDesktopCli --app-id=bareapp --app-install-dir=$WAM_BASE_PATH/test-apps/bareapp/
++```
+diff --git a/src/desktop/web_runtime_desktop.cc b/src/desktop/web_runtime_desktop.cc
+new file mode 100644
+index 0000000..49d1914
+--- /dev/null
++++ b/src/desktop/web_runtime_desktop.cc
+@@ -0,0 +1,28 @@
++#include "web_runtime_desktop.h"
++
++#include "include/cef_base.h"
++
++#include "wam_cef_browser_handler.h"
++
++int WebRuntimeDesktop::Run(int argc, char** argv) {
++ CefMainArgs main_args(argc, argv);
++
++ CefRefPtr<CefApp> app = new WamCefBrowserHandler;
++ auto exit_code = CefExecuteProcess(main_args, app.get(), nullptr);
++ if (exit_code >= 0) {
++ return exit_code;
++ }
++
++ CefSettings settings;
++ CefInitialize(main_args, settings, app.get(), nullptr);
++
++ CefRunMessageLoop();
++
++ CefShutdown();
++
++ return 0;
++}
++
++std::unique_ptr<WebRuntime> WebRuntime::Create() {
++ return std::make_unique<WebRuntimeDesktop>();
++}
+diff --git a/src/desktop/web_runtime_desktop.h b/src/desktop/web_runtime_desktop.h
+new file mode 100644
+index 0000000..e65f738
+--- /dev/null
++++ b/src/desktop/web_runtime_desktop.h
+@@ -0,0 +1,11 @@
++#ifndef DESKTOP_WEB_RUNTIME_CEF_H_
++#define DESKTOP_WEB_RUNTIME_CEF_H_
++
++#include "web_runtime.h"
++
++class WebRuntimeDesktop : public WebRuntime {
++ public:
++ int Run(int argc, char** argv) override;
++};
++
++#endif // DESKTOP_WEB_RUNTIME_CEF_H_
+diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt
+index 876921b..b401dca 100644
+--- a/src/platform/CMakeLists.txt
++++ b/src/platform/CMakeLists.txt
+@@ -17,10 +17,6 @@
+ project(WebAppMgr VERSION 1.0.0 DESCRIPTION "Web Application Manager library")
+
+ set(SOURCES
+- palm_system_webos.cc
+- web_app_wayland.cc
+- web_app_wayland_window.cc
+- web_app_window_impl.cc
+ webengine/blink_web_process_manager.cc
+ webengine/blink_web_view.cc
+ webengine/blink_web_view_profile_helper.cc
+@@ -32,12 +28,8 @@ set(SOURCES
+ )
+
+ set(HEADERS
+- palm_system_webos.h
+- web_app_wayland.h
+- web_app_wayland_window.h
+ web_app_window.h
+ web_app_window_factory.h
+- web_app_window_impl.h
+ webengine/blink_web_process_manager.h
+ webengine/blink_web_view.h
+ webengine/blink_web_view_profile_helper.h
+@@ -67,6 +59,21 @@ set(WAM_LIB_LIBS
+ WebAppMgrCore
+ )
+
++if (WEBENGINE_CBE)
++ LIST(APPEND SOURCES
++ web_app_window_impl.h
++ web_app_wayland_window.cc
++ palm_system_webos.cc
++ web_app_wayland.cc
++ )
++ LIST(APPEND HEADERS
++ palm_system_webos.h
++ web_app_wayland.h
++ web_app_wayland_window.h
++ web_app_window_impl.h
++ )
++endif()
++
+ if (OS_WEBOS)
+ LIST(APPEND SOURCES
+ ${WAM_ROOT_SOURCE_DIR}/webos/palm_service_base.cc
+diff --git a/src/platform/web_app_window.h b/src/platform/web_app_window.h
+index 7381b0c..a90127f 100644
+--- a/src/platform/web_app_window.h
++++ b/src/platform/web_app_window.h
+@@ -20,8 +20,6 @@
+ #include <string>
+ #include <vector>
+
+-#include "webos/webapp_window_base.h"
+-
+ class WebAppWayland;
+
+ class WebAppWindow {
+@@ -32,12 +30,14 @@ class WebAppWindow {
+ virtual int DisplayWidth() = 0;
+ virtual int DisplayHeight() = 0;
+ virtual void InitWindow(int width, int height) = 0;
+- virtual void SetLocationHint(webos::WebAppWindowBase::LocationHint value) = 0;
++ virtual void SetLocationHint(const std::string& value) = 0;
+ virtual webos::NativeWindowState GetWindowHostState() const = 0;
++#if defined(OS_WEBOS)
+ virtual void CreateWindowGroup(
+ const webos::WindowGroupConfiguration& config) = 0;
+ virtual void AttachToWindowGroup(const std::string& name,
+ const std::string& layer) = 0;
++#endif
+ virtual bool IsKeyboardVisible() = 0;
+ virtual void SetKeyMask(webos::WebOSKeyMask key_mask) = 0;
+ virtual void SetKeyMask(webos::WebOSKeyMask key_mask, bool set) = 0;
+diff --git a/src/util/log_msg_id.h b/src/util/log_msg_id.h
+index 7d114cf..71d9621 100644
+--- a/src/util/log_msg_id.h
++++ b/src/util/log_msg_id.h
+@@ -150,6 +150,8 @@
+
+ #define MSGID_DL_ERROR "DL_ERROR" /** Dinamic load library error **/
+
++#define MSGID_ERROR_CANNOT_LOCK_SERVICE "MSGID_CANNOT_LOCK_SERVICE" /** Cannot lock the GRPC IPC lock **/
++
+ // clang-format on
+
+ #endif // LOGMSGID_H
+diff --git a/src/util/timer.h b/src/util/timer.h
+index 795a38c..6824fb8 100644
+--- a/src/util/timer.h
++++ b/src/util/timer.h
+@@ -30,12 +30,12 @@ class Timer {
+ : source_id_(0), is_running_(false), is_repeating_(is_repeating) {}
+ virtual ~Timer() {}
+
+- // Timer
+ virtual void HandleCallback() = 0;
+- virtual void Start(int delay_in_milli_seconds);
+
+ bool IsRunning() { return is_running_; }
+ bool IsRepeating() { return is_repeating_; }
++
++ void Start(int delay_in_milli_seconds);
+ void Stop();
+
+ protected:
+diff --git a/src/wam_main.cc b/src/wam_main.cc
+index 0c04a40..d559ee4 100644
+--- a/src/wam_main.cc
++++ b/src/wam_main.cc
+@@ -14,11 +14,9 @@
+ //
+ // SPDX-License-Identifier: Apache-2.0
+
+-#include <webos/app/webos_main.h>
+-
+ #include "web_runtime.h"
+
+-int main(int argc, const char** argv) {
++int main(int argc, char** argv) {
+ std::unique_ptr<WebRuntime> web_runtime(WebRuntime::Create());
+ return web_runtime->Run(argc, argv);
+ }
+diff --git a/src/webos/web_app_manager_service_luna.cc b/src/webos/web_app_manager_service_luna.cc
+index 627cf31..b69635f 100644
+--- a/src/webos/web_app_manager_service_luna.cc
++++ b/src/webos/web_app_manager_service_luna.cc
+@@ -632,6 +632,7 @@ void WebAppManagerServiceLuna::GetForegroundAppInfoCallback(
+ if (cleared_cache_)
+ cleared_cache_ = false;
+
++#if defined(OS_WEBOS)
+ if (reply["returnValue"] == true) {
+ if (reply.isMember("appId") && reply["appId"].isString()) {
+ std::string appId = reply["appId"].asString();
+@@ -639,6 +640,7 @@ void WebAppManagerServiceLuna::GetForegroundAppInfoCallback(
+ WebAppManagerService::IsEnyoApp(appId.c_str()));
+ }
+ }
++#endif
+ }
+
+ void WebAppManagerServiceLuna::BootdConnectCallback(const Json::Value& reply) {
+diff --git a/src/webos/web_runtime_webos.cc b/src/webos/web_runtime_webos.cc
+index 9dd2f72..cab7e5a 100644
+--- a/src/webos/web_runtime_webos.cc
++++ b/src/webos/web_runtime_webos.cc
+@@ -70,7 +70,7 @@ class WebOSMainDelegateWAM : public webos::WebOSMainDelegate {
+ void AboutToCreateContentBrowserClient() override { StartWebAppManager(); }
+ };
+
+-int WebRuntimeWebOS::Run(int argc, const char** argv) {
++int WebRuntimeWebOS::Run(int argc, char** argv) {
+ WebOSMainDelegateWAM delegate;
+ webos::WebOSMain webos_main(&delegate);
+ return webOSMain.Run(argc, argv);
+diff --git a/src/webos/web_runtime_webos.h b/src/webos/web_runtime_webos.h
+index eb52348..fa031a2 100644
+--- a/src/webos/web_runtime_webos.h
++++ b/src/webos/web_runtime_webos.h
+@@ -21,7 +21,7 @@
+
+ class WebRuntimeWebOS : public WebRuntime {
+ public:
+- int Run(int argc, const char** argv) override;
++ int Run(int argc, char** argv) override;
+ };
+
+ #endif // WEBOS_WEB_RUNTIME_WEBOS_H_
+--
+2.39.2
+