[wam][cef] Make it possible to use cef as a backend 49/29049/3
authorRoger Zanoni <rzanoni@igalia.com>
Fri, 23 Jun 2023 12:06:20 +0000 (14:06 +0200)
committerRoger Zanoni <rzanoni@igalia.com>
Tue, 18 Jul 2023 13:28:52 +0000 (15:28 +0200)
This changes enables switching between the chromium and cef backends on
wam by passing the agl-cef feature to the agl-setup script.

Bug-AGL: SPEC-3872

Signed-off-by: Roger Zanoni <rzanoni@igalia.com>
Change-Id: Ib5b4b139789ec8303fef0a210a7e8f2f724d0c00

recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch [new file with mode: 0644]
recipes-wam/wam/files/WebAppMgr-cef.env [new file with mode: 0644]
recipes-wam/wam/files/WebAppMgr.env
recipes-wam/wam/files/WebAppMgr.service
recipes-wam/wam/files/WebAppMgrCli [new file with mode: 0755]
recipes-wam/wam/wam-cef.inc [new file with mode: 0644]
recipes-wam/wam/wam.inc [new file with mode: 0644]
recipes-wam/wam/wam_git.bb

diff --git a/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch b/recipes-wam/wam/files/0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch
new file mode 100644 (file)
index 0000000..65c2e7c
--- /dev/null
@@ -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
+
diff --git a/recipes-wam/wam/files/WebAppMgr-cef.env b/recipes-wam/wam/files/WebAppMgr-cef.env
new file mode 100644 (file)
index 0000000..2aa604c
--- /dev/null
@@ -0,0 +1,82 @@
+# Set wam executable file path
+HOOK_SEGV=NO
+
+# Set wam name for user-agent
+WAM_NAME="WebAppManager"
+
+WAM_IS_CEF=true
+
+# Only allow UTF8 encoding for luna-service messages.
+LS_ENABLE_UTF8=1
+
+# suspending javascript execution delay for page visibility
+WAM_SUSPEND_DELAY_IN_MS=250
+
+WAM_DATA_PATH="/home/agl-driver/wamdata"
+
+# setup 50 Mb maximum for ApplicationCache
+WAM_APPCACHE_MAXSIZE=52428800
+
+# setup 10 Mb maximum for ApplicationCache per domain
+WAM_APPCACHE_DOMAINLIMIT=10485760
+
+# setup 50 Mb maximum for DiskCache
+WAM_DISKCACHE_MAXSIZE=52428800
+
+# setup 256 Kb maximum for resource buffer allocation
+WAM_RESOURCE_BUFFER_MAX_ALLOC_SIZE=262144
+
+# setup 1 Mb for resource buffer
+WAM_RESOURCE_BUFFER_SIZE=1048576
+
+# setup 200 seconds for watchdog timeout of render process
+WATCHDOG_RENDER_TIMEOUT=200
+
+# setup nubmer of raster threads to 1
+BLINK_NUM_RASTER_THREADS=2
+
+# use default tile width if not sed by recipe
+BLINK_NUM_RASTER_THREADS=1
+
+# setup 6 Mb maximum for the program GPU cache
+GPU_PROGRAM_CACHE_SIZE=6144
+
+# Set location of all NPAPI plugins
+NPAPI_PLUGIN_PATH=${HBBTV_PLUGIN_PATH}":"${NETCAST_PLUGIN_PATH}":"${PRIVILEGED_PLUGIN_PATH}
+
+# setup 8 Mb minimum codecache capacity
+JSC_minGlobalCodeCacheCapacity=8388608
+
+# Enable more explicit logging of timing with regards to rendering
+# export WAM2_ENABLE_DEBUG_RENDER_TIMING=1
+
+# enable Web Inspector and Tellurium if in developer mode
+TELLURIUM_NUB_PATH=/usr/palm/tellurium/telluriumnub.js
+ENABLE_INSPECTOR=1
+
+# Enable cursor by default
+ENABLE_CURSOR_BY_DEFAULT=1
+
+# Enable launch optimization
+ENABLE_LAUNCH_OPTIMIZATION=1
+
+# Set the duration(seconds) passed from last network activity (e.g. FMP Detector)
+# If set to a positive value, adjust a custom timeout for a network stable timer in FMPDetector
+NETWORK_STABLE_TIMEOUT=3
+
+LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/share/wam
+
+WAM_SWITCHES="\
+  --user-data-dir=\"$WAM_DATA_PATH\" \
+  --use-gl=egl \
+  --ozone-platform=wayland \
+  --no-sandbox \
+  --use-viz-fmp-with-timeout=0 \
+  --in-process-gpu \
+  --remote-debugging-port=9998 \
+  --webos-wam \
+  --agl-shell-appid=homescreen \
+  --disable-gpu-vsync \
+  --ignore-gpu-blocklist \
+  --ignore-gpu-blacklist \
+  --allow-file-access-from-files"
index c8ddc51..7737d78 100644 (file)
@@ -1,42 +1,18 @@
-##### AGL: not set in WebAppMgr@.service
-#XDG_SESSION_ID="c2"
-
-##### AGL: set by WebAppMgr@.service (depends on user)
-#XDG_RUNTIME_DIR="/run/user/%i"
-
 # Set wam executable file path
 HOOK_SEGV=NO
-WAM_EXE_PATH="/usr/bin/WebAppMgr"
 
 # Set wam name for user-agent
 WAM_NAME="WebAppManager"
 
+WAM_IS_CEF=false
+
 # Only allow UTF8 encoding for luna-service messages.
 LS_ENABLE_UTF8=1
 
-# Set effective userid and groupid
-#WAM_UID="wam"
-#WAM_GID="compositor"
-
-# Set location of error page (will follow localization rules based on this path)
-#WAM_ERROR_PAGE="file:///usr/share/localization/wam/loaderror.html"
-
 # suspending javascript execution delay for page visibility
 WAM_SUSPEND_DELAY_IN_MS=250
 
-#if [ -e "etc/wam/make_shm.sh" ] ; then
-#    /etc/wam/make_shm.sh
-#fi
-
-# Set user data directory for WebAppMgr
-##### AGL: set by WebAppMgr@.service (depends on user)
-#WAM_DATA_PATH="/home/%i/wamdata"
-
-# ensure that wam data directories exist
-#mkdir -p ${WAM_DATA_PATH}
-
-# set directories permission
-#chown ${WAM_UID}:${WAM_GID} ${WAM_DATA_PATH}
+WAM_DATA_PATH="/home/agl-driver/wamdata"
 
 # setup 50 Mb maximum for ApplicationCache
 WAM_APPCACHE_MAXSIZE=52428800
@@ -60,48 +36,13 @@ WATCHDOG_RENDER_TIMEOUT=200
 BLINK_NUM_RASTER_THREADS=2
 
 # use default tile width if not sed by recipe
-#if [ "$BLINK_NUM_RASTER_THREADS" = "WEBOS${BLINK_NUM_RASTER_THREADS#WEBOS}" ]; then
 BLINK_NUM_RASTER_THREADS=1
-#fi
 
 # setup 6 Mb maximum for the program GPU cache
 GPU_PROGRAM_CACHE_SIZE=6144
-
-# disable using enyo system app specfic optimization
-# currently used optimizations : inline caching off
-#USE_SYSTEM_APP_OPTIMIZATION="0"
-
-# Set location of NaCl modules
-#CHROMIUM_PATH="/usr/palm/applications/com.lge.app.chromium"
-#NACL_PLUGIN=${CHROMIUM_PATH}"/libppGoogleNaClPluginChrome.so"
-#NACL_IRT_LIBRARY=${CHROMIUM_PATH}"/nacl_irt_arm.nexe"
-#NACL_HELPER=${CHROMIUM_PATH}"/nacl_helper"
-#NACL_HELPER_BOOTSTRAP=${CHROMIUM_PATH}"/nacl_helper_bootstrap"
-
-# Set location of NPAPI plugins for all Apps including default Apps
-# This is for the flash plugin of Signage, webOS TV doesn't use it.
-#PRIVILEGED_PLUGIN_PATH=""
-
-# Set location of NPAPI plugins for NetCast Apps
-# NetCast Apps should access only the plugins in this path
-#NETCAST_PLUGIN_PATH="/usr/lib/BrowserPlugins"
-
-# Set location of NPAPI plugins for HbbTV app.
-#HBBTV_PLUGIN_PATH="/usr/lib/HbbtvPlugins"
-
-# Set InetTV player stored path
-#INETTV_HTML_PLAYER_PATH="/usr/share/inettv/inettv_player/index.html"
-
-# Set location of extra libraries
-#CDM_LIB_PATH="/usr/lib"
-
 # Set location of all NPAPI plugins
 NPAPI_PLUGIN_PATH=${HBBTV_PLUGIN_PATH}":"${NETCAST_PLUGIN_PATH}":"${PRIVILEGED_PLUGIN_PATH}
 
-#if [ -e "etc/wam/make_shm.sh" ] ; then
-#    /etc/wam/make_shm.sh
-#fi
-
 # setup 8 Mb minimum codecache capacity
 JSC_minGlobalCodeCacheCapacity=8388608
 
@@ -122,81 +63,16 @@ ENABLE_LAUNCH_OPTIMIZATION=1
 # If set to a positive value, adjust a custom timeout for a network stable timer in FMPDetector
 NETWORK_STABLE_TIMEOUT=3
 
-# please keep it in alphabetical order
-#WAM_EXTRA_FLAGS=""
-#WAM_JS_FLAGS=""
-#WAM_COMMON_SWITCHES=" \
-#    --application-cache-domain-limit=$WAM_APPCACHE_DOMAINLIMIT \
-#    --application-cache-size=$WAM_APPCACHE_MAXSIZE \
-#    --browser-subprocess-path=$WAM_EXE_PATH \
-#    --disable-direct-npapi-requests \
-#    --disable-extensions \
-#    --disable-low-res-tiling \
-#    --disable-new-video-renderer \
-#    --disk-cache-size=$WAM_DISKCACHE_MAXSIZE \
-#    --enable-aggressive-release-policy \
-#    --enable-accelerated-plugin-rendering \
-#    --accelerated-plugin-rendering-blacklist=device;drmAgent;sound;service \
-#    --enable-gpu-rasterization \
-#    --disable-gpu-rasterization-for-first-frame \
-#    --enable-key-event-throttling \
-#    --enable-threaded-compositing \
-#    --enable-watchdog \
-#    --hide-selection-handles \
-#    --ignore-gpu-blacklist \
-#    --ignore-netif=p2p \
-#    --in-process-gpu \
-#    --max-unused-resource-memory-usage-percentage=0 \
-#    --network-stable-timeout=$NETWORK_STABLE_TIMEOUT \
-#    --noerrdialogs \
-#    --num-raster-threads=$BLINK_NUM_RASTER_THREADS \
-#    --ozone-platform=wayland \
-#    --remote-debugging-port=9998 \
-#    --resource-buffer-max-allocation-size=$WAM_RESOURCE_BUFFER_MAX_ALLOC_SIZE \
-#    --resource-buffer-size=$WAM_RESOURCE_BUFFER_SIZE \
-#    --touch-events=disabled \
-#    --ui-disable-opaque-shader-program \
-#    --user-agent-suffix=SmartTV \
-#    --user-data-dir=$WAM_DATA_PATH \
-#    --enable-devtools-experiments \
-#    --webos-wam \ "
-
-#WAM_LITE_SWITCHES=" --in-process-zygote "
-
-#export WAM_WEBOS_LITE=NO
-#if [ "${WAM_WEBOS_LITE}" = "YES" ] ; then
-#    export WAM_SWITCHES=${WAM_COMMON_SWITCHES}${WAM_LITE_SWITCHES}
-#    export SKIA_FONT_CACHE_SIZE=1
-#    export SKIA_IMAGE_CACHE_SIZE=40
-#    export SKIA_BACKGROUND_FONT_CACHE_SIZE=0
-#else
-#    export WAM_SWITCHES=${WAM_COMMON_SWITCHES}
-#    export SKIA_FONT_CACHE_SIZE=8
-#    export SKIA_IMAGE_CACHE_SIZE=80
-#    export SKIA_BACKGROUND_FONT_CACHE_SIZE=512
-#fi
-
-#export WAM_EXTRA_SKIA_CACHE_SWITCHES=" \
-#    --skia-font-cache-size-mb=$SKIA_FONT_CACHE_SIZE \
-#    --skia-image-cache-size-mb=$SKIA_IMAGE_CACHE_SIZE \
-#    --skia-background-font-cache-size-kb=$SKIA_BACKGROUND_FONT_CACHE_SIZE \
-#    "
-
-#export WAM_EXTRA_GPU_TUNING_SWITCHES=" \
-#    --gpu-program-cache-size-kb=$GPU_PROGRAM_CACHE_SIZE \
-#    "
-#export WAM_WATCHDOG_RENDER_TIMEOUT_SWITCHES=" \
-#    --watchdog-render-timeout=$WATCHDOG_RENDER_TIMEOUT \
-#    "
-
-#WEBOS_LOAD_ACCESSIBILITY_PLUGIN=1
-
-#WAM_V8_CODE_CACHE_SWITCHES=" --enable-local-resource-code-cache --disallow-code-cache-from-file-uris-with-query-string "
-
-# Load any special configuration from plugins
-#if [ -e "/etc/wam/plugins/conf.sh" ] ; then
-#    . /etc/wam/plugins/conf.sh || true
-#fi
-
-#exec $WAM_EXE_PATH $WAM_SWITCHES $WAM_EXTRA_SKIA_CACHE_SWITCHES $WAM_EXTRA_GPU_TUNING_SWITCHES $WAM_WATCHDOG_RENDER_TIMEOUT_SWITCHES $WAM_EXTRA_FLAGS $WAM_V8_CODE_CACHE_SWITCHES --js-flags="$WAM_JS_FLAGS"
-
+WAM_SWITCHES="\
+  --user-data-dir=\"$WAM_DATA_PATH\" \
+  --use-gl=egl \
+  --enable-features=UseOzonePlatform \
+  --ozone-platform=wayland \
+  --no-sandbox \
+  --use-viz-fmp-with-timeout=0 \
+  --in-process-gpu \
+  --remote-debugging-port=9998 \
+  --webos-wam \
+  --agl-shell-appid=homescreen \
+  --disable-gpu-vsync \
+  --ignore-gpu-blocklist"
index ca9a478..ed15040 100644 (file)
@@ -23,7 +23,7 @@ UMask=0077
 OOMScoreAdjust=-1000
 EnvironmentFile=-/etc/default/WebAppMgr.env
 Environment=XDG_RUNTIME_DIR=/run/user/1001/
-ExecStart=/usr/bin/WebAppMgr --use-gl=egl --enable-features=UseOzonePlatform --ozone-platform=wayland --no-sandbox --use-viz-fmp-with-timeout=0 --in-process-gpu --remote-debugging-port=9998 --user-data-dir="/home/%u/wamdata" --webos-wam --agl-shell-appid=homescreen --disable-gpu-vsync --ignore-gpu-blocklist
+ExecStart=@WAM_EXE_DIR@/WebAppMgr $WAM_SWITCHES
 ExecStop=pkill -U %U WebAppMgr
 Restart=on-failure
 RestartSec=50
diff --git a/recipes-wam/wam/files/WebAppMgrCli b/recipes-wam/wam/files/WebAppMgrCli
new file mode 100755 (executable)
index 0000000..4312e04
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+WAM_CLI_EXE="/usr/bin/WebAppMgr"
+
+if [ "$WAM_IS_CEF" = true ] ; then
+  WAM_CLI_EXE="/usr/share/wam/WebAppMgrCli"
+fi
+
+exec $WAM_CLI_EXE $@
diff --git a/recipes-wam/wam/wam-cef.inc b/recipes-wam/wam/wam-cef.inc
new file mode 100644 (file)
index 0000000..f0fdcd3
--- /dev/null
@@ -0,0 +1,40 @@
+SRC_URI:append = " file://0001-agl-cef-Snapshot-with-all-patches-needed-to-enable-w.patch"
+
+do_configure[depends] += "cef:do_populate_sysroot"
+
+CEF_ROOT = "${STAGING_DATADIR}/cef"
+WAM_OUT_DIR = "${datadir}/wam"
+
+RUNTIME = "llvm"
+TOOLCHAIN = "clang"
+
+DEPENDS:append:toolchain-clang = " clang-cross-${TARGET_ARCH}"
+
+PREFERRED_PROVIDER_libgcc = "compiler-rt"
+
+COMPATIBLE_MACHINE = "(-)"
+COMPATIBLE_MACHINE:aarch64 = "(.*)"
+COMPATIBLE_MACHINE:armv7 = "(.*)"
+COMPATIBLE_MACHINE:armv7a = "(.*)"
+COMPATIBLE_MACHINE:armv7ve = "(.*)"
+COMPATIBLE_MACHINE:x86 = "(.*)"
+COMPATIBLE_MACHINE:x86-64 = "(.*)"
+
+EXTRA_OECMAKE = "\
+    -DPROJECT_ARCH=${TUNE_ARCH} \
+    -DCMAKE_BUILD_TYPE=Release \
+    -DCMAKE_INSTALL_PREFIX=${prefix} \
+    -DPLATFORM_NAME=${@'${DISTRO}'.upper().replace('-', '_')} \
+    -DCEF_ROOT=${CEF_ROOT} \
+    -DCMAKE_INSTALL_BINDIR=${WAM_OUT_DIR}"
+
+do_install:append() {
+    cp -R --no-dereference --preserve=mode,links -v ${CEF_ROOT}/Release/* ${D}${WAM_OUT_DIR}
+    cp -R --no-dereference --preserve=mode,links -v ${CEF_ROOT}/Resources/* ${D}${WAM_OUT_DIR}
+    install -v -D -m 644 ${WORKDIR}/WebAppMgr-cef.env ${D}${sysconfdir}/default/WebAppMgr.env
+    sed -i -e 's#@WAM_EXE_DIR@#${WAM_OUT_DIR}#g' ${D}${systemd_system_unitdir}/WebAppMgr.service
+}
+
+INSANE_SKIP:${PN} += "already-stripped file-rdeps libdir"
+
+FILES:${PN} += "${WAM_OUT_DIR}"
diff --git a/recipes-wam/wam/wam.inc b/recipes-wam/wam/wam.inc
new file mode 100644 (file)
index 0000000..f78fcfe
--- /dev/null
@@ -0,0 +1,12 @@
+DEPENDS += " chromium "
+
+EXTRA_OECMAKE = "\
+    -DCMAKE_BUILD_TYPE=Release \
+    -DCMAKE_INSTALL_PREFIX=${prefix} \
+    -DPLATFORM_NAME=${@'${DISTRO}'.upper().replace('-', '_')} \
+    -DCHROMIUM_SRC_DIR=${STAGING_INCDIR}/chromium"
+
+do_install:append() {
+    install -v -D -m 644 ${WORKDIR}/WebAppMgr.env ${D}${sysconfdir}/default/WebAppMgr.env
+    sed -i -e 's#@WAM_EXE_DIR@#${bindir}#g' ${D}${systemd_system_unitdir}/WebAppMgr.service
+}
index 0f2087c..570c6f8 100644 (file)
@@ -3,13 +3,16 @@ AUTHOR = "Jani Hautakangas <jani.hautakangas@lge.com>"
 LICENSE = "Apache-2.0"
 LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
 
-DEPENDS = "glib-2.0 jsoncpp boost chromium protobuf protobuf-native grpc grpc-native"
+DEPENDS = "glib-2.0 jsoncpp boost protobuf protobuf-native grpc grpc-native"
 
 SRC_URI = "\
     git://github.com/igalia/${BPN}.git;branch=@58.agl;protocol=https \
+    file://WebAppMgrCli \
     file://WebAppMgr.service \
     file://WebAppMgr.env \
+    file://WebAppMgr-cef.env \
 "
+
 SRCREV = "4fbd6e648913bcf0fba63e4460eb44242c11f71b"
 
 PV = "ose58.agl"
@@ -18,12 +21,6 @@ S = "${WORKDIR}/git"
 
 inherit cmake pkgconfig systemd
 
-EXTRA_OECMAKE = "\
-    -DCMAKE_BUILD_TYPE=Release \
-    -DCMAKE_INSTALL_PREFIX=${prefix} \
-    -DPLATFORM_NAME=${@'${DISTRO}'.upper().replace('-', '_')} \
-    -DCHROMIUM_SRC_DIR=${STAGING_INCDIR}/chromium"
-
 # Disable some of security flags
 # Disable D_FORTIFY_SOURCE=2 and -fstack-protector-strong
 # Refer conf/distro/include/security_flags.inc in meta-webos/conf/distro/include/webos.inc
@@ -36,8 +33,7 @@ do_install:append() {
     install -v -d ${D}${sysconfdir}/wam
     install -v -m 644 ${S}/files/launch/security_policy.conf ${D}${sysconfdir}/wam/security_policy.conf
     install -v -D -m 644 ${WORKDIR}/WebAppMgr.service ${D}${systemd_system_unitdir}/WebAppMgr.service
-    install -v -D -m 644 ${WORKDIR}/WebAppMgr.env ${D}${sysconfdir}/default/WebAppMgr.env
-    ln -snf WebAppMgr ${D}${bindir}/web-runtime
+    install -v -D -m 755 ${WORKDIR}/WebAppMgrCli ${D}${bindir}/WebAppMgrCli
 }
 
 CXXFLAGS:append:agl-devel = " -DAGL_DEVEL"
@@ -49,8 +45,14 @@ do_install:append:agl-devel() {
     touch ${D}${localstatedir}/agl-devel/preferences/devmode_enabled
 }
 
-FILES:${PN} += "${sysconfdir}/init ${sysconfdir}/wam ${libdir}/webappmanager/plugins/*.so"
+require ${@bb.utils.contains('AGL_FEATURES', 'agl-cef', 'wam-cef.inc', 'wam.inc', d)}
+
+FILES:${PN} += "${sysconfdir}/init \
+                ${sysconfdir}/wam \
+                ${bindir} \
+                ${libdir}/webappmanager/plugins/*.so"
+
+RDEPENDS:${PN} += " bash"
 
 PROVIDES += "virtual/webruntime"
 RPROVIDES:${PN} += "virtual/webruntime"
-